第5章 状态管理

状态管理是游戏开发中的核心问题。角色需要在空闲、奔跑、跳跃、攻击等状态间切换;AI需要在巡逻、追击、攻击等状态间转换;UI需要在不同界面状态间导航。本章将介绍三种状态管理模式:有限状态机(FSM)、分层状态机(Hierarchical FSM)和下推自动机(Pushdown Automata)。

1. 设计原理与思路

1.1 状态机的数学模型

状态机的理论基础来自计算机科学中的有限自动机理论。一个有限状态机可以形式化地定义为五元组:

M = (S, Σ, δ, s₀, F)

其中:
- S:有限状态集合 {Idle, Run, Jump, Attack, ...}
- Σ:输入字母表(触发条件){InputMove, InputJump, InputAttack, ...}
- δ:状态转移函数 S × Σ → S
- s₀:初始状态
- F:接受状态集合(在游戏开发中较少使用)
状态机示意图:
┌─────────────────────────────────────────────────────────────────┐
│                    玩家状态机示例                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│     ┌──────────┐         InputJump          ┌──────────┐      │
│     │          │──────────────────────────▶│          │      │
│     │   Idle   │                            │   Jump   │      │
│     │          │◀──────────────────────────│          │      │
│     └────┬─────┘         OnGround           └──────────┘      │
│          │                                                      │
│   InputMove│                                         OnGround   │
│          │           ┌──────────┐          ┌──────────┐      │
│          └──────────▶│          │─────────▶│          │      │
│                      │   Run    │          │  Attack  │      │
│          ◀───────────│          │◀─────────│          │      │
│          NoInput     └──────────┘          └──────────┘      │
│                                              InputAttack      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.2 为什么要使用状态机

分离状态逻辑:每个状态封装自己的行为,避免复杂的if-else嵌套

// 不使用状态机:混乱的条件判断
public override void _Process(double delta)
{
    if (_isStunned)
    {
        // 眩晕逻辑
    }
    else if (_isAttacking)
    {
        // 攻击逻辑
    }
    else if (!_isGrounded)
    {
        // 空中逻辑
    }
    else if (_horizontalInput != 0)
    {
        // 移动逻辑
    }
    else
    {
        // 站立逻辑
    }
}

// 使用状态机:清晰的状态切换
public override void _Process(double delta)
{
    _stateMachine.Update((float)delta);
}

明确的转换规则:状态之间的转换条件清晰定义 易于扩展:添加新状态不会影响现有状态 可调试性:可以追踪当前状态和状态历史

1.3 FSM vs 分层状态机 vs 下推自动机的选择

有限状态机(FSM)

  • 适合状态数量较少(<10个)的简单场景
  • 状态转换关系清晰的场景
  • 玩家角色基础移动、简单AI

分层状态机(HFSM)

  • 解决状态爆炸问题(状态数量>20时)
  • 状态有明确的父子关系
  • 复杂角色AI、RPG角色的多种行为模式
分层状态机示例:
                    ┌──────────────┐
                    │   Grounded   │  ← 父状态(共享地面行为)
                    └──────┬───────┘
                           │
           ┌───────────────┼───────────────┐
           │               │               │
     ┌─────▼─────┐   ┌─────▼─────┐   ┌─────▼─────┐
     │   Idle    │   │    Run    │   │   Crouch  │  ← 子状态
     └───────────┘   └───────────┘   └───────────┘

     ┌──────────────┐
     │   InAir      │  ← 父状态(共享空中行为)
     └──────┬───────┘
            │
     ┌──────▼──────┐
     │   Jump      │  ← 子状态
     └─────────────┘

下推自动机(PDA)

  • 需要状态历史和返回功能的场景
  • UI导航、菜单系统
  • 游戏暂停机制
  • 支持任意层级的状态嵌套
特性FSMHFSMPDA
状态数量少(<10)多(分层管理)中等
转换复杂度简单中等复杂
状态历史有(父状态)有(完整栈)
使用场景简单角色复杂AIUI导航
实现复杂度中等

2. 使用场景分析

2.1 适用场景

AI行为:敌人AI需要在巡逻、追击、攻击、逃跑等状态间切换

// 敌人AI状态转换示例
_stateMachine.AddTransition("PatrolState", "ChaseState",
    ctx => ctx.CanSeePlayer());

_stateMachine.AddTransition("ChaseState", "AttackState",
    ctx => ctx.IsPlayerInAttackRange());

_stateMachine.AddTransition("AttackState", "FleeState",
    ctx => ctx.HealthPercent < 0.2f);

玩家状态:角色在移动、攻击、受伤、死亡等状态间转换 UI流程:菜单导航、对话框系统、商店界面

2.2 不适用场景

简单开关逻辑:只有一个布尔值的简单状态不需要状态机

// 不需要状态机
private bool _isVisible;

public void ToggleVisibility()
{
    _isVisible = !_isVisible;
    Visible = _isVisible;
}

// 对比:需要状态机的情况
private StateMachine _uiStateMachine;  // 处理复杂的UI状态

连续数值状态:血量、能量条等连续变化的数值 纯物理模拟:由物理引擎完全控制的运动

2.3 典型案例

敌人AI:巡逻时发现玩家追击,进入攻击范围后攻击,血量低时逃跑 玩家战斗状态:普通攻击可以连招,技能释放期间不能移动,受伤时进入硬直

敌人AI状态转换图:

┌──────────┐     发现玩家      ┌──────────┐
│  Patrol  │─────────────────▶│  Chase   │
│ (巡逻)   │◀─────────────────│ (追击)   │
└──────────┘  丢失玩家         └────┬─────┘
                                    │ 进入范围
                                    ▼
                              ┌──────────┐
                              │  Attack  │
                              │ (攻击)   │
                              └────┬─────┘
                                   │ 血量<20%
                                   ▼
                              ┌──────────┐
                              │   Flee   │
                              │ (逃跑)   │
                              └──────────┘

3. 实现细节讲解

3.1 状态切换的执行顺序

状态切换时,执行顺序至关重要:

public void ChangeState(string stateId)
{
    // 1. 保存当前状态ID
    string previousStateId = _currentState?.StateName ?? "None";

    // 2. 退出当前状态(执行清理逻辑)
    _currentState?.Exit();

    // 3. 切换状态引用
    _currentState = _states[stateId];

    // 4. 进入新状态(执行初始化逻辑)
    _currentState.Enter();

    // 5. 触发状态改变事件
    OnStateChanged?.Invoke(previousStateId, stateId);
}

为什么这样设计?

  • 确保当前状态的清理逻辑(如停止动画、重置变量)先执行
  • 避免两个状态同时活跃造成的冲突
  • 事件在状态切换完成后触发,订阅者可以获取正确的当前状态

3.2 如何处理状态间的共享数据

共享数据存储在上下文中:

public class PlayerStateContext
{
    // 只读引用(状态不能修改)
    public PlayerController Player { get; }
    public AnimationPlayer Animator { get; }

    // 可修改状态数据
    public float HorizontalInput { get; set; }
    public bool IsGrounded { get; set; }
    public float StateTime { get; set; }
}

// 状态可以读取和修改上下文
public class RunState : StateBase<PlayerStateContext>
{
    public override void Update(float deltaTime)
    {
        // 读取输入
        float input = Context.HorizontalInput;

        // 修改上下文中的计时器
        Context.StateTime += deltaTime;
    }
}

3.3 分层状态机的父状态与子状态关系

分层状态机的核心概念是状态继承

public override void Enter()
{
    // 1. 先调用父状态的Enter(如果存在)
    Parent?.OnChildEnter(this);

    // 2. 执行自身Enter
    base.Enter();

    // 3. 初始化子状态机(如果存在)
    SubStateMachine?.Initialize();
}

public override void Update(float deltaTime)
{
    // 1. 先执行自身Update
    base.Update(deltaTime);

    // 2. 再执行子状态机Update
    SubStateMachine?.Update(deltaTime);
}

父状态负责:

  • 处理所有子状态共享的行为(如地面检测)
  • 决定是否允许子状态转换
  • 在子状态进入/退出时执行通用逻辑
分层状态机的执行流程:

Frame N:
  ┌─────────────────────────────────────┐
  │ Grounded.Update()                   │ ← 父状态先执行
  │   - 检查是否还在地面                 │
  │   - 应用重力                         │
  └────────┬────────────────────────────┘
           │
           ▼
  ┌─────────────────────────────────────┐
  │ Run.Update()                        │ ← 子状态后执行
  │   - 处理输入                         │
  │   - 更新动画                         │
  └─────────────────────────────────────┘

注意:子状态可以覆盖父状态的行为

4. 性能与权衡

4.1 状态查找的时间复杂度

状态机操作复杂度分析:

1. 状态注册:O(1)
   _states[stateId] = state;

2. 状态查找:O(1)
   _states.TryGetValue(stateId, out var state)

3. 状态切换:O(1) + Enter/Exit执行时间
   _currentState?.Exit();
   _currentState = _states[stateId];
   _currentState.Enter();

4. 转换条件检查:O(n),n为当前状态的转换数
   foreach (var transition in _transitions[currentStateId])
   {
       if (transition.Condition(context))
           ChangeState(transition.ToStateId);
   }

4.2 状态对象的内存开销

状态对象的生命周期

  • 单例模式:所有状态对象预先创建,长期驻留内存
  • 临时模式:状态切换时创建新实例,立即销毁旧实例
// 单例模式(推荐):每个状态只创建一个实例
private void SetupStates()
{
    _stateMachine.RegisterState(new IdleState());  // 创建一次
    _stateMachine.RegisterState(new RunState());
    _stateMachine.RegisterState(new JumpState());
}

// 内存占用估算
// 假设每个状态对象约100字节
// 10个状态 = 1KB内存(可以忽略)

4.3 与行为树的对比

特性状态机行为树
可视化一般优秀(节点图)
AI复杂度中等
并行行为困难容易(Parallel节点)
中断处理需要AnyState自然支持(优先级)
学习曲线平缓陡峭
运行时调试状态名整棵树
性能高(O(1)切换)中等(每帧遍历)

何时选择行为树

  • AI需要复杂的决策逻辑
  • 需要可视化编辑器
  • 行为需要频繁中断和恢复

何时选择状态机

  • 状态数量有限且转换清晰
  • 需要高性能
  • 团队成员熟悉状态机概念

5. 实战指南

5.1 设计状态图的步骤

步骤1:识别所有状态

