Unity使用Animator.CrossFade后,脚本的OnExitState函数还执行吗
问题可以细化为两种
- Animator.CrossFade到别的动画
- Animator.CrossFade到自身动画,从而实现重新播放该动画
为了确认这个问题的答案,我做了一个简单的测试,Idle为正常状态,Hit为角色的挨打动画,想直接看答案的可以直接看后面加粗的字

当我按下T键时,会播放Hit动画:
public class Test : MonoBehaviour
{
private Animator animator;
private void Awake()
{
animator = GetComponent<Animator>();
}
private void Update()
{
if (Input.GetKeyDown("t"))
{
animator.SetTrigger("hit");
}
}
}
模型的Hit状态上加了如下脚本:
public class OnHitState : StateMachineBehaviour
{
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Debug.Log("Enter Hit");
}
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Debug.Log("Exit Hit");
animator.ResetTrigger("hit");
}
}
正常按T,能从Idle状态播放挨打动画,再回到Idle状态,Log如下:

如果加上CrossFade呢,CrossFade可以选择两个状态,一种是CrossFade到Idle状态,一种是CrossFade到Hit状态。
更新后的Test脚本为:
private void Update()
{
if (Input.GetKeyDown("t"))
{
animator.SetTrigger("hit");
}
if (Input.GetKeyDown("y"))
{
animator.CrossFade("Idle", 0.01f);
}
if (Input.GetKeyDown("u"))
{
animator.CrossFade("Hit", 0.01f);
}
}
为方便观察,给Idle也加个脚本:
public class OnIdleState : StateMachineBehaviour
{
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Debug.Log("Enter Idle");
}
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Debug.Log("Exit Idle");
}
}
Animator是这样的效果

OK,可以开始测试了
这是先按t,再按y后的效果,角色会立马切换回到Idle:

可以看出,CrossFade切换到另外一个状态,仍然会调用OnStateExit脚本(顺便注意到,Enter另外一个State的消息反而是先打印的,不过二者应该在同一帧)
那么如果CrossFade到自身的状态会怎么样呢?

这是测试的结果,值得注意的是,尽管按了t,再按u,也没有打印Exit Hit的动画,上面的Exit Hit打印信息是挨打动画播完了才自动打印的,也就是说:
CrossFade到自身动画的API不管用!!
随后又测到了一个奇怪的现象,我发现如果按完了 t 键,触发了挨打动画后,迅速按 u 键,能实现下述打印,为了方便查看,我加了两句帧数的显示情况:

思考后,发现Idle动画到Hit动画之间是有一个动画的切换过程的,如果此时这个转态过程按下了 u 键,这个转态也能转换到Hit状态,也就是说Animator不播放转态了,直接进入Hit的状态,这个时候由于状态介乎于Hit和Idle状态之间,会同时(准确的说是同一帧内)调用Hit和Idle的OnStateExit函数。
到这里,由于我做的游戏里面,角色要挨打,在挨打的OnStateExit的时候我做了相关逻辑处理,但是如果角色在挨打的时候,又挨打了,我需要重新播放该动画,而且希望OnStateExit函数能被调用,如果CrossFade到自身动画的API不管用,也不会走OnStateExit函数,那该怎么办呢?
想了个骚操作,看看这个行不行:
if (Input.GetKeyDown("u"))
{
animator.CrossFade("Idle", 0.000001f); //瞬间转换到Idle
animator.CrossFade("Hit", 0.000001f); //再转换回Hit,想要瞬间实现转态
}
然后发现没有,当角色挨打时,按u键,会立马切换到Idle状态,但是不会再走Hit了,因为这两个状态涉及时间的东西,所以我怀疑 animator.CrossFade不能连续使用,也有可能是由于HasExitTime的缘故。
既然方法行不通,就考虑别的把:
在UnityForum上看到了这个函数,完美的符合了我的情况,既能走OnStateExit函数,也能重新播放动画:
animator.Play("Hit", -1, 0);
看一下这个API:

所以在我的项目中,直接这么用就可以咯:
void OnParticleCollision(GameObject obj)
{
if (obj.tag == "Bullet")
{
//如果已经在播放挨打动画了
if (animator.GetCurrentAnimatorStateInfo(0).IsName("Hit"))
{
animator.Play("Hit", -1, 0);//重新播放动画,而且会调用OnStateExit
}
else
{
animator.SetTrigger("hit");
}
}
}
浙公网安备 33010602011771号