参考
豆包
目录
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。
浙公网安备 33010602011771号