Files
2025-12-14 22:38:43 +08:00

9.0 KiB

状态机模块 (Framework/FSM)

📋 模块概述

通用的有限状态机(FSM)框架,支持状态切换、状态生命周期管理,可用于游戏状态、AI行为、动画控制等场景。

🎯 核心特性

  • 标准状态接口定义
  • 状态基类实现
  • 状态机核心管理
  • 完整的生命周期
  • 状态切换管理
  • 状态更新机制

🗂️ 文件结构

Framework/FSM/
├── IState.ts      # 状态接口
├── BaseState.ts   # 状态基类
└── FSM.ts         # 状态机核心

📘 核心类详解

IState - 状态接口

职责: 定义状态机中状态的标准接口

interface IState {
    // 状态名称(只读)
    readonly name: string;
    
    // 进入状态时调用
    onEnter(params?: any): void;
    
    // 更新状态(可选,每帧调用)
    onUpdate?(dt: number): void;
    
    // 退出状态时调用
    onExit(): void;
}

BaseState - 状态基类

职责: 提供状态接口的基础实现

abstract class BaseState implements IState {
    protected _fsm: FSM;          // 状态所属的状态机
    readonly name: string;        // 状态名称
    
    constructor(fsm: FSM, name: string);
    
    // 进入状态(可重写)
    onEnter(params?: any): void {
        console.log(`[FSM] Enter state: ${this.name}`);
    }
    
    // 更新状态(可重写)
    onUpdate?(dt: number): void {}
    
    // 退出状态(可重写)
    onExit(): void {
        console.log(`[FSM] Exit state: ${this.name}`);
    }
}

FSM - 状态机核心

职责: 管理状态的添加、移除和切换

class FSM {
    private _states: Map<string, IState>;      // 状态集合
    private _currentState: IState | null;      // 当前状态
    
    // 添加状态
    addState(state: IState): void;
    
    // 移除状态
    removeState(stateName: string): void;
    
    // 切换状态
    changeState(stateName: string, params?: any): void;
    
    // 获取当前状态
    getCurrentState(): IState | null;
    
    // 获取当前状态名称
    getCurrentStateName(): string | null;
    
    // 检查状态是否存在
    hasState(stateName: string): boolean;
    
    // 获取状态实例
    getState(stateName: string): IState | undefined;
    
    // 更新状态机(调用当前状态的 onUpdate)
    update(dt: number): void;
    
    // 清空所有状态
    clear(): void;
}

📝 使用指南

1. 定义状态类

import { FSM } from './Framework/FSM/FSM';
import { BaseState } from './Framework/FSM/BaseState';

// 待机状态
class IdleState extends BaseState {
    constructor(fsm: FSM) {
        super(fsm, "Idle");
    }
    
    onEnter(params?: any): void {
        super.onEnter(params);
        console.log("角色进入待机状态");
    }
    
    onUpdate(dt: number): void {
        // 检测输入,切换到移动状态
        if (hasInput()) {
            this._fsm.changeState("Move");
        }
    }
}

// 移动状态
class MoveState extends BaseState {
    private _speed: number = 5;
    
    constructor(fsm: FSM) {
        super(fsm, "Move");
    }
    
    onEnter(params?: any): void {
        super.onEnter(params);
        if (params?.speed) {
            this._speed = params.speed;
        }
        console.log(`角色开始移动,速度: ${this._speed}`);
    }
    
    onUpdate(dt: number): void {
        // 执行移动逻辑
        this.move(dt);
        
        // 检测停止输入
        if (!hasInput()) {
            this._fsm.changeState("Idle");
        }
    }
    
    onExit(): void {
        super.onExit();
        console.log("角色停止移动");
    }
    
    private move(dt: number): void {
        // 移动逻辑
    }
}

2. 创建并使用状态机

import { FSM } from './Framework/FSM/FSM';

// 1. 创建状态机
const fsm = new FSM();

// 2. 添加状态
fsm.addState(new IdleState(fsm));
fsm.addState(new MoveState(fsm));
fsm.addState(new AttackState(fsm));

// 3. 切换到初始状态
fsm.changeState("Idle");

// 4. 在游戏主循环中更新
function update(dt: number) {
    fsm.update(dt);
}

// 5. 切换状态(可传递参数)
fsm.changeState("Move", { speed: 10 });

3. 角色 AI 示例

// 敌人 AI 状态机
class EnemyAI {
    private _fsm: FSM;
    
    constructor() {
        this._fsm = new FSM();
        
        // 添加 AI 状态
        this._fsm.addState(new PatrolState(this._fsm));
        this._fsm.addState(new ChaseState(this._fsm));
        this._fsm.addState(new AttackState(this._fsm));
        this._fsm.addState(new FleeState(this._fsm));
        
        // 从巡逻状态开始
        this._fsm.changeState("Patrol");
    }
    
