当前位置:   article > 正文

Unity使用Animator.CrossFade后,脚本的OnExitState函数还执行吗

animator.crossfade

问题可以细化为两种

  • 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");
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

模型的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");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

正常按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);
        }

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

为方便观察,给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");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Animator是这样的效果
在这里插入图片描述

OK,可以开始测试了
这是先按t,再按y后的效果,角色会立马切换回到Idle:
在这里插入图片描述
可以看出,CrossFade切换到另外一个状态,仍然会调用OnStateExit脚本(顺便注意到,Enter另外一个State的消息反而是先打印的,不过二者应该在同一帧)

那么如果CrossFade到自身的状态会怎么样呢?
在这里插入图片描述
这是测试的结果,值得注意的是,尽管按了t,再按u,也没有打印Exit Hit的动画,上面的Exit Hit打印信息是挨打动画播完了才自动打印的,也就是说:
CrossFade到自身动画的API不管用!!

随后又测到了一个奇怪的现象,我发现如果按完了 t 键(SetTrigger(“hit”)),触发了挨打动画后,迅速按 u 键(CrossFade(“Hit”, 0.01f)),能实现下述打印,为了方便查看,我加了两句帧数的显示情况:
在这里插入图片描述
思考后,发现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,想要瞬间实现转态
        }
  • 1
  • 2
  • 3
  • 4
  • 5

然后发现没有,当角色挨打时,按u键,会立马切换到Idle状态,但是不会再走Hit了,因为这两个状态涉及时间的东西,所以我怀疑 animator.CrossFade不能连续使用,也有可能是由于HasExitTime的缘故。

既然方法行不通,就考虑别的把:
在UnityForum上看到了这个函数,完美的符合了我的情况,既能走OnStateExit函数,也能重新播放动画:

animator.Play("Hit", -1, 0);
  • 1

看一下这个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");
            }

        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号