Files
esengine/docs/entity-manager-example.md
2025-08-11 09:31:44 +08:00

9.5 KiB
Raw Blame History

EntityManager 使用指南

本文档详细介绍 EntityManager 的使用方法和最佳实践。

目录

  1. 基础用法
  2. 查询系统
  3. 实体管理
  4. 性能监控
  5. 最佳实践

基础用法

创建 EntityManager

import { EntityManager, Scene } from '@esengine/ecs-framework';

// 创建场景和实体管理器
const scene = new Scene();
const entityManager = new EntityManager();

// 批量创建实体使用Scene方法
const enemies = scene.createEntities(50, "Enemy");

// 为实体添加组件
enemies.forEach((enemy, index) => {
    enemy.addComponent(new PositionComponent(
        Math.random() * 800, 
        Math.random() * 600
    ));
    enemy.addComponent(new HealthComponent(100));
    enemy.addComponent(new VelocityComponent(
        (Math.random() - 0.5) * 100,
        (Math.random() - 0.5) * 100
    ));
    enemy.tag = 2; // 敌人标签
});

查询系统

基础查询

// 按组件类型查询
const healthEntities = entityManager.getEntitiesWithComponent(HealthComponent);

// 按标签查询
const enemies = entityManager.getEntitiesByTag(2);
const players = entityManager.getEntitiesByTag(1);

// 按名称查询
const boss = entityManager.getEntityByName("BossEnemy");

// 获取所有实体
const allEntities = entityManager.getAllEntities();

流式查询 API

// 复杂查询条件
const movingEnemies = entityManager
    .query()
    .withAll(PositionComponent, VelocityComponent, HealthComponent)
    .withTag(2) // 敌人标签
    .execute();

// 查询活跃的玩家
const activePlayers = entityManager
    .query()
    .withAll(PositionComponent)
    .withTag(1) // 玩家标签
    .active() // 只查询活跃实体
    .execute();

// 排除特定组件的实体
const nonCombatEntities = entityManager
    .query()
    .withAll(PositionComponent)
    .without(WeaponComponent, HealthComponent)
    .execute();

// 自定义条件查询
const nearbyEnemies = entityManager
    .query()
    .withAll(PositionComponent)
    .withTag(2)
    .where(entity => {
        const pos = entity.getComponent(PositionComponent);
        return pos && Math.abs(pos.x - playerX) < 100;
    })
    .execute();

实体管理

创建和销毁实体

// 创建单个实体
const player = entityManager.createEntity("Player");
player.addComponent(new PositionComponent(400, 300));
player.addComponent(new HealthComponent(100));
player.tag = 1;

// 销毁实体
entityManager.destroyEntity(player);

// 按名称销毁
entityManager.destroyEntity("Enemy_1");

// 按ID销毁
entityManager.destroyEntity(123);

实体查找

// 按ID查找
const entity = entityManager.getEntity(123);

// 按名称查找
const player = entityManager.getEntityByName("Player");

// 检查实体是否存在
if (entity && !entity.isDestroyed) {
    // 实体有效
}

性能监控

基础统计

// 获取实体数量
console.log('总实体数:', entityManager.entityCount);
console.log('活跃实体数:', entityManager.activeEntityCount);

// 获取场景统计
const sceneStats = scene.getStats();
console.log('场景统计:', {
    实体数量: sceneStats.entityCount,
    系统数量: sceneStats.processorCount
});

// 获取查询系统统计
const queryStats = scene.querySystem.getStats();
console.log('查询统计:', queryStats);

最佳实践

1. 高效查询

// 好的做法:缓存查询结果
class CombatSystem extends EntitySystem {
    private cachedEnemies: Entity[] = [];
    private lastUpdateFrame = 0;
    
    protected process(entities: Entity[]): void {
        // 每5帧更新一次缓存
        if (Time.frameCount - this.lastUpdateFrame > 5) {
            this.cachedEnemies = this.entityManager
                .query()
                .withAll(PositionComponent, HealthComponent)
                .withTag(2)
                .execute();
            this.lastUpdateFrame = Time.frameCount;
        }
        
        // 使用缓存的结果
        this.cachedEnemies.forEach(enemy => {
            // 处理敌人逻辑
        });
    }
}

2. 批量操作

