利用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
{
///
/// Animator 播放动画回调相关
///
public class AnimatorPlayCall : MonoBehaviour
{
List Pools = new List();
static AnimatorPlayCall instance;
public static AnimatorPlayCall I
{
get
{
if (instance == null)
{
instance = new GameObject("AnimatorPlayCall").AddComponent();
GameObject.DontDestroyOnLoad(instance);
}
return instance;
}
}
///
/// 检测Animator是否播放完毕
///
///
///
///
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]);
}
}
}
}
///
/// 播放回调参数
///
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;
}
///
/// 层
///
public int Layer { private set; get; }
///
/// 回调行为
///
public System.Action Action { private set; get; }
///
/// Animator 动画
///
public Animator Animator { private set; get; }
///
/// 是否当前动画
///
public bool IsCurrent { private set; get; }
///
/// 缓存的NameHash
///
private int shortNameHash=0;
private AnimatorStateInfo CurInfo
{
get
{
return Animator.GetCurrentAnimatorStateInfo(Layer);
}
}
private AnimatorStateInfo NextInfo
{
get
{
return Animator.GetNextAnimatorStateInfo(Layer);
}
}
///
/// 每帧检测动画状态
///
///
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;
}
}
}
///
/// Animator扩展
///
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
{
///
/// AnimatorOver完毕后回调管理
///
public class AnimatorOverCallMgr
{
private AnimatorOverCallMgr() { }
private static AnimatorOverCallMgr instance;
public static AnimatorOverCallMgr I
{
get
{
if (instance == null)
{
instance = new AnimatorOverCallMgr();
}
return instance;
}
}
private Dictionary callManager = new Dictionary();
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;
}
///
/// 添加一个监听
///
///
/// 返回值是(Layer,StateNameHash)
public 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].onOverByStateNameHash -= call;
callManager[c_InstanceID].onOverByStateNameHash += call;
}
///
/// 移除一个监听
///
///
///
public void RemoveListener(Animator animator, System.Action call)
{
string c_InstanceID = animator.gameObject.GetInstanceID() + "";
if (callManager.ContainsKey(c_InstanceID))
{
callManager[c_InstanceID].onOverByStateNameHash -= call;
}
}
AnimationClip[] c_clips;
AnimationEvent[] c_events;
///
/// 检查是否有Event
///
///
///
private void CheckEvent(Animator animator,string c_InstanceID)
{
if (animator.GetComponent() == null)
{
AnimatorOverCall animatorOverCall = animator.gameObject.AddComponent();
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[i] == null)
{
continue;
}
if (c_events[i].functionName == "OverCall")
{
break;
}
}
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 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
}
///
/// 对Animator扩展
///
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");
}
}
///
/// 添加完毕监听
///
///
/// 返回值是(Layer,StateNameHash)
public static void AddOverCall(this Animator animater, System.Action action)
{
AnimatorOverCallMgr.I.AddListener(animater, action);
}
///
/// 移除完毕监听
///
///
///
public static void RemoveOverCall(this Animator animater, System.Action 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!