状态机概述
状态机是一种计算模型,用于描述系统在不同状态之间的转换逻辑。它由状态(States)、事件(Events)和转换(Transitions)组成。状态机在任一时刻只能处于一个特定状态,通过触发事件使其从一个状态转换到另一个状态。
核心概念
- 状态(State):系统可能处于的一种情况或模式。
- 事件(Event):触发状态转换的信号或动作。
- 转换(Transition):定义从一个状态到另一个状态的规则。
- 回调(Callback):状态转换时执行的逻辑。
应用场景
状态机在以下场景中特别有用:
- 游戏开发:管理游戏角色的不同状态(站立、行走、攻击、受伤等)。
- UI 交互:控制界面元素在不同状态下的行为(正常、悬停、激活、禁用)。
- 工作流管理:追踪业务流程的不同阶段(创建、审核、发布)。
- 协议实现:网络协议的状态管理(连接中、已连接、断开连接)。
- 动画控制:管理动画序列和转换。
基本用法
// 定义状态和事件类型
type State = 'idle' | 'running' | 'paused' | 'stopped';
type Event = 'START' | 'PAUSE' | 'RESUME' | 'STOP';
// 创建状态机实例
const sm = new StateMachine<State, Event>('idle', [
t('idle', 'START', 'running'),
t('running', 'PAUSE', 'paused'),
t('paused', 'RESUME', 'running'),
t('running', 'STOP', 'stopped'),
]);
// 触发状态转换
await sm.dispatch('START'); // idle -> running
console.log(sm.getState()); // 'running'
高级特性
全局事件(ANY_STATE)
使用 ANY_STATE
符号定义可从任意状态触发的转换:
// RESET 事件可从任何状态回到 idle 状态
sm.addTransitions([
t(ANY_STATE, 'RESET', 'idle', resetCallback)
]);
状态查询和预测
// 检查当前状态是否可以接收特定事件
if (sm.can('PAUSE')) {
// 可以执行暂停操作
}
// 获取事件触发后的下一个状态
const nextState = sm.getNextState('STOP'); // 'stopped'
// 检查当前是否为终止状态(没有可用的出站转换)
if (sm.isFinal()) {
// 到达终止状态
}
子状态机
子状态机是一种嵌套状态机机制,允许在一个主状态内部有自己的状态转换逻辑。这对于构建复杂的分层状态行为非常有用
// 创建子状态机
const subMachine = new StateMachine<SubState, SubEvent>('subIdle', [
t('subIdle', 'SUB_START', 'subRunning'),
t('subRunning', 'SUB_STOP', 'subStopped')
]);
// 创建主状态机
const mainMachine = new StateMachine<MainState, MainEvent>('idle', [
t('idle', 'START', 'running'),
t('running', 'STOP', 'stopped')
]);
// 将子状态机附加到主状态机的特定状态
mainMachine.addSubStateMachine('running', subMachine);
核心设计问题
状态切换回掉的处理
- 每次派发事件,状态机会立即进行响应,更新新的状态
- 针对事件切换回调函数执行,不会卡住状态机的状态
- 极端情况下,存在多个状态回调函数同时在执行的情况
子状态机的生命周期
- 当主状态机进入子状态时,子状态机被激活
- 子状态机可以处理自己的事件和状态转换。
- 当主状态机离开子状态时,子状态机被自动重置(reset)
- 子状态机只有在父状态机处于激活状态时才能接收事件
实际应用示例
游戏角色控制
graph TD
Standing[Standing] --> |WALK| Walking[Walking]
Walking --> |STOP| Standing
Standing --> |JUMP| Jumping[Jumping]
Walking --> |JUMP| Jumping
Jumping --> |LAND| Standing
Standing --> |ATTACK| Attacking[Attacking]
Walking --> |ATTACK| Attacking
Jumping --> |ATTACK| Attacking
Attacking --> |STOP| Standing
type CharacterState = 'standing' | 'walking' | 'jumping' | 'attacking';
type CharacterEvent = 'WALK' | 'JUMP' | 'ATTACK' | 'STOP' | 'LAND';
// 创建角色状态机
const character = new StateMachine<CharacterState, CharacterEvent>('standing', [
t('standing', 'WALK', 'walking', startWalkAnimation),
t('walking', 'STOP', 'standing', stopWalkAnimation),
t('standing', 'JUMP', 'jumping', startJumpAnimation),
t('walking', 'JUMP', 'jumping', startJumpAnimation),
t('jumping', 'LAND', 'standing', landAnimation),
t(ANY_STATE, 'ATTACK', 'attacking', attackAnimation),
t('attacking', 'STOP', 'standing', stopAttackAnimation),
]);
UI 组件状态管理
graph TD
Normal[Normal] --> |MOUSE_ENTER| Hover[Hover]
Hover --> |MOUSE_LEAVE| Normal
Hover --> |MOUSE_DOWN| Pressed[Pressed]
Pressed --> |MOUSE_UP| Hover
Pressed --> |MOUSE_LEAVE| Normal
Normal --> |DISABLE| Disabled[Disabled]
Hover --> |DISABLE| Disabled
Pressed --> |DISABLE| Disabled
Disabled --> |ENABLE| Normal
type ButtonState = 'normal' | 'hover' | 'pressed' | 'disabled';
type ButtonEvent = 'MOUSE_ENTER' | 'MOUSE_LEAVE' | 'MOUSE_DOWN' | 'MOUSE_UP' | 'DISABLE' | 'ENABLE';
// 创建按钮状态机
const button = new StateMachine<ButtonState, ButtonEvent>('normal', [
t('normal', 'MOUSE_ENTER', 'hover', showHoverEffect),
t('hover', 'MOUSE_LEAVE', 'normal', removeHoverEffect),
t('hover', 'MOUSE_DOWN', 'pressed', showPressedEffect),
t('pressed', 'MOUSE_UP', 'hover', removePressedEffect),
t('pressed', 'MOUSE_LEAVE', 'normal', removeAllEffects),
t(ANY_STATE, 'DISABLE', 'disabled', disableButton),
t('disabled', 'ENABLE', 'normal', enableButton),
]);
游戏角色管理系统
这个例子展示了如何使用嵌套状态机来管理游戏角色的复杂状态。主状态机处理角色的生命周期(闲置、活动、死亡),而子状态机则专注于角色在"活动"状态下可以执行的各种行为
stateDiagram-v2
[*] --> idle
idle --> active: ACTIVATE
active --> idle: REST
active --> dead: DIE
dead --> idle: RESPAWN
state active {
[*] --> moving
moving --> fighting: ENGAGE
fighting --> moving: DISENGAGE
moving --> interacting: INTERACT
interacting --> moving: FINISH
}
// 主状态机状态和事件
type MainState = 'idle' | 'active' | 'dead';
type MainEvent = 'ACTIVATE' | 'REST' | 'DIE' | 'RESPAWN';
// 子状态机状态和事件
type ActiveState = 'moving' | 'fighting' | 'interacting';
type ActiveEvent = 'ENGAGE' | 'DISENGAGE' | 'INTERACT' | 'FINISH';
// 创建子状态机(角色活动状态)
const activeStateMachine = new StateMachine<ActiveState, ActiveEvent>('moving', [
t('moving', 'ENGAGE', 'fighting', startCombatSystem),
t('fighting', 'DISENGAGE', 'moving', endCombatSystem),
t('moving', 'INTERACT', 'interacting', startInteraction),
t('interacting', 'FINISH', 'moving', endInteraction)
]);
// 创建主状态机(角色生命周期)
const characterManager = new StateMachine<MainState, MainEvent>('idle', [
t('idle', 'ACTIVATE', 'active', activateCharacter),
t('active', 'REST', 'idle', deactivateCharacter),
t('active', 'DIE', 'dead', handleDeath),
t('dead', 'RESPAWN', 'idle', handleRespawn)
]);
// 将活动状态子状态机附加到主状态机的"active"状态
characterManager.addSubStateMachine('active', activeStateMachine);
// 使用示例
await characterManager.dispatch('ACTIVATE'); // 进入active状态,子状态机激活
await activeStateMachine.dispatch('ENGAGE'); // 切换到战斗状态
await activeStateMachine.dispatch('DISENGAGE'); // 返回移动状态
await characterManager.dispatch('REST'); // 回到idle状态,子状态机重置
结合
和UI结合
和AI结合
总结
状态机提供了一种清晰、直观的方式来管理复杂系统的状态转换逻辑。通过明确定义状态、事件和转换规则,可以有效避免状态管理中的混乱和错误。子状态机机制进一步增强了状态机的能力,使其能够处理更复杂的分层状态结构。无论是游戏开发、UI交互还是业务流程管理,状态机都是一种强大而灵活的解决方案。