Files
esengine/docs/event-system-example.md

12 KiB
Raw Permalink Blame History

ECS事件系统使用指南

本文档介绍如何使用ECS框架的增强事件系统包括类型安全的事件发布订阅、预定义的ECS事件类型和高级功能。

目录

  1. 基础用法
  2. 预定义ECS事件
  3. 事件装饰器
  4. 高级功能
  5. 性能优化
  6. 最佳实践

基础用法

创建事件总线

import { EventBus, GlobalEventBus } from './ECS';

// 方式1创建独立的事件总线
const eventBus = new EventBus(true); // true启用调试模式

// 方式2使用全局事件总线
const globalEventBus = GlobalEventBus.getInstance(true);

基本事件发布订阅

// 定义事件数据类型
interface PlayerDiedEvent {
    playerId: number;
    cause: string;
    position: { x: number; y: number };
}

// 监听事件
const listenerId = eventBus.on<PlayerDiedEvent>('player:died', (data) => {
    console.log(`Player ${data.playerId} died at (${data.position.x}, ${data.position.y})`);
    console.log(`Cause: ${data.cause}`);
});

// 发射事件
eventBus.emit('player:died', {
    playerId: 123,
    cause: 'enemy_attack',
    position: { x: 100, y: 200 }
});

// 移除监听器
eventBus.off('player:died', listenerId);

一次性事件监听

// 只监听一次
eventBus.once<PlayerDiedEvent>('player:died', (data) => {
    console.log('This will only be called once');
});

异步事件处理

// 异步事件监听
eventBus.onAsync<PlayerDiedEvent>('player:died', async (data) => {
    await savePlayerDeathToDatabase(data);
    await updateLeaderboard(data.playerId);
});

// 异步事件发射
await eventBus.emitAsync('player:died', playerData);

预定义ECS事件

框架提供了完整的ECS事件类型定义支持实体、组件、系统等核心概念的事件。

实体事件

import { ECSEventType, IEntityEventData } from './ECS';

// 监听实体创建事件
eventBus.onEntityCreated((data: IEntityEventData) => {
    console.log(`Entity created: ${data.entityName} (ID: ${data.entityId})`);
});

// 监听实体销毁事件
eventBus.on<IEntityEventData>(ECSEventType.ENTITY_DESTROYED, (data) => {
    console.log(`Entity destroyed: ${data.entityName}`);
});

// 手动发射实体事件
eventBus.emitEntityCreated({
    timestamp: Date.now(),
    source: 'GameManager',
    entityId: 123,
    entityName: 'Player',
    entityTag: 'player'
});

组件事件

import { IComponentEventData } from './ECS';

// 监听组件添加事件
eventBus.onComponentAdded((data: IComponentEventData) => {
    console.log(`Component ${data.componentType} added to entity ${data.entityId}`);
});

// 监听组件移除事件
eventBus.on<IComponentEventData>(ECSEventType.COMPONENT_REMOVED, (data) => {
    console.log(`Component ${data.componentType} removed from entity ${data.entityId}`);
});

系统事件

import { ISystemEventData } from './ECS';

// 监听系统错误
eventBus.onSystemError((data: ISystemEventData) => {
    console.error(`System error in ${data.systemName}: ${data.systemType}`);
});

// 监听系统处理开始/结束
eventBus.on<ISystemEventData>(ECSEventType.SYSTEM_PROCESSING_START, (data) => {
    console.log(`System ${data.systemName} started processing`);
});

性能事件

import { IPerformanceEventData } from './ECS';

// 监听性能警告
eventBus.onPerformanceWarning((data: IPerformanceEventData) => {
    console.warn(`Performance warning: ${data.operation} took ${data.executionTime}ms`);
});

// 监听内存使用过高
eventBus.on<IPerformanceEventData>(ECSEventType.MEMORY_USAGE_HIGH, (data) => {
    console.warn(`High memory usage: ${data.memoryUsage}MB`);
});

事件装饰器

使用装饰器可以自动注册事件监听器,简化代码编写。

基础装饰器

import { EventHandler, AsyncEventHandler, EventPriority } from './ECS';

