游戏战斗系统学习第2篇:有限状态机 (FSM)

一、 FSM 的核心概念详解

1. 原理与实现

FSM的核心由状态(State) 、转移(Transition) 和条件(Condition) 三要素构成。AI始终处于某一个确定状态中,持续执行该状态的逻辑,直到满足某个转移条件才切换到新状态。典型的实现是一个while(true)循环包裹switch(state)语句块,每个case对应一个状态的执行逻辑和转移判断。

2. 概念详解
2.1 状态(State)

状态表示 AI 当前的行为模式,而不是具体动作。

比如: 在战斗AI中,一个典型的状态集合是:Idle(待机 / 空闲) → Patrol(巡逻) → Alert(警戒) → Chase(追击) → Attack(攻击) → Retreat(撤退)。状态数量少于7个时,FSM是最经济的选择。

**注意:**一个设计良好的状态应该是高内聚的:状态内部逻辑围绕一个明确的行为模式组织。如果 Attack 状态内部包含了前摇、判定、后摇、连招分支等复杂逻辑,就应该考虑将其拆分为子状态机(HFSM)

2.2 转移(Transition)

转移定义了状态之间的切换关系。一个完整的转移包含:

  • 源状态:从哪个状态出发
  • 目标状态:切换到哪个状态
  • 触发事件:什么事件触发转移,如 SeePlayer
  • 守卫条件:转移必须满足的条件,如 distance < 10 && hasAmmo
  • 转移动作:转移瞬间执行的动作,如播放音效、设置变量
  • 优先级:多个转移同时满足时的选择顺序

转移的触发方式有两种:

  • 轮询式:每帧检查所有转移条件。简单但可能浪费性能。
  • 事件驱动式:只在特定事件发生时检查相关转移。更高效,但需要事件系统支持。
2.3 动作(Action)

动作是状态机在特定时刻执行的逻辑。通常分为三类:

  • 进入动作(Entry Action):进入状态时执行一次,如播放动画、设置速度、重置计时器
  • 更新动作(Update Action):状态持续期间每帧执行,如移动、检测玩家、更新计时器
  • 退出动作(Exit Action):离开状态时执行一次,如停止移动、清理资源、恢复默认值

将动作按进入/更新/退出分离,是状态模式实现的关键,能有效避免资源泄漏和状态残留。

3. 适用场景

FSM非常适合状态少且稳定、转移关系明确、行为模式化的场景。例如2D游戏中的简单敌人AI、Boss战的阶段切换、塔防游戏中怪物的行进逻辑等。在《星际争霸》这类RTS中,FSM也被用于控制单位的底层行为。

技术选型要点:状态 ≤ 7个 → FSM;状态转移条件清晰且不随功能迭代频繁增加 → FSM;需要精确调试每个状态行为 → FSM(因为状态显式,调试最直观)。

二、分层有限状态机 (HFSM)

HFSM是对FSM的直接改进,通过将状态分组并层级化来解决状态转移的组合爆炸问题。

1. 原理与实现

HFSM的核心思想是:把一组相关的低层状态封装到一个高层状态中,低层状态之间的转移在内部处理,不需要暴露给外部状态。高层状态之间只维护粗粒度的转移关系,从而大幅减少需要显式管理的转移数量。

示例:战斗AI可以这样分层

TypeScript 复制代码
战斗(Combat)[高层状态]
├── 近战攻击(MeleeAttack)[低层状态]
├── 远程攻击(RangedAttack)[低层状态]
└── 防御(Defend)[低层状态]

非战斗(NonCombat)[高层状态]
├── 巡逻(Patrol)
└── 待机(Idle)

从NonCombat进入Combat只需要一条高层转移,而不是从Patrol、Idle分别连接到MeleeAttack、RangedAttack、Defend的九条转移。HFSM还支持转移的重用------高层状态的转移条件可以被所有低层状态共享,减少了代码冗余

2. 为什么需要 HFSM:从状态爆炸说起
2.1 传统 FSM 的状态爆炸问题

假设一个战斗 AI 有以下状态:

TypeScript 复制代码
Idle, Patrol, Alert, Chase, MeleeAttack, RangedAttack, Block, Dodge, Flee, Dead

