为Unity3D的Animator动画状态增加播放完毕回调

利用AnimatorStateInfo信息来处理当前的动画信息。

目前没出现什么bug,回调也相对精准。

使用方式:

Animator.SetTrigger("shoot", delegate() { });
Animator.SetBool("enable", true, delegate() { });
Animator.Play("shoot",delegate() { });

 源码:实现方式1

using System.Collections.Generic;
using UnityEngine;

namespace KT.Core
{ 
    /// <summary>
    /// Animator 播放动画回调相关
    /// </summary>
    public class AnimatorPlayCall : MonoBehaviour
    {
        List<AnimatorPlayCallParam> Pools = new List<AnimatorPlayCallParam>();

        static AnimatorPlayCall instance;
        public static AnimatorPlayCall I
        {
            get
            {
                if (instance == null)
                {
                    instance = new GameObject("AnimatorPlayCall").AddComponent<AnimatorPlayCall>();
                    GameObject.DontDestroyOnLoad(instance);
                }
                return instance;
            }
        }

        /// <summary>
        /// 检测Animator是否播放完毕
        /// </summary>
        /// <param name="animator"></param>
        /// <param name="name"></param>
        /// <param name="action"></param>
        public void OnIsPlayOver(Animator animator,bool isCurrent, System.Action action)
        {
            if (action == null)
                return;
            Pools.Add(new AnimatorPlayCallParam(animator, isCurrent, action));
        }

        void Update()
        {
            if (Pools.Count > 0)
            {
                for (int i = 0; i < Pools.Count; i++)
                {
                    if (Pools[i].Check())
                    {
                        Pools.Remove(Pools[i]);
                    }
                }
            }
        }

        /// <summary>
        /// 播放回调参数
        /// </summary>
        class AnimatorPlayCallParam
        {
            public AnimatorPlayCallParam(Animator animator, bool isCurrent, System.Action call)
            {
                Layer = 0;
                Action = call;
                Animator = animator;
                IsCurrent = isCurrent;
            }

            public AnimatorPlayCallParam(Animator animator, bool isCurrent,int layer, System.Action call)
            {
                Layer = layer;
                Action = call;
                Animator = animator;
                IsCurrent = isCurrent;
            }

            /// <summary>
            /// 层
            /// </summary>
            public int Layer { private set; get; }

            /// <summary>
            /// 回调行为
            /// </summary>
            public System.Action Action { private set; get; }

            /// <summary>
            /// Animator 动画
            /// </summary>
            public Animator Animator { private set; get; }

            /// <summary>
            /// 是否当前动画
            /// </summary>
            public bool IsCurrent { private set; get; }

            /// <summary>
            /// 缓存的NameHash
            /// </summary>
            private int shortNameHash=0;

            private AnimatorStateInfo CurInfo
            {
                get
                {
                    return Animator.GetCurrentAnimatorStateInfo(Layer);
                }
            }

            private AnimatorStateInfo NextInfo
            {
                get
                {
                    return Animator.GetNextAnimatorStateInfo(Layer);
                }
            }

            /// <summary>
            /// 每帧检测动画状态
            /// </summary>
            /// <returns></returns>
            public bool Check()
            {
                if (Action == null)
                    return true;
                if(IsCurrent)
                {                    
                    if(shortNameHash == 0 && CurInfo.shortNameHash != 0)
                    {
                        shortNameHash = CurInfo.shortNameHash;
                    }
                }
                else
                {
                    if (shortNameHash == 0 && NextInfo.shortNameHash != 0)
                    {
                        shortNameHash = NextInfo.shortNameHash;
                    }
                }
                if (CurInfo.shortNameHash != shortNameHash && shortNameHash != NextInfo.shortNameHash)
                {
                    Action?.Invoke();
                    return true;
                }
                return false;
            }
        }
    }

    /// <summary>
    /// Animator扩展
    /// </summary>
    public static class AnimatorExtension
    {
        public static void SetTrigger(this Animator animater, string name, System.Action action)
        {
            animater.SetTrigger(name);
            AnimatorPlayCall.I.OnIsPlayOver(animater,false, action);
        }

        public static void SetBool(this Animator animater, string name, bool value, System.Action action)
        {
            animater.SetBool(name, value);
            AnimatorPlayCall.I.OnIsPlayOver(animater, false, action);
        }

        public static void Play(this Animator animater, string name, System.Action action)
        {
            animater.Play(name);
            AnimatorPlayCall.I.OnIsPlayOver(animater, true, action);
        }

        public static void OnOver(this Animator animater,bool isCurrent, System.Action action)
        {
            AnimatorPlayCall.I.OnIsPlayOver(animater, isCurrent, action);
        }
    }

}

源码:实现方式2【目前自己用的方式】

using System.Collections.Generic;
using UnityEngine;
namespace KTF.Extend.AnimatorOverCall
{
    /// <summary>
    /// AnimatorOver完毕后回调管理
    /// </summary>
    public class AnimatorOverCallMgr
    {
        private AnimatorOverCallMgr() { }
        private static AnimatorOverCallMgr instance;
        public static AnimatorOverCallMgr I
        {
            get
            {
                if (instance == null)
                {
                    instance = new AnimatorOverCallMgr();
                }
                return instance;
            }
        }

        private Dictionary<string, AnimatorCallElement> callManager = new Dictionary<string, AnimatorCallElement>();

