Animator Controller
Animator Controller 是 Unity 掌管 Animation Clip 的可视化状态机,决定何时切换与如何混合 Idle、Walk、Run、Jump、Attack 等状态。适合角色与道具动画逻辑,让程序与动画师用 Parameters 和 Transitions 协作,而不是在脚本里硬切剪辑。
想象一下...
Animator Controller 是角色的舞台导演。它知道角色处于哪个状态(idle、run、jump),当条件满足(跳跃键、speed > 0)时,平滑过渡到下一段 clip。你把 States 声明为 clip,再画带条件的「箭头」(Transitions)——控制器会自行触发每条箭头。
概念详解
Animator Controller 是一种资源
(.controller),
含一个或多个 Layers,每层是独立状态机。Base 层通常放移动;上层可覆盖上半身
(边跑边扔)。每层有 Weight(0–1)与 Blending Mode
(Override/Additive)。
层内,States(橙/绿框)是 Animation Clips,
Transitions(箭头)定义何时切换。过渡有
Has Exit Time
(等 clip 播完)与
Transition Duration
(混合时间)。Parameters(Int、Float、Bool、Trigger)是 Animator 用来选过渡的变量。
Sub-State Machine 把复杂状态收成一个节点 (例如所有攻击变体放在 “Combat” 下)。Any State 很特殊——从 Any State 出发的过渡 可从当前任意状态触发,常用于 Death 或 Hit 打断。
Unity 2022+ 的 Animation Rigging 包在 Animator 之上加 IK: 运行时手/脚姿态(VR 手柄、攀爬),无需专用 clip。
基础角色状态机
动手步骤
创建 Animator Controller
Project → Create → Animator Controller。双击打开 Animator 窗口。把 Animation Clips 拖入即可创建 States。
添加 Parameters
Animator 窗口 → Parameters 标签(+)→ 添加 Float "Speed"、Bool "IsGrounded"、Trigger "Jump"。这些是 C# 要设置的变量。
创建带条件的 Transition
右键 State → Make Transition → 点击目标。选中 Transition → Inspector → Conditions → 添加(Speed > 0.1)。
赋给 GameObject
Add Component → Animator → 把 Controller 拖到 "Controller"。确认 Avatar 正确(Humanoid 设置)。
用脚本驱动
在 Update() 里 animator.SetFloat("Speed", velocity.magnitude)。用 Animator.StringToHash() 缓存哈希,避免字符串。
交互模拟器
点击状态跳转,或用控制按钮触发条件过渡。
代码示例
基础用 PlayerController 驱动 Animator 参数——基础移动:Speed 与 Jump 触发器。
using UnityEngine;
public class AnimatorDriver : MonoBehaviour
{
Animator anim;
// Cache hashes in Awake — faster than a string every frame
static readonly int SpeedHash = Animator.StringToHash("Speed");
static readonly int JumpHash = Animator.StringToHash("Jump");
static readonly int GroundHash = Animator.StringToHash("IsGrounded");
static readonly int DeathHash = Animator.StringToHash("Death");
void Awake() => anim = GetComponent<Animator>();
void Update()
{
Vector3 vel = GetComponent<Rigidbody>().velocity;
anim.SetFloat(SpeedHash, vel.magnitude, 0.1f, Time.deltaTime);
anim.SetBool(GroundHash, isGrounded);
if (Input.GetKeyDown(KeyCode.Space))
anim.SetTrigger(JumpHash); // Trigger auto-resets after 1 frame
}
public void Die()
{
anim.SetTrigger(DeathHash);
anim.SetLayerWeight(1, 0f); // Disable layer 1 on death
}
}代码示例
进阶PlayInFixedTime、CrossFade,以及读取当前状态——从代码做精细过渡。
using UnityEngine;
public class AdvancedAnimator : MonoBehaviour
{
Animator anim;
static readonly int BaseLayer = 0;
void Awake() => anim = GetComponent<Animator>();
// CrossFade: switch to a named state with a custom blend duration
public void PlayAttack(string attackName)
{
anim.CrossFadeInFixedTime(attackName, 0.15f, BaseLayer);
}
// Is the character in a given state (name or tag)
bool IsInState(string stateName)
{
return anim.GetCurrentAnimatorStateInfo(BaseLayer)
.IsName(stateName);
}
// Scale animation playback from gameplay
public void SetAttackSpeed(float speedMultiplier)
{
anim.SetFloat("AttackSpeed", speedMultiplier);
}
// Read normalized time to know how far the clip has played
void Update()
{
var info = anim.GetCurrentAnimatorStateInfo(BaseLayer);
if (info.normalizedTime >= 0.9f && info.IsTag("Attack"))
{
Debug.Log("Attack almost done — combo window");
}
}
}📌 快速记忆
- ▸Trigger 一帧后自动重置(Jump、Hit)
- ▸Animator.StringToHash() → 在 Awake 缓存,避免字符串 GC
- ▸Any State → 可从任意当前状态打断
- ▸SetFloat(hash, value, damping, dt) 让混合更平滑
- ▸normalizedTime:0 = clip 开始,1 = clip 结束
⚠️ 常见错误
❌ Trigger 无反应——状态从不切换
Has Exit Time 为 true 且 clip 未播完——触发器被忽略
✅ 对随时应触发的过渡关闭 Has Exit Time
❌ 动画不混合——硬切
Transition Duration = 0
✅ 把 Transition Duration 设为 0.1–0.25s 做混合