如果采用扁平 FSM,需要考虑的转移数量是:

  • 每个状态平均需要检查 3~5 个转移条件

  • 10 个状态 → 约 30~50 条转移

  • 每新增一个状态,需要接入的转移数随已有状态数线性增长

更严重的是语义重复。例如:

  • Idle、Patrol、Alert 都可能因为"发现玩家"转移到 Chase

  • MeleeAttack、RangedAttack、Block、Dodge 都可能因为"生命值过低"转移到 Flee

  • 所有状态都可能因为"生命值归零"转移到 Dead

这些转移条件在每个状态中重复编写,修改时需要同步修改多处,维护成本随状态数增长而急剧上升。

2.2 HFSM 的核心洞察

HFSM 的核心洞察是:很多状态共享相同的转移逻辑,很多转移只在特定状态组内才有意义。

将状态分组后:

  • 组内转移:只在组内状态之间发生,外部不需要知道

  • 组间转移:由父状态统一管理,子状态不需要重复编写

  • 通用转移:定义在高层,所有子状态自动继承

示例:将上述 10 个状态分组:

TypeScript 复制代码
Root
├── NonCombat(非战斗)
│   ├── Idle
│   ├── Patrol
│   └── Alert
└── Combat(战斗)
    ├── Chase
    ├── MeleeAttack
    ├── RangedAttack
    ├── Block
    └── Dodge
└── Flee(逃跑)
└── Dead(死亡)

分组后:

  • NonCombat 内部的 Idle → Patrol → Alert 转移只在组内处理

  • NonCombat → Combat 只需要一条高层转移(发现玩家)

  • Combat → Flee 只需要一条高层转移(生命值过低)

  • Any → Dead 只需要一条全局转移(生命值归零)

  • 转移数量从 30~50 条降至 15~20 条,且新增状态时只需接入组内转移。

3. HFSM 的设计原则

原则一:按语义分组,而非按实现分组

好的分组原则:

TypeScript 复制代码
Combat(战斗)
├── Chase(追击)
├── Attack(攻击)
└── Defend(防御)

不好的分组:

TypeScript 复制代码
StatesWithTimer(带计时器的状态)
├── Idle
├── Attack
└── Flee

原则二:父状态应该有明确的语义

父状态名应该能概括所有子状态的共同特征。如果找不到合适的名字,说明分组不合理

原则三:层级深度控制在 3~4 层以内

过深的层级会增加调试复杂度。大多数战斗 AI 的层级深度在 3 层左右:

TypeScript 复制代码
Root → Combat → MeleeAttack

三、状态机代码实现(TypeScript)

1. 状态模式(State Pattern)

每个状态一个类,符合开闭原则,易于扩展和复用。

1.1 接口定义
TypeScript 复制代码
interface IState<C> {
    /** 进入状态时调用一次 */
    enter(context: C): void;
    /** 每帧调用 */
    update(context: C, deltaTime: number): void;
    /** 离开状态时调用一次 */
    exit(context: C): void;
    /** 状态名称,用于调试 */
    readonly name: string;
}

interface IStateMachine<C> {
    currentState: IState<C>;
    changeState(newState: IState<C>): void;
    update(deltaTime: number): void;
}
1.2 战斗 AI 上下文与状态实现
TypeScript 复制代码
//状态类型枚举
export enum EnemyStateType  { 
    Idle="Idle",                //待机
    Patrol= "Patrol",           //巡逻
    Chase="Chase",              //追击
    Attack="Attack",            //攻击
    Flee="Flee",                //逃跑
    Dead="Dead",                //死亡
} 

//所有状态列表
const EnemyFsmState = {
    StateList: {
        [EnemyStateType.Idle]: new IdleState,
        [EnemyStateType.Patrol]: new PatrolState,
        [EnemyStateType.Chase]: new ChaseState,
        [EnemyStateType.Attack]: new AttackState,
        [EnemyStateType.Flee]: new FleeState,
        [EnemyStateType.Dead]: new DeadState,
    },
    Default: EnemyStateType.Idle,
}


