Playable是基于Animator运行的时,其本身依然支持AniamtionClip中的动画事件,并且在运行机制上并无区别,但是这种机制对我们来说并非最佳,所以在这个基础上做一层拓展。
Animator自带的Event事件通过名字匹配Animator所在对象上的所有脚本方法来实现,有同名方法就执行。
大体逻辑在玩家的某个状态(比如移动状态)里添加事件的监听,并在AnimatorController对应的事件同名方法(与面板上的)下进行触发。
// Animator会触发的实际事件函数
private void AnimationEvent(string eventName)
{
if (eventDic.TryGetValue(eventName,out Action action))
{
action?.Invoke();
}
}
public void AddAnimationEvent(string eventName,Action action)
{
if (eventDic.TryGetValue(eventName,out Action _action))
{
_action += action;
}
else
{
eventDic.Add(eventName, action);
}
}
public void RemoveAnimationEvent(string eventName,Action action)
{
if (eventDic.TryGetValue(eventName, out Action _action))
{
_action -= action;
}
}
public void RemoveAnimationEvent(string eventName)
{
eventDic.Remove(eventName);
}
public void ClearAllActionEvent()
{
eventDic.Clear();
}
#endregion
Animator的动画事件实际会触发执行的是AnimationEvent方法并传入string类型的参数,我们希望加一层字典维护的事件容器可以供其他脚本添加事件来触发,而不需要把所有方法的逻辑都现在AnimationEvent方法内,便于维护。
为此包了一层字典用来存方法(事件Action),常见情况一个string对应一个方法,也支持一个string对应多个action同时执行。
以添加脚步声动画事件为例,在需要播放脚步声动画的帧打上事件,填入AnimationEvent和参数名FootStep。
private Animation_Controller animation;
//Init方法内
animation = player.Animation_Controller;
public override void Enter()
{
......
animation.AddAnimationEvent("FootStep", OnFootStep);
}
public override void Exit()
{
......
animation.RemoveAnimationEvent("FootStep", OnFootStep);
}
为了方便调用,我们直接让Player_MoveState持有AnimationController,并在进入状态时添加事件监听,退出状态移除事件监听。
private void OnFootStep()
{
int index = UnityEngine.Random.Range(0, player.CharacterConfig.FootStepAudioClips.Length);
AudioManager.Instance.PlayOnShot(player.CharacterConfig.FootStepAudioClips[index], player.transform.position, 0.2f);
}
播放音乐的逻辑如上,对应CharacterConfig里面加一行脚步声资源。
[LabelText("脚步声资源")] public AudioClip[] FootStepAudioClips;
总之,对动画事件拓展了一层字典事件容器的好处在于,如果此时不仅要播放脚步声,还要加脚步的特效播放等额外逻辑,只需要向字典中FootStep对应的action中添加新方法就行了,而不需要在实际出发的AnimationEvent里面加逻辑,便于拓展。