class GameManager {
    @EventHandler(ECSEventType.ENTITY_CREATED, { priority: EventPriority.HIGH })
    onEntityCreated(data: IEntityEventData) {
        console.log(`New entity: ${data.entityName}`);
    }
    
    @AsyncEventHandler(ECSEventType.ENTITY_DESTROYED)
    async onEntityDestroyed(data: IEntityEventData) {
        await this.cleanupEntityResources(data.entityId);
    }
    
    @EventHandler('custom:game:event', { once: true })
    onGameStart(data: any) {
        console.log('Game started!');
    }
    
    // 需要手动调用初始化方法
    constructor() {
        // 如果类有initEventListeners方法会自动注册装饰器定义的监听器
        if (this.initEventListeners) {
            this.initEventListeners();
        }
    }
    
    private async cleanupEntityResources(entityId: number) {
        // 清理实体相关资源
    }
}

优先级和配置

class SystemManager {
    @EventHandler(ECSEventType.SYSTEM_ERROR, { 
        priority: EventPriority.CRITICAL,
        context: this 
    })
    handleSystemError(data: ISystemEventData) {
        this.logError(data);
        this.restartSystem(data.systemName);
    }
    
    @AsyncEventHandler(ECSEventType.PERFORMANCE_WARNING, {
        priority: EventPriority.LOW,
        async: true
    })
    async handlePerformanceWarning(data: IPerformanceEventData) {
        await this.optimizePerformance(data);
    }
    
    private logError(data: ISystemEventData) {
        // 错误日志记录
    }
    
    private restartSystem(systemName: string) {
        // 重启系统
    }
    
    private async optimizePerformance(data: IPerformanceEventData) {
        // 性能优化逻辑
    }
}

高级功能

事件批处理

// 设置批处理配置
eventBus.setBatchConfig('entity:update', 100, 16); // 批量100个延迟16ms

// 发射事件(会被批处理)
for (let i = 0; i < 1000; i++) {
    eventBus.emit('entity:update', { entityId: i, data: 'update' });
}

// 手动刷新批处理队列
eventBus.flushBatch('entity:update');

事件统计和监控

// 获取单个事件统计
const stats = eventBus.getStats('entity:created');
console.log(`Event triggered ${stats.triggerCount} times`);
console.log(`Average execution time: ${stats.averageExecutionTime}ms`);

// 获取所有事件统计
const allStats = eventBus.getStats();
if (allStats instanceof Map) {
    allStats.forEach((stat, eventType) => {
        console.log(`${eventType}: ${stat.triggerCount} triggers`);
    });
}

// 重置统计
eventBus.resetStats('entity:created');

事件类型验证

import { EventTypeValidator } from './ECS';

// 检查事件类型是否有效
if (EventTypeValidator.isValid('entity:created')) {
    eventBus.emit('entity:created', data);
}

// 添加自定义事件类型
EventTypeValidator.addCustomType('game:custom:event');

// 获取所有有效事件类型
const validTypes = EventTypeValidator.getAllValidTypes();
console.log('Valid event types:', validTypes);

调试和日志

// 启用调试模式
eventBus.setDebugMode(true);

// 设置最大监听器数量
eventBus.setMaxListeners(50);

// 检查是否有监听器
if (eventBus.hasListeners('entity:created')) {
    console.log('Has listeners for entity:created');
}

// 获取监听器数量
const count = eventBus.getListenerCount('entity:created');
console.log(`${count} listeners for entity:created`);

性能优化

事件过滤和条件监听

// 使用条件过滤减少不必要的事件处理
eventBus.on<IEntityEventData>(ECSEventType.ENTITY_CREATED, (data) => {
    // 只处理玩家实体
    if (data.entityTag === 'player') {
        handlePlayerCreated(data);
    }
});

// 更好的方式:使用具体的事件类型
eventBus.on<IEntityEventData>('entity:player:created', handlePlayerCreated);

内存管理

class EventManager {
    private listeners: string[] = [];
    
    public setupListeners() {
        // 保存监听器ID以便清理
        this.listeners.push(
            eventBus.on('event1', this.handler1.bind(this)),
            eventBus.on('event2', this.handler2.bind(this))
        );
    }
    
    public cleanup() {
        // 清理所有监听器
        this.listeners.forEach(id => {
            eventBus.off('event1', id);
            eventBus.off('event2', id);
        });
        this.listeners.length = 0;
    }
    