// --- 上下文 ---
class EnemyContext {
    public health: number = 100;
    public maxHealth: number = 100;
    public position = { x: 0, y: 0 };
    public playerPosition = { x: 0, y: 0 };
    public attackRange: number = 2.0;
    public chaseRange: number = 10.0;
    public fleeThreshold: number = 30;
    public stateMachine!: StateMachine<EnemyContext>;

    public distanceToPlayer(): number {
        const dx = this.position.x - this.playerPosition.x;
        const dy = this.position.y - this.playerPosition.y;
        return Math.sqrt(dx * dx + dy * dy);
    }

    public canSeePlayer(): boolean {
        return this.distanceToPlayer() <= this.chaseRange;
    }

    public inAttackRange(): boolean {
        return this.distanceToPlayer() <= this.attackRange;
    }

    public isDead(): boolean {
        return this.health <= 0;
    }
}

// --- 待机状态 ---
class IdleState implements IState<EnemyContext> {
    public readonly name = "Idle";
    private timer = 0;

    public enter(ctx: EnemyContext): void {
        this.timer = 0;
        console.log("[AI] 进入待机");
    }

    public update(ctx: EnemyContext, dt: number): void {
        this.timer += dt;

        if (ctx.canSeePlayer()) {
            ctx.stateMachine.changeState(new ChaseState());
            return;
        }
        if (this.timer > 2.0) {
            ctx.stateMachine.changeState(new PatrolState());
        }
    }

    public exit(ctx: EnemyContext): void {
        console.log("[AI] 离开待机");
    }
}

// --- 巡逻状态 ---
class PatrolState implements IState<EnemyContext> {
    public readonly name = "Patrol";
    private pathIndex = 0;
    private patrolPath = [
        { x: 0, y: 0 },
        { x: 5, y: 0 },
        { x: 5, y: 5 },
        { x: 0, y: 5 },
    ];

    public enter(ctx: EnemyContext): void {
        console.log("[AI] 进入巡逻");
    }

    public update(ctx: EnemyContext, dt: number): void {
        if (ctx.isDead()) {
            ctx.stateMachine.changeState(new DeadState());
            return;
        }
        if (ctx.canSeePlayer()) {
            ctx.stateMachine.changeState(new ChaseState());
            return;
        }
        this.moveAlongPath(ctx, dt);
    }

    public exit(ctx: EnemyContext): void {
        console.log("[AI] 离开巡逻");
    }

    private moveAlongPath(ctx: EnemyContext, dt: number): void {
        const target = this.patrolPath[this.pathIndex];
        const dx = target.x - ctx.position.x;
        const dy = target.y - ctx.position.y;
        const dist = Math.sqrt(dx * dx + dy * dy);

        if (dist < 0.1) {
            this.pathIndex = (this.pathIndex + 1) % this.patrolPath.length;
            return;
        }

        const speed = 2.0;
        ctx.position.x += (dx / dist) * speed * dt;
        ctx.position.y += (dy / dist) * speed * dt;
    }
}

// --- 追击状态 ---
class ChaseState implements IState<EnemyContext> {
    public readonly name = "Chase";

    public enter(ctx: EnemyContext): void {
        console.log("[AI] 进入追击");
    }

    public update(ctx: EnemyContext, dt: number): void {
        if (ctx.isDead()) {
            ctx.stateMachine.changeState(new DeadState());
            return;
        }
        if (ctx.health < ctx.fleeThreshold) {
            ctx.stateMachine.changeState(new FleeState());
            return;
        }
        if (ctx.inAttackRange()) {
            ctx.stateMachine.changeState(new AttackState());
            return;
        }
        if (!ctx.canSeePlayer()) {
            ctx.stateMachine.changeState(new PatrolState());
            return;
        }
        this.moveTowardPlayer(ctx, dt);
    }

    public exit(ctx: EnemyContext): void {
        console.log("[AI] 离开追击");
    }

    private moveTowardPlayer(ctx: EnemyContext, dt: number): void {
        const dx = ctx.playerPosition.x - ctx.position.x;
        const dy = ctx.playerPosition.y - ctx.position.y;
        const dist = Math.sqrt(dx * dx + dy * dy);
        if (dist < 0.01) return;

        const speed = 4.0;
        ctx.position.x += (dx / dist) * speed * dt;
        ctx.position.y += (dy / dist) * speed * dt;
    }
}

