【unity游戏开发】彻底理解AnimatorStateInfo,获取真实动画长度

前言

前置知识:设置参数后,不会立即切换对应状态,偶尔等一帧也不会进入

AnimatorStateInfo是结构体!值类型,要不断重复获取才是最新的

AnimatorStateInfo重要属性

  • normalizedTime :动画播放进度,播放完则是1,每次循环+1,需要注意不循环的动画可能只到9.x甚至更低就切换了
  • shortNameHash:状态名字 转换成的hash值
  • length:动画时长,单位秒
  • IsName: 将字符串转为hash进行判断
  • speed:状态上配置的时间缩放,混合树的话就是本身的时间缩放和动作域无关,不受animator影响

代码

三种等待方式

  1. 根据namehash判断:准确但是如果没有进入目标状态会死循环不够安全
  2. 基于动画播放进度和过渡:normalizedTime的值跳跃性大,不够准确
  3. 基于动画时长:灵活,但是无法判断状态的开始,也无法处理钝帧等中途缩放时间的情况
public static class AnimationTool
{
    #region  基于指定hash
    // 准确来说,不是基于动画而是基于状态
    public static async UniTask AwaitStateStartByName(Animator animator, Action callback = null, int nameHash = 0, int layer = 0, CancellationToken token = default)
    {
        while (animator.GetCurrentAnimatorStateInfo(layer).shortNameHash != nameHash)
        {
            // 在动画未结束或者在过渡中时继续等待
            await UniTask.Yield(token);
        }
        callback?.Invoke();
    }
    public static async UniTask AwaitStateStartByName(Animator animator, Action callback = null, string name = "", int layer = 0, CancellationToken token = default)
    {
        var nameHash = Animator.StringToHash(name);
        await AwaitStateStartByName(animator, callback, nameHash, layer);
    }
    public static async UniTask AwaitStateEndByName(Animator animator, Action callback = null, int nameHash = 0, int layer = 0, CancellationToken token = default)
    {
        var animInfo = animator.GetCurrentAnimatorStateInfo(layer);
        while (animator.GetCurrentAnimatorStateInfo(layer).shortNameHash == nameHash)
        {
            Debug.Log(animInfo.normalizedTime + " " + animInfo.shortNameHash);
            await UniTask.Yield(token);
        }
        Debug.Log(animInfo.normalizedTime + " " + animInfo.shortNameHash);
        callback?.Invoke();
    }
    public static async UniTask AwaitStateEndByName(Animator animator, Action callback = null, string name = "", int layer = 0, CancellationToken token = default)
    {
        var nameHash = Animator.StringToHash(name);
        await AwaitStateStartByName(animator, callback, nameHash, layer);
    }

    /// <summary>
    /// 组合用法  等待当前真实动画播放完毕
    /// </summary>
    /// <param name="animator"></param>
    /// <param name="callback"></param>
    /// <param name="name"></param>
    /// <param name="layer"></param>
    /// <returns></returns>
    public static async UniTask AwaitStateStartAndEndByName(Animator animator, Action callback = null, string name = "", int layer = 0, CancellationToken token = default)
    {
        var animInfo = animator.GetCurrentAnimatorStateInfo(layer);
        var nameHash = Animator.StringToHash(name);
        if (animInfo.shortNameHash != nameHash)
        {
            await AwaitStateStartByName(animator, null, name, layer, token);
        }
        while (animator.GetCurrentAnimatorStateInfo(layer).shortNameHash == nameHash)
        {
            // 在动画未结束或者在过渡中时继续等待
            await UniTask.Yield(token);
        }
        callback?.Invoke();
    }
    #endregion

    #region  基于当前hash
    /// <summary>
    /// 组合用法  等待当前真实动画播放完毕
    /// </summary>
    /// <param name="animator"></param>
    /// <param name="callback"></param>
    public static void AwaitCurrentStateEnd(Animator animator, Action callback, int layer = 0)
    {
        AwaitCurrentState(animator, () =>
        {
            AwaitAnimEnd(animator, callback, layer);
        }, layer);
    }

    /// <summary>
    /// 等待动画 严格等待  注意无法处理自循环的动画!只有一个动画用delay版本
    /// </summary>
    /// <param name="animator"></param>
    /// <returns></returns>
    public static void AwaitCurrentState(Animator animator, Action callback, int layer = 0)
    {
        AwaitCurrentStateAsync(animator, callback, layer).Forget();
    }

    public static async UniTask AwaitCurrentStateAsync(Animator animator, Action callback = null, int layer = 0)
    {
        var animInfo = animator.GetCurrentAnimatorStateInfo(layer);
        var nameHash = animInfo.fullPathHash;

        // 等待直到当前动画发生改变
        await UniTask.WaitUntil(() =>
        {
            var info = animator.GetCurrentAnimatorStateInfo(layer);
            return nameHash != info.fullPathHash;
        });

        callback?.Invoke();
    }
    public static void AwaitNextState(Animator animator, Action callback, int layer = 0)
    {
        AwaitNextStateAsync(animator, callback, layer).Forget();
    }
    public static async UniTask AwaitNextStateAsync(Animator animator, Action callback, int layer = 0)
    {
        await AwaitCurrentStateAsync(animator, null, layer);
        await AwaitCurrentStateAsync(animator, callback, layer);
    }
    #endregion

    #region 基于播放进度
    /// <summary>
    /// 等待动画播放进度结束  不包含过渡!
    /// </summary>
    /// <param name="animator"></param>
    /// <param name="callback"></param>
    /// <param name="layer"></param>
    public static void AwaitAnimEnd(Animator animator, Action callback, int layer = 0)
    {
        AwaitAnimEndAsync(animator, callback, layer).Forget();
    }
    /// <summary>
    /// 提前等待动画播放
    /// </summary>
    /// <param name="animator"></param>
    /// <param name="callback"></param>
    /// <param name="layer"></param>
    /// <param name="time">提前的进度百分比</param>
    public static void PreAwaitAnimEnd(Animator animator, Action callback, float time, int layer = 0)
    {
        AwaitAnimEndAsync(animator, callback, time, layer).Forget();
    }

    public static async UniTask AwaitAnimEndAsync(Animator animator, Action callback = null, float time = 0, int layer = 0, CancellationToken token = default)
    {
        while (animator.GetCurrentAnimatorStateInfo(layer).normalizedTime < (0.95f - time) || !animator.IsInTransition(layer))
        {
            // 在动画未结束或者在过渡中时继续等待
            await UniTask.Yield(token);
        }
        callback?.Invoke();
    }

    #endregion

    #region 基于动画时长
    /// <summary>
    /// 基于动画时长  更加安全   无法处理钝帧等中途缩放时间的情况
    /// </summary>
    /// <param name="animator"></param>
    /// <param name="callback"></param>
    /// <param name="layer"></param>
    /// <param name="token"></param>
    /// <param name="time">提前时间,单位秒</param>
    /// <returns></returns>
    public static async UniTask AwaitCurrentAnimEndByLength(Animator animator, Action callback = null, float time = 0, int layer = 0, CancellationToken token = default)
    {
         var animInfo = animator.GetCurrentAnimatorStateInfo(layer);
        // 获取真实动画时长 animator.speed  不影响 animInfo.speed
        var length = (animInfo.speed != 0 && animator.speed != 0) ? 
        (animInfo.length / (animInfo.speed * animator.speed)) - time : 0;
        await UniTask.Delay((int)(length * 1000), cancellationToken: token);
        callback?.Invoke();
    }

    #endregion
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值