前言:我们无法监听Animator是否播放完一个动画,我所知的办法就是将监听方法设置为Public,并且挂在带有Animator的物体上,并且还要在Clip文件内新增AnimEvent。于是我自己写了一个AnimCtrler。
需求:开始动画、暂停动画、将动画停止在某一个状态、动画播放完成后执行回调。
思路:
1.播放动画就用Animator自带的API,Animator.Play即可;
2.停止动画,我想要Animator.speed为0达到效果;
3.停止在某一个状态,我们可以先播动画,播放在第一帧时马上暂停;
4.完成回调,使用AnimatorStateInfo.normalizedTime是否>=1f判定
实现:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; [RequireComponent(typeof(Animator))] public class AnimCtrler : MonoBehaviour { private Animator m_animator; private float m_speed; private string m_curAnimName; private bool m_clipEndAutoSetNull; private Dictionary<string, Action<string>> clipEndCallbacks; public float Speed { get => m_speed; set { m_speed = value; if (m_animator != null) { m_animator.speed = m_speed; } } } public string CurAnimName { get => m_curAnimName; } /// <summary> /// 回调执行后,是否会自动设置为空 /// </summary> public bool ClipEndAutoSetNull { set => m_clipEndAutoSetNull = value; } private void Awake() { m_animator = GetComponent<Animator>(); m_speed = 1f; m_animator.speed = m_speed; m_curAnimName = string.Empty; m_clipEndAutoSetNull = true; clipEndCallbacks = new Dictionary<string, Action<string>>(); } public void Play(string animName, float speed = 1) { if (string.IsNullOrEmpty(animName)) return; Speed = speed; m_animator.Play(animName, 0, 0); m_curAnimName = animName; } public void Stop() { Speed = 0f; } /// <summary> /// 将动画定格在这一瞬间 /// </summary> /// <param name="animName"></param> /// <param name="progress"></param> public void SetAnimStartPose(string animName, float progress) { if (string.IsNullOrEmpty(animName)) return; m_animator.Play(animName, 0, progress); m_animator.Update(0); Speed = 0f; } /// <summary> /// 设置动画结束的回调 /// </summary> /// <param name="animName"></param> /// <param name="endCallback"></param> public void SetAnimEndCallback(string animName, Action<string> endCallback) { if (string.IsNullOrEmpty(animName)) return; if (clipEndCallbacks.ContainsKey(animName)) { clipEndCallbacks[animName] = endCallback; } else { clipEndCallbacks.Add(animName, endCallback); } } private void Update() { if (!string.IsNullOrEmpty(m_curAnimName)) { AnimatorStateInfo info = m_animator.GetCurrentAnimatorStateInfo(0); if (info.IsName(m_curAnimName) && info.normalizedTime >= 1f)//如果动画为loop模式,则此代码会有bug { Action<string> callback = null; clipEndCallbacks.TryGetValue(m_curAnimName, out callback); callback?.Invoke(m_curAnimName); if (m_clipEndAutoSetNull) { clipEndCallbacks[m_curAnimName] = null; } } } } }
测试:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Test : MonoBehaviour { public AnimCtrler ctrler; public void Update() { if (Input.GetMouseButtonDown(0)) { ctrler.ClipEndAutoSetNull = true; ctrler.SetAnimEndCallback("Move", (name) => Debug.Log(name)); ctrler.Play("Move"); } else if (Input.GetMouseButtonDown(1)) { ctrler.SetAnimStartPose("Exit", 1); } } }