// --- 攻击状态 ---
class AttackState implements IState<EnemyContext> {
    public readonly name = "Attack";
    private attackTimer = 0;
    private readonly attackDuration = 0.8;
    private hasDealtDamage = false;

    public enter(ctx: EnemyContext): void {
        console.log("[AI] 进入攻击");
        this.attackTimer = 0;
        this.hasDealtDamage = false;
    }

    public update(ctx: EnemyContext, dt: number): void {
        if (ctx.isDead()) {
            ctx.stateMachine.changeState(new DeadState());
            return;
        }

        this.attackTimer += dt;

        // 前摇 0~0.3s,判定 0.3s,后摇 0.3~0.8s
        if (this.attackTimer >= 0.3 && !this.hasDealtDamage) {
            this.dealDamage(ctx);
            this.hasDealtDamage = true;
        }

        if (this.attackTimer >= this.attackDuration) {
            if (ctx.inAttackRange()) {
                ctx.stateMachine.changeState(new AttackState());
            } else {
                ctx.stateMachine.changeState(new ChaseState());
            }
        }
    }

    public exit(ctx: EnemyContext): void {
        console.log("[AI] 离开攻击");
    }

    private dealDamage(ctx: EnemyContext): void {
        if (ctx.inAttackRange()) {
            console.log("[AI] 攻击命中玩家!");
        }
    }
}

// --- 逃跑状态 ---
class FleeState implements IState<EnemyContext> {
    public readonly name = "Flee";

    public enter(ctx: EnemyContext): void {
        console.log("[AI] 进入逃跑");
    }

    public update(ctx: EnemyContext, dt: number): void {
        if (ctx.isDead()) {
            ctx.stateMachine.changeState(new DeadState());
            return;
        }
        if (ctx.health > ctx.fleeThreshold * 1.5) {
            ctx.stateMachine.changeState(new ChaseState());
            return;
        }
        this.moveAwayFromPlayer(ctx, dt);
    }

    public exit(ctx: EnemyContext): void {
        console.log("[AI] 离开逃跑");
    }

    private moveAwayFromPlayer(ctx: EnemyContext, dt: number): void {
        const dx = ctx.position.x - ctx.playerPosition.x;
        const dy = ctx.position.y - ctx.playerPosition.y;
        const dist = Math.sqrt(dx * dx + dy * dy);
        if (dist < 0.01) return;

        const speed = 5.0;
        ctx.position.x += (dx / dist) * speed * dt;
        ctx.position.y += (dy / dist) * speed * dt;
    }
}

// --- 死亡状态 ---
class DeadState implements IState<EnemyContext> {
    public readonly name = "Dead";

    public enter(ctx: EnemyContext): void {
        console.log("[AI] 进入死亡状态");
        // 播放死亡动画、禁用碰撞等
    }

    public update(ctx: EnemyContext, dt: number): void {
        // 死亡状态不处理任何逻辑
    }

    public exit(ctx: EnemyContext): void {
        // 通常不会离开死亡状态
    }
}
1.3 状态机核心
TypeScript 复制代码
class StateMachine<C> implements IStateMachine<C> {
    private _currentState: IState<C>;
    private _defaultState: string;
    private context: C;
    private stateTime: number = 0;
    private previousState: IState<C> | null = null;
    private states: Map<string, FSMState> = new Map();

    constructor(context: C) {
        this.context = context;
        this._defaultState = EnemyFsmState.Default;

        this.states.clear();
        Object.entries(EnemyFsmState.stateList).forEach(([key, state]) => {
            this.states.set(key, state);
        });

        this._currentState = this.states.get(this._defaultState);
        this._currentState.enter(context);
    }

    public get currentState(): IState<C> {
        return this._currentState;
    }

    public get timeInState(): number {
        return this.stateTime;
    }

    public changeState(stateType:EnemyStateType): void {
        let newState = this.states.get(stateType);
        if (newState === this._currentState) {
            return; // 避免重复进入同一状态
        }

        this._currentState.exit(this.context);
        this.previousState = this._currentState;
        this._currentState = newState;
        this.stateTime = 0;
        this._currentState.enter(this.context);
    }

