CocosCreator 游戏开发 - 多维度状态机架构设计与实现

背景与场景

在游戏开发中,实体对象(如角色、敌人、NPC)往往需要同时管理多个维度的状态。以一个典型的游戏敌人为例:

  • 行为维度:移动、停止、受击、击退、防御、死亡
  • 属性维度:正常、冰冻、燃烧、雷电、中毒

如果用单一状态机描述,需要维护 6 × 5 = 30 个组合状态。当维度增加时,状态数量呈指数级增长。

本文探讨的架构通过正交状态分离策略,将不同维度拆分到独立状态机中,只需维护 6 + 5 = 11 个状态类,通过共享宿主实现协作。

核心架构设计

三层职责划分

css 复制代码
┌─────────────────────────────────────┐
│          宿主层 (Host)              │
│  - 持有多个状态机实例                │
│  - 提供状态切换入口                  │
│  - 管理跨维度协调                    │
│  - 处理生命周期                      │
└──────────┬──────────────────────────┘
           │
    ┌──────┴──────┐
    ▼             ▼
┌─────────┐  ┌─────────┐
│ 状态机A  │  │ 状态机B  │
│ - 状态缓存│  │ - 状态缓存│
│ - 状态分发│  │ - 状态分发│
└────┬────┘  └────┬────┘
     │            │
  ┌──┴──┐      ┌──┴──┐
  ▼  ▼  ▼      ▼  ▼  ▼
 状态类群A    状态类群B

维度协作机制

  1. 共享参数修正 :属性状态修改 effectSpeed,行为状态读取 baseSpeed * effectSpeed
  2. 状态间触发:属性恢复时主动将行为切回默认状态
  3. 组合条件判断:暂停/恢复时同时读取两个维度的状态值

通用 FSM 框架实现

1. 状态基类

typescript 复制代码
/**
 * 通用状态基类
 * @template THost 宿主类型
 * @template TStateEnum 状态枚举类型
 */
export abstract class BaseState<THost, TStateEnum> {
    public readonly value: TStateEnum;
    protected host: THost;

    constructor(host: THost, stateValue: TStateEnum) {
        this.host = host;
        this.value = stateValue;
    }

    /**
     * 状态进入时执行的逻辑
     * @param context 上下文信息
     */
    abstract execute(context?: any): void | Promise<void>;

    /**
     * 状态退出时的清理逻辑(可选)
     */
    onExit?(): void;
}

2. 通用状态机

kotlin 复制代码
/**
 * 通用状态机
 * @template THost 宿主类型
 * @template TStateEnum 状态枚举类型
 */
export class StateMachine<THost, TStateEnum> {
    private currentState: BaseState<THost, TStateEnum> | null = null;
    private stateMap: Map<TStateEnum, BaseState<THost, TStateEnum>> = new Map();
    private host: THost;

    constructor(host: THost) {
        this.host = host;
    }

    /**
     * 注册状态
     */
    registerState(state: BaseState<THost, TStateEnum>): void {
        this.stateMap.set(state.value, state);
    }

    /**
     * 批量注册状态
     */
    registerStates(states: BaseState<THost, TStateEnum>[]): void {
        states.forEach(state => this.registerState(state));
    }

    /**
     * 切换状态
     */
    switchState(stateValue: TStateEnum, context?: any): void {
        const nextState = this.stateMap.get(stateValue);

        if (!nextState) {
            console.warn(`State ${stateValue} not found`);
            return;
        }

        // 退出当前状态
        if (this.currentState?.onExit) {
            this.currentState.onExit();
        }

        // 切换到新状态
        this.currentState = nextState;
        this.currentState.execute(context);
    }

    /**
     * 获取当前状态值
     */
    get value(): TStateEnum | null {
        return this.currentState?.value ?? null;
    }

    /**
     * 获取当前状态实例
     */
    get current(): BaseState<THost, TStateEnum> | null {
        return this.currentState;
    }
}

3. 宿主接口

scss 复制代码
/**
 * 状态机宿主接口
 */
export interface IStateMachineHost {
    /**
     * 初始化状态机
     */
    initStateMachines(): void;

    /**
     * 暂停
     */
    pause?(): void;

    /**
     * 恢复
     */
    resume?(): void;

    /**
     * 销毁
     */
    destroy?(): void;
}