// 好的做法:批量创建和配置
function createBulletWave(count: number): Entity[] {
    // 使用Scene的批量创建
    const bullets = scene.createEntities(count, "Bullet");
    
    // 批量配置组件
    bullets.forEach((bullet, index) => {
        const angle = (index / count) * Math.PI * 2;
        bullet.addComponent(new PositionComponent(400, 300));
        bullet.addComponent(new VelocityComponent(
            Math.cos(angle) * 200,
            Math.sin(angle) * 200
        ));
        bullet.addComponent(new BulletComponent());
        bullet.tag = 3; // 子弹标签
    });
    
    return bullets;
}

3. 内存管理

// 好的做法:及时清理无用实体
class CleanupSystem extends EntitySystem {
    protected process(entities: Entity[]): void {
        // 清理超出边界的子弹
        const bullets = this.entityManager.getEntitiesByTag(3);
        bullets.forEach(bullet => {
            const pos = bullet.getComponent(PositionComponent);
            if (pos && (pos.x < -100 || pos.x > 900 || pos.y < -100 || pos.y > 700)) {
                this.entityManager.destroyEntity(bullet);
            }
        });
        
        // 清理死亡的敌人
        const deadEnemies = this.entityManager
            .query()
            .withAll(HealthComponent)
            .withTag(2)
            .where(entity => {
                const health = entity.getComponent(HealthComponent);
                return health && health.currentHealth <= 0;
            })
            .execute();
            
        deadEnemies.forEach(enemy => {
            this.entityManager.destroyEntity(enemy);
        });
    }
}

4. 查询优化

// 好的做法:使用合适的查询方法
class GameSystem extends EntitySystem {
    findTargetsInRange(attacker: Entity, range: number): Entity[] {
        const attackerPos = attacker.getComponent(PositionComponent);
        if (!attackerPos) return [];
        
        // 先按标签快速筛选,再按距离过滤
        return this.entityManager
            .getEntitiesByTag(2) // 敌人标签
            .filter(enemy => {
                const enemyPos = enemy.getComponent(PositionComponent);
                if (!enemyPos) return false;
                
                const distance = Math.sqrt(
                    Math.pow(attackerPos.x - enemyPos.x, 2) + 
                    Math.pow(attackerPos.y - enemyPos.y, 2)
                );
                return distance <= range;
            });
    }
}

完整示例

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

// 组件定义
class PositionComponent extends Component {
    constructor(public x: number = 0, public y: number = 0) {
        super();
    }
}

class HealthComponent extends Component {
    constructor(
        public maxHealth: number = 100,
        public currentHealth: number = 100
    ) {
        super();
    }
}

// 游戏管理器
class GameManager {
    private scene: Scene;
    private entityManager: EntityManager;
    
    constructor() {
        this.scene = new Scene();
        this.entityManager = new EntityManager();
        this.setupGame();
    }
    
    private setupGame(): void {
        // 创建玩家
        const player = this.entityManager.createEntity("Player");
        player.addComponent(new PositionComponent(400, 300));
        player.addComponent(new HealthComponent(100));
        player.tag = 1;
        
        // 创建敌人
        const enemies = this.scene.createEntities(10, "Enemy");
        enemies.forEach(enemy => {
            enemy.addComponent(new PositionComponent(
                Math.random() * 800,
                Math.random() * 600
            ));
            enemy.addComponent(new HealthComponent(50));
            enemy.tag = 2;
        });
        
        // 添加系统
        this.scene.addEntityProcessor(new HealthSystem());
    }
    
    public update(): void {
        this.scene.update();
        
        // 输出统计信息
        console.log('实体数量:', this.entityManager.entityCount);
        console.log('活跃实体数:', this.entityManager.activeEntityCount);
    }
}

// 生命值系统
class HealthSystem extends EntitySystem {
    constructor() {
        super(Matcher.empty().all(HealthComponent));
    }
    
    protected process(entities: Entity[]): void {
        const healthEntities = this.scene.querySystem.queryAll(HealthComponent);
        
        healthEntities.entities.forEach(entity => {
            const health = entity.getComponent(HealthComponent);
            if (health && health.currentHealth <= 0) {
                console.log(`实体 ${entity.name} 死亡`);
                entity.destroy();
            }
        });
    }
}

// 启动游戏
const game = new GameManager();
setInterval(() => game.update(), 16); // 60 FPS

总结

EntityManager 提供了强大的实体管理功能:

  • 创建管理createEntity(), destroyEntity()
  • 查询功能getEntitiesWithComponent(), getEntitiesByTag(), query()
  • 实体查找getEntity(), getEntityByName()
  • 统计信息entityCount, activeEntityCount

通过合理使用这些API可以构建高性能的游戏系统。记住要及时清理无用实体缓存频繁查询的结果并使用合适的查询方法来优化性能。