    /** 返回到上一个状态 */
    public revertToPreviousState(): void {
        if (this.previousState) {
            this.changeState(this.previousState);
        }
    }

    public update(deltaTime: number): void {
        this.stateTime += deltaTime;
        this._currentState.update(this.context, deltaTime);
    }
}

使用示例

TypeScript 复制代码
function main(): void {
    const ctx = new EnemyContext();
    const stateMachine = new StateMachine<EnemyContext>(ctx);
    ctx.stateMachine = stateMachine;

    // 模拟游戏循环
    const fixedDeltaTime = 1 / 60;
    let elapsed = 0;

    const gameLoop = setInterval(() => {
        stateMachine.update(fixedDeltaTime);
        elapsed += fixedDeltaTime;

        // 模拟玩家靠近
        if (elapsed > 1.0 && elapsed < 5.0) {
            ctx.playerPosition = { x: ctx.position.x + 3, y: ctx.position.y };
        }

        if (elapsed >= 10.0) {
            clearInterval(gameLoop);
            console.log("模拟结束");
        }
    }, fixedDeltaTime * 1000);
}
2. 分层有限状态机(HFSM)

HFSM 通过将状态分组并层级化,解决 FSM 的状态爆炸问题。

2.1 核心类型定义
TypeScript 复制代码
enum TransitionResult {
    /** 转移已处理,不需要冒泡到父状态 */
    Handled = 0,
    /** 未处理,冒泡到父状态 */
    Unhandled = 1,
}

interface IHState<C> {
    readonly name: string;
    enter(ctx: C): void;
    update(ctx: C, dt: number): void;
    exit(ctx: C): void;
    /** 处理转移,返回是否已处理 */
    handleTransition(ctx: C): TransitionResult;
    /** 获取当前活跃的子状态(用于调试) */
    getActiveChild(): IHState<C> | null;
}
2.2 分层状态基类
TypeScript 复制代码
abstract class HierarchicalState<C> implements IHState<C> {
    public readonly name: string;
    protected parent: HierarchicalState<C> | null = null;

    constructor(name: string) {
        this.name = name;
    }

    public setParent(parent: HierarchicalState<C>): void {
        this.parent = parent;
    }

    public enter(ctx: C): void {
        console.log(`[HFSM] 进入 ${this.name}`);
    }

    public update(ctx: C, dt: number): void {
        // 先让子状态处理
        const child = this.getActiveChild();
        if (child) {
            child.update(ctx, dt);
        }
    }

    public exit(ctx: C): void {
        console.log(`[HFSM] 离开 ${this.name}`);
    }

    public handleTransition(ctx: C): TransitionResult {
        // 先让子状态处理
        const child = this.getActiveChild();
        if (child) {
            const result = child.handleTransition(ctx);
            if (result === TransitionResult.Handled) {
                return TransitionResult.Handled;
            }
        }
        // 子状态未处理,当前状态处理
        return this.onHandleTransition(ctx);
    }

    protected onHandleTransition(ctx: C): TransitionResult {
        return TransitionResult.Unhandled;
    }

    public getActiveChild(): IHState<C> | null {
        return null;
    }
}
2.3 复合状态(带子状态机)
TypeScript 复制代码
class CompositeState<C> extends HierarchicalState<C> {
    private children: Map<string, HierarchicalState<C>> = new Map();
    private activeChild: HierarchicalState<C> | null = null;
    private initialStateName: string;

    constructor(name: string, initialStateName: string) {
        super(name);
        this.initialStateName = initialStateName;
    }

    public addChild(child: HierarchicalState<C>): this {
        child.setParent(this);
        this.children.set(child.name, child);
        return this;
    }

    public enter(ctx: C): void {
        super.enter(ctx);
        const initial = this.children.get(this.initialStateName);
        if (initial) {
            this.activeChild = initial;
            initial.enter(ctx);
        }
    }

    public update(ctx: C, dt: number): void {
        // 先检查转移
        const result = this.handleTransition(ctx);
        if (result === TransitionResult.Handled) {
            return;
        }
        // 再更新
        super.update(ctx, dt);
    }