使用示例:实现游戏实体状态管理

1. 定义状态枚举

ini 复制代码
enum ActionState {
    MOVE = 'move',
    STOP = 'stop',
    HIT = 'hit',
    DEAD = 'dead',
}

enum AttributeState {
    NORMAL = 'normal',
    FROZEN = 'frozen',
    BURNING = 'burning',
}

2. 实现具体状态类

scala 复制代码
// 行为状态:移动
class MoveState extends BaseState<EntityController, ActionState> {
    constructor(host: EntityController) {
        super(host, ActionState.MOVE);
    }

    execute(): void {
        const speed = this.host.baseSpeed * this.host.effectSpeed;
        this.host.setVelocity(0, -speed);
    }
}

// 行为状态:死亡
class DeadState extends BaseState<EntityController, ActionState> {
    constructor(host: EntityController) {
        super(host, ActionState.DEAD);
    }

    execute(): void {
        this.host.stopPhysics();
        this.host.playDeathEffect();
        this.host.scheduleDestroy(0.5);
    }
}

// 属性状态:正常
class NormalAttributeState extends BaseState<EntityController, AttributeState> {
    constructor(host: EntityController) {
        super(host, AttributeState.NORMAL);
    }

    execute(): void {
        this.host.effectSpeed = 1;
        this.host.effectDamage = 0;
        this.host.resetColor();
    }
}

// 属性状态:冰冻
class FrozenAttributeState extends BaseState<EntityController, AttributeState> {
    constructor(host: EntityController) {
        super(host, AttributeState.FROZEN);
    }

    execute(): void {
        this.host.effectSpeed = 0.5;
        this.host.setColor(0, 255, 255);
    }
}

3. 实现宿主控制器

kotlin 复制代码
class EntityController implements IStateMachineHost {
    // 基础属性
    baseSpeed: number = 1;
    baseHealth: number = 100;

    // 状态修正属性
    effectSpeed: number = 1;
    effectDamage: number = 0;

    // 状态机实例
    private actionStateMachine: StateMachine<EntityController, ActionState>;
    private attributeStateMachine: StateMachine<EntityController, AttributeState>;

    // 调度器任务 ID
    private attributeTimerId: number | null = null;
    private dotTimerId: number | null = null;

    constructor() {
        this.initStateMachines();
    }

    initStateMachines(): void {
        // 初始化行为状态机
        this.actionStateMachine = new StateMachine(this);
        this.actionStateMachine.registerStates([
            new MoveState(this),
            new StopState(this),
            new HitState(this),
            new DeadState(this),
        ]);

        // 初始化属性状态机
        this.attributeStateMachine = new StateMachine(this);
        this.attributeStateMachine.registerStates([
            new NormalAttributeState(this),
            new FrozenAttributeState(this),
            new BurningAttributeState(this),
        ]);

        // 设置初始状态
        this.actionStateMachine.switchState(ActionState.MOVE);
        this.attributeStateMachine.switchState(AttributeState.NORMAL);
    }

    // 行为状态切换入口
    switchActionState(state: ActionState, context?: any): void {
        const currentState = this.actionStateMachine.value;

        // 防重入(MOVE 除外,允许重置)
        if (currentState === state && state !== ActionState.MOVE) {
            return;
        }

        this.actionStateMachine.switchState(state, context);
    }

    // 属性状态切换入口
    switchAttributeState(state: AttributeState, context?: any): void {
        const currentState = this.attributeStateMachine.value;

        // 已经是目标状态,跳过
        if (currentState === state) {
            return;
        }

        // 清理旧的调度任务
        this.clearAttributeTimers();

        // 切换到新状态
        this.attributeStateMachine.switchState(state, context);

        // 如果不是 NORMAL,设置定时恢复
        if (state !== AttributeState.NORMAL) {
            this.attributeTimerId = this.scheduleOnce(() => {
                this.switchAttributeState(AttributeState.NORMAL);
            }, 3.0);

            // 如果有持续伤害,设置周期性伤害
            if (this.effectDamage > 0) {
                this.dotTimerId = this.scheduleRepeat(() => {
                    this.takeDamage(this.effectDamage);
                }, 0.8);
            }
        }
    }

