本文旨在深入解析Unity引擎中驱动角色动画的核心组件——Animator Controller。你将了解到状态机的基础设计、参数驱动的动画切换、以及IK动画、根运动(Root Motion)、动画层等高级功能的应用,为你在游戏开发中实现流畅复杂的角色动画提供系统性的指导。
Animator Controller 核心概念
Animator Controller本质上是一个动画状态机,它定义了角色可能处于的各个动画状态(State)以及状态之间的转换(Transition)条件。其核心职责是执行状态机逻辑、驱动动画播放,并通过脚本可控的参数(Parameters)来响应游戏逻辑,从而实现动画的动态切换。
状态 (States)
每个动画状态通常关联一个具体的动画片段(Animation Clip)。Unity在创建状态机时会自动生成三个系统状态:
- Any State: 代表任意状态。可以在此创建条件转换,当条件满足时,无论当前处于何种状态,都会触发向目标状态的切换。
- Entry: 状态机的入口点。进入状态机时,会自动执行从Entry到默认状态(Default State)的转换。
- Exit: 状态机的出口点。用于从子状态机退出到父状态机。
开发者可以创建自定义的“用户状态”,并通过右键菜单将其设置为默认状态。更强大的是,可以为状态附加继承自 StateMachineBehaviour 的脚本,以执行状态相关的逻辑。
StateMachineBehaviour 关键生命周期方法:
OnStateEnter: 进入该状态时调用。
OnStateUpdate: 在该状态持续的每一帧调用。
OnStateExit: 退出该状态时调用。
OnStateMove: 若启用了根运动,则在 OnAnimatorMove 之后调用,可用于处理位移后的逻辑。
OnStateIK: 若启用了IK,则在 OnAnimatorIK 之后调用,可用于处理IK相关逻辑。
注意:在复杂的状态切换中(如高频切换),OnStateExit 可能不会被调用,因此不建议将关键的游戏逻辑完全交由动画状态驱动。
状态转换 (Transitions)
状态转换的触发依赖于在Animator Controller中定义的参数。
参数类型与脚本控制
- Float:
Animator.SetFloat(string name, float value)
- Int:
Animator.SetInt(string name, int value)
- Bool:
Animator.SetBool(string name, bool value)
- Trigger:
Animator.SetTrigger(string name), Animator.ResetTrigger(string name)
Float、Int、Bool类型的参数在代码中设置后,可在转换条件中进行数值比较。Trigger类型较为特殊,调用SetTrigger后,该触发器会在被某次状态转换消费后自动重置,或在调用ResetTrigger时手动重置。若想深入学习如何通过代码高效管理游戏对象的状态与交互,可以参考关于游戏开发中的状态模式与逻辑控制的讨论。
转换属性详解
选中一条转换连线,可在Inspector面板配置以下属性:
| 属性 |
功能说明 |
| Has Exit Time |
是否启用退出时间。启用后,会将归一化播放时间 > Exit Time作为一个附加条件。 |
| Exit Time |
归一化时间(0-1)。例如设为0.9,表示当前动画播放到90%的那一帧,条件方为真。对于循环动画,小于1的值会在每个循环周期检查;大于1(如3.5)则表示循环3次后,在第4次播放到50%时检查。 |
| Fixed Duration |
切换持续时间(Transition Duration)是以秒为单位还是归一化时间单位。 |
| Transition Duration |
状态融合过渡的持续时间。 |
| Transition Offset |
目标状态动画开始播放的时间偏移(归一化值)。例如0.5表示从目标动画的50%处开始播放。 |
| Interruption Source |
定义当前转换可被哪些来源打断。 |
| Ordered Interruption |
启用后,高优先级的转换可以打断低优先级的,反之则不行。 |
| Conditions |
触发转换的条件列表。多个条件间为“与”关系。条件由参数名、比较符、比较值构成。若启用Has Exit Time,则Exit Time也将作为一个条件。 |
转换打断 (Transition Interruption)
通过 Interruption Source 和 Ordered Interruption 属性,可以精细控制转换过程如何被其他转换打断。
可视化调整:转换图 (Transition Graph)
除了直接输入数值,还可以通过转换图进行直观调整。

拖动图中的 Duration out、Duration in、Target State 等标记,可以实时修改过渡时间、退出时间以及目标状态偏移等参数。
涉及混合树 (Blend Tree) 的转换
如果转换的源状态或目标状态是混合树,在Inspector面板中会显示该混合树的参数供你调节,以便预览在不同混合参数下的过渡效果。此处的调节仅用于编辑时预览,不影响运行时。

在预览模式下调整BlendTree参数,观察过渡效果。
IK 动画实现
IK(Inverse Kinematics,反向动力学)允许通过控制子骨骼(如手、脚)的目标位置,来反向求解并驱动父骨骼链的旋转,常用于实现抓取、踏准台阶、注视等效果。
Unity 内置IK系统
首先,需要在动画层的设置中启用IK Pass。