    public exit(ctx: C): void {
        this.activeChild?.exit(ctx);
        this.activeChild = null;
        super.exit(ctx);
    }

    public getActiveChild(): IHState<C> | null {
        return this.activeChild;
    }

    /** 切换子状态 */
    public changeChild(newChildName: string, ctx: C): void {
        const newChild = this.children.get(newChildName);
        if (!newChild) {
            console.warn(`[HFSM] 子状态 ${newChildName} 不存在于 ${this.name}`);
            return;
        }
        if (this.activeChild === newChild) return;

        this.activeChild?.exit(ctx);
        this.activeChild = newChild;
        newChild.enter(ctx);
    }
}
2.4 战斗 AI 的 HFSM 实现
TypeScript 复制代码
// --- 复合状态:非战斗 ---
class NonCombatState<C extends EnemyContext> extends CompositeState<C> {
    constructor() {
        super("NonCombat", "Idle");
    }

    protected onHandleTransition(ctx: C): TransitionResult {
        if (ctx.canSeePlayer()) {
            // 冒泡到根状态,由根状态处理到 Combat 的转移
            return TransitionResult.Unhandled;
        }
        return TransitionResult.Unhandled;
    }
}

// --- 复合状态:战斗 ---
class CombatState<C extends EnemyContext> extends CompositeState<C> {
    constructor() {
        super("Combat", "Chase");
    }

    protected onHandleTransition(ctx: C): TransitionResult {
        if (!ctx.canSeePlayer() && !ctx.inAttackRange()) {
            // 冒泡,由根状态处理回 NonCombat
            return TransitionResult.Unhandled;
        }
        return TransitionResult.Unhandled;
    }
}

// --- 叶子状态:待机 ---
class IdleLeaf<C extends EnemyContext> extends HierarchicalState<C> {
    private timer = 0;

    constructor() {
        super("Idle");
    }

    public enter(ctx: C): void {
        super.enter(ctx);
        this.timer = 0;
    }

    public update(ctx: C, dt: number): void {
        this.timer += dt;
        // 转移由父状态处理
    }

    protected onHandleTransition(ctx: C): TransitionResult {
        if (this.timer > 2.0) {
            const parent = this.parent as CompositeState<C>;
            parent.changeChild("Patrol", ctx);
            return TransitionResult.Handled;
        }
        return TransitionResult.Unhandled;
    }
}

// --- 叶子状态:巡逻 ---
class PatrolLeaf<C extends EnemyContext> extends HierarchicalState<C> {
    constructor() {
        super("Patrol");
    }

    public update(ctx: C, dt: number): void {
        // 巡逻移动逻辑
    }
}

// --- 叶子状态:追击 ---
class ChaseLeaf<C extends EnemyContext> extends HierarchicalState<C> {
    constructor() {
        super("Chase");
    }

    public update(ctx: C, dt: number): void {
        // 追击移动逻辑
    }

    protected onHandleTransition(ctx: C): TransitionResult {
        if (ctx.health < ctx.fleeThreshold) {
            // 在战斗复合状态内切换到逃跑
            const parent = this.parent as CompositeState<C>;
            parent.changeChild("Flee", ctx);
            return TransitionResult.Handled;
        }
        if (ctx.inAttackRange()) {
            const parent = this.parent as CompositeState<C>;
            parent.changeChild("Attack", ctx);
            return TransitionResult.Handled;
        }
        return TransitionResult.Unhandled;
    }
}

// --- 根状态 ---
class RootState<C extends EnemyContext> extends CompositeState<C> {
    constructor() {
        super("Root", "NonCombat");
    }

    protected onHandleTransition(ctx: C): TransitionResult {
        const activeChild = this.getActiveChild();

        // 全局转移:死亡
        if (ctx.isDead()) {
            this.changeChild("Dead", ctx);
            return TransitionResult.Handled;
        }

        // NonCombat <-> Combat 切换
        if (activeChild?.name === "NonCombat" && ctx.canSeePlayer()) {
            this.changeChild("Combat", ctx);
            return TransitionResult.Handled;
        }

        if (activeChild?.name === "Combat" && !ctx.canSeePlayer() && !ctx.inAttackRange()) {
            this.changeChild("NonCombat", ctx);
            return TransitionResult.Handled;
        }

        return TransitionResult.Unhandled;
    }
}
2.5 HFSM 的优缺点

