Files
esengine/docs/system-guide.md

662 lines
19 KiB
Markdown
Raw Normal View History

2025-06-10 13:12:14 +08:00
# 系统System详解指南
系统是ECS架构中的"S",负责处理拥有特定组件的实体。本指南详细介绍框架中的各种系统类型及其使用方法。
## 系统基础概念
### 什么是系统?
系统是处理游戏逻辑的地方,它们:
- **专注单一职责** - 每个系统只处理一种类型的逻辑
- **自动执行** - 系统会在每帧自动被调用
- **基于组件过滤** - 只处理包含特定组件的实体
- **高性能** - 利用ECS的数据局部性优势
2025-06-10 13:12:14 +08:00
### 系统的工作原理
```typescript
// 系统的基本工作流程:
// 1. 查询符合条件的实体
// 2. 遍历这些实体
// 3. 读取/修改实体的组件数据
// 4. 执行游戏逻辑
class MovementSystem extends EntitySystem {
process(entities: Entity[]) {
for (const entity of entities) {
const position = entity.getComponent(PositionComponent);
const velocity = entity.getComponent(VelocityComponent);
// 更新位置 = 当前位置 + 速度 * 时间
position.x += velocity.x * Time.deltaTime;
position.y += velocity.y * Time.deltaTime;
}
}
}
```
## 系统类型详解
### 1. EntitySystem - 基础系统
最常用的系统类型,每帧处理所有符合条件的实体。
```typescript
import { EntitySystem, Entity, Matcher } from '@esengine/ecs-framework';
class HealthSystem extends EntitySystem {
constructor() {
// 使用Matcher创建查询条件
super(Matcher.all(HealthComponent));
// 或者使用链式语法
// super(Matcher.empty().all(HealthComponent));
2025-06-10 13:12:14 +08:00
}
// 主要处理逻辑
protected process(entities: Entity[]) {
// 直接使用传入的entities参数已经是匹配的实体
2025-06-10 13:12:14 +08:00
for (const entity of entities) {
const health = entity.getComponent(HealthComponent)!;
2025-06-10 13:12:14 +08:00
// 处理生命值逻辑
if (health.currentHealth <= 0) {
this.handleDeath(entity);
} else if (health.currentHealth < health.maxHealth) {
this.handleRegeneration(health);
}
}
}
private handleDeath(entity: Entity) {
// 添加死亡标记
entity.addComponent(new DeadComponent());
// 触发死亡事件
const eventBus = this.scene.entityManager.eventBus;
eventBus.emit('entity:died', {
2025-06-10 13:12:14 +08:00
entityId: entity.id,
entityName: entity.name
});
}
private handleRegeneration(health: HealthComponent) {
// 缓慢恢复生命值
health.currentHealth += health.regenRate * Time.deltaTime;
health.currentHealth = Math.min(health.currentHealth, health.maxHealth);
}
}
```
**适用场景:**
- 移动系统
- 渲染系统
- 碰撞检测系统
- AI系统
### 2. ProcessingSystem - 简化处理系统
不需要处理具体实体,主要用于执行全局逻辑或不依赖特定实体的系统处理。
```typescript
import { ProcessingSystem, Matcher } from '@esengine/ecs-framework';
class GameLogicSystem extends ProcessingSystem {
constructor() {
// ProcessingSystem可以不指定Matcher或使用空Matcher
super(Matcher.empty());
}
// 处理系统逻辑(每帧执行)
public processSystem() {
// 执行全局游戏逻辑
this.updateGameState();
this.checkWinConditions();
this.updateUI();
}
private updateGameState() {
// 更新游戏状态逻辑
console.log("更新游戏状态");
}
private checkWinConditions() {
// 检查胜利条件
const players = this.scene.findEntitiesByTag(EntityTags.PLAYER);
const enemies = this.scene.findEntitiesByTag(EntityTags.ENEMY);
if (enemies.length === 0) {
this.triggerVictory();
} else if (players.length === 0) {
this.triggerGameOver();
}
}
private updateUI() {
// 更新UI显示
const gameTime = Time.totalTime;
console.log(`游戏时间: ${gameTime.toFixed(1)}秒`);
}
2025-06-22 21:21:46 +08:00
private triggerVictory() {
console.log("游戏胜利!");
// 处理胜利逻辑
}
private triggerGameOver() {
console.log("游戏结束!");
// 处理游戏结束逻辑
}
}
```
**适用场景:**
- 全局游戏逻辑系统
- 胜负判断系统
- UI更新系统
- 不依赖特定实体的处理
## AI系统示例
下面是一个完整的AI系统示例展示EntitySystem的典型用法
```typescript
import { EntitySystem, Matcher, Entity } from '@esengine/ecs-framework';
enum AIState {
IDLE,
PATROL,
CHASE,
ATTACK
2025-06-10 13:12:14 +08:00
}
2025-06-22 21:21:46 +08:00
class AISystem extends EntitySystem {
constructor() {
// 复杂匹配条件可以使用链式语法
2025-06-22 21:21:46 +08:00
super(Matcher.empty().all(AIComponent, PositionComponent));
// 或者使用简洁语法
// super(Matcher.all(AIComponent, PositionComponent));
2025-06-22 21:21:46 +08:00
}
// 处理所有匹配的实体
protected process(entities: Entity[]) {
for (const entity of entities) {
this.processEntity(entity);
}
}
// 处理单个实体的逻辑(自定义方法)
private processEntity(entity: Entity) {
2025-06-22 21:21:46 +08:00
const ai = entity.getComponent(AIComponent);
const position = entity.getComponent(PositionComponent);
switch (ai.state) {
case AIState.IDLE:
this.processIdle(entity, ai);
break;
case AIState.PATROL:
this.processPatrol(entity, ai, position);
break;
case AIState.CHASE:
this.processChase(entity, ai, position);
break;
case AIState.ATTACK:
this.processAttack(entity, ai);
break;
}
}
2025-06-10 13:12:14 +08:00
private processIdle(entity: Entity, ai: AIComponent) {
ai.idleTimer += Time.deltaTime;
if (ai.idleTimer >= ai.idleTime) {
ai.state = AIState.PATROL;
ai.idleTimer = 0;
}
// 检查附近是否有玩家
const nearbyPlayer = this.findNearbyPlayer(entity, ai.detectionRange);
if (nearbyPlayer) {
ai.state = AIState.CHASE;
ai.target = nearbyPlayer;
}
}
private processPatrol(entity: Entity, ai: AIComponent, position: PositionComponent) {
// 简单的来回巡逻
if (!ai.patrolTarget) {
ai.patrolTarget = this.getNextPatrolPoint(ai);
}
const direction = ai.patrolTarget.subtract(position);
const distance = direction.length();
if (distance < 10) {
ai.patrolTarget = this.getNextPatrolPoint(ai);
} else {
const normalized = direction.normalize();
position.x += normalized.x * ai.moveSpeed * Time.deltaTime;
position.y += normalized.y * ai.moveSpeed * Time.deltaTime;
}
}
}
```
**适用场景:**
- 全局游戏逻辑系统
- 胜负判断系统
- UI更新系统
- 不依赖特定实体的处理
### 3. IntervalSystem - 间隔执行系统
不是每帧都执行,而是按指定间隔执行的系统,适合不需要高频更新的逻辑。
```typescript
import { IntervalSystem, Matcher } from '@esengine/ecs-framework';
class SpawnSystem extends IntervalSystem {
private spawnPoints: { x: number; y: number }[] = [
{ x: 100, y: 100 },
{ x: 700, y: 100 },
{ x: 400, y: 500 }
];
// 每2秒执行一次
constructor() {
// IntervalSystem需要指定间隔时间和Matcher
super(2.0, Matcher.all(SpawnerComponent));
2025-06-10 13:12:14 +08:00
}
// 间隔执行的逻辑重写process方法
protected process(entities: Entity[]) {
// entities就是匹配的生成器实体
for (const spawner of entities) {
const spawnerComp = spawner.getComponent(SpawnerComponent);
if (this.shouldSpawn(spawnerComp)) {
this.spawnEnemy(spawner, spawnerComp);
}
}
}
private shouldSpawn(spawner: SpawnerComponent): boolean {
// 检查是否应该生成
const currentEnemyCount = this.getCurrentEnemyCount();
return currentEnemyCount < spawner.maxEnemies &&
Math.random() < spawner.spawnChance;
}
private spawnEnemy(spawnerEntity: Entity, spawner: SpawnerComponent) {
// 随机选择生成点
const spawnPoint = this.spawnPoints[
Math.floor(Math.random() * this.spawnPoints.length)
];
// 创建敌人实体
const enemy = this.scene.createEntity("Enemy");
enemy.addComponent(new PositionComponent(spawnPoint.x, spawnPoint.y));
enemy.addComponent(new HealthComponent(50));
enemy.addComponent(new AIComponent());
enemy.addComponent(new VelocityComponent(0, 0));
enemy.tag = EntityTags.ENEMY;
// 更新生成器统计
spawner.spawnedCount++;
spawner.lastSpawnTime = Time.totalTime;
// 发送生成事件
const eventBus = this.scene.entityManager.eventBus;
eventBus.emit('enemy:spawned', {
2025-06-10 13:12:14 +08:00
enemyId: enemy.id,
spawnPoint: spawnPoint,
spawnerEntity: spawnerEntity.id
});
}
}
```
**适用场景:**
- 敌人生成系统
- 自动保存系统
- 资源回收系统
- 定期数据同步
### 4. PassiveSystem - 被动系统
不处理实体的系统,主要用于事件监听和响应。
2025-06-10 13:12:14 +08:00
```typescript
import { PassiveSystem, Matcher, Core } from '@esengine/ecs-framework';
class ScoreSystem extends PassiveSystem {
private score: number = 0;
private multiplier: number = 1;
private combo: number = 0;
constructor() {
// PassiveSystem也需要Matcher即使不使用
super(Matcher.empty());
}
initialize() {
super.initialize();
// 监听游戏事件使用EntityManager的事件系统
const eventBus = this.scene.entityManager.eventBus;
eventBus.on('enemy:killed', this.onEnemyKilled, { context: this });
eventBus.on('item:collected', this.onItemCollected, { context: this });
eventBus.on('combo:broken', this.onComboBroken, { context: this });
2025-06-10 13:12:14 +08:00
}
// PassiveSystem被移除时清理
destroy() {
// 事件监听会在系统销毁时自动清理
// 如需手动清理可以保存listenerId并调用eventBus.off()
2025-06-10 13:12:14 +08:00
}
private onEnemyKilled(data: { enemyType: string; position: { x: number; y: number } }) {
// 根据敌人类型给分
let baseScore = this.getScoreForEnemyType(data.enemyType);
// 连击奖励
this.combo++;
if (this.combo > 3) {
this.multiplier = Math.min(this.combo * 0.1, 3.0); // 最多3倍
}
const finalScore = Math.floor(baseScore * this.multiplier);
this.addScore(finalScore);
// 显示分数奖励
this.showScorePopup(data.position, finalScore);
}
private addScore(points: number) {
this.score += points;
// 发送分数更新事件
const eventBus = this.scene.entityManager.eventBus;
eventBus.emit('score:updated', {
2025-06-10 13:12:14 +08:00
score: this.score,
points: points,
multiplier: this.multiplier,
combo: this.combo
});
}
}
```
**适用场景:**
- 分数统计系统
- 音效播放系统
- UI更新系统
- 成就系统
2025-06-23 16:08:31 +08:00
## 系统生命周期方法
系统提供了多个生命周期方法,可以根据需要重写:
### 重要的生命周期方法
```typescript
class ExampleSystem extends EntitySystem {
/**
* 系统初始化回调 - 系统被添加到场景时调用
2025-06-23 16:08:31 +08:00
* 用于设置事件监听器、初始化资源等
* 注意不要重写initialize()方法而是重写onInitialize()
2025-06-23 16:08:31 +08:00
*/
protected onInitialize() {
2025-06-23 16:08:31 +08:00
// 设置事件监听
const eventBus = this.scene.entityManager.eventBus;
eventBus.on('someEvent', this.handleEvent, { context: this });
console.log('系统已初始化');
}
/**
* 每帧处理开始前调用
*/
protected onBegin() {
2025-06-23 16:08:31 +08:00
// 预处理逻辑,如重置计数器
this.frameCounter++;
}
/**
* 主要处理逻辑 - 每帧调用
* @param entities 符合条件的实体列表
*/
protected process(entities: Entity[]) {
for (const entity of entities) {
// 处理每个实体
this.processEntity(entity);
}
}
/**
* 后期处理 - 在process之后调用
* @param entities 符合条件的实体列表
*/
protected lateProcess(entities: Entity[]) {
// 后期处理逻辑,如碰撞检测后的响应
this.handlePostProcessing();
}
/**
* 每帧处理结束后调用
*/
protected onEnd() {
2025-06-23 16:08:31 +08:00
// 后处理逻辑,如统计数据更新
this.updateStatistics();
}
}
```
### 生命周期执行顺序
系统的生命周期方法按以下顺序执行:
1. **initialize()** - 系统被添加到场景时执行一次(框架调用)
- **onInitialize()** - 用户可重写的初始化回调
2. 每帧循环:
- **onBegin()** - 帧开始前(用户可重写)
- **process(entities)** - 主要处理逻辑(用户必须实现)
- **lateProcess(entities)** - 后期处理(用户可重写)
- **onEnd()** - 帧结束后(用户可重写)
2025-06-23 16:08:31 +08:00
2025-06-10 13:12:14 +08:00
## 系统管理和注册
### 在场景中添加系统
```typescript
import { Scene, Core } from '@esengine/ecs-framework';
const scene = new Scene();
// 添加各种系统使用addEntityProcessor方法
scene.addEntityProcessor(new MovementSystem());
scene.addEntityProcessor(new GameLogicSystem());
scene.addEntityProcessor(new SpawnSystem());
scene.addEntityProcessor(new ScoreSystem());
// 设置系统的执行优先级
const movementSystem = scene.getEntityProcessor(MovementSystem);
if (movementSystem) {
movementSystem.updateOrder = 10; // 数值越小越先执行
}
const renderSystem = scene.getEntityProcessor(RenderSystem);
if (renderSystem) {
renderSystem.updateOrder = 100; // 渲染系统最后执行
}
// 设置为当前场景
Core.scene = scene;
```
### 系统的启用和禁用
```typescript
// 暂时禁用某个系统
const gameLogicSystem = scene.getEntityProcessor(GameLogicSystem);
if (gameLogicSystem) {
gameLogicSystem.enabled = false;
}
// 重新启用
if (gameLogicSystem) {
gameLogicSystem.enabled = true;
}
// 移除系统
scene.removeEntityProcessor(gameLogicSystem);
```
## 系统设计最佳实践
### 1. 单一职责原则
```typescript
// 好的设计:每个系统只负责一件事
2025-06-10 13:12:14 +08:00
class MovementSystem extends EntitySystem {
// 只负责移动
}
class CollisionSystem extends EntitySystem {
// 只负责碰撞检测
}
class RenderSystem extends EntitySystem {
// 只负责渲染
}
// 不好的设计:一个系统做太多事情
2025-06-10 13:12:14 +08:00
class GameplaySystem extends EntitySystem {
// 既处理移动,又处理碰撞,还处理渲染...
}
```
### 2. 合理的系统执行顺序
```typescript
// 设置合理的执行顺序
scene.addEntityProcessor(new InputSystem()).updateOrder = 0; // 输入最先
scene.addEntityProcessor(new GameLogicSystem()).updateOrder = 10; // 游戏逻辑
scene.addEntityProcessor(new MovementSystem()).updateOrder = 20; // 移动计算
scene.addEntityProcessor(new CollisionSystem()).updateOrder = 30; // 碰撞检测
scene.addEntityProcessor(new HealthSystem()).updateOrder = 40; // 生命值处理
scene.addEntityProcessor(new RenderSystem()).updateOrder = 100; // 渲染最后
```
### 3. 系统间通信
```typescript
// 使用事件进行系统间通信
class CollisionSystem extends EntitySystem {
process(entities: Entity[]) {
// ... 碰撞检测逻辑
if (collision) {
// 发送碰撞事件,让其他系统响应
const eventBus = this.scene.entityManager.eventBus;
eventBus.emit('collision:detected', {
2025-06-10 13:12:14 +08:00
entity1: collider1,
entity2: collider2,
collisionPoint: point
});
}
}
}
class HealthSystem extends PassiveSystem {
2025-06-23 16:08:31 +08:00
initialize() {
super.initialize();
2025-06-10 13:12:14 +08:00
// 监听碰撞事件
const eventBus = this.scene.entityManager.eventBus;
eventBus.on('collision:detected', this.onCollision, { context: this });
2025-06-10 13:12:14 +08:00
}
private onCollision(data: CollisionEventData) {
// 处理碰撞伤害
if (data.entity1.hasComponent(HealthComponent)) {
const health = data.entity1.getComponent(HealthComponent);
health.takeDamage(10);
}
}
}
```
### 4. 性能优化
```typescript
class OptimizedMovementSystem extends EntitySystem {
private lastUpdateTime: number = 0;
private readonly UPDATE_INTERVAL = 16; // 60FPS
process(entities: Entity[]) {
const currentTime = Time.totalTime;
// 限制更新频率
if (currentTime - this.lastUpdateTime < this.UPDATE_INTERVAL) {
return;
}
// 批量处理
this.processBatch(entities);
this.lastUpdateTime = currentTime;
}
private processBatch(entities: Entity[]) {
// 使用for循环而不是forEach性能更好
for (let i = 0; i < entities.length; i++) {
const entity = entities[i];
// 处理逻辑...
}
}
}
```
## 常见问题
### Q: 系统的执行顺序重要吗?
A: 非常重要!合理的执行顺序可以避免逻辑错误:
```typescript
// 正确顺序:
// 1. 输入系统(收集玩家输入)
// 2. AI系统敌人决策
// 3. 移动系统(更新位置)
// 4. 碰撞系统(检测碰撞)
// 5. 渲染系统(显示画面)
```
### Q: 什么时候使用哪种系统类型?
A:
- **EntitySystem** - 大部分游戏逻辑移动、AI、碰撞等
- **ProcessingSystem** - 不依赖特定实体的全局处理(游戏状态管理、全局逻辑)
2025-06-10 13:12:14 +08:00
- **IntervalSystem** - 不需要每帧执行的逻辑(生成器、自动保存)
- **PassiveSystem** - 事件响应系统分数、音效、UI更新
### Q: 系统可以访问其他系统吗?
A: 不建议直接访问。推荐使用事件系统进行系统间通信,保持松耦合。
### Q: 如何调试系统性能?
A: 使用框架内置的性能监控:
```typescript
const monitor = PerformanceMonitor.instance;
monitor.startFrame('MovementSystem');
// 系统逻辑...
monitor.endFrame('MovementSystem');
// 查看性能报告
console.log(monitor.getReport());
```
通过合理使用这些系统类型,你可以构建出高性能、易维护的游戏逻辑!