// 列出玩家所有可能的状态
public enum PlayerState
{
    Idle,      // 站立
    Run,       // 奔跑
    Jump,      // 跳跃中
    Fall,      // 下落中
    Attack,    // 攻击中
    Hurt,      // 受伤
    Die,       // 死亡
    Block      // 格挡
}

步骤2:绘制状态转换图

使用纸笔或工具(如draw.io)绘制状态转换图:

- 用圆角矩形表示状态
- 用箭头表示转换
- 在箭头上标注触发条件

示例:
  ┌─────┐   按下跳跃键    ┌─────┐
  │Idle │ ─────────────▶ │Jump │
  └─────┘               └─────┘
    ▲                      │
    │                      │着地
    └──────────────────────┘

步骤3:确定转换条件

// 为每条转换线编写条件
_stateMachine.AddTransition("Idle", "Jump",
    ctx => ctx.InputJump && ctx.IsGrounded);

_stateMachine.AddTransition("Jump", "Idle",
    ctx => ctx.IsGrounded);

_stateMachine.AddAnyStateTransition("Hurt",
    ctx => ctx.IsHit, priority: -10);  // 高优先级

步骤4:实现状态类

public class JumpState : StateBase<PlayerStateContext>
{
    public override void Enter()
    {
        // 播放跳跃动画
        Context.Animator.Play("jump");

        // 应用跳跃力
        Context.Player.Velocity = new Vector2(
            Context.Player.Velocity.X,
            -Context.JumpForce
        );
    }

    public override void PhysicsUpdate(float deltaTime)
    {
        // 空中控制
        float airControl = 0.3f;
        float targetX = Context.HorizontalInput * Context.MoveSpeed * airControl;
        Context.Player.Velocity = new Vector2(targetX, Context.Player.Velocity.Y);
    }

    public override void Exit()
    {
        // 着陆时可能播放着陆动画
    }
}

5.2 常见错误:状态循环依赖

问题描述:两个状态可以互相直接转换,导致无限循环

// 错误:Run和Attack可以互相直接转换
_stateMachine.AddTransition("Run", "Attack", ctx => ctx.InputAttack);
_stateMachine.AddTransition("Attack", "Run", ctx => !ctx.InputAttack);

// 问题:如果玩家按住攻击键,会每帧在Run和Attack间切换

解决方案

1. 添加冷却时间

_stateMachine.AddTransition("Run", "Attack",
    ctx => ctx.InputAttack && ctx.CanAttack);

// CanAttack在AttackState.Enter时设为false,0.5秒后设为true

2. 添加状态持续时间检查

_stateMachine.AddTransition("Attack", "Run",
    ctx => ctx.StateTime >= 0.3f && !ctx.InputAttack);

// StateTime在每次状态切换时重置为0

3. 使用中间状态

// Attack -> AttackRecovery -> Run
_stateMachine.AddTransition("Attack", "AttackRecovery",
    ctx => ctx.AnimationFinished);

_stateMachine.AddTransition("AttackRecovery", "Run",
    ctx => ctx.AnimationFinished);

5.3 调试技巧:状态可视化

实时状态显示

public partial class StateMachineDebugger : Label
{
    [Export] private StateMachine _targetStateMachine;

    public override void _Process(double delta)
    {
        if (_targetStateMachine?.CurrentState != null)
        {
            Text = $"State: {_targetStateMachine.CurrentStateId}\n" +
                   $"Time: {_targetStateMachine.Context.StateTime:F2}s";
        }
    }
}

状态历史记录

public class StateHistory
{
    private readonly Queue<string> _history = new();
    private const int MaxHistory = 10;

    public void RecordStateChange(string from, string to)
    {
        _history.Enqueue($"[{Time.GetTimeDict()["tick"]}] {from} -> {to}");

        if (_history.Count > MaxHistory)
            _history.Dequeue();
    }

    public void PrintHistory()
    {
        GD.Print("=== 状态历史 ===");
        foreach (var record in _history)
        {
            GD.Print(record);
        }
    }
}

可视化调试工具

public partial class StateMachineVisualizer : Control
{
    private Dictionary<string, Vector2> _statePositions;
    private StateMachine _stateMachine;

    public override void _Draw()
    {
        // 绘制状态节点
        foreach (var state in _stateMachine.GetAllStates())
        {
            var pos = _statePositions[state.StateName];
            DrawCircle(pos, 30, state == _stateMachine.CurrentState ? Colors.Green : Colors.Gray);
            DrawString(GetThemeFont("font"), pos - new Vector2(20, 0), state.StateName);
        }

        // 绘制转换线
        foreach (var transition in _stateMachine.GetAllTransitions())
        {
            var from = _statePositions[transition.FromState];
            var to = _statePositions[transition.ToState];
            DrawLine(from, to, Colors.White, 2);
        }
    }
}

5.1 有限状态机(FSM)基础实现

有限状态机是最基础也是最常用的状态管理模式。它包含有限个状态,在任意时刻只能处于其中一个状态,通过触发条件在不同状态间转换。

5.1.1 IState 接口设计

首先定义状态接口,规定所有状态必须实现的方法。

// 文件路径: Scripts/Framework/StateMachine/IState.cs

using System;

namespace GameFramework.StateMachine
{
    /// <summary>
    /// 状态接口 - 定义状态的基本生命周期方法
    /// </summary>
    public interface IState
    {
        /// <summary>
        /// 进入状态时调用
        /// </summary>
        void Enter();

        /// <summary>
        /// 退出状态时调用
        /// </summary>
        void Exit();

        /// <summary>
        /// 每帧更新时调用
        /// </summary>
        /// <param name="deltaTime">帧间隔时间</param>
        void Update(float deltaTime);

        /// <summary>
        /// 物理更新时调用
        /// </summary>
        /// <param name="deltaTime">物理帧间隔时间</param>
        void PhysicsUpdate(float deltaTime);

        /// <summary>
        /// 获取状态名称
        /// </summary>
        string StateName { get; }
    }

    /// <summary>
    /// 泛型状态接口 - 允许状态访问上下文对象
    /// </summary>
    /// <typeparam name="T">上下文类型</typeparam>
    public interface IState<T> : IState
    {
        /// <summary>
        /// 设置上下文对象
        /// </summary>
        /// <param name="context">上下文对象</param>
        void SetContext(T context);

        /// <summary>
        /// 获取上下文对象
        /// </summary>
        T Context { get; }
    }
}

5.1.2 StateMachine 类实现

接下来实现状态机核心类,支持状态注册、切换和条件触发器。

// 文件路径: Scripts/Framework/StateMachine/StateMachine.cs

using System;
using System.Collections.Generic;
using Godot;

namespace GameFramework.StateMachine
{
    /// <summary>
    /// 状态转换条件委托
    /// </summary>
    /// <typeparam name="T">上下文类型</typeparam>
    /// <returns>是否满足转换条件</returns>
    public delegate bool StateCondition<T>(T context);

    /// <summary>
    /// 状态转换信息
    /// </summary>
    /// <typeparam name="T">上下文类型</typeparam>
    public class StateTransition<T>
    {
        /// <summary>
        /// 目标状态ID
        /// </summary>
        public string ToStateId { get; }

        /// <summary>
        /// 转换条件
        /// </summary>
        public StateCondition<T> Condition { get; }

        /// <summary>
        /// 优先级(数字越小优先级越高)
        /// </summary>
        public int Priority { get; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="toStateId">目标状态ID</param>
        /// <param name="condition">转换条件</param>
        /// <param name="priority">优先级</param>
        public StateTransition(string toStateId, StateCondition<T> condition, int priority = 0)
        {
            ToStateId = toStateId;
            Condition = condition;
            Priority = priority;
        }
    }

    /// <summary>
    /// 状态机类 - 管理状态的生命周期和转换
    /// </summary>
    /// <typeparam name="T">上下文类型</typeparam>
    public class StateMachine<T>
    {
        #region 字段

        // 存储所有注册的状态
        private readonly Dictionary<string, IState<T>> _states = new();

        // 存储每个状态的转换规则
        private readonly Dictionary<string, List<StateTransition<T>>> _transitions = new();

        // 当前激活的状态
        private IState<T> _currentState;

        // 默认状态ID
        private string _defaultStateId;

        // 是否已初始化
        private bool _isInitialized;

        #endregion

        #region 属性

        /// <summary>
        /// 当前状态
        /// </summary>
        public IState<T> CurrentState => _currentState;

        /// <summary>
        /// 当前状态ID
        /// </summary>
        public string CurrentStateId => _currentState?.StateName ?? "None";

        /// <summary>
        /// 上下文对象
        /// </summary>
        public T Context { get; private set; }

        /// <summary>
        /// 状态机是否正在运行
        /// </summary>
        public bool IsRunning => _currentState != null;

        /// <summary>
        /// 状态切换事件
        /// </summary>
        public event Action<string, string> OnStateChanged;

        #endregion

        #region 构造函数

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="context">上下文对象</param>
        public StateMachine(T context)
        {
            Context = context;
        }

        #endregion

        #region 状态注册

        /// <summary>
        /// 注册状态
        /// </summary>
        /// <param name="stateId">状态ID</param>
        /// <param name="state">状态实例</param>
        public void RegisterState(string stateId, IState<T> state)
        {
            if (_states.ContainsKey(stateId))
            {
                GD.PushWarning($"状态 '{stateId}' 已存在,将被覆盖");
                _states.Remove(stateId);
            }

            // 设置状态的上下文
            state.SetContext(Context);

            // 注册状态
            _states[stateId] = state;

            // 为该状态初始化转换列表
            if (!_transitions.ContainsKey(stateId))
            {
                _transitions[stateId] = new List<StateTransition<T>>();
            }
        }

        /// <summary>
        /// 注册状态(使用状态名称作为ID)
        /// </summary>
        /// <param name="state">状态实例</param>
        public void RegisterState(IState<T> state)
        {
            RegisterState(state.StateName, state);
        }

        /// <summary>
        /// 设置默认状态
        /// </summary>
        /// <param name="stateId">默认状态ID</param>
        public void SetDefaultState(string stateId)
        {
            _defaultStateId = stateId;
        }

        #endregion

        #region 转换规则配置