    // 清理属性相关定时器
    private clearAttributeTimers(): void {
        if (this.attributeTimerId !== null) {
            this.unschedule(this.attributeTimerId);
            this.attributeTimerId = null;
        }
        if (this.dotTimerId !== null) {
            this.unschedule(this.dotTimerId);
            this.dotTimerId = null;
        }
    }

    // 获取当前状态
    get actionState(): ActionState | null {
        return this.actionStateMachine.value;
    }

    get attributeState(): AttributeState | null {
        return this.attributeStateMachine.value;
    }

    // 暂停
    pause(): void {
        this.stopPhysics();
        // 根据当前状态暂停对应的动画/特效
    }

    // 恢复
    resume(): void {
        // 根据当前状态恢复对应的行为
        if (this.actionState === ActionState.MOVE) {
            const speed = this.baseSpeed * this.effectSpeed;
            this.setVelocity(0, -speed);
        }
    }

    // 销毁
    destroy(): void {
        this.clearAttributeTimers();
        // 其他清理逻辑
    }

    // 以下是宿主提供给状态类使用的方法
    setVelocity(x: number, y: number): void { /* ... */ }
    stopPhysics(): void { /* ... */ }
    playDeathEffect(): void { /* ... */ }
    setColor(r: number, g: number, b: number): void { /* ... */ }
    resetColor(): void { /* ... */ }
    takeDamage(damage: number): void { /* ... */ }
    scheduleOnce(callback: () => void, delay: number): number { /* ... */ return 0; }
    scheduleRepeat(callback: () => void, interval: number): number { /* ... */ return 0; }
    unschedule(timerId: number): void { /* ... */ }
    scheduleDestroy(delay: number): void { /* ... */ }
}

状态生命周期模式

瞬时状态

依赖外部事件自动回切,适合表现类状态:

scala 复制代码
class HitState extends BaseState<EntityController, ActionState> {
    execute(): void {
        this.host.playHitEffect();

        // 监听动画结束事件
        this.host.onEffectFinished(() => {
            this.host.switchActionState(ActionState.MOVE);
        });
    }
}

持续状态

由宿主管理生命周期,适合带持续效果的状态:

kotlin 复制代码
// 在宿主的 switchAttributeState 中统一管理
switchAttributeState(state: AttributeState): void {
    // 清理旧状态任务
    this.clearAttributeTimers();

    // 切换状态
    this.attributeStateMachine.switchState(state);

    // 注册新状态的定时任务
    if (state !== AttributeState.NORMAL) {
        this.scheduleOnce(() => {
            this.switchAttributeState(AttributeState.NORMAL);
        }, 3.0);
    }
}

终止状态

集中处理资源回收:

scala 复制代码
class DeadState extends BaseState<EntityController, ActionState> {
    execute(): void {
        // 停止物理
        this.host.stopPhysics();

        // 播放特效
        this.host.playDeathEffect();

        // 发放奖励
        this.host.grantRewards();

        // 记录统计
        this.host.recordKill();

        // 清理调度任务
        this.host.clearAllTimers();

        // 延迟回收
        this.host.scheduleDestroy(0.5);
    }
}

架构优势

状态空间可控

  • 单一状态机:N × M 个状态类
  • 多维度状态机:N + M 个状态类

职责清晰

  • 状态机:状态查找与分发,不关心业务逻辑
  • 状态类:具体行为执行,持有宿主引用
  • 宿主:协调多个状态机,管理生命周期

扩展性强

新增维度只需:

  1. 创建新的状态机实例
  2. 实现该维度的状态类
  3. 在宿主中添加切换入口

已有维度无需修改。

设计权衡

状态类与宿主的耦合度

当前方案:状态类直接持有宿主引用,可以访问宿主的所有方法和属性。

优点:开发效率高,状态实现直观

缺点:耦合度高,状态类依赖宿主的具体实现

替代方案:定义状态操作接口,状态类只能通过接口操作宿主。

scala 复制代码
interface IStateOperations {
    setVelocity(x: number, y: number): void;
    playEffect(effectName: string): void;
    // ...
}

class MoveState extends BaseState<IStateOperations, ActionState> {
    execute(): void {
        // 只能通过接口操作
        this.host.setVelocity(0, -1);
    }
}

生命周期管理位置

当前方案:持续状态的生命周期由宿主统一管理。

优点:调度任务集中管理,便于清理

