4.获取某个目标对象的动画信息

4.如何获取某个目标对象的动画信息


4.1 知识点

整段 VAT 流水线都在 Editor 里跑,读 AnimatorController 要用 UnityEditor.Animations。这一节只负责把「角色身上到底有哪些 AnimationClip」列出来;从 layers[0].stateMachine.states 拿到的 childAnimatorStateArray,后面第 5、6 篇会原样接着用,不必改字段名。

为何要获取某个目标对象的动画信息

  • 第一个目的是要生成 VAT
  • VAT 中存储的就是对象动画的每帧顶点位置信息。
  • 因此首先要做的,就是获取目标对象的动画信息。

获取某个目标对象的动画信息

主要目的

  • 获取某个对象所使用到的全部 AnimationClip 信息。
  • 这样就能得到所有动画的关键信息(例如帧率、时长等)。

主要思路

  • Animator 上挂着 RuntimeAnimatorController,强转成 AnimatorController 之后可以读到各层状态机。
  • 状态上的 motion 经常是 AnimationClip(也可能是 BlendTree 等,那种要另写分支,这里按单 Clip 处理)。

主要步骤

  1. 得到目标 GameObject
  2. 得到该 GameObject 上的 Animator 组件。
  3. 通过 Animator 得到 AnimatorControllerruntimeAnimatorController 强转为 AnimatorController);若为 null 则结束。
  4. AnimatorController 中取对应层(示例为 layers[0])的状态机 AnimatorStateMachine
  5. 从状态机中取出所有状态,得到 ChildAnimatorState[]
  6. 遍历该数组:每个状态上的 motion 转成 AnimationClip 后,可读 frameRatelength,并用 Mathf.CeilToInt(animationClip.frameRate * animationClip.length) 算出该 Clip 大约多少帧。

对应步骤的代码如下(与文末 4.2 逻辑一致,这里未写 animationClip 判空,复制到工程里时建议补上):

using UnityEngine;
using UnityEditor.Animations;

//1.得到目标GameObject

//2.得到GameObject上的Animator组件
Animator animator = targetGameObject.GetComponent<Animator>();

//3.通过Animator组件得到AnimatorController
AnimatorController animatorController = animator.runtimeAnimatorController as AnimatorController;
if (animatorController == null)
{
    return;
}

//4.得到AnimatorController中对应层的状态机AnimatorStateMachine
AnimatorStateMachine animatorStateMachine = animatorController.layers[0].stateMachine;

//5.得到状态机中的所有状态ChildAnimatorState[]数组
ChildAnimatorState[] childAnimatorStateArray = animatorStateMachine.states;

//之所以绕一圈去获取动画切片 是为了之后做铺垫
//我们可以结合Animator,蒙皮网格渲染器,利用他们一起去得到某一个动画某一帧对应的顶点数据

//6.遍历该数组
//  其中每个状态的motion就是对应的动画切片AnimationClip
//  这样我们就能得到帧率、时长等关键信息
for (int i = 0; i < childAnimatorStateArray.Length; i++)
{
    AnimatorState animatorState = childAnimatorStateArray[i].state;
    AnimationClip animationClip = animatorState.motion as AnimationClip;

    // animationClip.frameRate 帧率
    // animationClip.length 时长
    int frameCount = Mathf.CeilToInt(animationClip.frameRate * animationClip.length);//得到这个clip多少帧
}

先拿到 AnimationClip,后面才能用 SampleAnimation 定帧、用 SkinnedMeshRenderer.BakeMesh 把蒙皮结果落成网格顶点,这一环在第 5 篇接。


4.2 知识点代码

GetTargetAnimationInfoExample.cs

using UnityEngine;
using UnityEditor.Animations;

// 将本脚本放在项目的 Editor 目录下,并确保程序集引用了 UnityEditor

public static class GetTargetAnimationInfoExample
{
    public static void ExampleInspect(GameObject targetGameObject)
    {
        if (targetGameObject == null)
        {
            return;
        }

        //我们的第一个目的是要生成VAT
        //VAT中存储的就是对象动画的每帧顶点位置信息
        //因此我们首先要做的就是获取目标对象的动画信息

        //主要目的:
        //获取某个对象的所有使用的AnimationClip信息
        //这样就能得到所有动画的关键信息

        //主要思路:
        //通过获取Animator动画状态机中的状态
        //从而得到动画信息,因为状态的本质就是动画

        //主要步骤:

        //1.得到目标GameObject

        //2.得到GameObject上的Animator组件
        Animator animator = targetGameObject.GetComponent<Animator>();
        if (animator == null)
        {
            return;
        }

        //3.通过Animator组件得到AnimatorController
        AnimatorController animatorController = animator.runtimeAnimatorController as AnimatorController;
        if (animatorController == null)
        {
            return;
        }

        //4.得到AnimatorController中对应层的状态机AnimatorStateMachine
        AnimatorStateMachine animatorStateMachine = animatorController.layers[0].stateMachine;

        //5.得到状态机中的所有状态ChildAnimatorState[]数组
        ChildAnimatorState[] childAnimatorStateArray = animatorStateMachine.states;

        //6.遍历该数组
        //  其中每个状态的motion就是对应的动画切片AnimationClip
        //  这样我们就能得到帧率、时长等关键信息
        for (int i = 0; i < childAnimatorStateArray.Length; i++)
        {
            AnimatorState animatorState = childAnimatorStateArray[i].state;
            AnimationClip animationClip = animatorState.motion as AnimationClip;
            if (animationClip == null)
            {
                continue;
            }

            // animationClip.frameRate 帧率
            // animationClip.length 时长
            int frameCount = Mathf.CeilToInt(animationClip.frameRate * animationClip.length);//得到这个clip多少帧
        }

        //之所以绕一圈去获取动画切片 是为了之后做铺垫
        //我们可以结合Animator,蒙皮网格渲染器,利用他们一起去得到某一个动画某一帧对应的顶点数据
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