Files
esengine/docs/modules/fsm/index.md
YHH 4a16e30794 docs(modules): 添加框架模块文档 (#350)
* docs(modules): 添加框架模块文档

添加以下模块的完整文档:
- FSM (状态机): 状态定义、转换条件、优先级、事件监听
- Timer (定时器): 定时器调度、冷却系统、服务令牌
- Spatial (空间索引): GridSpatialIndex、AOI 兴趣区域管理
- Pathfinding (寻路): A* 算法、网格地图、导航网格、路径平滑
- Procgen (程序化生成): 噪声函数、种子随机数、加权随机

所有文档均基于实际源码 API 编写,包含:
- 快速开始示例
- 完整 API 参考
- 实际使用案例
- 蓝图节点说明
- 最佳实践建议

* docs(modules): 添加 Blueprint 模块文档和所有模块英文版

新增中文文档:
- Blueprint (蓝图可视化脚本): VM、自定义节点、组合系统、触发器

新增英文文档 (docs/en/modules/):
- FSM: State machine API, transitions, ECS integration
- Timer: Timers, cooldowns, service tokens
- Spatial: Grid spatial index, AOI management
- Pathfinding: A*, grid map, NavMesh, path smoothing
- Procgen: Noise functions, seeded random, weighted random
- Blueprint: Visual scripting, custom nodes, composition

所有文档均基于实际源码 API 编写。
2025-12-26 20:02:21 +08:00

338 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 状态机 (FSM)
`@esengine/fsm` 提供了一个类型安全的有限状态机实现用于角色、AI 或任何需要状态管理的场景。
## 安装
```bash
npm install @esengine/fsm
```
## 快速开始
```typescript
import { createStateMachine } from '@esengine/fsm';
// 定义状态类型
type PlayerState = 'idle' | 'walk' | 'run' | 'jump';
// 创建状态机
const fsm = createStateMachine<PlayerState>('idle');
// 定义状态和回调
fsm.defineState('idle', {
onEnter: (ctx, from) => console.log(`${from} 进入 idle`),
onExit: (ctx, to) => console.log(`从 idle 退出到 ${to}`),
onUpdate: (ctx, dt) => { /* 每帧更新 */ }
});
fsm.defineState('walk', {
onEnter: () => console.log('开始行走')
});
// 手动切换状态
fsm.transition('walk');
console.log(fsm.current); // 'walk'
```
## 核心概念
### 状态配置
每个状态可以配置以下回调:
```typescript
interface StateConfig<TState, TContext> {
name: TState; // 状态名称
onEnter?: (context: TContext, from: TState | null) => void; // 进入回调
onExit?: (context: TContext, to: TState) => void; // 退出回调
onUpdate?: (context: TContext, deltaTime: number) => void; // 更新回调
tags?: string[]; // 状态标签
metadata?: Record<string, unknown>; // 元数据
}
```
### 转换条件
可以定义带条件的状态转换:
```typescript
interface Context {
isMoving: boolean;
isRunning: boolean;
isGrounded: boolean;
}
const fsm = createStateMachine<PlayerState, Context>('idle', {
context: { isMoving: false, isRunning: false, isGrounded: true }
});
// 定义转换条件
fsm.defineTransition('idle', 'walk', (ctx) => ctx.isMoving);
fsm.defineTransition('walk', 'run', (ctx) => ctx.isRunning);
fsm.defineTransition('walk', 'idle', (ctx) => !ctx.isMoving);
// 自动评估并执行满足条件的转换
fsm.evaluateTransitions();
```
### 转换优先级
当多个转换条件同时满足时,优先级高的先执行:
```typescript
// 优先级数字越大越优先
fsm.defineTransition('idle', 'attack', (ctx) => ctx.isAttacking, 10);
fsm.defineTransition('idle', 'walk', (ctx) => ctx.isMoving, 1);
// 如果同时满足,会先尝试 attack优先级 10
```
## API 参考
### createStateMachine
```typescript
function createStateMachine<TState extends string, TContext = unknown>(
initialState: TState,
options?: StateMachineOptions<TContext>
): IStateMachine<TState, TContext>
```
**参数:**
- `initialState` - 初始状态
- `options.context` - 上下文对象,在回调中可访问
- `options.maxHistorySize` - 最大历史记录数(默认 100
- `options.enableHistory` - 是否启用历史记录(默认 true
### 状态机属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `current` | `TState` | 当前状态 |
| `previous` | `TState \| null` | 上一个状态 |
| `context` | `TContext` | 上下文对象 |
| `isTransitioning` | `boolean` | 是否正在转换中 |
| `currentStateDuration` | `number` | 当前状态持续时间(毫秒) |
### 状态机方法
#### 状态定义
```typescript
// 定义状态
fsm.defineState('idle', {
onEnter: (ctx, from) => {},
onExit: (ctx, to) => {},
onUpdate: (ctx, dt) => {}
});
// 检查状态是否存在
fsm.hasState('idle'); // true
// 获取状态配置
fsm.getStateConfig('idle');
// 获取所有状态
fsm.getStates(); // ['idle', 'walk', ...]
```
#### 转换操作
```typescript
// 定义转换
fsm.defineTransition('idle', 'walk', condition, priority);
// 移除转换
fsm.removeTransition('idle', 'walk');
// 获取从某状态出发的所有转换
fsm.getTransitionsFrom('idle');
// 检查是否可以转换
fsm.canTransition('walk'); // true/false
// 手动转换
fsm.transition('walk');
// 强制转换(忽略条件)
fsm.transition('walk', true);
// 自动评估转换条件
fsm.evaluateTransitions();
```
#### 生命周期
```typescript
// 更新状态机(调用当前状态的 onUpdate
fsm.update(deltaTime);
// 重置状态机
fsm.reset(); // 重置到当前状态
fsm.reset('idle'); // 重置到指定状态
```
#### 事件监听
```typescript
// 监听进入特定状态
const unsubscribe = fsm.onEnter('walk', (from) => {
console.log(`${from} 进入 walk`);
});
// 监听退出特定状态
fsm.onExit('walk', (to) => {
console.log(`从 walk 退出到 ${to}`);
});
// 监听任意状态变化
fsm.onChange((event) => {
console.log(`${event.from} -> ${event.to} at ${event.timestamp}`);
});
// 取消订阅
unsubscribe();
```
#### 调试
```typescript
// 获取状态历史
const history = fsm.getHistory();
// [{ from: 'idle', to: 'walk', timestamp: 1234567890 }, ...]
// 清除历史
fsm.clearHistory();
// 获取调试信息
const info = fsm.getDebugInfo();
// { current, previous, duration, stateCount, transitionCount, historySize }
```
## 实际示例
### 角色状态机
```typescript
import { createStateMachine } from '@esengine/fsm';
type CharacterState = 'idle' | 'walk' | 'run' | 'jump' | 'fall' | 'attack';
interface CharacterContext {
velocity: { x: number; y: number };
isGrounded: boolean;
isAttacking: boolean;
speed: number;
}
const characterFSM = createStateMachine<CharacterState, CharacterContext>('idle', {
context: {
velocity: { x: 0, y: 0 },
isGrounded: true,
isAttacking: false,
speed: 0
}
});
// 定义状态
characterFSM.defineState('idle', {
onEnter: (ctx) => {
ctx.speed = 0;
},
onUpdate: (ctx, dt) => {
// 播放待机动画
}
});
characterFSM.defineState('walk', {
onEnter: (ctx) => {
ctx.speed = 100;
}
});
characterFSM.defineState('run', {
onEnter: (ctx) => {
ctx.speed = 200;
}
});
characterFSM.defineState('jump', {
onEnter: (ctx) => {
ctx.velocity.y = -300;
ctx.isGrounded = false;
}
});
// 定义转换
characterFSM.defineTransition('idle', 'walk', (ctx) => Math.abs(ctx.velocity.x) > 0);
characterFSM.defineTransition('walk', 'idle', (ctx) => ctx.velocity.x === 0);
characterFSM.defineTransition('walk', 'run', (ctx) => Math.abs(ctx.velocity.x) > 150);
characterFSM.defineTransition('run', 'walk', (ctx) => Math.abs(ctx.velocity.x) <= 150);
// 跳跃有最高优先级
characterFSM.defineTransition('idle', 'jump', (ctx) => !ctx.isGrounded, 10);
characterFSM.defineTransition('walk', 'jump', (ctx) => !ctx.isGrounded, 10);
characterFSM.defineTransition('run', 'jump', (ctx) => !ctx.isGrounded, 10);
characterFSM.defineTransition('jump', 'fall', (ctx) => ctx.velocity.y > 0);
characterFSM.defineTransition('fall', 'idle', (ctx) => ctx.isGrounded);
// 游戏循环中使用
function gameUpdate(dt: number) {
// 更新上下文
characterFSM.context.velocity.x = getInputVelocity();
characterFSM.context.isGrounded = checkGrounded();
// 评估状态转换
characterFSM.evaluateTransitions();
// 更新当前状态
characterFSM.update(dt);
}
```
### 与 ECS 集成
```typescript
import { Component, EntitySystem, Matcher } from '@esengine/ecs-framework';
import { createStateMachine, type IStateMachine } from '@esengine/fsm';
// 状态机组件
class FSMComponent extends Component {
fsm: IStateMachine<string>;
constructor(initialState: string) {
super();
this.fsm = createStateMachine(initialState);
}
}
// 状态机系统
class FSMSystem extends EntitySystem {
constructor() {
super(Matcher.all(FSMComponent));
}
protected processEntity(entity: Entity, dt: number): void {
const fsmComp = entity.getComponent(FSMComponent);
fsmComp.fsm.evaluateTransitions();
fsmComp.fsm.update(dt);
}
}
```
## 蓝图节点
FSM 模块提供了可视化脚本支持的蓝图节点:
- `GetCurrentState` - 获取当前状态
- `TransitionTo` - 转换到指定状态
- `CanTransition` - 检查是否可以转换
- `IsInState` - 检查是否在指定状态
- `WasInState` - 检查是否曾在指定状态
- `GetStateDuration` - 获取状态持续时间
- `EvaluateTransitions` - 评估转换条件
- `ResetStateMachine` - 重置状态机