缺点:状态逻辑被拆分到状态类和宿主两处

替代方案:状态类自己管理生命周期。

scala 复制代码
class BurningState extends BaseState<EntityController, AttributeState> {
    private timerId: number | null = null;

    execute(): void {
        this.host.setColor(255, 0, 0);

        // 状态自己注册定时器
        this.timerId = this.host.scheduleOnce(() => {
            this.host.switchAttributeState(AttributeState.NORMAL);
        }, 3.0);
    }

    onExit(): void {
        // 状态退出时清理
        if (this.timerId !== null) {
            this.host.unschedule(this.timerId);
        }
    }
}

适用场景

适合使用

  • 实体状态由多个正交维度组成
  • 状态切换频繁,需要高性能
  • 状态行为与表现资源紧密绑定
  • 需要清晰的生命周期管理

不适合使用

  • 状态维度单一且简单(直接用枚举 + switch 即可)
  • 状态转换有复杂的条件依赖(需要状态转换图)
  • 状态之间有严格的顺序约束

项目应用实例

场景描述

在一个塔防类游戏项目中,敌人系统需要同时管理:

行为维度(7 个状态):

  • MOVE:向下移动
  • STOP:停止
  • HIT:普通受击
  • CRITICAL_HIT:暴击受击
  • REPULSE:击退
  • DEFENSE:防御
  • DEAD:死亡

属性维度(5 个状态):

  • NORMAL:正常
  • FROZEN:冰冻(减速 + 视觉效果)
  • FIRE:燃烧(持续伤害 + 视觉效果)
  • THUNDER:雷电(定身 + 视觉效果)
  • POISON:中毒(持续伤害 + 视觉效果)

如果用单一状态机,需要维护 7 × 5 = 35 个组合状态。采用双状态机架构后,只需维护 7 + 5 = 12 个状态类。

核心实现

1. 行为状态机实现

kotlin 复制代码
// EnemyActionStateMachine.ts
export class EnemyActionStateMachine {
    private _state: BaseState;
    private _stateMap: Map<ENEMY_ACTION_STATE, BaseState> = new Map();

    constructor(node: Node, EnemyController: any, initialState: ENEMY_ACTION_STATE) {
        // 预创建所有状态实例
        this._initState(node, EnemyController, ENEMY_ACTION_STATE.MOVE);
        this._initState(node, EnemyController, ENEMY_ACTION_STATE.STOP);
        this._initState(node, EnemyController, ENEMY_ACTION_STATE.HIT);
        this._initState(node, EnemyController, ENEMY_ACTION_STATE.CRITICAL_HIT);
        this._initState(node, EnemyController, ENEMY_ACTION_STATE.REPULSE);
        this._initState(node, EnemyController, ENEMY_ACTION_STATE.DEFENSE);
        this._initState(node, EnemyController, ENEMY_ACTION_STATE.DEAD);

        this.switchState(initialState);
    }

    private async _initState(node: Node, Controller: any, state: ENEMY_ACTION_STATE) {
        let stateInstance: BaseState;
        switch (state) {
            case ENEMY_ACTION_STATE.MOVE:
                stateInstance = new MoveState(node, Controller);
                break;
            case ENEMY_ACTION_STATE.DEAD:
                stateInstance = new DeadState(node, Controller);
                break;
            // ... 其他状态
        }
        this._stateMap.set(state, stateInstance);
    }

    switchState(actionState: ENEMY_ACTION_STATE, info?: any) {
        let state = this._stateMap.get(actionState);
        if (!state) state = this._stateMap.get(ENEMY_ACTION_STATE.MOVE);
        this._state = state;
        return this._state.action(info);
    }

    get value(): ENEMY_ACTION_STATE {
        return this._state?.value;
    }
}

2. 关键状态类实现

移动状态:读取属性状态的速度修正

scala 复制代码
// MoveState.ts
export default class MoveState extends BaseState {
    async action(info: any) {
        // 速度 = 基础速度 × 属性效果修正
        const finalSpeed = this.EnemyController.baseSpeed
                         * this.EnemyController.effectSpeed;

        this.EnemyController.RigidBodyBox.linearVelocity = new Vec2(0, -finalSpeed);
    }
}

死亡状态:作为生命周期终点,集中处理清理逻辑

