perf(core): 优化 EntitySystem 迭代性能,添加 CommandBuffer 延迟命令 (#281)
* perf(core): 优化 EntitySystem 迭代性能,添加 CommandBuffer 延迟命令 ReactiveQuery 快照优化: - 添加快照机制,避免每帧拷贝数组 - 只在实体列表变化时创建新快照 - 静态场景下多个系统共享同一快照 CommandBuffer 延迟命令系统: - 支持延迟添加/移除组件、销毁实体、设置实体激活状态 - 每个系统拥有独立的 commands 属性 - 命令在帧末统一执行,避免迭代过程中修改实体列表 Scene 更新: - 在 lateUpdate 后自动刷新所有系统的命令缓冲区 文档: - 更新系统文档,添加 CommandBuffer 使用说明 * fix(ci): upgrade first-interaction action to v1.3.0 Fix Docker build failure in welcome workflow. * fix(ci): upgrade pnpm/action-setup to v4 and fix unused import - Upgrade pnpm/action-setup@v2 to v4 in all workflow files - Remove unused CommandType import in CommandBuffer.test.ts * fix(ci): remove duplicate pnpm version specification
This commit is contained in:
@@ -334,6 +334,110 @@ class DamageSystem extends EntitySystem {
|
||||
|
||||
框架会在每次 `process`/`lateProcess` 调用前创建实体列表的快照,确保迭代过程中的组件变化不会导致跳过实体或重复处理。
|
||||
|
||||
## 命令缓冲区 (CommandBuffer)
|
||||
|
||||
> **v2.2.22+**
|
||||
|
||||
CommandBuffer 提供了一种延迟执行实体操作的机制。当你需要在迭代过程中销毁实体或进行其他可能影响迭代的操作时,使用 CommandBuffer 可以将这些操作推迟到帧末统一执行。
|
||||
|
||||
### 基本用法
|
||||
|
||||
每个 EntitySystem 都内置了 `commands` 属性:
|
||||
|
||||
```typescript
|
||||
@ECSSystem('Damage')
|
||||
class DamageSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.all(Health, DamageReceiver));
|
||||
}
|
||||
|
||||
protected process(entities: readonly Entity[]): void {
|
||||
for (const entity of entities) {
|
||||
const health = entity.getComponent(Health);
|
||||
const damage = entity.getComponent(DamageReceiver);
|
||||
|
||||
if (health && damage) {
|
||||
health.current -= damage.amount;
|
||||
|
||||
// 使用命令缓冲区延迟移除组件
|
||||
this.commands.removeComponent(entity, DamageReceiver);
|
||||
|
||||
if (health.current <= 0) {
|
||||
// 延迟添加死亡标记
|
||||
this.commands.addComponent(entity, new Dead());
|
||||
// 延迟销毁实体
|
||||
this.commands.destroyEntity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 支持的命令
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `addComponent(entity, component)` | 延迟添加组件 |
|
||||
| `removeComponent(entity, ComponentType)` | 延迟移除组件 |
|
||||
| `destroyEntity(entity)` | 延迟销毁实体 |
|
||||
| `setEntityActive(entity, active)` | 延迟设置实体激活状态 |
|
||||
|
||||
### 执行时机
|
||||
|
||||
命令缓冲区中的命令会在每帧的 `lateUpdate` 阶段之后自动执行。执行顺序与命令入队顺序一致。
|
||||
|
||||
```
|
||||
场景更新流程:
|
||||
1. onBegin()
|
||||
2. process()
|
||||
3. lateProcess()
|
||||
4. onEnd()
|
||||
5. flushCommandBuffers() <-- 命令在这里执行
|
||||
```
|
||||
|
||||
### 使用场景
|
||||
|
||||
CommandBuffer 适用于以下场景:
|
||||
|
||||
1. **在迭代中销毁实体**:避免修改正在遍历的集合
|
||||
2. **批量延迟操作**:将多个操作合并到帧末执行
|
||||
3. **跨系统协调**:一个系统标记,另一个系统响应
|
||||
|
||||
```typescript
|
||||
// 示例:敌人死亡系统
|
||||
@ECSSystem('EnemyDeath')
|
||||
class EnemyDeathSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.all(Enemy, Health));
|
||||
}
|
||||
|
||||
protected process(entities: readonly Entity[]): void {
|
||||
for (const entity of entities) {
|
||||
const health = entity.getComponent(Health);
|
||||
if (health && health.current <= 0) {
|
||||
// 播放死亡动画、掉落物品等
|
||||
this.spawnLoot(entity);
|
||||
|
||||
// 延迟销毁,不影响当前迭代
|
||||
this.commands.destroyEntity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private spawnLoot(entity: Entity): void {
|
||||
// 掉落物品逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
- 命令会跳过已销毁的实体(安全检查)
|
||||
- 单个命令执行失败不会影响其他命令
|
||||
- 命令按入队顺序执行
|
||||
- 每次 `flush()` 后命令队列会清空
|
||||
|
||||
## 系统属性和方法
|
||||
|
||||
### 重要属性
|
||||
|
||||
Reference in New Issue
Block a user