Files

420 lines
13 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using AibisDream.Framework;
using UnityEngine;
using UnityEngine.ResourceManagement.AsyncOperations;
namespace AibisDream
{
public class AnimatorHandler : MonoBehaviour
{
private const string AnimationPrefix = "Animation/";
public string animatorName;
[SerializeField] private Animator animator;
[Tooltip("若当前状态为 Loop:勾选则播完一整轮后触发完成回调;不勾选则循环状态不会结束(异步会一直等待)")]
[SerializeField]
private bool completeAfterOneCycleWhenStateLoops = true;
private float _speedBeforePause = 1f;
private AsyncOperationHandle<RuntimeAnimatorController> _addressableHandle;
private RuntimeAnimatorController _savedRuntimeAnimatorController;
private Coroutine _playCompletionCoroutine;
private Action _onCompleteCallback;
#if UNITY_EDITOR
private void OnValidate()
{
ValidateSerializedAnimator();
}
#endif
private void Start()
{
AnimatorCenter.Instance.RegisterHandler(this);
}
private void OnDestroy()
{
AnimatorCenter.Instance.UnregisterHandler(this);
CancelCompletionCoroutine();
if (animator != null)
animator.speed = _speedBeforePause > 0f ? _speedBeforePause : 1f;
ReleaseAddressableHandle();
}
private void ValidateSerializedAnimator()
{
if (animator != null && animator.transform == transform)
animator = null;
}
private bool EnsureAnimator()
{
if (animator != null)
return true;
Debug.LogError($"AnimatorHandler {animatorName}: 未在子物体上找到 Animator。", this);
return false;
}
private void CancelCompletionCoroutine()
{
if (_playCompletionCoroutine == null)
return;
StopCoroutine(_playCompletionCoroutine);
_playCompletionCoroutine = null;
}
private void ReleaseAddressableHandle()
{
if (!_addressableHandle.IsValid())
return;
if (animator != null && animator.runtimeAnimatorController == _addressableHandle.Result)
animator.runtimeAnimatorController = _savedRuntimeAnimatorController;
ResourceKit.Release(_addressableHandle);
_addressableHandle = default;
_savedRuntimeAnimatorController = null;
}
private void EnsurePlayingSpeedForNewClip()
{
if (animator != null && animator.speed < 0.001f)
animator.speed = _speedBeforePause > 0f ? _speedBeforePause : 1f;
}
/// <summary>
/// 对挂载 <see cref="Animator"/> 的 <see cref="GameObject"/> 调用 <see cref="GameObject.SetActive"/>
/// 不是修改 <see cref="Animator.enabled"/>。Handler 根物体可保持激活以便协程与注册。
/// </summary>
public void SetAnimatorObjectActive(bool active)
{
if (animator == null)
return;
animator.gameObject.SetActive(active);
}
private void ActivateAnimatorGameObject()
{
SetAnimatorObjectActive(true);
}
/// <summary>
/// 使用当前 Animator 上已绑定的控制器播放指定状态。
/// </summary>
public void Play(string stateName, int layer = 0, Action onComplete = null)
{
if (!EnsureAnimator())
return;
ActivateAnimatorGameObject();
CancelCompletionCoroutine();
ReleaseAddressableHandle();
_onCompleteCallback = onComplete;
EnsurePlayingSpeedForNewClip();
animator.Play(stateName, layer, 0f);
animator.Update(0f);
if (onComplete != null)
_playCompletionCoroutine = StartCoroutine(WaitForStatePlaybackEnd(layer, stateName, true));
}
public IEnumerator PlayAsync(string stateName, int layer = 0)
{
if (!EnsureAnimator())
yield break;
ActivateAnimatorGameObject();
CancelCompletionCoroutine();
ReleaseAddressableHandle();
EnsurePlayingSpeedForNewClip();
animator.Play(stateName, layer, 0f);
animator.Update(0f);
yield return WaitForStatePlaybackEnd(layer, stateName, false);
}
private void OnPlaybackComplete()
{
_playCompletionCoroutine = null;
var completionCallback = _onCompleteCallback;
_onCompleteCallback = null;
ReleaseAddressableHandle();
completionCallback?.Invoke();
}
/// <param name="invokeCompletionCallback">为 true 时在结束时调用 <see cref="OnPlaybackComplete"/>(含 Addressable 释放与 Action);为 false 时仅做 Addressable 释放(用于无回调异步)</param>
private IEnumerator WaitForStatePlaybackEnd(int layer, string stateName, bool invokeCompletionCallback)
{
var timeout = 10f;
while (timeout > 0f)
{
var currentStateInfo = animator.GetCurrentAnimatorStateInfo(layer);
if (currentStateInfo.IsName(stateName) && !animator.IsInTransition(layer))
break;
timeout -= Time.deltaTime;
yield return null;
}
while (true)
{
var currentStateInfo = animator.GetCurrentAnimatorStateInfo(layer);
if (!currentStateInfo.IsName(stateName))
{
if (invokeCompletionCallback)
OnPlaybackComplete();
else
ReleaseAddressableHandle();
yield break;
}
if (animator.IsInTransition(layer))
{
yield return null;
continue;
}
if (currentStateInfo.loop)
{
if (completeAfterOneCycleWhenStateLoops && currentStateInfo.normalizedTime >= 1f)
{
if (invokeCompletionCallback)
OnPlaybackComplete();
else
ReleaseAddressableHandle();
yield break;
}
}
else if (currentStateInfo.normalizedTime >= 1f)
{
if (invokeCompletionCallback)
OnPlaybackComplete();
else
ReleaseAddressableHandle();
yield break;
}
yield return null;
}
}
/// <summary>
/// 从 Addressables 加载 RuntimeAnimatorController(键为 Animation/ + addressableKey)并播放状态。
/// </summary>
public void PlayFromAddressable(string addressableKey, string stateName, int layer = 0, Action onComplete = null)
{
StartCoroutine(PlayFromAddressableCoroutine(addressableKey, stateName, layer, onComplete));
}
public IEnumerator PlayFromAddressableAsync(string addressableKey, string stateName, int layer = 0)
{
yield return PlayFromAddressableCoroutine(addressableKey, stateName, layer, null);
}
private IEnumerator PlayFromAddressableCoroutine(string addressableKey, string stateName, int layer,
Action onComplete)
{
if (!EnsureAnimator())
yield break;
ActivateAnimatorGameObject();
if (string.IsNullOrEmpty(addressableKey))
{
Debug.LogError($"AnimatorHandler {animatorName}: addressableKey 为空。", this);
yield break;
}
ReleaseAddressableHandle();
var loadKey = AnimationPrefix + addressableKey;
RuntimeAnimatorController loaded = null;
AsyncOperationHandle<RuntimeAnimatorController> loadedHandle = default;
var loadDone = false;
ResourceKit.LoadAssetAsyncWithHandle<RuntimeAnimatorController>(loadKey, loadHandle =>
{
loadedHandle = loadHandle;
if (loadHandle.Status == AsyncOperationStatus.Succeeded)
loaded = loadHandle.Result;
loadDone = true;
});
yield return new WaitUntil(() => loadDone);
if (loaded == null)
{
Debug.LogError($"AnimatorHandler {animatorName}: 加载失败 '{loadKey}'", this);
yield break;
}
CancelCompletionCoroutine();
_savedRuntimeAnimatorController = animator.runtimeAnimatorController;
animator.runtimeAnimatorController = loaded;
animator.Rebind();
animator.Update(0f);
_addressableHandle = loadedHandle;
EnsurePlayingSpeedForNewClip();
_onCompleteCallback = onComplete;
animator.Play(stateName, layer, 0f);
animator.Update(0f);
if (onComplete != null)
{
_playCompletionCoroutine = StartCoroutine(WaitForStatePlaybackEnd(layer, stateName, true));
while (_playCompletionCoroutine != null)
yield return null;
}
else
{
yield return WaitForStatePlaybackEnd(layer, stateName, false);
}
}
public void Stop()
{
if (!EnsureAnimator())
return;
CancelCompletionCoroutine();
_onCompleteCallback = null;
animator.speed = 0f;
ReleaseAddressableHandle();
}
public void Pause()
{
if (!EnsureAnimator())
return;
if (animator.speed > 0f)
{
_speedBeforePause = animator.speed;
animator.speed = 0f;
}
}
public void Resume()
{
if (!EnsureAnimator())
return;
if (animator.speed == 0f)
animator.speed = _speedBeforePause > 0f ? _speedBeforePause : 1f;
}
public void Reset()
{
if (!EnsureAnimator())
return;
CancelCompletionCoroutine();
_onCompleteCallback = null;
animator.speed = 1f;
animator.Rebind();
animator.Update(0f);
ReleaseAddressableHandle();
}
/// <summary>
/// 将 Animator 状态机恢复到当前 <see cref="RuntimeAnimatorController"/> 的默认入口状态(参数与层会按控制器重置),
/// 不释放 Addressable、不还原已替换的运行时控制器。用于在保留当前加载的控制器前提下清掉过渡与参数。
/// </summary>
public void ReturnToDefaultState()
{
if (!EnsureAnimator())
return;
CancelCompletionCoroutine();
_onCompleteCallback = null;
EnsurePlayingSpeedForNewClip();
animator.Rebind();
animator.Update(0f);
}
/// <summary>
/// 当前层当前状态下,已播放时长(秒)。过渡或非目标状态时语义以当前 StateInfo 为准。
/// </summary>
public float GetPlaybackTime(int layer = 0)
{
if (animator == null)
return 0f;
var currentStateInfo = animator.GetCurrentAnimatorStateInfo(layer);
return currentStateInfo.normalizedTime * currentStateInfo.length;
}
/// <summary>
/// 当前层当前状态片段长度(秒)。
/// </summary>
public float GetDuration(int layer = 0)
{
if (animator == null)
return 0f;
return animator.GetCurrentAnimatorStateInfo(layer).length;
}
public void SetPlaybackSpeed(float speed)
{
if (!EnsureAnimator())
return;
animator.speed = speed;
if (speed > 0f)
_speedBeforePause = speed;
}
/// <summary>
/// 在当前层下,将播放位置 seek 到指定时间(秒),相对当前状态片段长度归一化后播放。
/// </summary>
public void SeekToTime(double timeSeconds, int layer = 0)
{
if (!EnsureAnimator())
return;
var currentStateInfo = animator.GetCurrentAnimatorStateInfo(layer);
var stateLengthSeconds = currentStateInfo.length;
if (stateLengthSeconds <= 0.0001f)
return;
var normalizedPlaybackTime = Mathf.Clamp01((float)(timeSeconds / stateLengthSeconds));
animator.Play(currentStateInfo.shortNameHash, layer, normalizedPlaybackTime);
animator.Update(0f);
}
}
}