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;
	}
}

代码有点绕,主要是别把AnimatorOverrideControllerRuntimeAnimatorController搞混了,AnimatorOverrideController是AnimatorController的子类,而RuntimeAnimatorController是AnimatorController的Runtime的展示出来的属性。


总结

  • Mecanim机制下,只能在Runtime创建和编辑legacy的AnimationClip并播放
  • Mecanim机制下,如果想AddClip,然后Runtime更改动画状态机的逻辑,是不大可能的,只能创建一个AnimatorOverrideController,替换里面的AnimationClip,然后复制给原本的Animator.runtimeAnimatorController,也就是说,Runtime改变动画逻辑是不可行的,只能改变播放的动画片段(就是AnimationState里面具体的电话)

PS:如果想要Runtime改变动画逻辑,又要支持动画重定向、BlendTree等功能,需要参考Unity的Playable API

posted @ 2021-03-11 18:24  弹吉他的小刘鸭  阅读(516)  评论(0编辑  收藏  举报