Unity Runtime创建和编辑AnimationClip
这篇文章旨在实现Unity在Runtime创建和编辑AnimationClip的功能,读这篇文章之前最好有Animator和Animation组件的用法基础。
首先需要介绍两个概念:AnimatorOverrideController和RuntimeAnimatorController,如果只想看怎么写代码的可以直接看后面。
Animator Override Controllers
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,感觉这跟代码里的继承非常像:
RuntimeAnimatorController
关于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;
Runtime创建和编辑AnimationClip
前面介绍过,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会更麻烦一些,需要进行以下操作:
- 在Editor下,创建一个AnimatorController.asset文件,双击该文件,进入Animator窗口,在Base Layer里,右键创建一个空的AnimationState,然后随便指定一个AnimationClip(具体是哪个Clip不重要,因为后面Runtime创建的AnimationClip会替换掉这个Clip,但我不确定可以不可以不指定AnimationClip),如下图所示:
- 给场景中的Cube挂一个Animator组件,然后把刚刚创建的文件拖入Animator的AnimatorController里(这个操作应该可以在Runtime做,不过要Runtime读取AnimatorController.asset文件),这个时候按Play,就能播放动画(如果之前随便拖入的动画Clip可以播放),这个操作也就是常规的Editor下设置,Runtime播放动画的操作。
- 然后在Editor下面,给Cube挂载一个脚本,就行了,脚本内容如下:
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的展示出来的属性。
总结
- Mecanim机制下,只能在Runtime创建和编辑legacy的AnimationClip并播放
- Mecanim机制下,如果想AddClip,然后Runtime更改动画状态机的逻辑,是不大可能的,只能创建一个AnimatorOverrideController,替换里面的AnimationClip,然后复制给原本的Animator.runtimeAnimatorController,也就是说,Runtime改变动画逻辑是不可行的,只能改变播放的动画片段(就是AnimationState里面具体的电话)
PS:如果想要Runtime改变动画逻辑,又要支持动画重定向、BlendTree等功能,需要参考Unity的Playable API