    private handler1(data: any) { /* ... */ }
    private handler2(data: any) { /* ... */ }
}

异步事件优化

// 使用Promise.all并行处理多个异步事件
const promises = [
    eventBus.emitAsync('save:player', playerData),
    eventBus.emitAsync('update:leaderboard', scoreData),
    eventBus.emitAsync('notify:friends', notificationData)
];

await Promise.all(promises);

最佳实践

1. 事件命名规范

// 推荐的事件命名格式:模块:对象:动作
const EVENT_NAMES = {
    // 实体相关
    ENTITY_PLAYER_CREATED: 'entity:player:created',
    ENTITY_ENEMY_DESTROYED: 'entity:enemy:destroyed',
    
    // 游戏逻辑相关
    GAME_LEVEL_COMPLETED: 'game:level:completed',
    GAME_SCORE_UPDATED: 'game:score:updated',
    
    // UI相关
    UI_MENU_OPENED: 'ui:menu:opened',
    UI_BUTTON_CLICKED: 'ui:button:clicked'
};

2. 类型安全

// 定义强类型的事件数据接口
interface GameEvents {
    'player:levelup': { playerId: number; newLevel: number; experience: number };
    'inventory:item:added': { itemId: string; quantity: number; playerId: number };
    'combat:damage:dealt': { attackerId: number; targetId: number; damage: number };
}

// 创建类型安全的事件发射器
class TypedEventBus {
    private eventBus = new EventBus();
    
    emit<K extends keyof GameEvents>(eventType: K, data: GameEvents[K]) {
        this.eventBus.emit(eventType, data);
    }
    
    on<K extends keyof GameEvents>(
        eventType: K, 
        handler: (data: GameEvents[K]) => void
    ) {
        return this.eventBus.on(eventType, handler);
    }
}

3. 错误处理

// 在事件处理器中添加错误处理
eventBus.on<IEntityEventData>(ECSEventType.ENTITY_CREATED, (data) => {
    try {
        processEntityCreation(data);
    } catch (error) {
        console.error('Error processing entity creation:', error);
        // 发射错误事件
        eventBus.emit(ECSEventType.ERROR_OCCURRED, {
            timestamp: Date.now(),
            source: 'EntityCreationHandler',
            error: error.message,
            context: data
        });
    }
});

4. 模块化事件管理

// 为不同模块创建专门的事件管理器
class PlayerEventManager {
    constructor(private eventBus: EventBus) {
        this.setupListeners();
    }
    
    private setupListeners() {
        this.eventBus.onEntityCreated(this.onPlayerCreated.bind(this));
        this.eventBus.on('player:levelup', this.onPlayerLevelUp.bind(this));
        this.eventBus.on('player:died', this.onPlayerDied.bind(this));
    }
    
    private onPlayerCreated(data: IEntityEventData) {
        if (data.entityTag === 'player') {
            // 处理玩家创建逻辑
        }
    }
    
    private onPlayerLevelUp(data: any) {
        // 处理玩家升级逻辑
    }
    
    private onPlayerDied(data: any) {
        // 处理玩家死亡逻辑
    }
}

5. 与EntityManager集成

import { EntityManager } from './ECS';

// EntityManager会自动设置事件总线
const entityManager = new EntityManager();

// 获取事件总线实例
const eventBus = entityManager.eventBus;

// 监听自动发射的ECS事件
eventBus.onEntityCreated((data) => {
    console.log('Entity created automatically:', data);
});

eventBus.onComponentAdded((data) => {
    console.log('Component added automatically:', data);
});

// 创建实体时会自动发射事件
const entity = entityManager.createEntity('Player');

// 添加组件时会自动发射事件
entity.addComponent(new HealthComponent(100));

总结

ECS框架的事件系统提供了

  • 类型安全完整的TypeScript类型支持
  • 高性能:批处理、缓存和优化机制
  • 易用性:装饰器、预定义事件类型
  • 可扩展:自定义事件类型和验证
  • 调试友好:详细的统计信息和调试模式

通过合理使用事件系统,可以实现松耦合的模块化架构,提高代码的可维护性和扩展性。