参考

豆包

 

目录

一 什么是状态机

二 用有限状态机实现游戏状态

三 并发状态机实现角色状态

 

一 什么是状态机

1 状态机定义

在游戏开发中,游戏状态机(Game State Machine) 是一种用于管理游戏流程和逻辑状态的设计模式,

它通过将游戏的不同阶段或实体行为划分为 “状态”,并定义状态之间的转换规则,来实现对复杂交互逻辑的有序控制。

 

例如游戏常用到如下状态:

游戏状态:初始化、开始、暂停、恢复、下一波敌人、3选1技能、失败、胜利、退出游戏等

角色状态:站立、移动、巡逻、跳跃、攻击、受伤、死亡等

 

2 为什么需要状态机?

使用状态机,可以避免使用if-else/switch-case进行逻辑的条件判断,导致代码臃肿,难以维护,扩展性差。

状态机通过 “封装状态行为” 和 “明确转换规则”,将复杂逻辑拆解为独立模块,让代码更清晰、可复用。

通过模块化、可控的状态管理,解决了游戏开发中 “行为混乱、逻辑冲突、难以维护” 的痛点。无论是简单的角色控制,还是复杂的 AI 行为,状态机都是一种高效、可靠的设计模式。

 

3 常见状态机

                                            特点                                        使用场景

有限状态机      单一当前状态,明确转换条件            简单系统,流程控制

层次状态机      状态可嵌套,共享父类行为                复杂系统,减少代码重复

并发状态机      同时存在多个状态                               需要并行行为的实体

 

4 状态机核心概念

1 状态(State)

系统在特定时刻的行为模式,例如:

游戏中开始、暂停、胜利、退出

 

2 事件/转换(Event/Transition)

触发状态切换条件,例如:

玩家点击退出、玩家生命为0游戏失败

 

3 动作(Action)

状态转换前后执行的操作,例如:

玩家死亡,播放死亡动画并禁用操作

游戏暂停,所有角色停止移动,弹出暂停菜单

 

简单来说就是多个状态切换,用if-else/switch-case写的话会让代码臃肿不清晰,扩展也不方便。用状态机拆成独立模块更清晰易扩展。

 

 

二 用有限状态机实现游戏状态

用有限状态机实现游戏的初始化、开始、暂停、恢复、下一波敌人、失败、胜利

 

游戏状态枚举

enum EGameState {
    INIT,           // 初始化
    START,          // 开始游戏
    PAUSE,          // 暂停游戏
    RESUME,         // 恢复游戏
    NEXT_WAVE,      // 下一波敌人
    GAME_OVER,      // 游戏结束
    VICTORY         // 游戏胜利
}

  

游戏状态基类

class GameState {
    protected gm: GameManager;

    constructor(gm: GameManager) {
        this.gm = gm;
    }

    //进入状态时调用
    onEnter() { };

    //状态更新
    update(dt: number) { };

    //离开状态时调用
    onExit() { };

    //处理输入
    handleInput(input: EGameState) { };
}

  

有限状态机

class FSM {
    //当前状态
    private curState: GameState;
    //状态映射表
    private stateMap: Map<EGameState, GameState> = new Map();

    //注册状态
    public register(e: EGameState, state: GameState) {
        this.stateMap.set(e, state);
    }

    //更新状态
    public update(dt: number) {
        if (this.curState) {
            this.curState.update(dt);
        }
    }

    //改变状态
    public changeState(newState: EGameState) {
        if (this.curState) {
            this.curState.onExit();
        }
        this.curState = this.stateMap.get(newState);
        this.curState.onEnter();
    }

    //处理输入
    public handleInput(input: EGameState) {
        if (this.curState) {
            this.curState.handleInput(input);
        }
    }
}

  

游戏管理类

class GameManager {
    public fsm: FSM = new FSM();

    init() {
        //注册
        this.fsm.register(EGameState.INIT, new GameInitState(this));
        this.fsm.register(EGameState.PAUSE, new GamePauseState(this));
        //注册其它,省略...


        //默认初始化状态
        this.fsm.changeState(EGameState.INIT);
    }

    update(dt: number) {
        this.fsm.update(dt);
    }
}

  

游戏初始化状态

class GameInitState extends GameState {

    onEnter(): void {
        //初始化游戏
    }

    handleInput(input: EGameState): void {
        switch (input) {
            case EGameState.START:
                this.gm.fsm.changeState(EGameState.START);
                break;
        }
    }
}

  

游戏暂停状态

class GamePauseState extends GameState {
    
    onEnter(): void {
        //暂停游戏,显示菜单
    }

    handleInput(input: EGameState): void {
        switch (input) {
            case EGameState.RESUME:
                this.gm.fsm.changeState(EGameState.RESUME);
                break;
            case EGameState.START:
                //进行重置游戏数据

                this.gm.fsm.changeState(EGameState.START);
                break;
        }
    }
}

  

游戏开始状态

class GameStartState extends GameState {

    onEnter(): void {
        //初始化
    }

    update(dt: number): void {
        //更新游戏逻辑,角色移动等
    }