        /// <summary>
        /// 添加状态转换
        /// </summary>
        /// <param name="fromStateId">起始状态ID</param>
        /// <param name="toStateId">目标状态ID</param>
        /// <param name="condition">转换条件</param>
        /// <param name="priority">优先级</param>
        public void AddTransition(string fromStateId, string toStateId, StateCondition<T> condition, int priority = 0)
        {
            if (!_states.ContainsKey(fromStateId))
            {
                GD.PushError($"无法添加转换:起始状态 '{fromStateId}' 未注册");
                return;
            }

            if (!_states.ContainsKey(toStateId))
            {
                GD.PushError($"无法添加转换:目标状态 '{toStateId}' 未注册");
                return;
            }

            var transition = new StateTransition<T>(toStateId, condition, priority);
            _transitions[fromStateId].Add(transition);

            // 按优先级排序
            _transitions[fromStateId].Sort((a, b) => a.Priority.CompareTo(b.Priority));
        }

        /// <summary>
        /// 添加任意状态到目标状态的转换
        /// </summary>
        /// <param name="toStateId">目标状态ID</param>
        /// <param name="condition">转换条件</param>
        /// <param name="priority">优先级</param>
        public void AddAnyStateTransition(string toStateId, StateCondition<T> condition, int priority = -1)
        {
            if (!_states.ContainsKey(toStateId))
            {
                GD.PushError($"无法添加转换:目标状态 '{toStateId}' 未注册");
                return;
            }

            // 使用特殊键 "*" 表示任意状态
            if (!_transitions.ContainsKey("*"))
            {
                _transitions["*"] = new List<StateTransition<T>>();
            }

            var transition = new StateTransition<T>(toStateId, condition, priority);
            _transitions["*"].Add(transition);
            _transitions["*"].Sort((a, b) => a.Priority.CompareTo(b.Priority));
        }

        /// <summary>
        /// 移除转换
        /// </summary>
        /// <param name="fromStateId">起始状态ID</param>
        /// <param name="toStateId">目标状态ID</param>
        public void RemoveTransition(string fromStateId, string toStateId)
        {
            if (_transitions.ContainsKey(fromStateId))
            {
                _transitions[fromStateId].RemoveAll(t => t.ToStateId == toStateId);
            }
        }

        #endregion

        #region 状态控制

        /// <summary>
        /// 初始化状态机
        /// </summary>
        public void Initialize()
        {
            if (_isInitialized)
            {
                return;
            }

            if (string.IsNullOrEmpty(_defaultStateId))
            {
                GD.PushError("状态机初始化失败:未设置默认状态");
                return;
            }

            if (!_states.ContainsKey(_defaultStateId))
            {
                GD.PushError($"状态机初始化失败:默认状态 '{_defaultStateId}' 未注册");
                return;
            }

            // 进入默认状态
            ChangeState(_defaultStateId);
            _isInitialized = true;
        }

        /// <summary>
        /// 强制切换到指定状态
        /// </summary>
        /// <param name="stateId">目标状态ID</param>
        public void ChangeState(string stateId)
        {
            if (!_states.ContainsKey(stateId))
            {
                GD.PushError($"无法切换到状态 '{stateId}':状态未注册");
                return;
            }

            // 保存上一个状态ID
            string previousStateId = _currentState?.StateName ?? "None";

            // 退出当前状态
            _currentState?.Exit();

            // 切换状态
            _currentState = _states[stateId];

            // 进入新状态
            _currentState.Enter();

            // 触发状态改变事件
            OnStateChanged?.Invoke(previousStateId, stateId);

            GD.Print($"[{GetType().Name}] 状态切换: {previousStateId} -> {stateId}");
        }

