Unity中实现AnimatorController

前言:我们无法监听Animator是否播放完一个动画,我所知的办法就是将监听方法设置为Public,并且挂在带有Animator的物体上,并且还要在Clip文件内新增AnimEvent。于是我自己写了一个AnimCtrler。

需求:开始动画、暂停动画、将动画停止在某一个状态、动画播放完成后执行回调。

思路:

1.播放动画就用Animator自带的API,Animator.Play即可;

2.停止动画,我想要Animator.speed为0达到效果;

3.停止在某一个状态,我们可以先播动画,播放在第一帧时马上暂停;

4.完成回调,使用AnimatorStateInfo.normalizedTime是否>=1f判定

实现:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

[RequireComponent(typeof(Animator))]
public class AnimCtrler : MonoBehaviour
{
    private Animator m_animator;
    private float m_speed;
    private string m_curAnimName;
    private bool m_clipEndAutoSetNull;
    private Dictionary<string, Action<string>> clipEndCallbacks;

    public float Speed
    {
        get => m_speed;
        set
        {
            m_speed = value;
            if (m_animator != null)
            {
                m_animator.speed = m_speed;
            }
        }
    }

    public string CurAnimName
    {
        get => m_curAnimName;
    }

    /// <summary>
    /// 回调执行后,是否会自动设置为空
    /// </summary>
    public bool ClipEndAutoSetNull
    {
        set => m_clipEndAutoSetNull = value;
    }

    private void Awake()
    {
        m_animator = GetComponent<Animator>();
        m_speed = 1f;
        m_animator.speed = m_speed;
        m_curAnimName = string.Empty;
        m_clipEndAutoSetNull = true;
        clipEndCallbacks = new Dictionary<string, Action<string>>();
    }

    public void Play(string animName, float speed = 1)
    {
        if (string.IsNullOrEmpty(animName))
            return;

        Speed = speed;
        m_animator.Play(animName, 0, 0);
        m_curAnimName = animName;
    }

    public void Stop()
    {
        Speed = 0f;
    }

    /// <summary>
    /// 将动画定格在这一瞬间
    /// </summary>
    /// <param name="animName"></param>
    /// <param name="progress"></param>
    public void SetAnimStartPose(string animName, float progress)
    {
        if (string.IsNullOrEmpty(animName))
            return;
        m_animator.Play(animName, 0, progress);
        m_animator.Update(0);
        Speed = 0f;
    }

    /// <summary>
    /// 设置动画结束的回调
    /// </summary>
    /// <param name="animName"></param>
    /// <param name="endCallback"></param>
    public void SetAnimEndCallback(string animName, Action<string> endCallback)
    {
        if (string.IsNullOrEmpty(animName))
            return;

        if (clipEndCallbacks.ContainsKey(animName))
        {
            clipEndCallbacks[animName] = endCallback;
        }
        else
        {
            clipEndCallbacks.Add(animName, endCallback);
        }
    }

    private void Update()
    {
        if (!string.IsNullOrEmpty(m_curAnimName))
        {
            AnimatorStateInfo info = m_animator.GetCurrentAnimatorStateInfo(0);
            if (info.IsName(m_curAnimName) && info.normalizedTime >= 1f)//如果动画为loop模式,则此代码会有bug
            {
                Action<string> callback = null;
                clipEndCallbacks.TryGetValue(m_curAnimName, out callback);
                callback?.Invoke(m_curAnimName);
                if (m_clipEndAutoSetNull)
                {
                    clipEndCallbacks[m_curAnimName] = null;
                }
            }
        }
    }
}

测试:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour
{
    public AnimCtrler ctrler;
    public void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            ctrler.ClipEndAutoSetNull = true;
            ctrler.SetAnimEndCallback("Move", (name) => Debug.Log(name));
            ctrler.Play("Move");
        }
        else if (Input.GetMouseButtonDown(1))
        {
            ctrler.SetAnimStartPose("Exit", 1);
        }
    }
}

 

总结:

在监听动画完成条件这里会有一个bug,如果Clip的模式为loop,normalizedTime是会一直增长的,所以使用AnimCtrler脚本,动画片段都不应该是loop模式(靠normalizedTime判断是否播放完,对这个问题是无解的,我在考虑是否用我以前写的Timer来监听)。当然如果你使用Play重复播,监听还是有效的。Animator的参数意义可以参考【Unity3D】Animation 和 Animator 动画重置到起始帧的方法_机灵鹤的博客-CSDN博客_animator.update

posted @ 2022-12-15 13:19  军酱不是酱  阅读(760)  评论(0编辑  收藏  举报