        internal void HandleEvent(string name)
        {
            if (callManager.ContainsKey(name))
            {
                callManager[name].DoOver();
            }
        }

        internal void RemoveHandleEvent(string name)
        {
            if (callManager.ContainsKey(name))
            {
                callManager.Remove(name);
            }
        }

        internal void AddListener(Animator animator, System.Action call)
        {
            string c_InstanceID = animator.gameObject.GetInstanceID() + "";
            if (!callManager.ContainsKey(c_InstanceID))
            {
                callManager.Add(c_InstanceID, new AnimatorCallElement(animator));
            }
            CheckEvent(animator, c_InstanceID);

            callManager[c_InstanceID].onOver -= call;
            callManager[c_InstanceID].onOver += call;
        }

        /// <summary>
        /// 添加一个监听
        /// </summary>
        /// <param name="animator"></param>
        /// <param name="call">返回值是(Layer,StateNameHash)</param>
        public void AddListener(Animator animator, System.Action<int,int> call)
        {
            string c_InstanceID = animator.gameObject.GetInstanceID() + "";
            if (!callManager.ContainsKey(c_InstanceID))
            {
                callManager.Add(c_InstanceID, new AnimatorCallElement(animator));
            }
            CheckEvent(animator, c_InstanceID);

            callManager[c_InstanceID].onOverByStateNameHash -= call;
            callManager[c_InstanceID].onOverByStateNameHash += call;
        }

        /// <summary>
        /// 移除一个监听
        /// </summary>
        /// <param name="animator"></param>
        /// <param name="call"></param>
        public void RemoveListener(Animator animator, System.Action<int,int> call)
        {
            string c_InstanceID = animator.gameObject.GetInstanceID() + "";
            if (callManager.ContainsKey(c_InstanceID))
            {
                callManager[c_InstanceID].onOverByStateNameHash -= call;
            }
        }

        AnimationClip[] c_clips;
        AnimationEvent[] c_events;
        /// <summary>
        /// 检查是否有Event
        /// </summary>
        /// <param name="animator"></param>
        /// <param name="c_InstanceID"></param>
        private void CheckEvent(Animator animator,string c_InstanceID)
        {
            if (animator.GetComponent<AnimatorOverCall>() == null)
            {
                AnimatorOverCall animatorOverCall = animator.gameObject.AddComponent<AnimatorOverCall>();
                animatorOverCall.SetInstanceID(c_InstanceID);
                c_clips = animator.runtimeAnimatorController.animationClips;
                for (int i = 0,length = c_clips.Length; i < length; i++)
                {
                    if (c_clips[i] == null)
                    {
                        continue;
                    }
                    c_events = c_clips[i].events;
                    for (int j = 0; j < c_events.Length; j++)
                    {
                        if (c_events[j] == null)
                        {
                            continue;
                        }
                        if (c_events[j].functionName == "OverCall")
                        {
                            return;
                        }
                    }
                    c_clips[i].AddEvent(new AnimationEvent() { functionName = "OverCall", time = c_clips[i].length });
                }
            }
        }

        #region 内部类
        private class AnimatorCallElement
        {
            public AnimatorCallElement(Animator animator)
            {
                this.animator = animator;
            }

            private Animator animator;

            internal System.Action onOver;

            internal event System.Action<int, int> onOverByStateNameHash;

            internal void DoOver()
            {
                if (onOverByStateNameHash != null)
                {
                    for (int i = 0, length = animator.layerCount; i < length; i++)
                    {
                        onOverByStateNameHash.Invoke(i, animator.GetCurrentAnimatorStateInfo(i).shortNameHash);
                    }
                }
                onOver?.Invoke();
                onOver = null;
            }
        }
        #endregion
    }

    /// <summary>
    /// 对Animator扩展
    /// </summary>
    public static class AnimatorExtend
    {
        public static void Play(this Animator animator, string name, System.Action action,int layout=0)
        {
            if (animator.HasState(layout, Animator.StringToHash(name)))
            {
                animator.Play(name, layout);
                AnimatorOverCallMgr.I.AddListener(animator, action);
            }
            else
            {
                action?.Invoke();
                Debug.LogError("没有:" + name+"State");
            }
        }

        /// <summary>
        /// 添加完毕监听
        /// </summary>
        /// <param name="animater"></param>
        /// <param name="action">返回值是(Layer,StateNameHash)</param>
        public static void AddOverCall(this Animator animater, System.Action<int, int> action)
        {
            AnimatorOverCallMgr.I.AddListener(animater, action);
        }

        /// <summary>
        /// 移除完毕监听
        /// </summary>
        /// <param name="animater"></param>
        /// <param name="action"></param>
        public static void RemoveOverCall(this Animator animater, System.Action<int,int> action)
        {
            AnimatorOverCallMgr.I.RemoveListener(animater, action);
        }        
    }    

    [DisallowMultipleComponent]
    internal class AnimatorOverCall : MonoBehaviour
    {        
        private string instanceID;
        internal void SetInstanceID(string id)
        {
            instanceID = id;
        }
        public void OverCall()
        {
            AnimatorOverCallMgr.I.HandleEvent(instanceID);
        }

        private void OnDestroy()
        {
            AnimatorOverCallMgr.I.RemoveHandleEvent(instanceID);
        }
    }
}

更复杂的可以考虑StateMachineBehaviour!

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值