之前加动画事件一直使用Animation上的AddEvent或者AnimationClip的Event,这两种方法虽然是直接在界面编辑上动画事件,用起来很方便了,想在什么时候触发事件就把Event添加在时间轴是上,非常方便了!但由于编辑面板上的参数是直接和动画文件直接绑定,当遇到动画是外包做到,并且外包公司的质量不敢恭维,一次次在修改和覆盖你的动画文件时,每一次覆盖就意味着你要重新把每个动画的几个事件关键帧找到,并且输入对应参数时,这谁顶得住!!
因为之前接触动画状态机少,原来一直不知道这个做法,之前的项目因为要用到,踩着几次坑之后,我都感动哭了。
OnStateEnter | 当状态机评估此状态时,在第一个Update帧上调用。 |
OnStateExit | 当状态机评估此状态时,在最后一个更新帧上调用。 |
OnStateIK | 在MonoBehaviour.OnAnimatorIK之后立即调用。 |
OnStateMove | 在MonoBehaviour.OnAnimatorMove之后立即调用。 |
OnStateUpdate | 除了第一帧和最后一帧之外,在每个更新帧中调用。 |
官方文档: StateMachineBehaviour
下面是我之前项目用到的一个示例脚本,因为事件太多,我都把关键参数存在表里,每次动画被覆盖就重新赋值一次,只修改动画改过的关键事件。
Public class AniEvent: StateMachineBehaviour
{
Public int DefultFrameRate = 30;//动画的帧率
Pubilc string AniID;//当前动画的唯一标识
Public int EventTriggerFrames[];//当前动画片段上需要触发事件的帧数
Private int TotalFrame = 0;
private int curEventIdx = 0;
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
//计算动画片段段总帧数
totalFrame = Math.Round(stateInfo.length * DefultFrameRate);
}
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
curEventIdx = 0;
}
override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
if(EventTriggerFrame == null || EventTriggerFrame.length == 0) return;
if(curEventIdx >= EventTriggerFrame.length) return;
curFrame = EventTriggerFrames[curEventIdx];
//normalizedTime 整数部分是已经循环的次数,小数部分是当前循环的进程。
if(curFrame / (float)totalFrame <= Mathf.Repeat(stateInfo.normalizedTime, 1))
{
//调用特定的回调
WholeGameCtrl.Instance.AniTrigger(AniID + “-“ + curFrame);
curEventIdx += 1;
}
}
override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
}
override public void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
}
}