在Layer设置中勾选“IK Pass”以启用IK计算。
随后,便可在脚本的 OnAnimatorIK 回调中使用以下关键API:
Animator.SetIKPositionWeight(AvatarIKGoal goal, float value): 设置IK位置权重(与原始动画的混合值)。
Animator.SetIKPosition(AvatarIKGoal goal, Vector3 position): 设置IK目标位置。
Animator.SetIKRotationWeight / SetIKRotation: 设置IK旋转权重与目标旋转。
Animator.SetLookAtPosition(Vector3 lookAtPosition): 设置角色注视点。
Animator.bodyPosition / bodyRotation: 在OnAnimatorIK中设置身体质心的位置与旋转。
技巧:为使角色自然抓取物体,通常不应直接将物体Transform赋给IK目标,而应为物体设置一个逻辑上的“握点”(Handle)作为IK目标,避免模型穿插。
第三方解决方案:FinalIK
Unity内置IK功能有限,对于更复杂的需求(如全身IK、两足动物IK等),推荐使用强大的第三方插件FinalIK。网络上已有丰富的教程资源可供参考。
根运动 (Root Motion)
根运动允许动画本身的位移和旋转数据直接驱动角色的Transform,从而实现与动画完全同步、非匀速的逼真运动。
在动画剪辑中配置
首先,需要在动画剪辑的导入设置中正确配置根变换。

Root Transform Position (XZ) 等选项决定了动画中的根节点位移是否会应用到角色Transform上。“Bake Into Pose”表示将此变换烘焙到骨骼姿态中,而非驱动角色。
应用根运动
在Animator组件的Inspector中勾选“Apply Root Motion”,或通过代码设置 animator.applyRootMotion = true,即可启用根运动。
通过代码处理运动:OnAnimatorMove
即使不依赖动画中的位移数据,你也可以在 MonoBehaviour.OnAnimatorMove 方法中编写自定义的运动逻辑。这是整合移动平台游戏复杂角色控制的常用方法。
public class MyMove : MonoBehaviour
{
void OnAnimatorMove()
{
float moveSpeed = 1.0f;
Vector3 newPos = transform.position;
newPos += transform.forward * moveSpeed * Time.deltaTime;
transform.position = newPos;
}
}
使用动画曲线 (Animation Curves) 驱动运动
更精细的做法是在动画中定义曲线参数。例如,添加一条名为“RunSpeed”的曲线。

在动画中定义自定义曲线,如“RunSpeed”,用于在代码中读取并控制移动速度。
同时,在Animator Controller中定义一个同名的Float参数。

在Animator Controller中创建与动画曲线同名的参数。
随后,便可在 OnAnimatorMove 中读取该曲线值:
public class MyMove : MonoBehaviour
{
void OnAnimatorMove()
{
Animator animator = GetComponent<Animator>();
// 获取当前动画帧对应的曲线值
float moveSpeed = animator.GetFloat("RunSpeed");
Vector3 newPos = transform.position;
newPos += transform.forward * moveSpeed * Time.deltaTime;
transform.position = newPos;
}
}
动画曲线还可用于动态控制角色控制器(CharacterController)的高度、偏移量等,以实现钻过障碍等效果。
动画层 (Animation Layers)
动画层用于管理角色不同身体部位的独立动画状态机,例如下层负责移动(走、跑、跳),上层负责上半身动作(投掷、射击、挥手)。
层的创建与配置
在Animator Controller的Layers面板中添加新层,并通过齿轮图标进入层设置。

Layers面板用于管理多个动画层。

层的关键配置:权重(Weight)、遮罩(AvatarMask)和混合模式(Blending)。
- Weight: 该层的影响权重,0表示完全禁用。
- Blending: 混合模式。
Override 会覆盖底层动画,Additive 会叠加到底层动画之上。
- Mask (AvatarMask): 指定该层动画影响哪些骨骼。例如,可以创建一个只包含上半身骨骼的遮罩,实现上半身单独播放投掷动画。

使用AvatarMask限定动画层影响的骨骼范围。图标“M”表示该层应用了遮罩。
层同步 (Layer Syncing)
层同步功能允许一个层(子层)复用另一个层(源层)的状态机结构,但使用自己的一套动画片段。常用于创建“受伤状态层”,其拥有与“基础移动层”相同的状态机(Idle, Walk, Run),但播放的是受伤版本的动画。
在层设置中选择“Sync”,并指定一个源层即可。

同步层会复制源层的状态机结构。图中“Fatigued”层同步自“Base Layer”。
- Timing 选项: 当同步层与源层的动画长度不同时,此选项控制如何同步。勾选后,会根据
Weight在两个动画的进度间插值;未勾选时,同步层的动画会被拉伸或压缩以匹配源层动画的时长。
其他高级功能
Animator Override Controller
当多个角色共享同一套状态机逻辑,但需要使用不同的动画资源时,可以使用Animator Override Controller。它继承自一个基础的Animator Controller,但允许你逐一替换其中的动画片段,极大提升了资源复用率。

在Animator Override Controller中覆盖基础控制器里的动画片段。
手动采样动画 (Sample Animation)
有时需要在编辑器模式或非游戏运行时播放动画(例如制作预览工具、生成烘焙数据)。这可以通过直接调用 AnimationClip.SampleAnimation(GameObject gameObject, float time) 方法实现。
Animator animator = GetComponent<Animator>();
AnimatorController ac = (AnimatorController)animator.runtimeAnimatorController;
AnimationClip[] clips = ac.animationClips;
AnimationClip animClip = clips[0];
// 在动画中点时间采样
animClip.SampleAnimation(gameObject, animClip.length * 0.5f);
混合树 (Blend Trees)
混合树是用于平滑混合多个动画的高级状态,特别适用于基于速度、方向等参数(如Forward, Strafe)的移动动画混合。由于其内容较为丰富,通常需要专题进行详解。