        /// <summary>
        /// 触发器 - 检查并执行状态转换
        /// </summary>
        /// <param name="triggerName">触发器名称</param>
        /// <returns>是否成功转换</returns>
        public bool Trigger(string triggerName)
        {
            if (_currentState == null)
            {
                return false;
            }

            string currentStateId = _currentState.StateName;

            // 检查当前状态的转换
            if (_transitions.ContainsKey(currentStateId))
            {
                foreach (var transition in _transitions[currentStateId])
                {
                    // 检查触发器名称匹配(这里简化处理,实际可以扩展)
                    if (transition.Condition?.Invoke(Context) == true)
                    {
                        ChangeState(transition.ToStateId);
                        return true;
                    }
                }
            }

            // 检查任意状态的转换
            if (_transitions.ContainsKey("*"))
            {
                foreach (var transition in _transitions["*"])
                {
                    if (transition.Condition?.Invoke(Context) == true)
                    {
                        ChangeState(transition.ToStateId);
                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// 更新状态机
        /// </summary>
        /// <param name="deltaTime">帧间隔时间</param>
        public void Update(float deltaTime)
        {
            if (!_isInitialized)
            {
                return;
            }

            // 检查自动转换
            CheckTransitions();

            // 更新当前状态
            _currentState?.Update(deltaTime);
        }

        /// <summary>
        /// 物理更新
        /// </summary>
        /// <param name="deltaTime">物理帧间隔时间</param>
        public void PhysicsUpdate(float deltaTime)
        {
            if (!_isInitialized)
            {
                return;
            }

            _currentState?.PhysicsUpdate(deltaTime);
        }

        /// <summary>
        /// 检查转换条件
        /// </summary>
        private void CheckTransitions()
        {
            if (_currentState == null)
            {
                return;
            }

            string currentStateId = _currentState.StateName;

            // 检查任意状态的转换(优先级更高)
            if (_transitions.ContainsKey("*"))
            {
                foreach (var transition in _transitions["*"])
                {
                    if (transition.Condition?.Invoke(Context) == true)
                    {
                        ChangeState(transition.ToStateId);
                        return;
                    }
                }
            }

            // 检查当前状态的转换
            if (_transitions.ContainsKey(currentStateId))
            {
                foreach (var transition in _transitions[currentStateId])
                {
                    if (transition.Condition?.Invoke(Context) == true)
                    {
                        ChangeState(transition.ToStateId);
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// 停止状态机
        /// </summary>
        public void Stop()
        {
            _currentState?.Exit();
            _currentState = null;
            _isInitialized = false;
        }

        #endregion

        #region 工具方法

        /// <summary>
        /// 获取状态实例
        /// </summary>
        /// <param name="stateId">状态ID</param>
        /// <returns>状态实例</returns>
        public IState<T> GetState(string stateId)
        {
            return _states.TryGetValue(stateId, out var state) ? state : null;
        }

        /// <summary>
        /// 检查是否处于指定状态
        /// </summary>
        /// <param name="stateId">状态ID</param>
        /// <returns>是否处于该状态</returns>
        public bool IsInState(string stateId)
        {
            return _currentState?.StateName == stateId;
        }

        /// <summary>
        /// 检查是否存在指定状态
        /// </summary>
        /// <param name="stateId">状态ID</param>
        /// <returns>是否存在</returns>
        public bool HasState(string stateId)
        {
            return _states.ContainsKey(stateId);
        }

        #endregion
    }
}

5.1.3 基础状态抽象类

为了简化状态实现,提供抽象基类。

// 文件路径: Scripts/Framework/StateMachine/StateBase.cs

using System;

namespace GameFramework.StateMachine
{
    /// <summary>
    /// 状态抽象基类
    /// </summary>
    /// <typeparam name="T">上下文类型</typeparam>
    public abstract class StateBase<T> : IState<T>
    {
        /// <summary>
        /// 上下文对象
        /// </summary>
        public T Context { get; private set; }

        /// <summary>
        /// 状态名称(默认使用类名)
        /// </summary>
        public virtual string StateName => GetType().Name;

        /// <summary>
        /// 设置上下文
        /// </summary>
        /// <param name="context">上下文对象</param>
        public void SetContext(T context)
        {
            Context = context;
        }

        /// <summary>
        /// 进入状态
        /// </summary>
        public virtual void Enter()
        {
            // 子类可重写
        }

        /// <summary>
        /// 退出状态
        /// </summary>
        public virtual void Exit()
        {
            // 子类可重写
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="deltaTime">帧间隔时间</param>
        public virtual void Update(float deltaTime)
        {
            // 子类可重写
        }

        /// <summary>
        /// 物理更新
        /// </summary>
        /// <param name="deltaTime">物理帧间隔时间</param>
        public virtual void PhysicsUpdate(float deltaTime)
        {
            // 子类可重写
        }

        /// <summary>
        /// 切换到指定状态
        /// </summary>
        /// <param name="stateId">目标状态ID</param>
        protected void ChangeState(string stateId)
        {
            // 通过上下文获取状态机并切换
            // 具体实现取决于上下文的设计
        }
    }
}

5.2 分层状态机(Hierarchical FSM)

当状态数量增多时,普通FSM会出现转换爆炸问题。分层状态机通过状态嵌套解决这个问题,子状态可以继承父状态的行为。

5.2.1 分层状态机实现

// 文件路径: Scripts/Framework/StateMachine/HierarchicalStateMachine.cs

using System;
using System.Collections.Generic;
using Godot;

namespace GameFramework.StateMachine
{
    /// <summary>
    /// 分层状态接口
    /// </summary>
    /// <typeparam name="T">上下文类型</typeparam>
    public interface IHierarchicalState<T> : IState<T>
    {
        /// <summary>
        /// 父状态
        /// </summary>
        IHierarchicalState<T> Parent { get; set; }

        /// <summary>
        /// 子状态机(如果有)
        /// </summary>
        HierarchicalStateMachine<T> SubStateMachine { get; }

        /// <summary>
        /// 是否有子状态机
        /// </summary>
        bool HasSubStateMachine { get; }

        /// <summary>
        /// 初始化子状态机
        /// </summary>
        /// <param name="subStateMachine">子状态机</param>
        void SetSubStateMachine(HierarchicalStateMachine<T> subStateMachine);
    }

    /// <summary>
    /// 分层状态抽象基类
    /// </summary>
    /// <typeparam name="T">上下文类型</typeparam>
    public abstract class HierarchicalStateBase<T> : StateBase<T>, IHierarchicalState<T>
    {
        /// <summary>
        /// 父状态
        /// </summary>
        public IHierarchicalState<T> Parent { get; set; }

        /// <summary>
        /// 子状态机
        /// </summary>
        public HierarchicalStateMachine<T> SubStateMachine { get; private set; }

        /// <summary>
        /// 是否有子状态机
        /// </summary>
        public bool HasSubStateMachine => SubStateMachine != null;

        /// <summary>
        /// 设置子状态机
        /// </summary>
        /// <param name="subStateMachine">子状态机</param>
        public void SetSubStateMachine(HierarchicalStateMachine<T> subStateMachine)
        {
            SubStateMachine = subStateMachine;
            if (SubStateMachine != null)
            {
                SubStateMachine.ParentState = this;
            }
        }

        /// <summary>
        /// 进入状态(调用父状态后再调用子状态)
        /// </summary>
        public override void Enter()
        {
            // 先调用父状态的Enter
            Parent?.OnChildEnter(this);

            base.Enter();

            // 如果有子状态机,初始化它
            SubStateMachine?.Initialize();
        }

        /// <summary>
        /// 退出状态(先退出子状态)
        /// </summary>
        public override void Exit()
        {
            // 先停止子状态机
            SubStateMachine?.Stop();

            base.Exit();

            // 通知父状态
            Parent?.OnChildExit(this);
        }

        /// <summary>
        /// 更新(先更新自身,再更新子状态机)
        /// </summary>
        public override void Update(float deltaTime)
        {
            base.Update(deltaTime);
            SubStateMachine?.Update(deltaTime);
        }

        /// <summary>
        /// 物理更新
        /// </summary>
        public override void PhysicsUpdate(float deltaTime)
        {
            base.PhysicsUpdate(deltaTime);
            SubStateMachine?.PhysicsUpdate(deltaTime);
        }

        /// <summary>
        /// 子状态进入时调用
        /// </summary>
        /// <param name="childState">子状态</param>
        protected virtual void OnChildEnter(IHierarchicalState<T> childState)
        {
        }

        /// <summary>
        /// 子状态退出时调用
        /// </summary>
        /// <param name="childState">子状态</param>
        protected virtual void OnChildExit(IHierarchicalState<T> childState)
        {
        }
    }

    /// <summary>
    /// 分层状态机类
    /// </summary>
    /// <typeparam name="T">上下文类型</typeparam>
    public class HierarchicalStateMachine<T> : StateMachine<T>
    {
        /// <summary>
        /// 父状态
        /// </summary>
        public IHierarchicalState<T> ParentState { get; set; }

        /// <summary>
        /// 根状态机
        /// </summary>
        public HierarchicalStateMachine<T> RootStateMachine { get; set; }

        /// <summary>
        /// 完整路径
        /// </summary>
        public string FullPath
        {
            get
            {
                if (ParentState == null)
                {
                    return CurrentStateId;
                }
                return $"{ParentState.StateName}/{CurrentStateId}";
            }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="context">上下文对象</param>
        public HierarchicalStateMachine(T context) : base(context)
        {
        }

        /// <summary>
        /// 重写状态切换,通知父状态
        /// </summary>
        public new void ChangeState(string stateId)
        {
            base.ChangeState(stateId);
        }

        /// <summary>
        /// 向上层状态机请求切换
        /// </summary>
        /// <param name="targetStateId">目标状态ID</param>
        public void RequestTransition(string targetStateId)
        {
            // 如果父状态存在,通知父状态处理
            if (ParentState != null)
            {
                // 父状态决定是否处理这个转换
                // 如果不处理,继续向上传递
            }
        }
    }
}

5.3 下推自动机(Pushdown Automata)实现

下推自动机使用栈结构管理状态,支持状态的历史记录和返回上一状态,非常适合UI导航、游戏暂停等场景。

5.3.1 下推状态机实现

// 文件路径: Scripts/Framework/StateMachine/PushdownStateMachine.cs

using System;
using System.Collections.Generic;
using Godot;

namespace GameFramework.StateMachine
{
    /// <summary>
    /// 下推自动机状态机 - 使用栈管理状态
    /// </summary>
    /// <typeparam name="T">上下文类型</typeparam>
    public class PushdownStateMachine<T>
    {
        #region 字段

        // 状态栈
        private readonly Stack<IState<T>> _stateStack = new();

        // 所有注册的状态
        private readonly Dictionary<string, IState<T>> _states = new();

        // 状态历史记录(用于调试)
        private readonly List<string> _stateHistory = new();

        // 最大历史记录数
        private const int MaxHistoryCount = 50;

        #endregion

        #region 属性

        /// <summary>
        /// 当前状态(栈顶)
        /// </summary>
        public IState<T> CurrentState => _stateStack.Count > 0 ? _stateStack.Peek() : null;

        /// <summary>
        /// 当前状态ID
        /// </summary>
        public string CurrentStateId => CurrentState?.StateName ?? "None";

        /// <summary>
        /// 栈中状态数量
        /// </summary>
        public int StackCount => _stateStack.Count;

        /// <summary>
        /// 上下文对象
        /// </summary>
        public T Context { get; private set; }

        /// <summary>
        /// 状态改变事件
        /// </summary>
        public event Action<string, string> OnStateChanged;

        /// <summary>
        /// 状态弹出事件
        /// </summary>
        public event Action<string> OnStatePopped;

        #endregion

        #region 构造函数

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="context">上下文对象</param>
        public PushdownStateMachine(T context)
        {
            Context = context;
        }

        #endregion

        #region 状态注册

        /// <summary>
        /// 注册状态
        /// </summary>
        /// <param name="stateId">状态ID</param>
        /// <param name="state">状态实例</param>
        public void RegisterState(string stateId, IState<T> state)
        {
            if (_states.ContainsKey(stateId))
            {
                GD.PushWarning($"状态 '{stateId}' 已存在,将被覆盖");
            }

            state.SetContext(Context);
            _states[stateId] = state;
        }

        /// <summary>
        /// 注册状态(使用类名作为ID)
        /// </summary>
        /// <param name="state">状态实例</param>
        public void RegisterState(IState<T> state)
        {
            RegisterState(state.StateName, state);
        }

        #endregion

        #region 栈操作

        /// <summary>
        /// 推入新状态(暂停当前状态,进入新状态)
        /// </summary>
        /// <param name="stateId">状态ID</param>
        public void PushState(string stateId)
        {
            if (!_states.ContainsKey(stateId))
            {
                GD.PushError($"无法推入状态 '{stateId}':状态未注册");
                return;
            }

            string previousStateId = CurrentStateId;

            // 暂停当前状态
            if (_stateStack.Count > 0)
            {
                var current = _stateStack.Peek();
                OnStatePaused(current);
            }

            // 推入新状态
            var newState = _states[stateId];
            _stateStack.Push(newState);

            // 进入新状态
            newState.Enter();

            // 记录历史
            AddToHistory($"Push: {stateId}");

            // 触发事件
            OnStateChanged?.Invoke(previousStateId, stateId);

            GD.Print($"[PushdownStateMachine] Push: {previousStateId} -> {stateId} (Stack: {_stateStack.Count})");
        }

        /// <summary>
        /// 弹出当前状态(返回上一状态)
        /// </summary>
        /// <returns>是否成功弹出</returns>
        public bool PopState()
        {
            if (_stateStack.Count == 0)
            {
                GD.PushWarning("状态栈为空,无法弹出");
                return false;
            }

            string poppedStateId = CurrentStateId;

            // 退出当前状态
            var current = _stateStack.Pop();
            current.Exit();

            // 触发弹出事件
            OnStatePopped?.Invoke(poppedStateId);

            string newStateId = CurrentStateId;

            // 恢复上一状态
            if (_stateStack.Count > 0)
            {
                var resumed = _stateStack.Peek();
                OnStateResumed(resumed);
            }

            // 记录历史
            AddToHistory($"Pop: {poppedStateId} -> {newStateId}");

            // 触发事件
            OnStateChanged?.Invoke(poppedStateId, newStateId);

            GD.Print($"[PushdownStateMachine] Pop: {poppedStateId} -> {newStateId} (Stack: {_stateStack.Count})");

            return true;
        }

        /// <summary>
        /// 替换当前状态(不保存历史)
        /// </summary>
        /// <param name="stateId">新状态ID</param>
        public void ReplaceState(string stateId)
        {
            if (!_states.ContainsKey(stateId))
            {
                GD.PushError($"无法替换状态 '{stateId}':状态未注册");
                return;
            }

            if (_stateStack.Count == 0)
            {
                PushState(stateId);
                return;
            }

            string previousStateId = CurrentStateId;

            // 退出并移除当前状态
            var current = _stateStack.Pop();
            current.Exit();

            // 推入新状态
            var newState = _states[stateId];
            _stateStack.Push(newState);
            newState.Enter();

            // 记录历史
            AddToHistory($"Replace: {previousStateId} -> {stateId}");

            // 触发事件
            OnStateChanged?.Invoke(previousStateId, stateId);

            GD.Print($"[PushdownStateMachine] Replace: {previousStateId} -> {stateId}");
        }

        /// <summary>
        /// 清空栈并设置新状态
        /// </summary>
        /// <param name="stateId">状态ID</param>
        public void ClearAndPush(string stateId)
        {
            // 清空所有状态
            while (_stateStack.Count > 0)
            {
                var state = _stateStack.Pop();
                state.Exit();
            }

            // 推入新状态
            PushState(stateId);

            AddToHistory($"ClearAndPush: {stateId}");
        }

        /// <summary>
        /// 返回到指定状态(弹出栈直到该状态)
        /// </summary>
        /// <param name="stateId">目标状态ID</param>
        /// <returns>是否找到该状态</returns>
        public bool PopToState(string stateId)
        {
            // 检查状态是否在栈中
            var tempStack = new Stack<IState<T>>();
            bool found = false;

            while (_stateStack.Count > 0)
            {
                var state = _stateStack.Pop();
                if (state.StateName == stateId)
                {
                    found = true;
                    _stateStack.Push(state); // 放回去
                    break;
                }
                else
                {
                    state.Exit();
                    tempStack.Push(state);
                }
            }

            if (found)
            {
                // 恢复找到的状态
                if (_stateStack.Count > 0)
                {
                    var resumed = _stateStack.Peek();
                    OnStateResumed(resumed);
                }

                AddToHistory($"PopTo: {stateId}");
            }
            else
            {
                // 恢复栈
                while (tempStack.Count > 0)
                {
                    _stateStack.Push(tempStack.Pop());
                }
                GD.PushWarning($"状态 '{stateId}' 不在栈中");
            }

            return found;
        }

        /// <summary>
        /// 状态暂停时调用(可被重写)
        /// </summary>
        /// <param name="state">被暂停的状态</param>
        protected virtual void OnStatePaused(IState<T> state)
        {
            // 默认实现:状态保持,但停止更新
            // 子类可以重写以实现特定行为(如暂停动画)
        }

        /// <summary>
        /// 状态恢复时调用(可被重写)
        /// </summary>
        /// <param name="state">被恢复的状态</param>
        protected virtual void OnStateResumed(IState<T> state)
        {
            // 默认实现
        }

        #endregion

        #region 更新

        /// <summary>
        /// 更新当前状态
        /// </summary>
        /// <param name="deltaTime">帧间隔时间</param>
        public void Update(float deltaTime)
        {
            CurrentState?.Update(deltaTime);
        }

        /// <summary>
        /// 物理更新
        /// </summary>
        /// <param name="deltaTime">物理帧间隔时间</param>
        public void PhysicsUpdate(float deltaTime)
        {
            CurrentState?.PhysicsUpdate(deltaTime);
        }

        #endregion

        #region 工具方法

        /// <summary>
        /// 获取栈中所有状态(从底到顶)
        /// </summary>
        /// <returns>状态列表</returns>
        public List<IState<T>> GetStackStates()
        {
            return new List<IState<T>>(_stateStack);
        }

        /// <summary>
        /// 获取栈中所有状态ID
        /// </summary>
        /// <returns>状态ID列表</returns>
        public List<string> GetStackStateIds()
        {
            var ids = new List<string>();
            foreach (var state in _stateStack)
            {
                ids.Add(state.StateName);
            }
            return ids;
        }

        /// <summary>
        /// 检查栈中是否包含指定状态
        /// </summary>
        /// <param name="stateId">状态ID</param>
        /// <returns>是否包含</returns>
        public bool ContainsState(string stateId)
        {
            foreach (var state in _stateStack)
            {
                if (state.StateName == stateId)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// 获取状态历史
        /// </summary>
        /// <returns>历史记录</returns>
        public IReadOnlyList<string> GetStateHistory()
        {
            return _stateHistory.AsReadOnly();
        }

        /// <summary>
        /// 清空历史
        /// </summary>
        public void ClearHistory()
        {
            _stateHistory.Clear();
        }

        /// <summary>
        /// 添加到历史
        /// </summary>
        private void AddToHistory(string record)
        {
            _stateHistory.Add($"[{DateTime.Now:HH:mm:ss.fff}] {record}");
            if (_stateHistory.Count > MaxHistoryCount)
            {
                _stateHistory.RemoveAt(0);
            }
        }

        /// <summary>
        /// 清空状态机
        /// </summary>
        public void Clear()
        {
            while (_stateStack.Count > 0)
            {
                var state = _stateStack.Pop();
                state.Exit();
            }
            _stateHistory.Clear();
        }

        #endregion
    }
}

5.4 完整示例:PlayerStateMachine

下面展示一个完整的玩家状态机实现,包含Idle、Run、Jump、Attack四个状态。

5.4.1 玩家状态上下文

// 文件路径: Scripts/Player/PlayerStateContext.cs

using Godot;

namespace GameFramework.Player
{
    /// <summary>
    /// 玩家状态上下文 - 保存玩家状态机所需的数据
    /// </summary>
    public class PlayerStateContext
    {
        #region 引用

        /// <summary>
        /// 玩家节点
        /// </summary>
        public PlayerController Player { get; }

        /// <summary>
        /// 动画播放器
        /// </summary>
        public AnimationPlayer Animator { get; }

        #endregion

        #region 输入状态

        /// <summary>
        /// 水平输入
        /// </summary>
        public float HorizontalInput { get; set; }

        /// <summary>
        /// 是否按下跳跃键
        /// </summary>
        public bool JumpPressed { get; set; }

        /// <summary>
        /// 是否按下攻击键
        /// </summary>
        public bool AttackPressed { get; set; }

        #endregion

        #region 物理状态

        /// <summary>
        /// 是否着地
        /// </summary>
        public bool IsGrounded { get; set; }

        /// <summary>
        /// 垂直速度
        /// </summary>
        public float VerticalVelocity { get; set; }

        #endregion

        #region 配置参数

        /// <summary>
        /// 移动速度
        /// </summary>
        public float MoveSpeed { get; }

        /// <summary>
        /// 跳跃力
        /// </summary>
        public float JumpForce { get; }

        /// <summary>
        /// 重力
        /// </summary>
        public float Gravity { get; }

        /// <summary>
        /// 攻击冷却时间
        /// </summary>
        public float AttackCooldown { get; }

        #endregion

        #region 运行时数据

        /// <summary>
        /// 当前状态持续时间
        /// </summary>
        public float StateTime { get; set; }

        /// <summary>
        /// 最后攻击时间
        /// </summary>
        public float LastAttackTime { get; set; } = float.MinValue;

        /// <summary>
        /// 连击计数
        /// </summary>
        public int ComboCount { get; set; }

        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="player">玩家控制器</param>
        public PlayerStateContext(PlayerController player)
        {
            Player = player;
            Animator = player.GetNode<AnimationPlayer>("AnimationPlayer");

            // 从配置读取参数
            MoveSpeed = player.MoveSpeed;
            JumpForce = player.JumpForce;
            Gravity = player.Gravity;
            AttackCooldown = player.AttackCooldown;
        }

        /// <summary>
        /// 更新输入状态
        /// </summary>
        public void UpdateInput()
        {
            HorizontalInput = Input.GetActionStrength("move_right") - Input.GetActionStrength("move_left");
            JumpPressed = Input.IsActionJustPressed("jump");
            AttackPressed = Input.IsActionJustPressed("attack");
        }

        /// <summary>
        /// 是否可以攻击
        /// </summary>
        public bool CanAttack => Time.GetTimeDict()["tick"] / 1000f - LastAttackTime >= AttackCooldown;
    }
}

5.4.2 玩家控制器

// 文件路径: Scripts/Player/PlayerController.cs

using Godot;
using GameFramework.StateMachine;

namespace GameFramework.Player
{
    /// <summary>
    /// 玩家控制器 - 主控制器类
    /// </summary>
    [GlobalClass]
    public partial class PlayerController : CharacterBody2D
    {
        #region 导出属性

        [Export] public float MoveSpeed { get; set; } = 200f;
        [Export] public float JumpForce { get; set; } = 400f;
        [Export] public float Gravity { get; set; } = 1000f;
        [Export] public float AttackCooldown { get; set; } = 0.3f;

        #endregion

        #region 组件

        // 状态机
        private StateMachine<PlayerStateContext> _stateMachine;

        // 状态上下文
        private PlayerStateContext _context;

        // 地面检测
        private RayCast2D _groundCheck;

        #endregion

        #region 生命周期

        public override void _Ready()
        {
            // 初始化组件
            _groundCheck = GetNode<RayCast2D>("GroundCheck");

            // 创建状态上下文
            _context = new PlayerStateContext(this);

            // 创建状态机
            _stateMachine = new StateMachine<PlayerStateContext>(_context);

            // 注册状态
            SetupStates();

            // 配置转换
            SetupTransitions();

            // 初始化状态机
            _stateMachine.SetDefaultState("IdleState");
            _stateMachine.Initialize();

            // 订阅状态改变事件
            _stateMachine.OnStateChanged += (from, to) =>
            {
                _context.StateTime = 0f;
            };
        }

        public override void _Process(double delta)
        {
            // 更新输入
            _context.UpdateInput();

            // 更新状态机
            _stateMachine.Update((float)delta);
        }

        public override void _PhysicsProcess(double delta)
        {
            // 更新物理状态
            _context.IsGrounded = _groundCheck.IsColliding();
            _context.VerticalVelocity = Velocity.Y;

            // 物理更新状态机
            _stateMachine.PhysicsUpdate((float)delta);

            // 应用重力
            if (!_context.IsGrounded)
            {
                Velocity = new Vector2(Velocity.X, Velocity.Y + Gravity * (float)delta);
            }

            // 更新状态时间
            _context.StateTime += (float)delta;

            MoveAndSlide();
        }

        #endregion

        #region 状态机配置

        /// <summary>
        /// 注册所有状态
        /// </summary>
        private void SetupStates()
        {
            _stateMachine.RegisterState(new IdleState());
            _stateMachine.RegisterState(new RunState());
            _stateMachine.RegisterState(new JumpState());
            _stateMachine.RegisterState(new AttackState());
        }

        /// <summary>
        /// 配置状态转换
        /// </summary>
        private void SetupTransitions()
        {
            // Idle -> Run: 有水平输入
            _stateMachine.AddTransition("IdleState", "RunState",
                ctx => Mathf.Abs(ctx.HorizontalInput) > 0.1f);

            // Idle -> Jump: 按下跳跃键且着地
            _stateMachine.AddTransition("IdleState", "JumpState",
                ctx => ctx.JumpPressed && ctx.IsGrounded);

            // Idle -> Attack: 按下攻击键
            _stateMachine.AddTransition("IdleState", "AttackState",
                ctx => ctx.AttackPressed && ctx.CanAttack);

            // Run -> Idle: 无水平输入
            _stateMachine.AddTransition("RunState", "IdleState",
                ctx => Mathf.Abs(ctx.HorizontalInput) < 0.1f);

            // Run -> Jump: 按下跳跃键且着地
            _stateMachine.AddTransition("RunState", "JumpState",
                ctx => ctx.JumpPressed && ctx.IsGrounded);

            // Run -> Attack: 按下攻击键
            _stateMachine.AddTransition("RunState", "AttackState",
                ctx => ctx.AttackPressed && ctx.CanAttack);

            // Jump -> Idle: 着地且速度很小
            _stateMachine.AddTransition("JumpState", "IdleState",
                ctx => ctx.IsGrounded && Mathf.Abs(ctx.VerticalVelocity) < 10f);

            // Jump -> Run: 着地且有水平输入
            _stateMachine.AddTransition("JumpState", "RunState",
                ctx => ctx.IsGrounded && Mathf.Abs(ctx.HorizontalInput) > 0.1f);

            // Attack -> Idle: 攻击结束(在AttackState内部控制)
            _stateMachine.AddTransition("AttackState", "IdleState",
                ctx => ctx.StateTime >= 0.3f && !ctx.AttackPressed);
        }

        #endregion

        #region 公共方法

        /// <summary>
        /// 播放动画
        /// </summary>
        public void PlayAnimation(string animName)
        {
            if (_context.Animator.HasAnimation(animName))
            {
                _context.Animator.Play(animName);
            }
        }

        /// <summary>
        /// 获取当前状态
        /// </summary>
        public string GetCurrentState() => _stateMachine.CurrentStateId;

        #endregion
    }
}

5.4.3 IdleState 空闲状态

// 文件路径: Scripts/Player/States/IdleState.cs

using GameFramework.StateMachine;

namespace GameFramework.Player
{
    /// <summary>
    /// 空闲状态 - 玩家站立不动
    /// </summary>
    public class IdleState : StateBase<PlayerStateContext>
    {
        public override void Enter()
        {
            // 步骤1: 播放空闲动画
            Context.Player.PlayAnimation("idle");

            // 步骤2: 停止水平移动
            Context.Player.Velocity = new Godot.Vector2(0, Context.Player.Velocity.Y);

            // 步骤3: 重置连击
            Context.ComboCount = 0;
        }

        public override void Update(float deltaTime)
        {
            // 步骤1: 检查是否需要转向(根据输入方向)
            if (Context.HorizontalInput > 0)
            {
                Context.Player.Scale = new Godot.Vector2(1, 1);
            }
            else if (Context.HorizontalInput < 0)
            {
                Context.Player.Scale = new Godot.Vector2(-1, 1);
            }

            // 步骤2: 空闲动画可能有随机变化,可以在这里处理
        }

        public override void PhysicsUpdate(float deltaTime)
        {
            // 步骤1: 保持垂直速度(受重力影响)
            // 水平速度已在Enter中归零
        }

        public override void Exit()
        {
            // 步骤1: 清理(如需要)
        }
    }
}

5.4.4 RunState 奔跑状态

// 文件路径: Scripts/Player/States/RunState.cs

using Godot;
using GameFramework.StateMachine;

namespace GameFramework.Player
{
    /// <summary>
    /// 奔跑状态 - 玩家左右移动
    /// </summary>
    public class RunState : StateBase<PlayerStateContext>
    {
        public override void Enter()
        {
            // 步骤1: 播放奔跑动画
            Context.Player.PlayAnimation("run");
        }

        public override void Update(float deltaTime)
        {
            // 步骤1: 根据输入方向翻转角色
            if (Context.HorizontalInput > 0)
            {
                Context.Player.Scale = new Vector2(1, 1);
            }
            else if (Context.HorizontalInput < 0)
            {
                Context.Player.Scale = new Vector2(-1, 1);
            }

            // 步骤2: 调整动画速度以匹配移动速度
            float speedRatio = Mathf.Abs(Context.HorizontalInput);
            // Context.Animator.SpeedScale = speedRatio; // 可选
        }

        public override void PhysicsUpdate(float deltaTime)
        {
            // 步骤1: 计算目标速度
            float targetVelocityX = Context.HorizontalInput * Context.MoveSpeed;

            // 步骤2: 应用速度
            Vector2 velocity = Context.Player.Velocity;
            velocity.X = targetVelocityX;
            Context.Player.Velocity = velocity;
        }

        public override void Exit()
        {
            // 步骤1: 恢复动画速度
            // Context.Animator.SpeedScale = 1f; // 可选
        }
    }
}

5.4.5 JumpState 跳跃状态

// 文件路径: Scripts/Player/States/JumpState.cs

using Godot;
using GameFramework.StateMachine;

namespace GameFramework.Player
{
    /// <summary>
    /// 跳跃状态 - 玩家跳跃和在空中
    /// </summary>
    public class JumpState : StateBase<PlayerStateContext>
    {
        private bool _hasJumped;

        public override void Enter()
        {
            // 步骤1: 播放跳跃动画
            Context.Player.PlayAnimation("jump");

            // 步骤2: 应用跳跃力
            Context.Player.Velocity = new Vector2(Context.Player.Velocity.X, -Context.JumpForce);

            // 步骤3: 标记已跳跃
            _hasJumped = true;
        }

        public override void Update(float deltaTime)
        {
            // 步骤1: 处理空中转向
            if (Context.HorizontalInput > 0)
            {
                Context.Player.Scale = new Vector2(1, 1);
            }
            else if (Context.HorizontalInput < 0)
            {
                Context.Player.Scale = new Vector2(-1, 1);
            }

            // 步骤2: 检测下落切换动画
            if (Context.Player.Velocity.Y > 0)
            {
                Context.Player.PlayAnimation("fall");
            }

            // 步骤3: 可扩展:短按跳跃和长按跳跃的高度控制
            if (!Input.IsActionPressed("jump") && Context.Player.Velocity.Y < 0)
            {
                // 提前释放跳跃键,减少跳跃高度
                Vector2 velocity = Context.Player.Velocity;
                velocity.Y *= 0.5f;
                Context.Player.Velocity = velocity;
            }
        }

        public override void PhysicsUpdate(float deltaTime)
        {
            // 步骤1: 空中可以稍微控制水平移动
            float airControl = 0.3f; // 空中控制力
            float targetVelocityX = Context.HorizontalInput * Context.MoveSpeed * airControl;

            Vector2 velocity = Context.Player.Velocity;
            velocity.X = Mathf.Lerp(velocity.X, targetVelocityX, 0.1f);
            Context.Player.Velocity = velocity;
        }

        public override void Exit()
        {
            // 步骤1: 重置标记
            _hasJumped = false;

            // 步骤2: 着陆时添加一点缓冲
            if (Context.IsGrounded)
            {
                Context.Player.PlayAnimation("land");
            }
        }
    }
}

5.4.6 AttackState 攻击状态

// 文件路径: Scripts/Player/States/AttackState.cs

using Godot;
using GameFramework.StateMachine;

namespace GameFramework.Player
{
    /// <summary>
    /// 攻击状态 - 玩家执行攻击
    /// </summary>
    public class AttackState : StateBase<PlayerStateContext>
    {
        private const float AttackDuration = 0.3f;
        private const float ComboWindow = 0.2f;

        public override void Enter()
        {
            // 步骤1: 更新最后攻击时间
            Context.LastAttackTime = Time.GetTimeDict()["tick"] / 1000f;

            // 步骤2: 根据连击数播放不同动画
            string animName = Context.ComboCount switch
            {
                0 => "attack_1",
                1 => "attack_2",
                _ => "attack_3"
            };
            Context.Player.PlayAnimation(animName);

            // 步骤3: 执行攻击判定
            PerformAttack();

            // 步骤4: 增加连击数
            Context.ComboCount++;
            if (Context.ComboCount > 2)
            {
                Context.ComboCount = 0;
            }

            // 步骤5: 停止移动
            Vector2 velocity = Context.Player.Velocity;
            velocity.X *= 0.3f; // 攻击时减速
            Context.Player.Velocity = velocity;
        }

        public override void Update(float deltaTime)
        {
            // 步骤1: 检查连击输入
            // 在攻击时间窗口内按下攻击,可以继续连击
            if (Context.StateTime >= AttackDuration - ComboWindow && Context.AttackPressed)
            {
                // 重置状态时间以延长攻击
                // 注意:实际实现可能需要更复杂的连击系统
            }

            // 步骤2: 攻击过程中可以稍微移动
        }

        public override void PhysicsUpdate(float deltaTime)
        {
            // 步骤1: 攻击时水平速度衰减
            Vector2 velocity = Context.Player.Velocity;
            velocity.X = Mathf.Lerp(velocity.X, 0, deltaTime * 5f);
            Context.Player.Velocity = velocity;
        }

        public override void Exit()
        {
            // 步骤1: 如果在连击窗口内退出,保留连击数
            if (Context.StateTime >= AttackDuration)
            {
                Context.ComboCount = 0;
            }
        }

        /// <summary>
        /// 执行攻击判定
        /// </summary>
        private void PerformAttack()
        {
            // 步骤1: 获取攻击区域
            var attackArea = Context.Player.GetNode<Area2D>("AttackArea");
            if (attackArea == null) return;

            // 步骤2: 检测碰撞
            var bodies = attackArea.GetOverlappingBodies();
            foreach (var body in bodies)
            {
                if (body is IDamageable damageable && body != Context.Player)
                {
                    // 计算伤害
                    int damage = 10 * (Context.ComboCount + 1);
                    damageable.TakeDamage(damage, Context.Player);
                }
            }

            // 步骤3: 播放攻击特效
            // Context.Player.PlayAttackEffect();
        }
    }

    /// <summary>
    /// 可受伤接口
    /// </summary>
    public interface IDamageable
    {
        void TakeDamage(int damage, Node2D attacker);
    }
}

5.5 敌人AI状态机示例

敌人AI通常需要更复杂的状态管理,包括巡逻、追击、攻击、逃跑等。

5.5.1 敌人AI状态机

// 文件路径: Scripts/Enemy/EnemyAIStateMachine.cs

using Godot;
using GameFramework.StateMachine;
using System;

namespace GameFramework.Enemy
{
    /// <summary>
    /// 敌人AI上下文
    /// </summary>
    public class EnemyAIContext
    {
        public EnemyController Enemy { get; }
        public PlayerController Player { get; set; }

        // 感知范围
        public float DetectionRange { get; }
        public float AttackRange { get; }
        public float LoseInterestRange { get; }

        // 导航
        public NavigationAgent2D Navigator { get; }

        // 状态数据
        public Vector2 PatrolStart { get; set; }
        public Vector2[] PatrolPoints { get; set; }
        public int CurrentPatrolIndex { get; set; }
        public float StateTime { get; set; }
        public float HealthPercent => Enemy.Health / Enemy.MaxHealth;

        public EnemyAIContext(EnemyController enemy)
        {
            Enemy = enemy;
            Navigator = enemy.GetNode<NavigationAgent2D>("NavigationAgent2D");
            DetectionRange = enemy.DetectionRange;
            AttackRange = enemy.AttackRange;
            LoseInterestRange = enemy.LoseInterestRange;
            PatrolStart = enemy.GlobalPosition;
        }

        /// <summary>
        /// 获取到玩家的距离
        /// </summary>
        public float GetDistanceToPlayer()
        {
            if (Player == null) return float.MaxValue;
            return Enemy.GlobalPosition.DistanceTo(Player.GlobalPosition);
        }

        /// <summary>
        /// 是否能看到玩家
        /// </summary>
        public bool CanSeePlayer()
        {
            if (Player == null) return false;
            float dist = GetDistanceToPlayer();
            return dist <= DetectionRange;
        }

        /// <summary>
        /// 玩家是否在攻击范围内
        /// </summary>
        public bool IsPlayerInAttackRange()
        {
            if (Player == null) return false;
            return GetDistanceToPlayer() <= AttackRange;
        }
    }

    /// <summary>
    /// 敌人AI状态机
    /// </summary>
    public class EnemyAIStateMachine : StateMachine<EnemyAIContext>
    {
        public EnemyAIStateMachine(EnemyAIContext context) : base(context)
        {
        }

        /// <summary>
        /// 配置敌人AI状态
        /// </summary>
        public void SetupEnemyStates()
        {
            // 注册状态
            RegisterState(new PatrolState());
            RegisterState(new ChaseState());
            RegisterState(new AttackState());
            RegisterState(new FleeState());
            RegisterState(new StunnedState());

            // Patrol -> Chase: 发现玩家
            AddTransition("PatrolState", "ChaseState",
                ctx => ctx.CanSeePlayer());

            // Chase -> Attack: 进入攻击范围
            AddTransition("ChaseState", "AttackState",
                ctx => ctx.IsPlayerInAttackRange());

            // Chase -> Patrol: 丢失玩家
            AddTransition("ChaseState", "PatrolState",
                ctx => ctx.GetDistanceToPlayer() > ctx.LoseInterestRange);

            // Attack -> Chase: 玩家离开攻击范围
            AddTransition("AttackState", "ChaseState",
                ctx => !ctx.IsPlayerInAttackRange());

            // Attack -> Flee: 血量过低
            AddTransition("AttackState", "FleeState",
                ctx => ctx.HealthPercent < 0.2f);

            // Flee -> Chase: 恢复血量或玩家远离
            AddTransition("FleeState", "ChaseState",
                ctx => ctx.HealthPercent > 0.5f || ctx.GetDistanceToPlayer() > ctx.DetectionRange * 1.5f);

            // 任意状态 -> Stunned: 受击
            AddAnyStateTransition("StunnedState",
                ctx => ctx.Enemy.IsStunned, priority: -10);

            // Stunned -> Patrol: 眩晕结束
            AddTransition("StunnedState", "PatrolState",
                ctx => !ctx.Enemy.IsStunned && !ctx.CanSeePlayer());

            // Stunned -> Chase: 眩晕结束且能看到玩家
            AddTransition("StunnedState", "ChaseState",
                ctx => !ctx.Enemy.IsStunned && ctx.CanSeePlayer());

            SetDefaultState("PatrolState");
        }
    }

    #region 敌人状态实现

    /// <summary>
    /// 巡逻状态
    /// </summary>
    public class PatrolState : StateBase<EnemyAIContext>
    {
        private float _waitTime;
        private const float MoveSpeed = 50f;

        public override void Enter()
        {
            Context.Enemy.PlayAnimation("walk");
            SetNextPatrolPoint();
        }

        public override void Update(float deltaTime)
        {
            // 检查是否到达目标
            if (Context.Navigator.IsNavigationFinished())
            {
                _waitTime -= deltaTime;
                if (_waitTime <= 0)
                {
                    SetNextPatrolPoint();
                }
            }
        }

        public override void PhysicsUpdate(float deltaTime)
        {
            if (!Context.Navigator.IsNavigationFinished())
            {
                Vector2 nextPos = Context.Navigator.GetNextPathPosition();
                Vector2 direction = (nextPos - Context.Enemy.GlobalPosition).Normalized();

                Context.Enemy.Velocity = direction * MoveSpeed;
                Context.Enemy.MoveAndSlide();

                // 转向
                Context.Enemy.Scale = new Vector2(Mathf.Sign(direction.X), 1);
            }
            else
            {
                Context.Enemy.Velocity = Vector2.Zero;
            }
        }

        private void SetNextPatrolPoint()
        {
            // 简单的巡逻:在起始点附近随机移动
            Vector2 randomOffset = new Vector2(
                GD.RandRange(-100, 100),
                GD.RandRange(-50, 50)
            );
            Context.Navigator.TargetPosition = Context.PatrolStart + randomOffset;
            _waitTime = GD.RandRange(1f, 3f);
        }
    }

    /// <summary>
    /// 追击状态
    /// </summary>
    public class ChaseState : StateBase<EnemyAIContext>
    {
        private const float MoveSpeed = 120f;

        public override void Enter()
        {
            Context.Enemy.PlayAnimation("run");
        }

        public override void Update(float deltaTime)
        {
            // 更新目标位置
            if (Context.Player != null)
            {
                Context.Navigator.TargetPosition = Context.Player.GlobalPosition;
            }
        }

        public override void PhysicsUpdate(float deltaTime)
        {
            if (Context.Player == null) return;

            Vector2 nextPos = Context.Navigator.GetNextPathPosition();
            Vector2 direction = (nextPos - Context.Enemy.GlobalPosition).Normalized();

            Context.Enemy.Velocity = direction * MoveSpeed;
            Context.Enemy.MoveAndSlide();

            Context.Enemy.Scale = new Vector2(Mathf.Sign(direction.X), 1);
        }
    }

    /// <summary>
    /// 攻击状态
    /// </summary>
    public class AttackState : StateBase<EnemyAIContext>
    {
        private const float AttackInterval = 1.0f;
        private float _attackTimer;

        public override void Enter()
        {
            Context.Enemy.PlayAnimation("attack");
            _attackTimer = 0;
        }

        public override void Update(float deltaTime)
        {
            _attackTimer += deltaTime;

            // 面向玩家
            if (Context.Player != null)
            {
                float dir = Mathf.Sign(Context.Player.GlobalPosition.X - Context.Enemy.GlobalPosition.X);
                Context.Enemy.Scale = new Vector2(dir, 1);

                // 执行攻击
                if (_attackTimer >= AttackInterval)
                {
                    PerformAttack();
                    _attackTimer = 0;
                }
            }
        }

        public override void PhysicsUpdate(float deltaTime)
        {
            // 攻击时停止移动
            Context.Enemy.Velocity = Context.Enemy.Velocity.MoveToward(Vector2.Zero, deltaTime * 500f);
            Context.Enemy.MoveAndSlide();
        }

        private void PerformAttack()
        {
            // 播放攻击动画
            Context.Enemy.PlayAnimation("attack");

            // 伤害判定
            var bodies = Context.Enemy.GetNode<Area2D>("AttackArea").GetOverlappingBodies();
            foreach (var body in bodies)
            {
                if (body is PlayerController player)
                {
                    // player.TakeDamage(Context.Enemy.AttackDamage);
                }
            }
        }
    }

    /// <summary>
    /// 逃跑状态
    /// </summary>
    public class FleeState : StateBase<EnemyAIContext>
    {
        private const float MoveSpeed = 150f;

        public override void Enter()
        {
            Context.Enemy.PlayAnimation("run");
        }

        public override void Update(float deltaTime)
        {
            if (Context.Player == null) return;

            // 远离玩家
            Vector2 fleeDirection = (Context.Enemy.GlobalPosition - Context.Player.GlobalPosition).Normalized();
            Context.Navigator.TargetPosition = Context.Enemy.GlobalPosition + fleeDirection * 200f;
        }

        public override void PhysicsUpdate(float deltaTime)
        {
            Vector2 nextPos = Context.Navigator.GetNextPathPosition();
            Vector2 direction = (nextPos - Context.Enemy.GlobalPosition).Normalized();

            Context.Enemy.Velocity = direction * MoveSpeed;
            Context.Enemy.MoveAndSlide();

            Context.Enemy.Scale = new Vector2(Mathf.Sign(direction.X), 1);
        }
    }

    /// <summary>
    /// 眩晕状态
    /// </summary>
    public class StunnedState : StateBase<EnemyAIContext>
    {
        public override void Enter()
        {
            Context.Enemy.PlayAnimation("stunned");
            Context.Enemy.Velocity = Vector2.Zero;
        }

        public override void PhysicsUpdate(float deltaTime)
        {
            // 眩晕时不移动
            Context.Enemy.Velocity = Vector2.Zero;
        }
    }

    #endregion
}

5.5.2 敌人控制器

// 文件路径: Scripts/Enemy/EnemyController.cs

using Godot;
using GameFramework.StateMachine;

namespace GameFramework.Enemy
{
    /// <summary>
    /// 敌人控制器
    /// </summary>
    [GlobalClass]
    public partial class EnemyController : CharacterBody2D
    {
        #region 导出属性

        [Export] public float DetectionRange { get; set; } = 200f;
        [Export] public float AttackRange { get; set; } = 40f;
        [Export] public float LoseInterestRange { get; set; } = 400f;
        [Export] public float MaxHealth { get; set; } = 100f;
        [Export] public float AttackDamage { get; set; } = 10f;

        #endregion

        #region 属性

        public float Health { get; private set; }
        public bool IsStunned { get; private set; }
        private float _stunTimer;

        #endregion

        #region 组件

        private EnemyAIStateMachine _stateMachine;
        private EnemyAIContext _context;
        private AnimationPlayer _animator;

        #endregion

        public override void _Ready()
        {
            Health = MaxHealth;
            _animator = GetNode<AnimationPlayer>("AnimationPlayer");

            // 查找玩家
            var player = GetTree().GetFirstNodeInGroup("Player") as PlayerController;

            // 创建上下文
            _context = new EnemyAIContext(this)
            {
                Player = player
            };

            // 创建状态机
            _stateMachine = new EnemyAIStateMachine(_context);
            _stateMachine.SetupEnemyStates();
            _stateMachine.Initialize();
        }

        public override void _Process(double delta)
        {
            _stateMachine.Update((float)delta);
        }

        public override void _PhysicsProcess(double delta)
        {
            // 更新眩晕状态
            if (IsStunned)
            {
                _stunTimer -= (float)delta;
                if (_stunTimer <= 0)
                {
                    IsStunned = false;
                }
            }

            _context.StateTime += (float)delta;
            _stateMachine.PhysicsUpdate((float)delta);
        }

        /// <summary>
        /// 播放动画
        /// </summary>
        public void PlayAnimation(string animName)
        {
            if (_animator.HasAnimation(animName))
            {
                _animator.Play(animName);
            }
        }

        /// <summary>
        /// 受伤
        /// </summary>
        public void TakeDamage(float damage)
        {
            Health -= damage;
            if (Health <= 0)
            {
                Die();
                return;
            }

            // 眩晕
            Stun(0.3f);
        }

        /// <summary>
        /// 眩晕
        /// </summary>
        public void Stun(float duration)
        {
            IsStunned = true;
            _stunTimer = duration;
        }

        /// <summary>
        /// 死亡
        /// </summary>
        private void Die()
        {
            _stateMachine.Stop();
            PlayAnimation("die");
            // 延迟销毁
            GetTree().CreateTimer(2f).Timeout += () => QueueFree();
        }
    }
}

5.6 使用下推自动机的UI导航系统

下推自动机非常适合UI导航系统,因为它天然支持"返回"操作。

// 文件路径: Scripts/UI/UIManager.cs

using Godot;
using GameFramework.StateMachine;
using System;

namespace GameFramework.UI
{
    /// <summary>
    /// UI状态枚举
    /// </summary>
    public enum UIState
    {
        MainMenu,
        Settings,
        LevelSelect,
        Pause,
        Inventory,
        Shop,
        Dialogue
    }

    /// <summary>
    /// UI上下文
    /// </summary>
    public class UIContext
    {
        public Control UIRoot { get; }
        public GameManager GameManager { get; }

        public UIContext(Control uiRoot, GameManager gameManager)
        {
            UIRoot = uiRoot;
            GameManager = gameManager;
        }
    }

    /// <summary>
    /// UI状态基类
    /// </summary>
    public abstract class UIStateBase : StateBase<UIContext>
    {
        protected Control Panel { get; private set; }

        public void SetPanel(Control panel)
        {
            Panel = panel;
        }

        public override void Enter()
        {
            Panel?.Show();
            Panel?.SetProcess(true);
        }

        public override void Exit()
        {
            Panel?.Hide();
            Panel?.SetProcess(false);
        }
    }

    /// <summary>
    /// UI管理器 - 使用下推自动机
    /// </summary>
    public partial class UIManager : Node
    {
        [Export] public Control MainMenuPanel;
        [Export] public Control SettingsPanel;
        [Export] public Control PausePanel;
        [Export] public Control InventoryPanel;

        private PushdownStateMachine<UIContext> _uiStateMachine;
        private UIContext _context;

        public override void _Ready()
        {
            // 创建上下文
            _context = new UIContext(GetParent<Control>(), GetNode<GameManager>("/root/GameManager"));

            // 创建下推状态机
            _uiStateMachine = new PushdownStateMachine<UIContext>(_context);

            // 注册UI状态
            RegisterUIStates();

            // 默认打开主菜单
            OpenUI("MainMenuState");
        }

        public override void _Process(double delta)
        {
            _uiStateMachine.Update((float)delta);

            // 处理返回键
            if (Input.IsActionJustPressed("ui_cancel"))
            {
                GoBack();
            }
        }

        /// <summary>
        /// 注册UI状态
        /// </summary>
        private void RegisterUIStates()
        {
            // 主菜单
            var mainMenu = new MainMenuState();
            mainMenu.SetPanel(MainMenuPanel);
            _uiStateMachine.RegisterState("MainMenuState", mainMenu);

            // 设置
            var settings = new SettingsState();
            settings.SetPanel(SettingsPanel);
            _uiStateMachine.RegisterState("SettingsState", settings);

            // 暂停
            var pause = new PauseState();
            pause.SetPanel(PausePanel);
            _uiStateMachine.RegisterState("PauseState", pause);

            // 背包
            var inventory = new InventoryState();
            inventory.SetPanel(InventoryPanel);
            _uiStateMachine.RegisterState("InventoryState", inventory);
        }

        /// <summary>
        /// 打开UI(推入新状态)
        /// </summary>
        public void OpenUI(string stateId)
        {
            _uiStateMachine.PushState(stateId);
        }

        /// <summary>
        /// 返回上一UI(弹出状态)
        /// </summary>
        public void GoBack()
        {
            // 如果栈中只有主菜单,不返回
            if (_uiStateMachine.StackCount <= 1)
                return;

            _uiStateMachine.PopState();
        }

        /// <summary>
        /// 替换当前UI
        /// </summary>
        public void ReplaceUI(string stateId)
        {
            _uiStateMachine.ReplaceState(stateId);
        }

        /// <summary>
        /// 清空并打开UI
        /// </summary>
        public void ClearAndOpen(string stateId)
        {
            _uiStateMachine.ClearAndPush(stateId);
        }

        /// <summary>
        /// 主菜单状态
        /// </summary>
        public class MainMenuState : UIStateBase
        {
            public override void Enter()
            {
                base.Enter();
                // 暂停游戏
                Context.GameManager?.PauseGame();
            }

            public override void Update(float deltaTime)
            {
                // 处理主菜单输入
            }
        }

        /// <summary>
        /// 设置状态
        /// </summary>
        public class SettingsState : UIStateBase
        {
            public override void Enter()
            {
                base.Enter();
                // 加载设置数据
            }
        }

        /// <summary>
        /// 暂停状态
        /// </summary>
        public class PauseState : UIStateBase
        {
            public override void Enter()
            {
                base.Enter();
                Context.GameManager?.PauseGame();
                // 显示暂停菜单
            }

            public override void Exit()
            {
                base.Exit();
                Context.GameManager?.ResumeGame();
            }
        }

        /// <summary>
        /// 背包状态
        /// </summary>
        public class InventoryState : UIStateBase
        {
            public override void Enter()
            {
                base.Enter();
                // 刷新背包显示
            }

            public override void Update(float deltaTime)
            {
                // 处理背包操作
            }
        }
    }
}

5.7 最佳实践

5.7.1 状态设计原则

  1. 单一职责:每个状态只负责一种行为,避免状态过于复杂
  2. 状态纯净:状态不应保存长期数据,数据应存储在上下文中
  3. 转换明确:状态转换条件应清晰明确,避免歧义

5.7.2 性能优化

// 文件路径: Scripts/Framework/StateMachine/StateMachineOptimizer.cs

using System.Collections.Generic;

namespace GameFramework.StateMachine
{
    /// <summary>
    /// 状态机优化工具
    /// </summary>
    public static class StateMachineOptimizer
    {
        /// <summary>
        /// 条件缓存 - 避免每帧重复计算
        /// </summary>
        public class ConditionCache<T>
        {
            private readonly Dictionary<string, bool> _cache = new();
            private int _frameCounter;
            private int _cacheFrame;

            /// <summary>
            /// 获取或计算条件值
            /// </summary>
            public bool GetOrCompute(string key, System.Func<T, bool> condition, T context, int currentFrame)
            {
                // 新帧时清空缓存
                if (currentFrame != _cacheFrame)
                {
                    _cache.Clear();
                    _cacheFrame = currentFrame;
                }

                if (_cache.TryGetValue(key, out var cached))
                {
                    return cached;
                }

                var result = condition(context);
                _cache[key] = result;
                return result;
            }
        }

        /// <summary>
        /// 延迟评估包装器 - 只在必要时评估条件
        /// </summary>
        public static StateCondition<T> Lazy<T>(StateCondition<T> condition)
        {
            return context => condition(context);
        }

        /// <summary>
        /// 组合条件 - 短路求优
        /// </summary>
        public static StateCondition<T> And<T>(params StateCondition<T>[] conditions)
        {
            return context =>
            {
                foreach (var condition in conditions)
                {
                    if (!condition(context))
                        return false;
                }
                return true;
            };
        }

        /// <summary>
        /// 任一条件满足
        /// </summary>
        public static StateCondition<T> Or<T>(params StateCondition<T>[] conditions)
        {
            return context =>
            {
                foreach (var condition in conditions)
                {
                    if (condition(context))
                        return true;
                }
                return false;
            };
        }

        /// <summary>
        /// 条件取反
        /// </summary>
        public static StateCondition<T> Not<T>(StateCondition<T> condition)
        {
            return context => !condition(context);
        }

        /// <summary>
        /// 添加冷却时间的条件
        /// </summary>
        public static StateCondition<T> WithCooldown<T>(StateCondition<T> condition, float cooldown, System.Func<T, float> getLastTime)
        {
            return context =>
            {
                if (!condition(context))
                    return false;

                float currentTime = Godot.Time.GetTimeDict()["tick"] / 1000f;
                return currentTime - getLastTime(context) >= cooldown;
            };
        }
    }
}

5.7.3 调试工具

// 文件路径: Scripts/Framework/StateMachine/StateMachineDebugger.cs

using Godot;
using System.Text;

namespace GameFramework.StateMachine
{
    /// <summary>
    /// 状态机调试器
    /// </summary>
    [GlobalClass]
    public partial class StateMachineDebugger : Node
    {
        [Export] public bool EnableDebug { get; set; } = true;
        [Export] public Vector2 DebugPosition { get; set; } = new Vector2(10, 10);

        private Label _debugLabel;

        public override void _Ready()
        {
            if (!EnableDebug)
                return;

            // 创建调试显示
            _debugLabel = new Label();
            _debugLabel.Position = DebugPosition;
            AddChild(_debugLabel);
        }

        /// <summary>
        /// 显示状态机信息
        /// </summary>
        public void ShowStateMachineInfo<T>(StateMachine<T> stateMachine)
        {
            if (!EnableDebug || _debugLabel == null)
                return;

            var sb = new StringBuilder();
            sb.AppendLine($"状态机: {stateMachine.GetType().Name}");
            sb.AppendLine($"当前状态: {stateMachine.CurrentStateId}");

            _debugLabel.Text = sb.ToString();
        }

        /// <summary>
        /// 显示下推状态机信息
        /// </summary>
        public void ShowPushdownStateMachineInfo<T>(PushdownStateMachine<T> stateMachine)
        {
            if (!EnableDebug || _debugLabel == null)
                return;

            var sb = new StringBuilder();
            sb.AppendLine($"下推状态机");
            sb.AppendLine($"当前状态: {stateMachine.CurrentStateId}");
            sb.AppendLine($"栈深度: {stateMachine.StackCount}");
            sb.AppendLine("栈内容:");
            foreach (var stateId in stateMachine.GetStackStateIds())
            {
                sb.AppendLine($"  - {stateId}");
            }

            _debugLabel.Text = sb.ToString();
        }

        public override void _Process(double delta)
        {
            // 可以在这里更新调试显示
        }
    }
}

5.8 小结

本章介绍了三种状态管理模式:

  1. 有限状态机(FSM):最基础的状态管理,适合状态数量较少、转换关系简单的场景
  2. 分层状态机(Hierarchical FSM):通过状态嵌套解决状态爆炸问题,适合复杂角色AI
  3. 下推自动机(Pushdown Automata):使用栈管理状态,支持历史记录和返回操作,适合UI导航

选择合适的状态管理模式应根据具体需求:

  • 玩家角色控制:FSM或Hierarchical FSM
  • 敌人AI:Hierarchical FSM
  • UI导航系统:Pushdown Automata
  • 游戏流程控制:Pushdown Automata

状态模式的核心价值在于将复杂的行为逻辑分解为独立的状态单元,每个单元只关注自身的逻辑,通过清晰的转换规则连接起来,使代码更易于理解和维护。