kotlin 复制代码
// DeadState.ts
export default class DeadState extends BaseState {
    async action(info: any) {
        // 1. 停止物理和动画
        this.EnemyController.Animation.pause();
        this.EnemyController.RigidBodyBox.enabled = false;
        this.EnemyController.RigidBodyBox.linearVelocity = new Vec2(0, 0);

        // 2. 播放死亡特效
        this.EnemyController.ExplosionEffect.active = true;
        this.EnemyController.ExplosionEffect.getComponent(Animation).play();

        // 3. 发放经验和记录统计
        PlayerDataManager.instance.setExp(this.EnemyController.baseExp);
        PlayDataManager.instance.killEnemiesMap.set(
            this.EnemyController.index,
            (PlayDataManager.instance.killEnemiesMap.get(this.EnemyController.index) || 0) + 1
        );

        // 4. 清空所有调度任务
        director.getScheduler().unscheduleAllForTarget(this.EnemyController);

        // 5. 延迟回收到对象池
        this.EnemyController.scheduleOnce(() => {
            this.EnemyNode.active = false;
            // 重置所有属性
            this.EnemyController.baseHp = this.EnemyController.originHp;
            this.EnemyController.effectSpeed = 1;
            this.EnemyController.effectDamage = 0;
            // 解绑事件
            this.EnemyController.ColliderBox.off(Contact2DType.BEGIN_CONTACT);
            this.EnemyController.ColliderBox.off(Contact2DType.END_CONTACT);
            // 放回对象池
            NodePoolManager.instance.putNodeToPool(
                `Enemy-${this.EnemyController.category}-${this.EnemyController.index}`,
                this.EnemyNode
            );
        }, 0.65);
    }
}

受击状态:瞬时状态,依赖动画事件自动回切

scala 复制代码
// HitState.ts
export default class HitState extends BaseState {
    async action(info: any) {
        // 激活受击特效
        this.EnemyController.HitEffect.active = true;
        this.EnemyController.HitEffect.getComponent(Animation).play();

        // 监听动画结束事件,自动切回移动状态
        this.EnemyController.HitEffect.getComponent(Animation).on(
            AnimationComponent.EventType.FINISHED,
            () => {
                this.EnemyController.HitEffect.active = false;
                this.EnemyController.switchActionState(ENEMY_ACTION_STATE.MOVE);
            }
        );
    }
}

3. 属性状态实现

正常状态:作为属性维度的重置模板

scala 复制代码
// NormalState.ts
export default class NormalState extends BaseState {
    async attribute(info: any) {
        // 重置所有属性修正
        this.EnemyController.effectSpeed = 1;
        this.EnemyController.effectDamage = 0;

        // 恢复默认视觉
        this.EnemyController.Sprite.color = new Color(255, 255, 255, 255);
        this.EnemyController.Animation.resume();

        // 恢复移动
        this.EnemyController.RigidBodyBox.linearVelocity = new Vec2(
            0,
            -1 * this.EnemyController.baseSpeed * this.EnemyController.effectSpeed
        );

        // 主动将行为状态拉回移动
        this.EnemyController.switchActionState(ENEMY_ACTION_STATE.MOVE);
    }
}

冰冻状态:修改速度修正参数

scala 复制代码
// FrozenState.ts
export default class FrozenState extends BaseState {
    async attribute(info: any) {
        // 修改视觉效果
        this.EnemyController.Sprite.color = new Color(255, 255, 0, 255);

        // 修改速度修正(不直接改速度,而是改修正系数)
        // 行为状态的 MoveState 会读取这个值
        this.EnemyController.effectSpeed = 0.5;
    }
}

燃烧状态:带持续伤害

scala 复制代码
// FireState.ts
export default class FireState extends BaseState {
    async attribute(info: any) {
        this.EnemyController.Sprite.color = new Color(65, 255, 65);

        // 设置持续伤害参数
        // 宿主会根据这个值注册周期性伤害回调
        this.EnemyController.effectDamage = info?.damage || 2;
    }
}

4. 宿主控制器实现

kotlin 复制代码
// EnemyController.ts
export class EnemyController extends Component {
    // 基础属性
    baseHp: number = 18;
    baseSpeed: number = 1.15;
    baseAttack: number = 4;

    // 状态修正属性(供状态类修改)
    effectSpeed: number = 1;
    effectDamage: number = 0;

