0x0d.Unity里面的状态模式
在 Unity 中,官方有一个极其强大的可视化状态模式工具。今天咱们就来点进阶的:**Unity 特供版状态模式 —— StateMachineBehaviour**!
💡 震撼真相:Animator 就是一个现成的状态机!
很多新手以为 Unity 的 Animator 窗口只是用来连线播动画的。大错特错!它的全称叫 Animator State Machine(动画状态机),它底层完全就是标准的“状态模式”架构。
Unity 允许你直接在 Animator 的每一个节点(State)上挂载特定的 C# 脚本。当动画进入、执行、退出该节点时,自动触发代码逻辑。这完美实现了状态与逻辑的绑定!
一、 如何使用 StateMachineBehaviour?
假设我们正在开发一个 Boss 战。Boss 有一个“砸地攻击(SmashAttack)”的状态。在这个状态下,Boss 无法移动,且在砸地瞬间会产生范围伤害。
1. 编写状态脚本
注意,这个脚本**不继承 MonoBehaviour**,而是继承 StateMachineBehaviour。
using UnityEngine;
// 这个脚本不挂载到 GameObject 上,而是挂载到 Animator 窗口的动画节点上!
public class BossSmashState : StateMachineBehaviour
{
public float damageRadius = 5f;
public int damageAmount = 50;
// 我们需要拿到 Boss 身上的控制脚本,方便调用它的方法
private BossController _boss;
// 1. 进入状态时调用 (相当于纯 C# 状态机里的 Enter)
public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
// 因为脚本不在 GameObject 上,所以我们需要通过 animator 去获取 BossController
if (_boss == null)
{
_boss = animator.GetComponent<BossController>();
}
Debug.Log("Boss 进入【砸地攻击】状态!");
// 锁定 Boss 移动
_boss.SetMovementLocked(true);
}
// 2. 状态持续期间,每帧调用 (相当于 Update)
public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
// 假设动画播放到 50% 的时候,是锤子落地的瞬间,触发伤害判定
// stateInfo.normalizedTime 表示动画播放的进度 (0~1)
if (stateInfo.normalizedTime >= 0.5f && stateInfo.normalizedTime <= 0.55f)
{
_boss.ApplyAOEDamage(damageRadius, damageAmount);
}
}
// 3. 离开状态时调用 (相当于 Exit)
public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Debug.Log("Boss 结束【砸地攻击】,恢复常态。");
// 解除移动锁定
_boss.SetMovementLocked(false);
}
}
2. 在 Unity 编辑器中配置 (零代码组装!)
- 打开 Boss 的
Animator窗口。 - 找到名为
SmashAttack的动画节点,选中它。 - 在 Inspector 面板里,点击
Add Behaviour按钮。 - 选择我们刚才写的
BossSmashState脚本。
就是这么简单! 你不需要写庞大的 PlayerStateMachine 去管理切换,你只需要在 Animator 里拉好线(配置好 Transitions 条件),当连线条件满足,动画切换过去时,这段代码就会自动接管 Boss 的行为!
🆚 进阶对比:纯 C# 状态机 vs Animator 状态机
既然有两个选择,实际开发中该用哪个呢?
| 对比维度 | 纯 C# 手写状态机 (上一次讲的) | Animator 状态机 (StateMachineBehaviour) |
|---|---|---|
| 优势 | 运行效率极高,完全由代码精确控制,不依赖动画组件,适合做纯逻辑(比如 UI 弹窗层级、游戏大阶段如开始/暂停/结算)。 | 极其直观的可视化连线,策划可以直接在面板配参数修改逻辑。能完美卡准动画的播放进度(如动作游戏的攻击判定帧)。 |
| 劣势 | 纯代码编写,缺乏可视化面板,对策划和美术不友好。状态一旦多了,阅读逻辑时需要在多个脚本间跳跃。 | 强行与 Animator 绑定,如果某个状态没有动画(比如纯粹的 AI 思考状态),用起来会很别扭。 |
💡 行业老兵的组合拳建议
在大型商业游戏(尤其是动作游戏 ARPG / 动作射击类)中,最完美的架构是将两者结合:
- 宏观层用纯 C# FSM:控制角色的底层大状态(例如:存活状态、死亡状态、昏迷状态、载具状态)。
- 微观动作层用 Animator StateMachine:在“存活状态”下,将角色的移动、连段攻击、技能前摇/后摇判定全部交给
StateMachineBehaviour来处理。
这样既保证了代码架构的严谨,又把动作调优的权力交给了擅长使用可视化面板的动作设计师!