    handleInput(input: EGameState): void {
        switch (input) {
            case EGameState.PAUSE:
                this.gm.fsm.changeState(EGameState.PAUSE);
                break;
            case EGameState.GAME_OVER:
                this.gm.fsm.changeState(EGameState.GAME_OVER);
                break;
            case EGameState.VICTORY:
                this.gm.fsm.changeState(EGameState.VICTORY);
                break;
            case EGameState.NEXT_WAVE:
                this.gm.fsm.changeState(EGameState.NEXT_WAVE);
                break;
        }
    }
}

  

 这里面的handleInput起到的作用。

1. 有状态规则表的作用,知道是否可以从当前状态转换到目标状态,例如在游戏初始化GameInitState去切换游戏暂停GamePauseState是切不了的。

2. 可以知道状态from-to,知道是从哪个状态向哪个状态进行切换,可以进行一些前置操作,例如在游戏暂停时重新开始游戏,可以在切换前重置游戏数据。

 

三 并发状态机实现角色状态

游戏流程的状态是单一的,从初始化,开始,结束等状态都只会同时存在一个。

但是角色的状态,行走、攻击、跳跃等是可以并行的,例如行走并攻击,跳跃并攻击。

实现一个多状态并行状态机

 

角色状态

enum ECharacterState {
    Idle,     //站立
    Attack,   //攻击
    Move,     //移动
    Jump,     //跳跃
    Die,      //死亡
}

  

状态基类

class CharacterState {

    private character: Character;

    constructor(character: Character) {
        this.character = character;
    }

    //进入状态时调用
    onEnable() { };

    //状态更新
    update(dt: number) { };

    //离开状态时调用
    onExit() { };

    // 判断是否可以转换到目标状态, 默认允许所有转换,子类可重写
    public canTransitionTo(state: ECharacterState): boolean {
        return true;
    }
}

 

站立状态

class IdleState extends CharacterState {

    onEnable(): void {
        //站立
    }
}

  

攻击状态

class AttackState extends CharacterState {
    private attackDuration: number = 1; //攻击时间,单位s
    private elapsedTime: number = 0;    //计时

    onEnable(): void {
        //执行攻击逻辑
    }

    update(dt: number): void {
        this.elapsedTime += dt;
        if (this.elapsedTime >= this.attackDuration) {
            //攻击结束,退出状态
        }
    }
}

  

状态机

class FSM {
    //当前激活状态
    public activeStates: Set<CharacterState> = new Set();
    //状态映射表
    public stateMap: Map<ECharacterState, CharacterState> = new Map();

    register(eState: ECharacterState, state: CharacterState) {
        this.stateMap.set(eState, state);
    }

    //所有当前激活状态刷新
    update(dt: number) {
        this.activeStates.forEach((state) => {
            state.update(dt);
        })
    }

    //添加状态
    addState(newState: ECharacterState) {
        const state = this.stateMap.get(newState);

        //判断是否可添加状态
        if (!this.canAddState(newState)) {
            return;
        }

        //判断是否状态重复添加
        if (this.isStateActive(newState)) {
            return;
        }

        //退出冲突状态
        this.exitConfictinStates(newState);

        //进入新状态
        this.activeStates.add(state);
        state.onEnable();
    }

    //是否可添加状态
    canAddState(state: ECharacterState) {
        for (const activeState of this.activeStates) {
            if (!activeState.canTransitionTo(state)) {
                return false;
            }
        }
        return true;
    }

    //是否状态已激活
    isStateActive(eSstate: ECharacterState) {
        const state = this.stateMap.get(eSstate);
        return state ? this.activeStates.has(state) : false;
    }

    // 移除状态
    removeState(eState: ECharacterState) {
        const state = this.stateMap.get(eState);
        if (this.activeStates.has(state)) {
            state.onExit();
            this.activeStates.delete(state);
        }
    }

    //退出冲突状态
    exitConfictinStates(eState: ECharacterState) {
        // 定义状态冲突规则
        const conflictingStates = {
            [ECharacterState.Idle]: [ECharacterState.Move, ECharacterState.Die, ECharacterState.Jump],
            [ECharacterState.Attack]: [ECharacterState.Die],
            [ECharacterState.Move]: [ECharacterState.Idle, ECharacterState.Die],
            [ECharacterState.Jump]: [ECharacterState.Idle, ECharacterState.Die],
            [ECharacterState.Die]: [ECharacterState.Idle, ECharacterState.Attack, ECharacterState.Jump, ECharacterState.Move],
        };

        const conflicts = conflictingStates[eState] || [];

        conflicts.forEach(conflictingState => {
            if (this.isStateActive(conflictingState)) {
                this.removeState(conflictingState);
            }
        });
    }
}

  

角色类

class Character {
    public fsm: FSM = new FSM();

    start() {
        this.fsm.register(ECharacterState.Idle, new IdleState(this));
        this.fsm.register(ECharacterState.Attack, new AttackState(this));
        //省略...

        //默认站立状态
        this.fsm.addState(ECharacterState.Idle);
    }

    handleInput(input: string) {
        switch (input) {
            case "站立":
                this.fsm.addState(ECharacterState.Attack);
                break;
            case "攻击":
                this.fsm.addState(ECharacterState.Attack);
                break;
        }
    }
}

  

并发状态机和有限状态机的重要实现区别,就是并发状态机维护了一个状态列表activeStates,有限状态机只有一个当前状态curState。

 

posted on 2025-07-20 14:48  gamedaybyday  阅读(415)  评论(0)    收藏  举报