    // 状态机实例
    enemyActionState: EnemyActionStateMachine;
    enemyAttributeState: EnemyAttributeStateMachine;

    initStateMachine() {
        this.enemyActionState = new EnemyActionStateMachine(
            this.node,
            EnemyController,
            ENEMY_ACTION_STATE.MOVE
        );
        this.enemyAttributeState = new EnemyAttributeStateMachine(
            this.node,
            EnemyController,
            ENEMY_ATTRIBUTE_STATE.NORMAL
        );
    }

    // 行为状态切换入口
    switchActionState(actionState: ENEMY_ACTION_STATE, info?: any) {
        // 防重入,但 MOVE 允许重复进入(用于重置)
        if ((this.actionState !== actionState) || (actionState === ENEMY_ACTION_STATE.MOVE)) {
            return this.enemyActionState.switchState(actionState, info);
        }
    }

    // 属性状态切换入口(带生命周期管理)
    switchAttributeState(attrState: ENEMY_ATTRIBUTE_STATE, info?: any) {
        // 某些行为状态下禁止切换属性
        if ([ENEMY_ACTION_STATE.STOP].includes(this.actionState)) return;
        if (this.attributeState === attrState) return;

        // 清理旧属性的调度任务
        this.unschedule(this.enemyAttributeState.switchState);
        this.unschedule(this.runAttributeHit);

        // 立即切换到新属性
        this.runSwitchAttributeState(attrState, info);

        // 3.2 秒后自动恢复到 NORMAL
        this.scheduleOnce(this.runSwitchAttributeState, 3.2);

        // 如果有持续伤害,注册周期性伤害回调
        if (this.effectDamage) {
            this.schedule(this.runAttributeHit, 0.88);
        }
    }

    runSwitchAttributeState(attrState: ENEMY_ATTRIBUTE_STATE = ENEMY_ATTRIBUTE_STATE.NORMAL, info?: any) {
        return this.enemyAttributeState.switchState(attrState, info);
    }

    runAttributeHit() {
        if (this.effectDamage) {
            handleHurtEvent(this, this.effectDamage);
        }
    }

    // 暂停/恢复时需要同时考虑两个维度的状态
    setResume() {
        director.getScheduler().resumeTarget(this);

        // 恢复移动速度(读取属性修正)
        this.RigidBodyBox.linearVelocity = new Vec2(
            0,
            -1 * this.baseSpeed * this.effectSpeed
        );

        // 根据属性状态决定是否恢复主动画
        if (this.attributeState !== ENEMY_ATTRIBUTE_STATE.THUNDER) {
            this.Animation.resume();
        }

        // 根据行为状态恢复对应特效
        if (this.actionState === ENEMY_ACTION_STATE.HIT) {
            this.HitEffect.getComponent(Animation).resume();
        }
        if (this.actionState === ENEMY_ACTION_STATE.DEAD) {
            this.ExplosionEffect.getComponent(Animation).resume();
        }
    }

    get actionState(): ENEMY_ACTION_STATE {
        return this.enemyActionState?.value || ENEMY_ACTION_STATE.MOVE;
    }

    get attributeState(): ENEMY_ATTRIBUTE_STATE {
        return this.enemyAttributeState?.value || ENEMY_ATTRIBUTE_STATE.NORMAL;
    }
}

实际运行效果

场景 1:敌人被冰冻后受击

scss 复制代码
// 1. 敌人初始状态
enemy.actionState === ENEMY_ACTION_STATE.MOVE
enemy.attributeState === ENEMY_ATTRIBUTE_STATE.NORMAL
enemy.effectSpeed === 1  // 正常速度

// 2. 玩家使用冰冻技能
enemy.switchAttributeState(ENEMY_ATTRIBUTE_STATE.FROZEN);
// → FrozenState.attribute() 执行
// → enemy.effectSpeed = 0.5
// → 颜色变为黄色
// → 3.2 秒后自动恢复 NORMAL

// 3. 此时敌人仍在移动,但速度变慢
// MoveState 读取 baseSpeed * effectSpeed = 1.15 * 0.5 = 0.575

// 4. 敌人被攻击
enemy.switchAttributeState(ENEMY_ACTION_STATE.HIT);
// → HitState.action() 执行
// → 播放受击特效
// → 动画结束后自动切回 MOVE