优点:

  • 大幅减少转移数量,解决状态爆炸

  • 保留 FSM 的低开销和显式控制

  • 支持转移冒泡,父状态可处理通用转移

缺点:

  • 调试复杂度高于纯 FSM

  • 可能出现高层与低层状态的语义冲突

3. 并行状态机 与 栈式状态机
3.1 并行状态机

并行状态机允许同时运行多个独立的状态机,处理不同维度的行为。

实现:

TypeScript 复制代码
class ParallelStateMachine<C> {
    private machines: Map<string, StateMachine<C>> = new Map();
    private context: C;

    constructor(context: C) {
        this.context = context;
    }

    public addMachine(name: string, machine: StateMachine<C>): void {
        this.machines.set(name, machine);
    }

    public getMachine(name: string): StateMachine<C> | undefined {
        return this.machines.get(name);
    }

    public update(deltaTime: number): void {
        for (const machine of this.machines.values()) {
            machine.update(deltaTime);
        }
    }
}

使用示例:角色多维度行为

TypeScript 复制代码
class CharacterContext {
    public health = 100;
    public isMoving = false;
    public isAttacking = false;
    public isCasting = false;
    public movementMachine!: StateMachine<CharacterContext>;
    public actionMachine!: StateMachine<CharacterContext>;
}

// 移动状态机:Idle / Walk / Run / Jump
// 动作状态机:None / Attack / Block / Cast
// 两个状态机并行运行,通过共享上下文协调

并行状态机适合复杂角色的行为组合,但需要注意:

  • 状态机之间的互斥规则(如攻击时不能移动)

  • 优先级冲突解决

  • 共享状态的同步

3.2 栈式状态机

用栈管理状态,支持 Push / Pop 操作,适合需要"返回"的场景。

TypeScript 复制代码
class StackStateMachine<C> {
    private stack: IState<C>[] = [];
    private context: C;

    constructor(context: C) {
        this.context = context;
    }

    public get currentState(): IState<C> | null {
        return this.stack.length > 0 ? this.stack[this.stack.length - 1] : null;
    }

    /** 压入新状态,当前状态暂停 */
    public push(state: IState<C>): void {
        this.currentState?.exit(this.context);
        this.stack.push(state);
        state.enter(this.context);
    }

    /** 弹出当前状态,恢复到上一个状态 */
    public pop(): void {
        if (this.stack.length === 0) return;

        const state = this.stack.pop()!;
        state.exit(this.context);

        this.currentState?.enter(this.context);
    }

    /** 替换当前状态 */
    public replace(state: IState<C>): void {
        if (this.stack.length > 0) {
            const old = this.stack.pop()!;
            old.exit(this.context);
        }
        this.stack.push(state);
        state.enter(this.context);
    }

    public update(deltaTime: number): void {
        this.currentState?.update(this.context, deltaTime);
    }
}
相关推荐
剑客的茶馆1 小时前
在影视、动画和游戏特效(VFX/Animation Pipeline)行业中Python 调用RV Play
游戏·rv play·rv
an12321214 小时前
ARM嵌入式学习笔记:SPI通信协议详解
arm开发·笔记·学习
by209994 小时前
从结构体到对象:正式学习C++类的骨架、封装与this指针
c++·经验分享·学习
乌暮5 小时前
深入理解 Java 泛型:把「万能盒子」用对、用稳
java·开发语言·后端·学习
小雪崩6 小时前
嵌入式学习 day60:ARM汇编——触摸屏与SPI
汇编·arm开发·学习
FYKJ_20106 小时前
django电影推荐系统55066-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
问天_观心7 小时前
大模型训练与推理优化(二)
人工智能·深度学习·学习·大模型·transformer
海海不掉头发8 小时前
软件项目管理学习笔记-从软件危机到敏捷与DevOps
笔记·学习·devops
进击的明明8 小时前
TypeScript速通笔记(下)
前端·面试·typescript