Files
esengine/docs/system-guide.md
2025-06-19 10:38:31 +08:00

15 KiB
Raw Permalink Blame History

系统System详解指南

系统是ECS架构中的"S",负责处理拥有特定组件的实体。本指南详细介绍框架中的各种系统类型及其使用方法。

系统基础概念

什么是系统?

系统是处理游戏逻辑的地方,它们:

  • 🎯 专注单一职责 - 每个系统只处理一种类型的逻辑
  • 🔄 自动执行 - 系统会在每帧自动被调用
  • 📊 基于组件过滤 - 只处理包含特定组件的实体
  • 高性能 - 利用ECS的数据局部性优势

系统的工作原理

// 系统的基本工作流程:
// 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 - 基础系统

最常用的系统类型,每帧处理所有符合条件的实体。

import { EntitySystem, Entity, Matcher } from '@esengine/ecs-framework';

class HealthSystem extends EntitySystem {
    constructor() {
        // 使用Matcher指定需要的组件
        super(Matcher.empty().all(HealthComponent));
    }
    
    // 主要处理逻辑
    protected process(entities: Entity[]) {
        for (const entity of entities) {
            const health = entity.getComponent(HealthComponent);
            
            // 处理生命值逻辑
            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', {
            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 - 简化处理系统

不需要处理具体实体,主要用于执行全局逻辑或不依赖特定实体的系统处理。

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)}秒`);
    }
}
    
    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 - 间隔执行系统

不是每帧都执行,而是按指定间隔执行的系统,适合不需要高频更新的逻辑。

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(Matcher.empty().all(SpawnerComponent), 2.0);
    }
    
    // 间隔执行的逻辑重写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', {
            enemyId: enemy.id,
            spawnPoint: spawnPoint,
            spawnerEntity: spawnerEntity.id
        });
    }
}

适用场景:

  • 敌人生成系统
  • 自动保存系统
  • 资源回收系统
  • 定期数据同步

4. PassiveSystem - 被动系统

不主动遍历实体,而是响应事件的系统。

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 });
    }
    
    // PassiveSystem被移除时清理
    destroy() {
        // 事件监听会在系统销毁时自动清理
        // 如需手动清理可以保存listenerId并调用eventBus.off()
    }
    
    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', {
            score: this.score,
            points: points,
            multiplier: this.multiplier,
            combo: this.combo
        });
    }
}

适用场景:

  • 分数统计系统
  • 音效播放系统
  • UI更新系统
  • 成就系统

系统管理和注册

在场景中添加系统

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;

系统的启用和禁用

// 暂时禁用某个系统
const gameLogicSystem = scene.getEntityProcessor(GameLogicSystem);
if (gameLogicSystem) {
    gameLogicSystem.enabled = false;
}

// 重新启用
if (gameLogicSystem) {
    gameLogicSystem.enabled = true;
}

// 移除系统
scene.removeEntityProcessor(gameLogicSystem);

系统设计最佳实践

1. 单一职责原则

// ✅ 好的设计:每个系统只负责一件事
class MovementSystem extends EntitySystem {
    // 只负责移动
}

class CollisionSystem extends EntitySystem {
    // 只负责碰撞检测
}

class RenderSystem extends EntitySystem {
    // 只负责渲染
}

// ❌ 不好的设计:一个系统做太多事情
class GameplaySystem extends EntitySystem {
    // 既处理移动,又处理碰撞,还处理渲染...
}

2. 合理的系统执行顺序

// 设置合理的执行顺序
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. 系统间通信

// 使用事件进行系统间通信
class CollisionSystem extends EntitySystem {
    process(entities: Entity[]) {
        // ... 碰撞检测逻辑
        
        if (collision) {
            // 发送碰撞事件,让其他系统响应
            const eventBus = this.scene.entityManager.eventBus;
            eventBus.emit('collision:detected', {
                entity1: collider1,
                entity2: collider2,
                collisionPoint: point
            });
        }
    }
}

class HealthSystem extends PassiveSystem {
    onAddedToScene() {
        // 监听碰撞事件
        const eventBus = this.scene.entityManager.eventBus;
        eventBus.on('collision:detected', this.onCollision, { context: this });
    }
    
    private onCollision(data: CollisionEventData) {
        // 处理碰撞伤害
        if (data.entity1.hasComponent(HealthComponent)) {
            const health = data.entity1.getComponent(HealthComponent);
            health.takeDamage(10);
        }
    }
}

4. 性能优化

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: 非常重要!合理的执行顺序可以避免逻辑错误:

// 正确顺序:
// 1. 输入系统(收集玩家输入)
// 2. AI系统敌人决策
// 3. 移动系统(更新位置)
// 4. 碰撞系统(检测碰撞)
// 5. 渲染系统(显示画面)

Q: 什么时候使用哪种系统类型?

A:

  • EntitySystem - 大部分游戏逻辑移动、AI、碰撞等
  • ProcessingSystem - 复杂的单实体处理复杂AI、粒子系统
  • IntervalSystem - 不需要每帧执行的逻辑(生成器、自动保存)
  • PassiveSystem - 事件响应系统分数、音效、UI更新

Q: 系统可以访问其他系统吗?

A: 不建议直接访问。推荐使用事件系统进行系统间通信,保持松耦合。

Q: 如何调试系统性能?

A: 使用框架内置的性能监控:

const monitor = PerformanceMonitor.instance;
monitor.startFrame('MovementSystem');
// 系统逻辑...
monitor.endFrame('MovementSystem');

// 查看性能报告
console.log(monitor.getReport());

通过合理使用这些系统类型,你可以构建出高性能、易维护的游戏逻辑!