监听AnimationClip的开始和结束
# 实现要点:Unity的动画不管是Animation(legncy)或Animator都没有提供监听动画开始或结束的接口,不过他的AnimationClip可以在某一帧上添加event,event触发时可以调用某个函数,
这边就是利用了这个功能,来实现监听的。监听开始就是第1帧加个事件,监听结束就是在最后一帧加个事件(使用AnimationClip.AddEvent函数)。
public class AnimClipTimePositionListeners : MonoBehaviour { public class TimePositionEvent : UnityEvent<AnimationEvent> { } const int Time_Position_Scale = 100000; struct ListenersKey { public AnimationClip _clip; // public int _timePosition; //监听某个clip的不同时间 public ListenersKey(AnimationClip clip, float timePosition) { _clip = clip; _timePosition = (int)(timePosition * Time_Position_Scale); } } Dictionary<ListenersKey, TimePositionEvent> _listenersDict = new Dictionary<ListenersKey, TimePositionEvent>(); public void OnEvent(AnimationEvent evt) { //Debug.Log($"OnEvent: AnimationEvent"); var clip = evt.animatorClipInfo.clip; var timePosition = evt.time; var key = new ListenersKey(clip, timePosition); if (_listenersDict.TryGetValue(key, out var tpe)) { tpe.Invoke(evt); } } public void AddListener(AnimationClip clip, float timePosition, UnityAction<AnimationEvent> l) { if (timePosition < 0) Debug.LogError($"timePos < 0: {timePosition}"); var key = new ListenersKey(clip, timePosition); if (!_listenersDict.TryGetValue(key, out var tpe)) { tpe = new TimePositionEvent(); _listenersDict.Add(key, tpe); } tpe.AddListener(l); } public void RemoveListener(AnimationClip clip, float timePosition, UnityAction<AnimationEvent> l) { if (timePosition < 0) Debug.LogError($"timePos < 0: {timePosition}"); var key = new ListenersKey(clip, timePosition); if (_listenersDict.TryGetValue(key, out var tpe)) { tpe.RemoveListener(l); } } public void RemoveAllListeners(AnimationClip clip, float timePosition) { var key = new ListenersKey(clip, timePosition); if (_listenersDict.TryGetValue(key, out var tpe)) { tpe.RemoveAllListeners(); } } public static bool TryGetAnimatorClip(Animator animator, string clipName, out AnimationClip outClip) { var clips = animator.runtimeAnimatorController.animationClips; for (var i = 0; i < clips.Length; ++i) { var clip = clips[i]; if (clip.name == clipName) { outClip = clip; return true; } } outClip = null; return false; } public static bool TryGetAnimatorClip(AnimatorClipInfo[] clipInfo, string clipName, out AnimationClip outClip) { for (var i = 0; i < clipInfo.Length; ++i) { var clip = clipInfo[i].clip; if (clip.name == clipName) { outClip = clip; return true; } } outClip = null; return false; } static bool IsTimePositionEquals(float t1, float t2) { return ((int)t1 * Time_Position_Scale) == ((int)t2 * Time_Position_Scale); } public static bool TryGetClipAnimationEvent(AnimationClip clip, float timePosition, out AnimationEvent outEvt) { for (var i = 0; i < clip.events.Length; ++i) { var evt = clip.events[i]; if ("OnEvent" == evt.functionName) { if (IsTimePositionEquals(evt.time, timePosition)) { outEvt = evt; return true; } } } outEvt = null; return false; } private bool TryGetAnimationEvent(int layerIndex, string clipName, ref float timePosition, out AnimationClip clip, out AnimationEvent evt) { var animator = GetComponent<Animator>(); var clipInfo = animator.GetCurrentAnimatorClipInfo(layerIndex); if (TryGetAnimatorClip(clipInfo, clipName, out clip)) { if (timePosition < 0) timePosition = clip.length; if (!TryGetClipAnimationEvent(clip, timePosition, out evt)) { evt = new AnimationEvent(); evt.functionName = "OnEvent"; evt.time = timePosition; clip.AddEvent(evt); } return true; } clip = null; evt = null; return false; } public void AddListener(int layerIndex, string clipName, float timePosition, UnityAction<AnimationEvent> l) { if (TryGetAnimationEvent(layerIndex, clipName, ref timePosition, out var clip, out var evt)) { AddListener(clip, timePosition, l); } else { Debug.LogError($"clip not exist: layer:{layerIndex}, name:{clipName}"); } } public void RemoveListener(int layerIndex, string clipName, float timePosition, UnityAction<AnimationEvent> l) { if (TryGetAnimationEvent(layerIndex, clipName, ref timePosition, out var clip, out var evt)) { RemoveListener(clip, timePosition, l); } else { Debug.LogError($"clip not exist: layer:{layerIndex}, name:{clipName}"); } } public void RemoveAllListeners(int layerIndex, string clipName, float timePosition) { if (TryGetAnimationEvent(layerIndex, clipName, ref timePosition, out var clip, out var evt)) { RemoveAllListeners(clip, timePosition); } else { Debug.LogError($"clip not exist: layer:{layerIndex}, name:{clipName}"); } } }
# 注册监听
public class Test : MonoBehaviour { public AnimClipTimePositionListeners listeners; void Start() { listeners.AddListener(0, "New Animation", 0, (evt) => { Debug.Log($"start"); }); listeners.AddListener(0, "New Animation", -1, (evt) => { Debug.Log($"end"); }); } }