这篇文章旨在实现Unity在Runtime创建和编辑AnimationClip的功能,读这篇文章之前最好有Animator和Animation组件的用法基础。
首先需要介绍两个概念:AnimatorOverrideController和RuntimeAnimatorController,如果只想看怎么写代码的可以直接看后面。
Animator Override Controller是一种asset文件,其文件后缀为.overrideController,它需要以一个AnimatorController对应的文件作为Base Asset文件,其Inspector界面如下所示:
Animator Override Controllers与其base Animator Controller的动画逻辑、参数完全相同,只是具体的AnimationState对应的AnimationClip不同罢了。
这个很有用,举个例子,游戏里面,很多NPC的动画状态机的逻辑是一样的,都是那些Idle动画,攻击和挨打的逻辑也是一样的,唯一不同的就是他们的具体的动画clip不一样,这时候就可以创建一个作为基类的AnimatorController,当我只想替换其中的某些动画,不想改变逻辑的时候,就创建我的AnimatorOverrideController。
在Project视图下面可以创建Animator Override Controller,其文件后缀为.overrideController,二者的图标也有少许区别,如下图所示:
创建之后,需要指定其base的Controller:
选中之后,就会出现原本的controller用到的动画片段,在这里可以进行替换:
在Component组件下,就可以看到显示的是overrideController,感觉这跟代码里的继承非常像:
关于RuntimeAnimatorContoller这个类型,在Unity的Scripting API里有提到,但是在Unity Manual里完全没看到这个东西。(Manual里只介绍了Animator Override Controllers)
关于该类,Unity的解释是:
RuntimeAnimatorController is the runtime representation of the AnimatorController. Use this representation to change the Animator Controller during runtime.
该类的API很少,如下图所示:
而且从RuntimeAnimatorController获取的AnimationClip是只读的:
// Summary: The runtime representation of the AnimatorController. Use this representation to change the Animator Controller during runtime.
public class RuntimeAnimatorController : Object
{
protected RuntimeAnimatorController();
// Summary:
// Retrieves all AnimationClip used by the controller.
public AnimationClip[] animationClips { get; }
}
// 这种操作是错误的
RuntimeAnmatorController c;
AnimationClip a = new AnimationClip();
c.animationClips[0] = a;
前面介绍过,RuntimeAnimatorController用于帮助实现Runtime的动画相关功能,假设有这么一个需求,要在Runtime创建一个AnimationClip,然后在Runtime把它应用到一个Cube上,让他模拟这个动画,下面是具体的实现方法。
注意这里的AnimationClip的类型必须是legacy类型的,因为AnimationClip里面本质上是一堆Property随着时间变化的关键帧数据,每一个Property对应一个AnimationCurve,而Humanoid或Generic类型的AnimationClip是不允许在Runtime调用SetCurve函数的。
方法一. 使用Animation组件实现
下面先用legacy的动画状态机,也就是Animation组件,实现Runtime创建和播放AnimationClip的操作。
// 把下面这个脚本组件挂到场景的一个Cube身上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Animation))]
public class AnimationBehaviour : MonoBehaviour {
public Animation anim;
AnimationClip animationClip;
void Start () {
anim = GetComponent<Animation> ();
// define animation curve
AnimationCurve translateX = AnimationCurve.Linear(0.0f, 0.0f, 2.0f, 2.0f);
animationClip = new AnimationClip();
// set animation clip to be legacy
animationClip.legacy = true;
animationClip.SetCurve("", typeof(Transform), "localPosition.x", translateX);
anim.AddClip(animationClip, "test");// 注意Animation组件,有AddClip这个接口
anim.Play("test");
}
}
方法二. 使用Animator组件实现
再来用Animator组件来实现相同的效果,使用Animator会更麻烦一些,需要进行以下操作:
public class AnimatorBehaviourScript : MonoBehaviour {
Animator anim;
AnimatorOverrideController animOverrideController;
AnimationClip animationClip;
protected int weaponIndex;
// Use this for initialization
void Start () {
// 仍然是创建一个AnimationClip
AnimationCurve translateX = AnimationCurve.Linear(0.0f, 0.0f, 2.0f, 2.0f);
animationClip = new AnimationClip();
animationClip.SetCurve("", typeof(Transform), "localPosition.x", translateX);
anim = GetComponent<Animator>();
// Runtime创建一个AnimatorOverrideController
AnimatorOverrideController animatorOverrideController= new AnimatorOverrideController();
// 把原本的Animator的runtimeAnimatorController赋给创建的AnimatorOverrideController
animatorOverrideController.runtimeAnimatorController= anim.runtimeAnimatorController;
// 直接指定AnimationClip, sphereanim是原本的AnimationClip的名字
animatorOverrideController["sphereanim"]= animationClip;
// 然后把创建的overrideController赋值回去
anim.runtimeAnimatorController= animatorOverrideController;
}
}
代码有点绕,主要是别把AnimatorOverrideController
和RuntimeAnimatorController
搞混了,AnimatorOverrideController是AnimatorController的子类,而RuntimeAnimatorController是AnimatorController的Runtime的展示出来的属性。
总结
PS:如果想要Runtime改变动画逻辑,又要支持动画重定向、BlendTree等功能,需要参考Unity的Playable API