    update(dt: number): void {
        this._fsm.update(dt);
    }
}

// 巡逻状态
class PatrolState extends BaseState {
    onEnter(params?: any): void {
        super.onEnter(params);
        console.log("敌人开始巡逻");
    }
    
    onUpdate(dt: number): void {
        // 检测玩家
        if (detectPlayer()) {
            this._fsm.changeState("Chase");
        }
    }
}

// 追击状态
class ChaseState extends BaseState {
    onEnter(params?: any): void {
        super.onEnter(params);
        console.log("敌人发现玩家,开始追击");
    }
    
    onUpdate(dt: number): void {
        // 靠近玩家
        moveToPlayer();
        
        // 进入攻击范围
        if (inAttackRange()) {
            this._fsm.changeState("Attack");
        }
        // 玩家逃离
        else if (!canSeePlayer()) {
            this._fsm.changeState("Patrol");
        }
    }
}

4. 动画状态机示例

// 角色动画状态机
class CharacterAnimFSM {
    private _fsm: FSM;
    private _animator: any;  // Cocos 动画组件
    
    constructor(animator: any) {
        this._animator = animator;
        this._fsm = new FSM();
        
        // 添加动画状态
        this._fsm.addState(new IdleAnimState(this._fsm, this._animator));
        this._fsm.addState(new RunAnimState(this._fsm, this._animator));
        this._fsm.addState(new JumpAnimState(this._fsm, this._animator));
        this._fsm.addState(new AttackAnimState(this._fsm, this._animator));
        
        this._fsm.changeState("Idle");
    }
    
    // 播放动画
    play(animName: string, params?: any): void {
        this._fsm.changeState(animName, params);
    }
}

// 待机动画状态
class IdleAnimState extends BaseState {
    private _animator: any;
    
    constructor(fsm: FSM, animator: any) {
        super(fsm, "Idle");
        this._animator = animator;
    }
    
    onEnter(params?: any): void {
        super.onEnter(params);
        this._animator.play("idle");
    }
}

🔄 状态切换流程

changeState("NewState", params)
    ↓
1. 检查新状态是否存在
    ↓
2. 如果有当前状态,调用 currentState.onExit()
    ↓
3. 切换到新状态
    ↓
4. 调用 newState.onEnter(params)
    ↓
5. 更新当前状态引用

📊 状态生命周期

[创建状态实例]
    ↓
addState(state) - 添加到状态机
    ↓
changeState("StateName") - 切换状态
    ↓
onEnter(params) - 进入状态
    ↓
onUpdate(dt) - 每帧更新(如果实现)
    ↓
[触发状态切换]
    ↓
onExit() - 退出状态
    ↓
removeState("StateName") - 从状态机移除(可选)

⚠️ 注意事项

  1. 状态名称唯一: 每个状态的名称必须唯一,重复添加会覆盖
  2. 构造函数传递 FSM: 状态类的构造函数需要接收 FSM 实例
  3. 调用 super: 重写生命周期方法时,建议先调用 super.xxx()
  4. 状态切换时机: 建议在 onUpdate() 中判断并切换状态
  5. 参数传递: 切换状态时可以通过 params 传递数据
  6. 避免循环切换: 注意状态切换逻辑,避免无限循环

🔍 调试技巧

启用状态日志

// BaseState 默认会输出状态切换日志
// [FSM] Enter state: Idle
// [FSM] Exit state: Idle

获取当前状态信息

// 获取当前状态名称
const stateName = fsm.getCurrentStateName();
console.log(`当前状态: ${stateName}`);

// 获取当前状态实例
const state = fsm.getCurrentState();
if (state) {
    console.log(`状态实例: ${state.name}`);
}

检查状态是否存在

if (fsm.hasState("Attack")) {
    console.log("Attack 状态已添加");
}

💡 最佳实践

  1. 单一职责: 每个状态只负责一种行为
  2. 状态解耦: 状态之间通过 FSM 切换,避免直接依赖
  3. 参数传递: 使用 params 在状态间传递数据
  4. 及时清理: 不再使用的状态及时移除
  5. 文档注释: 为每个状态类添加清晰的职责说明

📚 应用场景

  • 游戏状态管理(Boot、Login、Game 等)
  • 角色状态控制(Idle、Move、Attack 等)
  • 敌人 AI 行为(Patrol、Chase、Attack、Flee 等)
  • 动画状态机(各种动画之间的切换)
  • UI 界面流转(各个界面之间的切换)
  • 关卡状态(Ready、Playing、Pause、GameOver 等)