在 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 编辑器中配置 (零代码组装!)

  1. 打开 Boss 的 Animator 窗口。
  2. 找到名为 SmashAttack 的动画节点,选中它。
  3. 在 Inspector 面板里,点击 Add Behaviour 按钮。
  4. 选择我们刚才写的 BossSmashState 脚本。

就是这么简单! 你不需要写庞大的 PlayerStateMachine 去管理切换,你只需要在 Animator 里拉好线(配置好 Transitions 条件),当连线条件满足,动画切换过去时,这段代码就会自动接管 Boss 的行为!


🆚 进阶对比:纯 C# 状态机 vs Animator 状态机

既然有两个选择,实际开发中该用哪个呢?

对比维度 纯 C# 手写状态机 (上一次讲的) Animator 状态机 (StateMachineBehaviour)
优势 运行效率极高,完全由代码精确控制,不依赖动画组件,适合做纯逻辑(比如 UI 弹窗层级、游戏大阶段如开始/暂停/结算)。 极其直观的可视化连线,策划可以直接在面板配参数修改逻辑。能完美卡准动画的播放进度(如动作游戏的攻击判定帧)。
劣势 纯代码编写,缺乏可视化面板,对策划和美术不友好。状态一旦多了,阅读逻辑时需要在多个脚本间跳跃。 强行与 Animator 绑定,如果某个状态没有动画(比如纯粹的 AI 思考状态),用起来会很别扭。

💡 行业老兵的组合拳建议

在大型商业游戏(尤其是动作游戏 ARPG / 动作射击类)中,最完美的架构是将两者结合

  1. 宏观层用纯 C# FSM:控制角色的底层大状态(例如:存活状态、死亡状态、昏迷状态、载具状态)。
  2. 微观动作层用 Animator StateMachine:在“存活状态”下,将角色的移动、连段攻击、技能前摇/后摇判定全部交给 StateMachineBehaviour 来处理。

这样既保证了代码架构的严谨,又把动作调优的权力交给了擅长使用可视化面板的动作设计师!