// 5. 冰冻效果持续,移动速度仍然是减速状态

场景 2:敌人中毒后死亡

scss 复制代码
// 1. 敌人中毒
enemy.switchAttributeState(ENEMY_ATTRIBUTE_STATE.POISON, { damage: 3 });
// → PoisonState.attribute() 执行
// → enemy.effectDamage = 3
// → 颜色变为紫色
// → 宿主注册周期性伤害回调(每 0.88 秒触发一次)

// 2. 持续伤害触发
// 每 0.88 秒执行一次 runAttributeHit()
// → handleHurtEvent(enemy, 3)
// → enemy.baseHp -= 3

// 3. 血量归零
if (enemy.baseHp <= 0) {
    enemy.switchActionState(ENEMY_ACTION_STATE.DEAD);
    // → DeadState.action() 执行
    // → 停止物理和动画
    // → 播放爆炸特效
    // → 发放经验
    // → 记录击杀统计
    // → 清空所有调度任务(包括中毒的持续伤害)
    // → 0.65 秒后回收到对象池
}

场景 3:游戏暂停/恢复

scss 复制代码
// 暂停时
gameManager.pauseGame();
// → 遍历所有敌人
// → 调用 enemy.setPause()
// → 停止物理、动画、特效

// 恢复时
gameManager.resumeGame();
// → 遍历所有敌人
// → 调用 enemy.setResume()
// → 根据 actionState 和 attributeState 的组合决定恢复行为

// 示例:敌人处于"移动 + 冰冻"状态
if (enemy.actionState === ENEMY_ACTION_STATE.MOVE) {
    // 恢复移动,速度 = baseSpeed * effectSpeed(冰冻修正)
    enemy.setVelocity(0, -1.15 * 0.5);
}
if (enemy.attributeState !== ENEMY_ATTRIBUTE_STATE.THUNDER) {
    // 非雷电状态才恢复主动画
    enemy.Animation.resume();
}

架构收益

通过这套双状态机架构,项目获得了:

  1. 状态数量可控:12 个状态类 vs 35 个组合状态
  2. 代码复用:4 种敌人类型(Tiny、Sub、Boss、Special)共享同一套状态机逻辑
  3. 易于扩展:新增属性效果(如"眩晕")只需添加一个属性状态类
  4. 清晰的生命周期:死亡状态集中处理所有清理逻辑,不会遗漏
  5. 表现驱动:受击、暴击等状态依赖动画事件自动回切,无需轮询

总结

多维度状态机架构通过正交分离策略,将复杂状态空间拆解为多个独立维度。核心价值在于:

  1. 控制状态爆炸:N + M 而非 N × M
  2. 职责清晰:状态机、状态类、宿主各司其职
  3. 易于扩展:新增维度不影响已有维度
  4. 表现驱动:状态切换由事件驱动,而非轮询判断

实现时需要权衡:

  • 状态类与宿主的耦合度
  • 生命周期管理的位置
  • 状态机数量与协调复杂度

通用框架提供了基础的状态机、状态基类和宿主接口,可以根据具体场景灵活扩展。

相关推荐
Bigger2 小时前
CodeWalkers:让 AI 助手化身桌面宠物,陪你敲代码的赛博伙伴!
前端·app·ai编程
cyclv3 小时前
无网络地图展示轨迹,地图瓦片下载,绘制管线
前端·javascript
土豆12503 小时前
Tauri 入门与实践:用 Rust 构建你的下一个桌面应用
前端·rust
小陈工5 小时前
2026年4月2日技术资讯洞察:数据库融合革命、端侧AI突破与脑机接口产业化
开发语言·前端·数据库·人工智能·python·安全
IT_陈寒5 小时前
Vue的这个响应式问题,坑了我整整两小时
前端·人工智能·后端
C澒6 小时前
AI 生码:A 类生码方案架构升级
前端·ai编程
前端Hardy6 小时前
前端必看!LocalStorage这么用,再也不踩坑(多框架通用,直接复制)
前端·javascript·面试
前端Hardy6 小时前
前端必看!前端路由守卫这么写,再也不担心权限混乱(Vue/React通用)
前端·javascript·面试
Lee川6 小时前
从零构建现代化登录界面:React + Tailwind CSS 前端工程实践
前端·react.js