feat: 添加ECS核心功能模块 - EntityManager实体管理器、EventBus事件总线、性能优化系统
This commit is contained in:
261
source/src/ECS/Core/ArchetypeSystem.ts
Normal file
261
source/src/ECS/Core/ArchetypeSystem.ts
Normal file
@@ -0,0 +1,261 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { ComponentType } from './ComponentStorage';
|
||||
|
||||
/**
|
||||
* 原型标识符
|
||||
*/
|
||||
export type ArchetypeId = string;
|
||||
|
||||
/**
|
||||
* 原型数据结构
|
||||
*/
|
||||
export interface Archetype {
|
||||
/** 原型唯一标识符 */
|
||||
id: ArchetypeId;
|
||||
/** 包含的组件类型 */
|
||||
componentTypes: ComponentType[];
|
||||
/** 属于该原型的实体列表 */
|
||||
entities: Entity[];
|
||||
/** 原型创建时间 */
|
||||
createdAt: number;
|
||||
/** 最后更新时间 */
|
||||
updatedAt: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 原型查询结果
|
||||
*/
|
||||
export interface ArchetypeQueryResult {
|
||||
/** 匹配的原型列表 */
|
||||
archetypes: Archetype[];
|
||||
/** 所有匹配实体的总数 */
|
||||
totalEntities: number;
|
||||
/** 查询执行时间(毫秒) */
|
||||
executionTime: number;
|
||||
/** 是否使用了缓存 */
|
||||
fromCache: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Archetype系统
|
||||
*
|
||||
* 根据实体的组件组合将实体分组到不同的原型中,提供高效的查询性能。
|
||||
*/
|
||||
export class ArchetypeSystem {
|
||||
/** 所有原型的映射表 */
|
||||
private _archetypes = new Map<ArchetypeId, Archetype>();
|
||||
|
||||
/** 实体到原型的映射 */
|
||||
private _entityToArchetype = new Map<Entity, Archetype>();
|
||||
|
||||
/** 组件类型到原型的映射 */
|
||||
private _componentToArchetypes = new Map<ComponentType, Set<Archetype>>();
|
||||
|
||||
/** 查询缓存 */
|
||||
private _queryCache = new Map<string, {
|
||||
result: ArchetypeQueryResult;
|
||||
timestamp: number;
|
||||
}>();
|
||||
|
||||
private _cacheTimeout = 5000;
|
||||
private _maxCacheSize = 100;
|
||||
|
||||
/**
|
||||
* 添加实体到原型系统
|
||||
*/
|
||||
public addEntity(entity: Entity): void {
|
||||
const componentTypes = this.getEntityComponentTypes(entity);
|
||||
const archetypeId = this.generateArchetypeId(componentTypes);
|
||||
|
||||
let archetype = this._archetypes.get(archetypeId);
|
||||
if (!archetype) {
|
||||
archetype = this.createArchetype(componentTypes);
|
||||
}
|
||||
|
||||
archetype.entities.push(entity);
|
||||
archetype.updatedAt = Date.now();
|
||||
this._entityToArchetype.set(entity, archetype);
|
||||
|
||||
this.updateComponentIndexes(archetype, componentTypes, true);
|
||||
this.invalidateQueryCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从原型系统中移除实体
|
||||
*/
|
||||
public removeEntity(entity: Entity): void {
|
||||
const archetype = this._entityToArchetype.get(entity);
|
||||
if (!archetype) return;
|
||||
|
||||
const index = archetype.entities.indexOf(entity);
|
||||
if (index !== -1) {
|
||||
archetype.entities.splice(index, 1);
|
||||
archetype.updatedAt = Date.now();
|
||||
}
|
||||
|
||||
this._entityToArchetype.delete(entity);
|
||||
this.invalidateQueryCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询包含指定组件组合的原型
|
||||
*/
|
||||
public queryArchetypes(componentTypes: ComponentType[], operation: 'AND' | 'OR' = 'AND'): ArchetypeQueryResult {
|
||||
const startTime = performance.now();
|
||||
|
||||
const cacheKey = `${operation}:${componentTypes.map(t => t.name).sort().join(',')}`;
|
||||
|
||||
// 检查缓存
|
||||
const cached = this._queryCache.get(cacheKey);
|
||||
if (cached && (Date.now() - cached.timestamp < this._cacheTimeout)) {
|
||||
return {
|
||||
...cached.result,
|
||||
executionTime: performance.now() - startTime,
|
||||
fromCache: true
|
||||
};
|
||||
}
|
||||
|
||||
const matchingArchetypes: Archetype[] = [];
|
||||
let totalEntities = 0;
|
||||
|
||||
if (operation === 'AND') {
|
||||
for (const archetype of this._archetypes.values()) {
|
||||
if (this.archetypeContainsAllComponents(archetype, componentTypes)) {
|
||||
matchingArchetypes.push(archetype);
|
||||
totalEntities += archetype.entities.length;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const foundArchetypes = new Set<Archetype>();
|
||||
|
||||
for (const componentType of componentTypes) {
|
||||
const archetypes = this._componentToArchetypes.get(componentType);
|
||||
if (archetypes) {
|
||||
for (const archetype of archetypes) {
|
||||
foundArchetypes.add(archetype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const archetype of foundArchetypes) {
|
||||
matchingArchetypes.push(archetype);
|
||||
totalEntities += archetype.entities.length;
|
||||
}
|
||||
}
|
||||
|
||||
const result: ArchetypeQueryResult = {
|
||||
archetypes: matchingArchetypes,
|
||||
totalEntities,
|
||||
executionTime: performance.now() - startTime,
|
||||
fromCache: false
|
||||
};
|
||||
|
||||
// 缓存结果
|
||||
this._queryCache.set(cacheKey, {
|
||||
result,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体所属的原型
|
||||
*/
|
||||
public getEntityArchetype(entity: Entity): Archetype | undefined {
|
||||
return this._entityToArchetype.get(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有原型
|
||||
*/
|
||||
public getAllArchetypes(): Archetype[] {
|
||||
return Array.from(this._archetypes.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有数据
|
||||
*/
|
||||
public clear(): void {
|
||||
this._archetypes.clear();
|
||||
this._entityToArchetype.clear();
|
||||
this._componentToArchetypes.clear();
|
||||
this._queryCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体的组件类型列表
|
||||
*/
|
||||
private getEntityComponentTypes(entity: Entity): ComponentType[] {
|
||||
return entity.components.map(component => component.constructor as ComponentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成原型ID
|
||||
*/
|
||||
private generateArchetypeId(componentTypes: ComponentType[]): ArchetypeId {
|
||||
return componentTypes
|
||||
.map(type => type.name)
|
||||
.sort()
|
||||
.join('|');
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新原型
|
||||
*/
|
||||
private createArchetype(componentTypes: ComponentType[]): Archetype {
|
||||
const id = this.generateArchetypeId(componentTypes);
|
||||
|
||||
const archetype: Archetype = {
|
||||
id,
|
||||
componentTypes: [...componentTypes],
|
||||
entities: [],
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now()
|
||||
};
|
||||
|
||||
this._archetypes.set(id, archetype);
|
||||
return archetype;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查原型是否包含所有指定组件
|
||||
*/
|
||||
private archetypeContainsAllComponents(archetype: Archetype, componentTypes: ComponentType[]): boolean {
|
||||
for (const componentType of componentTypes) {
|
||||
if (!archetype.componentTypes.includes(componentType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新组件索引
|
||||
*/
|
||||
private updateComponentIndexes(archetype: Archetype, componentTypes: ComponentType[], add: boolean): void {
|
||||
for (const componentType of componentTypes) {
|
||||
let archetypes = this._componentToArchetypes.get(componentType);
|
||||
if (!archetypes) {
|
||||
archetypes = new Set();
|
||||
this._componentToArchetypes.set(componentType, archetypes);
|
||||
}
|
||||
|
||||
if (add) {
|
||||
archetypes.add(archetype);
|
||||
} else {
|
||||
archetypes.delete(archetype);
|
||||
if (archetypes.size === 0) {
|
||||
this._componentToArchetypes.delete(componentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使查询缓存失效
|
||||
*/
|
||||
private invalidateQueryCache(): void {
|
||||
this._queryCache.clear();
|
||||
}
|
||||
}
|
||||
488
source/src/ECS/Core/ComponentIndex.ts
Normal file
488
source/src/ECS/Core/ComponentIndex.ts
Normal file
@@ -0,0 +1,488 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType } from './ComponentStorage';
|
||||
|
||||
/**
|
||||
* 组件索引类型
|
||||
*/
|
||||
export enum IndexType {
|
||||
/** 哈希索引 - 最快查找 */
|
||||
HASH = 'hash',
|
||||
/** 位图索引 - 内存高效 */
|
||||
BITMAP = 'bitmap',
|
||||
/** 排序索引 - 支持范围查询 */
|
||||
SORTED = 'sorted'
|
||||
}
|
||||
|
||||
/**
|
||||
* 索引统计信息
|
||||
*/
|
||||
export interface IndexStats {
|
||||
/** 索引类型 */
|
||||
type: IndexType;
|
||||
/** 索引大小 */
|
||||
size: number;
|
||||
/** 内存使用量(字节) */
|
||||
memoryUsage: number;
|
||||
/** 查询次数 */
|
||||
queryCount: number;
|
||||
/** 平均查询时间(毫秒) */
|
||||
avgQueryTime: number;
|
||||
/** 最后更新时间 */
|
||||
lastUpdated: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件索引接口
|
||||
*/
|
||||
export interface IComponentIndex {
|
||||
/** 索引类型 */
|
||||
readonly type: IndexType;
|
||||
/** 添加实体到索引 */
|
||||
addEntity(entity: Entity): void;
|
||||
/** 从索引中移除实体 */
|
||||
removeEntity(entity: Entity): void;
|
||||
/** 查询包含指定组件的实体 */
|
||||
query(componentType: ComponentType): Set<Entity>;
|
||||
/** 批量查询多个组件 */
|
||||
queryMultiple(componentTypes: ComponentType[], operation: 'AND' | 'OR'): Set<Entity>;
|
||||
/** 清空索引 */
|
||||
clear(): void;
|
||||
/** 获取索引统计信息 */
|
||||
getStats(): IndexStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 哈希索引实现
|
||||
*
|
||||
* 使用Map数据结构,提供O(1)的查找性能。
|
||||
* 适合大多数查询场景。
|
||||
*/
|
||||
export class HashComponentIndex implements IComponentIndex {
|
||||
public readonly type = IndexType.HASH;
|
||||
|
||||
private _componentToEntities = new Map<ComponentType, Set<Entity>>();
|
||||
private _entityToComponents = new Map<Entity, Set<ComponentType>>();
|
||||
private _queryCount = 0;
|
||||
private _totalQueryTime = 0;
|
||||
private _lastUpdated = Date.now();
|
||||
|
||||
public addEntity(entity: Entity): void {
|
||||
const components = entity.components;
|
||||
const componentTypes = new Set<ComponentType>();
|
||||
|
||||
for (const component of components) {
|
||||
const componentType = component.constructor as ComponentType;
|
||||
componentTypes.add(componentType);
|
||||
|
||||
let entities = this._componentToEntities.get(componentType);
|
||||
if (!entities) {
|
||||
entities = new Set();
|
||||
this._componentToEntities.set(componentType, entities);
|
||||
}
|
||||
entities.add(entity);
|
||||
}
|
||||
|
||||
this._entityToComponents.set(entity, componentTypes);
|
||||
this._lastUpdated = Date.now();
|
||||
}
|
||||
|
||||
public removeEntity(entity: Entity): void {
|
||||
const componentTypes = this._entityToComponents.get(entity);
|
||||
if (!componentTypes) return;
|
||||
|
||||
for (const componentType of componentTypes) {
|
||||
const entities = this._componentToEntities.get(componentType);
|
||||
if (entities) {
|
||||
entities.delete(entity);
|
||||
if (entities.size === 0) {
|
||||
this._componentToEntities.delete(componentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._entityToComponents.delete(entity);
|
||||
this._lastUpdated = Date.now();
|
||||
}
|
||||
|
||||
public query(componentType: ComponentType): Set<Entity> {
|
||||
const startTime = performance.now();
|
||||
const result = new Set(this._componentToEntities.get(componentType) || []);
|
||||
|
||||
this._queryCount++;
|
||||
this._totalQueryTime += performance.now() - startTime;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public queryMultiple(componentTypes: ComponentType[], operation: 'AND' | 'OR'): Set<Entity> {
|
||||
const startTime = performance.now();
|
||||
|
||||
if (componentTypes.length === 0) {
|
||||
return new Set();
|
||||
}
|
||||
|
||||
if (componentTypes.length === 1) {
|
||||
return this.query(componentTypes[0]);
|
||||
}
|
||||
|
||||
let result: Set<Entity>;
|
||||
|
||||
if (operation === 'AND') {
|
||||
let smallestSet: Set<Entity> | undefined;
|
||||
let smallestSize = Infinity;
|
||||
|
||||
for (const componentType of componentTypes) {
|
||||
const entities = this._componentToEntities.get(componentType);
|
||||
if (!entities || entities.size === 0) {
|
||||
this._queryCount++;
|
||||
this._totalQueryTime += performance.now() - startTime;
|
||||
return new Set();
|
||||
}
|
||||
if (entities.size < smallestSize) {
|
||||
smallestSize = entities.size;
|
||||
smallestSet = entities;
|
||||
}
|
||||
}
|
||||
|
||||
result = new Set();
|
||||
if (smallestSet) {
|
||||
for (const entity of smallestSet) {
|
||||
let hasAll = true;
|
||||
for (const componentType of componentTypes) {
|
||||
const entities = this._componentToEntities.get(componentType);
|
||||
if (!entities || !entities.has(entity)) {
|
||||
hasAll = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasAll) {
|
||||
result.add(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = new Set();
|
||||
for (const componentType of componentTypes) {
|
||||
const entities = this._componentToEntities.get(componentType);
|
||||
if (entities) {
|
||||
for (const entity of entities) {
|
||||
result.add(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._queryCount++;
|
||||
this._totalQueryTime += performance.now() - startTime;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._componentToEntities.clear();
|
||||
this._entityToComponents.clear();
|
||||
this._lastUpdated = Date.now();
|
||||
}
|
||||
|
||||
public getStats(): IndexStats {
|
||||
let memoryUsage = 0;
|
||||
|
||||
memoryUsage += this._componentToEntities.size * 64;
|
||||
memoryUsage += this._entityToComponents.size * 64;
|
||||
|
||||
for (const entities of this._componentToEntities.values()) {
|
||||
memoryUsage += entities.size * 8;
|
||||
}
|
||||
|
||||
for (const components of this._entityToComponents.values()) {
|
||||
memoryUsage += components.size * 8;
|
||||
}
|
||||
|
||||
return {
|
||||
type: this.type,
|
||||
size: this._componentToEntities.size,
|
||||
memoryUsage,
|
||||
queryCount: this._queryCount,
|
||||
avgQueryTime: this._queryCount > 0 ? this._totalQueryTime / this._queryCount : 0,
|
||||
lastUpdated: this._lastUpdated
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 位图索引实现
|
||||
*
|
||||
* 使用位操作进行快速集合运算,内存效率高。
|
||||
* 适合有限组件类型和大量实体的场景。
|
||||
*/
|
||||
export class BitmapComponentIndex implements IComponentIndex {
|
||||
public readonly type = IndexType.BITMAP;
|
||||
|
||||
private _componentTypeToBit = new Map<ComponentType, number>();
|
||||
private _entityToBitmap = new Map<Entity, number>();
|
||||
private _bitToEntities = new Map<number, Set<Entity>>();
|
||||
private _nextBit = 0;
|
||||
private _queryCount = 0;
|
||||
private _totalQueryTime = 0;
|
||||
private _lastUpdated = Date.now();
|
||||
|
||||
public addEntity(entity: Entity): void {
|
||||
let bitmap = 0;
|
||||
|
||||
for (const component of entity.components) {
|
||||
const componentType = component.constructor as ComponentType;
|
||||
let bit = this._componentTypeToBit.get(componentType);
|
||||
|
||||
if (bit === undefined) {
|
||||
bit = this._nextBit++;
|
||||
this._componentTypeToBit.set(componentType, bit);
|
||||
}
|
||||
|
||||
bitmap |= (1 << bit);
|
||||
|
||||
let entities = this._bitToEntities.get(1 << bit);
|
||||
if (!entities) {
|
||||
entities = new Set();
|
||||
this._bitToEntities.set(1 << bit, entities);
|
||||
}
|
||||
entities.add(entity);
|
||||
}
|
||||
|
||||
this._entityToBitmap.set(entity, bitmap);
|
||||
this._lastUpdated = Date.now();
|
||||
}
|
||||
|
||||
public removeEntity(entity: Entity): void {
|
||||
const bitmap = this._entityToBitmap.get(entity);
|
||||
if (bitmap === undefined) return;
|
||||
|
||||
// 从所有相关的位集合中移除实体
|
||||
for (const [bitMask, entities] of this._bitToEntities) {
|
||||
if ((bitmap & bitMask) !== 0) {
|
||||
entities.delete(entity);
|
||||
if (entities.size === 0) {
|
||||
this._bitToEntities.delete(bitMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._entityToBitmap.delete(entity);
|
||||
this._lastUpdated = Date.now();
|
||||
}
|
||||
|
||||
public query(componentType: ComponentType): Set<Entity> {
|
||||
const startTime = performance.now();
|
||||
|
||||
const bit = this._componentTypeToBit.get(componentType);
|
||||
if (bit === undefined) {
|
||||
this._queryCount++;
|
||||
this._totalQueryTime += performance.now() - startTime;
|
||||
return new Set();
|
||||
}
|
||||
|
||||
const result = new Set(this._bitToEntities.get(1 << bit) || []);
|
||||
|
||||
this._queryCount++;
|
||||
this._totalQueryTime += performance.now() - startTime;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public queryMultiple(componentTypes: ComponentType[], operation: 'AND' | 'OR'): Set<Entity> {
|
||||
const startTime = performance.now();
|
||||
|
||||
if (componentTypes.length === 0) {
|
||||
return new Set();
|
||||
}
|
||||
|
||||
let targetBitmap = 0;
|
||||
const validBits: number[] = [];
|
||||
|
||||
for (const componentType of componentTypes) {
|
||||
const bit = this._componentTypeToBit.get(componentType);
|
||||
if (bit !== undefined) {
|
||||
targetBitmap |= (1 << bit);
|
||||
validBits.push(1 << bit);
|
||||
}
|
||||
}
|
||||
|
||||
const result = new Set<Entity>();
|
||||
|
||||
if (operation === 'AND') {
|
||||
for (const [entity, entityBitmap] of this._entityToBitmap) {
|
||||
if ((entityBitmap & targetBitmap) === targetBitmap) {
|
||||
result.add(entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const bitMask of validBits) {
|
||||
const entities = this._bitToEntities.get(bitMask);
|
||||
if (entities) {
|
||||
for (const entity of entities) {
|
||||
result.add(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._queryCount++;
|
||||
this._totalQueryTime += performance.now() - startTime;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._componentTypeToBit.clear();
|
||||
this._entityToBitmap.clear();
|
||||
this._bitToEntities.clear();
|
||||
this._nextBit = 0;
|
||||
this._lastUpdated = Date.now();
|
||||
}
|
||||
|
||||
public getStats(): IndexStats {
|
||||
let memoryUsage = 0;
|
||||
|
||||
memoryUsage += this._componentTypeToBit.size * 12;
|
||||
memoryUsage += this._entityToBitmap.size * 12;
|
||||
memoryUsage += this._bitToEntities.size * 64;
|
||||
|
||||
for (const entities of this._bitToEntities.values()) {
|
||||
memoryUsage += entities.size * 8;
|
||||
}
|
||||
|
||||
return {
|
||||
type: this.type,
|
||||
size: this._componentTypeToBit.size,
|
||||
memoryUsage,
|
||||
queryCount: this._queryCount,
|
||||
avgQueryTime: this._queryCount > 0 ? this._totalQueryTime / this._queryCount : 0,
|
||||
lastUpdated: this._lastUpdated
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能组件索引管理器
|
||||
*
|
||||
* 根据使用模式自动选择最优的索引策略。
|
||||
* 支持动态切换索引类型以获得最佳性能。
|
||||
*/
|
||||
export class ComponentIndexManager {
|
||||
private _activeIndex: IComponentIndex;
|
||||
private _indexHistory: Map<IndexType, IndexStats> = new Map();
|
||||
private _autoOptimize = true;
|
||||
private _optimizationThreshold = 1000;
|
||||
|
||||
constructor(initialType: IndexType = IndexType.HASH) {
|
||||
this._activeIndex = this.createIndex(initialType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加实体到索引
|
||||
*/
|
||||
public addEntity(entity: Entity): void {
|
||||
this._activeIndex.addEntity(entity);
|
||||
this.checkOptimization();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从索引中移除实体
|
||||
*/
|
||||
public removeEntity(entity: Entity): void {
|
||||
this._activeIndex.removeEntity(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询包含指定组件的实体
|
||||
*/
|
||||
public query(componentType: ComponentType): Set<Entity> {
|
||||
return this._activeIndex.query(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询多个组件
|
||||
*/
|
||||
public queryMultiple(componentTypes: ComponentType[], operation: 'AND' | 'OR'): Set<Entity> {
|
||||
return this._activeIndex.queryMultiple(componentTypes, operation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动切换索引类型
|
||||
*/
|
||||
public switchIndexType(type: IndexType): void {
|
||||
if (type === this._activeIndex.type) return;
|
||||
|
||||
this._indexHistory.set(this._activeIndex.type, this._activeIndex.getStats());
|
||||
|
||||
const oldIndex = this._activeIndex;
|
||||
this._activeIndex = this.createIndex(type);
|
||||
|
||||
oldIndex.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用/禁用自动优化
|
||||
*/
|
||||
public setAutoOptimize(enabled: boolean): void {
|
||||
this._autoOptimize = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前索引统计信息
|
||||
*/
|
||||
public getStats(): IndexStats {
|
||||
return this._activeIndex.getStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有索引类型的历史统计信息
|
||||
*/
|
||||
public getAllStats(): Map<IndexType, IndexStats> {
|
||||
const current = this._activeIndex.getStats();
|
||||
return new Map([
|
||||
...this._indexHistory,
|
||||
[current.type, current]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空索引
|
||||
*/
|
||||
public clear(): void {
|
||||
this._activeIndex.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定类型的索引
|
||||
*/
|
||||
private createIndex(type: IndexType): IComponentIndex {
|
||||
switch (type) {
|
||||
case IndexType.HASH:
|
||||
return new HashComponentIndex();
|
||||
case IndexType.BITMAP:
|
||||
return new BitmapComponentIndex();
|
||||
case IndexType.SORTED:
|
||||
return new HashComponentIndex();
|
||||
default:
|
||||
return new HashComponentIndex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否需要优化索引
|
||||
*/
|
||||
private checkOptimization(): void {
|
||||
if (!this._autoOptimize) return;
|
||||
|
||||
const stats = this._activeIndex.getStats();
|
||||
if (stats.queryCount < this._optimizationThreshold) return;
|
||||
|
||||
|
||||
if (stats.avgQueryTime > 1.0 && stats.type !== IndexType.HASH) {
|
||||
this.switchIndexType(IndexType.HASH);
|
||||
} else if (stats.memoryUsage > 10 * 1024 * 1024 && stats.type !== IndexType.BITMAP) {
|
||||
this.switchIndexType(IndexType.BITMAP);
|
||||
}
|
||||
}
|
||||
}
|
||||
375
source/src/ECS/Core/DirtyTrackingSystem.ts
Normal file
375
source/src/ECS/Core/DirtyTrackingSystem.ts
Normal file
@@ -0,0 +1,375 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType } from './ComponentStorage';
|
||||
|
||||
/**
|
||||
* 脏标记类型
|
||||
*/
|
||||
export enum DirtyFlag {
|
||||
/** 组件数据已修改 */
|
||||
COMPONENT_MODIFIED = 1 << 0,
|
||||
/** 组件已添加 */
|
||||
COMPONENT_ADDED = 1 << 1,
|
||||
/** 组件已移除 */
|
||||
COMPONENT_REMOVED = 1 << 2,
|
||||
/** 实体位置已改变 */
|
||||
TRANSFORM_CHANGED = 1 << 3,
|
||||
/** 实体状态已改变 */
|
||||
STATE_CHANGED = 1 << 4,
|
||||
/** 自定义标记1 */
|
||||
CUSTOM_1 = 1 << 8,
|
||||
/** 自定义标记2 */
|
||||
CUSTOM_2 = 1 << 9,
|
||||
/** 自定义标记3 */
|
||||
CUSTOM_3 = 1 << 10,
|
||||
/** 所有标记 */
|
||||
ALL = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
/**
|
||||
* 脏标记数据
|
||||
*/
|
||||
export interface DirtyData {
|
||||
/** 实体引用 */
|
||||
entity: Entity;
|
||||
/** 脏标记位 */
|
||||
flags: number;
|
||||
/** 修改的组件类型列表 */
|
||||
modifiedComponents: Set<ComponentType>;
|
||||
/** 标记时间戳 */
|
||||
timestamp: number;
|
||||
/** 帧编号 */
|
||||
frameNumber: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 脏标记监听器
|
||||
*/
|
||||
export interface DirtyListener {
|
||||
/** 感兴趣的标记类型 */
|
||||
flags: number;
|
||||
/** 回调函数 */
|
||||
callback: (dirtyData: DirtyData) => void;
|
||||
/** 监听器优先级(数字越小优先级越高) */
|
||||
priority?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 脏标记统计信息
|
||||
*/
|
||||
export interface DirtyStats {
|
||||
/** 当前脏实体数量 */
|
||||
dirtyEntityCount: number;
|
||||
/** 总标记次数 */
|
||||
totalMarkings: number;
|
||||
/** 总清理次数 */
|
||||
totalCleanups: number;
|
||||
/** 监听器数量 */
|
||||
listenerCount: number;
|
||||
/** 平均每帧脏实体数量 */
|
||||
avgDirtyPerFrame: number;
|
||||
/** 内存使用量估算 */
|
||||
estimatedMemoryUsage: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 脏标记追踪系统
|
||||
*
|
||||
* 提供高效的组件和实体变更追踪,避免不必要的计算和更新。
|
||||
* 支持细粒度的脏标记和批量处理机制。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const dirtySystem = new DirtyTrackingSystem();
|
||||
*
|
||||
* // 标记实体的位置组件已修改
|
||||
* dirtySystem.markDirty(entity, DirtyFlag.TRANSFORM_CHANGED, [PositionComponent]);
|
||||
*
|
||||
* // 监听位置变化
|
||||
* dirtySystem.addListener({
|
||||
* flags: DirtyFlag.TRANSFORM_CHANGED,
|
||||
* callback: (data) => {
|
||||
* console.log('Entity position changed:', data.entity.name);
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* // 处理所有脏标记
|
||||
* dirtySystem.processDirtyEntities();
|
||||
* ```
|
||||
*/
|
||||
export class DirtyTrackingSystem {
|
||||
/** 脏实体映射表 */
|
||||
private _dirtyEntities = new Map<Entity, DirtyData>();
|
||||
|
||||
/** 脏标记监听器 */
|
||||
private _listeners: DirtyListener[] = [];
|
||||
|
||||
/** 性能统计 */
|
||||
private _stats = {
|
||||
totalMarkings: 0,
|
||||
totalCleanups: 0,
|
||||
frameCount: 0,
|
||||
totalDirtyPerFrame: 0
|
||||
};
|
||||
|
||||
/** 当前帧编号 */
|
||||
private _currentFrame = 0;
|
||||
|
||||
private _batchSize = 100;
|
||||
private _maxProcessingTime = 16;
|
||||
|
||||
/** 延迟处理队列 */
|
||||
private _processingQueue: DirtyData[] = [];
|
||||
private _isProcessing = false;
|
||||
|
||||
/**
|
||||
* 标记实体为脏状态
|
||||
*
|
||||
* @param entity 要标记的实体
|
||||
* @param flags 脏标记位
|
||||
* @param modifiedComponents 修改的组件类型列表
|
||||
*/
|
||||
public markDirty(entity: Entity, flags: DirtyFlag, modifiedComponents: ComponentType[] = []): void {
|
||||
this._stats.totalMarkings++;
|
||||
|
||||
let dirtyData = this._dirtyEntities.get(entity);
|
||||
if (!dirtyData) {
|
||||
dirtyData = {
|
||||
entity,
|
||||
flags: 0,
|
||||
modifiedComponents: new Set(),
|
||||
timestamp: performance.now(),
|
||||
frameNumber: this._currentFrame
|
||||
};
|
||||
this._dirtyEntities.set(entity, dirtyData);
|
||||
}
|
||||
|
||||
dirtyData.flags |= flags;
|
||||
dirtyData.timestamp = performance.now();
|
||||
dirtyData.frameNumber = this._currentFrame;
|
||||
|
||||
for (const componentType of modifiedComponents) {
|
||||
dirtyData.modifiedComponents.add(componentType);
|
||||
}
|
||||
|
||||
this.notifyListeners(dirtyData, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查实体是否有指定的脏标记
|
||||
*
|
||||
* @param entity 要检查的实体
|
||||
* @param flags 要检查的标记位
|
||||
* @returns 是否有指定的脏标记
|
||||
*/
|
||||
public isDirty(entity: Entity, flags: DirtyFlag = DirtyFlag.ALL): boolean {
|
||||
const dirtyData = this._dirtyEntities.get(entity);
|
||||
return dirtyData ? (dirtyData.flags & flags) !== 0 : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除实体的脏标记
|
||||
*
|
||||
* @param entity 要清除的实体
|
||||
* @param flags 要清除的标记位,默认清除所有
|
||||
*/
|
||||
public clearDirty(entity: Entity, flags: DirtyFlag = DirtyFlag.ALL): void {
|
||||
const dirtyData = this._dirtyEntities.get(entity);
|
||||
if (!dirtyData) return;
|
||||
|
||||
if (flags === DirtyFlag.ALL) {
|
||||
this._dirtyEntities.delete(entity);
|
||||
} else {
|
||||
dirtyData.flags &= ~flags;
|
||||
if (dirtyData.flags === 0) {
|
||||
this._dirtyEntities.delete(entity);
|
||||
}
|
||||
}
|
||||
|
||||
this._stats.totalCleanups++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有脏实体
|
||||
*
|
||||
* @param flags 过滤标记位,只返回包含指定标记的实体
|
||||
* @returns 脏实体数据数组
|
||||
*/
|
||||
public getDirtyEntities(flags: DirtyFlag = DirtyFlag.ALL): DirtyData[] {
|
||||
const result: DirtyData[] = [];
|
||||
|
||||
for (const dirtyData of this._dirtyEntities.values()) {
|
||||
if ((dirtyData.flags & flags) !== 0) {
|
||||
result.push(dirtyData);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量处理脏实体
|
||||
*
|
||||
* 使用时间分片的方式处理脏实体,避免单帧卡顿
|
||||
*/
|
||||
public processDirtyEntities(): void {
|
||||
if (this._isProcessing) return;
|
||||
|
||||
this._isProcessing = true;
|
||||
const startTime = performance.now();
|
||||
|
||||
if (this._processingQueue.length === 0) {
|
||||
this._processingQueue.push(...this._dirtyEntities.values());
|
||||
}
|
||||
|
||||
let processed = 0;
|
||||
while (this._processingQueue.length > 0 && processed < this._batchSize) {
|
||||
const elapsed = performance.now() - startTime;
|
||||
if (elapsed > this._maxProcessingTime) {
|
||||
break;
|
||||
}
|
||||
|
||||
const dirtyData = this._processingQueue.shift()!;
|
||||
this.processEntity(dirtyData);
|
||||
processed++;
|
||||
}
|
||||
|
||||
if (this._processingQueue.length === 0) {
|
||||
this._isProcessing = false;
|
||||
this.onFrameEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加脏标记监听器
|
||||
*
|
||||
* @param listener 监听器配置
|
||||
*/
|
||||
public addListener(listener: DirtyListener): void {
|
||||
this._listeners.push(listener);
|
||||
|
||||
this._listeners.sort((a, b) => (a.priority || 100) - (b.priority || 100));
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除脏标记监听器
|
||||
*
|
||||
* @param callback 要移除的回调函数
|
||||
*/
|
||||
public removeListener(callback: (dirtyData: DirtyData) => void): void {
|
||||
const index = this._listeners.findIndex(l => l.callback === callback);
|
||||
if (index !== -1) {
|
||||
this._listeners.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始新的帧
|
||||
*/
|
||||
public beginFrame(): void {
|
||||
this._currentFrame++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束当前帧
|
||||
*/
|
||||
public endFrame(): void {
|
||||
if (!this._isProcessing) {
|
||||
this.processDirtyEntities();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取统计信息
|
||||
*/
|
||||
public getStats(): DirtyStats {
|
||||
return {
|
||||
dirtyEntityCount: this._dirtyEntities.size,
|
||||
totalMarkings: this._stats.totalMarkings,
|
||||
totalCleanups: this._stats.totalCleanups,
|
||||
listenerCount: this._listeners.length,
|
||||
avgDirtyPerFrame: this._stats.frameCount > 0 ?
|
||||
this._stats.totalDirtyPerFrame / this._stats.frameCount : 0,
|
||||
estimatedMemoryUsage: this.estimateMemoryUsage()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有脏标记和统计信息
|
||||
*/
|
||||
public clear(): void {
|
||||
this._dirtyEntities.clear();
|
||||
this._processingQueue.length = 0;
|
||||
this._isProcessing = false;
|
||||
this._stats = {
|
||||
totalMarkings: 0,
|
||||
totalCleanups: 0,
|
||||
frameCount: 0,
|
||||
totalDirtyPerFrame: 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置批量处理参数
|
||||
*
|
||||
* @param batchSize 每次处理的最大实体数量
|
||||
* @param maxProcessingTime 每帧最大处理时间(毫秒)
|
||||
*/
|
||||
public configureBatchProcessing(batchSize: number, maxProcessingTime: number): void {
|
||||
this._batchSize = batchSize;
|
||||
this._maxProcessingTime = maxProcessingTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个脏实体
|
||||
*/
|
||||
private processEntity(dirtyData: DirtyData): void {
|
||||
for (const listener of this._listeners) {
|
||||
if ((dirtyData.flags & listener.flags) !== 0) {
|
||||
try {
|
||||
listener.callback(dirtyData);
|
||||
} catch (error) {
|
||||
console.error('Dirty listener error:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.clearDirty(dirtyData.entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知监听器
|
||||
*/
|
||||
private notifyListeners(dirtyData: DirtyData, newFlags: DirtyFlag): void {
|
||||
for (const listener of this._listeners) {
|
||||
if ((newFlags & listener.flags) !== 0) {
|
||||
try {
|
||||
listener.callback(dirtyData);
|
||||
} catch (error) {
|
||||
console.error('Dirty listener notification error:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 帧结束时的统计更新
|
||||
*/
|
||||
private onFrameEnd(): void {
|
||||
this._stats.frameCount++;
|
||||
this._stats.totalDirtyPerFrame += this._dirtyEntities.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 估算内存使用量
|
||||
*/
|
||||
private estimateMemoryUsage(): number {
|
||||
let usage = 0;
|
||||
|
||||
usage += this._dirtyEntities.size * 100;
|
||||
usage += this._listeners.length * 50;
|
||||
usage += this._processingQueue.length * 8;
|
||||
|
||||
return usage;
|
||||
}
|
||||
}
|
||||
674
source/src/ECS/Core/EntityManager.ts
Normal file
674
source/src/ECS/Core/EntityManager.ts
Normal file
@@ -0,0 +1,674 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType } from './ComponentStorage';
|
||||
import { IdentifierPool } from '../Utils/IdentifierPool';
|
||||
import { ComponentIndexManager, IndexType } from './ComponentIndex';
|
||||
import { ArchetypeSystem } from './ArchetypeSystem';
|
||||
import { DirtyTrackingSystem, DirtyFlag } from './DirtyTrackingSystem';
|
||||
import { EventBus } from './EventBus';
|
||||
import { ECSEventType } from '../CoreEvents';
|
||||
import { IEntityEventData, IComponentEventData } from '../../Types';
|
||||
|
||||
/**
|
||||
* 实体查询构建器
|
||||
*
|
||||
* 提供流式API来构建复杂的实体查询条件。支持组件过滤、标签过滤、状态过滤和自定义条件。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const results = entityManager.query()
|
||||
* .withAll(PositionComponent, HealthComponent)
|
||||
* .without(VelocityComponent)
|
||||
* .withTag(1)
|
||||
* .active()
|
||||
* .where(entity => entity.name.startsWith("Player"))
|
||||
* .execute();
|
||||
* ```
|
||||
*/
|
||||
export class EntityQueryBuilder {
|
||||
/** 必须包含的组件类型 */
|
||||
private _allComponents: ComponentType[] = [];
|
||||
/** 至少包含一个的组件类型 */
|
||||
private _anyComponents: ComponentType[] = [];
|
||||
/** 不能包含的组件类型 */
|
||||
private _withoutComponents: ComponentType[] = [];
|
||||
/** 必须包含的标签 */
|
||||
private _withTags: number[] = [];
|
||||
/** 不能包含的标签 */
|
||||
private _withoutTags: number[] = [];
|
||||
/** 是否只查询激活状态的实体 */
|
||||
private _activeOnly: boolean = false;
|
||||
/** 是否只查询启用状态的实体 */
|
||||
private _enabledOnly: boolean = false;
|
||||
/** 自定义过滤条件 */
|
||||
private _customPredicates: Array<(entity: Entity) => boolean> = [];
|
||||
|
||||
/**
|
||||
* 创建查询构建器实例
|
||||
* @param entityManager 实体管理器实例
|
||||
*/
|
||||
constructor(private entityManager: EntityManager) {}
|
||||
|
||||
/**
|
||||
* 添加必须包含的组件条件
|
||||
*
|
||||
* 返回的实体必须包含所有指定的组件类型。
|
||||
*
|
||||
* @param componentTypes 组件类型列表
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public withAll(...componentTypes: ComponentType[]): EntityQueryBuilder {
|
||||
this._allComponents.push(...componentTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加至少包含一个的组件条件
|
||||
*
|
||||
* 返回的实体必须至少包含其中一个指定的组件类型。
|
||||
*
|
||||
* @param componentTypes 组件类型列表
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public withAny(...componentTypes: ComponentType[]): EntityQueryBuilder {
|
||||
this._anyComponents.push(...componentTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加不能包含的组件条件
|
||||
*
|
||||
* 返回的实体不能包含任何指定的组件类型。
|
||||
*
|
||||
* @param componentTypes 组件类型列表
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public without(...componentTypes: ComponentType[]): EntityQueryBuilder {
|
||||
this._withoutComponents.push(...componentTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加必须包含的标签条件
|
||||
*
|
||||
* 返回的实体必须具有指定的标签。
|
||||
*
|
||||
* @param tag 标签值
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public withTag(tag: number): EntityQueryBuilder {
|
||||
this._withTags.push(tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加不能包含的标签条件
|
||||
*
|
||||
* 返回的实体不能具有指定的标签。
|
||||
*
|
||||
* @param tag 标签值
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public withoutTag(tag: number): EntityQueryBuilder {
|
||||
this._withoutTags.push(tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加激活状态过滤条件
|
||||
*
|
||||
* 返回的实体必须处于激活状态(active = true)。
|
||||
*
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public active(): EntityQueryBuilder {
|
||||
this._activeOnly = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加启用状态过滤条件
|
||||
*
|
||||
* 返回的实体必须处于启用状态(enabled = true)。
|
||||
*
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*/
|
||||
public enabled(): EntityQueryBuilder {
|
||||
this._enabledOnly = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加自定义过滤条件
|
||||
*
|
||||
* 允许用户定义复杂的过滤逻辑。
|
||||
*
|
||||
* @param predicate 自定义过滤函数,接收实体作为参数,返回布尔值
|
||||
* @returns 查询构建器实例,支持链式调用
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* .where(entity => entity.name.startsWith("Player"))
|
||||
* .where(entity => entity.components.length > 5)
|
||||
* ```
|
||||
*/
|
||||
public where(predicate: (entity: Entity) => boolean): EntityQueryBuilder {
|
||||
this._customPredicates.push(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询并返回所有匹配的实体
|
||||
*
|
||||
* @returns 符合所有查询条件的实体数组
|
||||
*/
|
||||
public execute(): Entity[] {
|
||||
let candidates: Entity[] = [];
|
||||
|
||||
if (this._allComponents.length > 0) {
|
||||
const indexResult = this.entityManager.queryWithComponentIndex(this._allComponents, 'AND');
|
||||
candidates = Array.from(indexResult);
|
||||
} else if (this._anyComponents.length > 0) {
|
||||
const indexResult = this.entityManager.queryWithComponentIndex(this._anyComponents, 'OR');
|
||||
candidates = Array.from(indexResult);
|
||||
} else {
|
||||
candidates = this.entityManager.getAllEntities();
|
||||
}
|
||||
|
||||
return candidates.filter(entity => this.matchesEntity(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询并返回第一个匹配的实体
|
||||
*
|
||||
* @returns 第一个符合查询条件的实体,如果没有找到则返回null
|
||||
*/
|
||||
public first(): Entity | null {
|
||||
const entities = this.entityManager.getAllEntities();
|
||||
return entities.find(entity => this.matchesEntity(entity)) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询并返回匹配实体的数量
|
||||
*
|
||||
* @returns 符合查询条件的实体数量
|
||||
*/
|
||||
public count(): number {
|
||||
const entities = this.entityManager.getAllEntities();
|
||||
return entities.filter(entity => this.matchesEntity(entity)).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对所有匹配的实体执行指定操作
|
||||
*
|
||||
* @param action 要执行的操作函数,接收匹配的实体作为参数
|
||||
*/
|
||||
public forEach(action: (entity: Entity) => void): void {
|
||||
const entities = this.entityManager.getAllEntities();
|
||||
entities.forEach(entity => {
|
||||
if (this.matchesEntity(entity)) {
|
||||
action(entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查实体是否匹配所有查询条件
|
||||
*
|
||||
* 按优先级顺序检查各种过滤条件,一旦发现不匹配立即返回false。
|
||||
*
|
||||
* @param entity 要检查的实体
|
||||
* @returns 实体是否匹配所有查询条件
|
||||
*/
|
||||
private matchesEntity(entity: Entity): boolean {
|
||||
// 检查激活状态
|
||||
if (this._activeOnly && !entity.active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查启用状态
|
||||
if (this._enabledOnly && !entity.enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查必须包含的组件
|
||||
if (this._allComponents.length > 0) {
|
||||
for (const componentType of this._allComponents) {
|
||||
if (!entity.hasComponent(componentType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查至少包含一个的组件
|
||||
if (this._anyComponents.length > 0) {
|
||||
let hasAny = false;
|
||||
for (const componentType of this._anyComponents) {
|
||||
if (entity.hasComponent(componentType)) {
|
||||
hasAny = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasAny) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查不能包含的组件
|
||||
if (this._withoutComponents.length > 0) {
|
||||
for (const componentType of this._withoutComponents) {
|
||||
if (entity.hasComponent(componentType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查必须包含的标签
|
||||
if (this._withTags.length > 0) {
|
||||
if (!this._withTags.includes(entity.tag)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查不能包含的标签
|
||||
if (this._withoutTags.length > 0) {
|
||||
if (this._withoutTags.includes(entity.tag)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查自定义条件
|
||||
if (this._customPredicates.length > 0) {
|
||||
for (const predicate of this._customPredicates) {
|
||||
if (!predicate(entity)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体管理器
|
||||
*
|
||||
* 提供统一的实体管理和查询机制,支持高效的实体操作。
|
||||
* 包括实体的创建、销毁、查询和索引管理功能。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const entityManager = new EntityManager();
|
||||
*
|
||||
* // 创建实体
|
||||
* const player = entityManager.createEntity("Player");
|
||||
*
|
||||
* // 查询实体
|
||||
* const playerEntity = entityManager.getEntityByName("Player");
|
||||
*
|
||||
* // 复杂查询
|
||||
* const results = entityManager.query()
|
||||
* .withAll(HealthComponent, PositionComponent)
|
||||
* .active()
|
||||
* .execute();
|
||||
* ```
|
||||
*/
|
||||
export class EntityManager {
|
||||
/** 主要实体存储,使用ID作为键 */
|
||||
private _entities: Map<number, Entity> = new Map();
|
||||
/** 按名称索引的实体映射 */
|
||||
private _entitiesByName: Map<string, Entity[]> = new Map();
|
||||
/** 按标签索引的实体映射 */
|
||||
private _entitiesByTag: Map<number, Entity[]> = new Map();
|
||||
/** 实体ID分配器 */
|
||||
private _identifierPool: IdentifierPool;
|
||||
/** 已销毁实体的ID集合 */
|
||||
private _destroyedEntities: Set<number> = new Set();
|
||||
|
||||
/** 性能优化系统 */
|
||||
private _componentIndexManager: ComponentIndexManager;
|
||||
private _archetypeSystem: ArchetypeSystem;
|
||||
private _dirtyTrackingSystem: DirtyTrackingSystem;
|
||||
/** 事件总线 */
|
||||
private _eventBus: EventBus;
|
||||
|
||||
/**
|
||||
* 创建实体管理器实例
|
||||
*
|
||||
* 初始化内部数据结构和ID分配器。
|
||||
*/
|
||||
constructor() {
|
||||
this._identifierPool = new IdentifierPool();
|
||||
|
||||
// 初始化性能优化系统
|
||||
this._componentIndexManager = new ComponentIndexManager(IndexType.HASH);
|
||||
this._archetypeSystem = new ArchetypeSystem();
|
||||
this._dirtyTrackingSystem = new DirtyTrackingSystem();
|
||||
this._eventBus = new EventBus(false);
|
||||
|
||||
// 设置Entity的静态事件总线引用
|
||||
Entity.eventBus = this._eventBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体总数
|
||||
*
|
||||
* @returns 当前管理的实体总数量
|
||||
*/
|
||||
public get entityCount(): number {
|
||||
return this._entities.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取激活状态的实体数量
|
||||
*
|
||||
* 只计算同时满足激活状态且未被销毁的实体。
|
||||
*
|
||||
* @returns 激活状态的实体数量
|
||||
*/
|
||||
public get activeEntityCount(): number {
|
||||
let count = 0;
|
||||
for (const entity of this._entities.values()) {
|
||||
if (entity.active && !entity.isDestroyed) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新实体
|
||||
*
|
||||
* 分配唯一ID并将实体添加到管理系统中,同时更新相关索引。
|
||||
*
|
||||
* @param name 实体名称,如果未指定则使用时间戳生成默认名称
|
||||
* @returns 创建的实体实例
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const player = entityManager.createEntity("Player");
|
||||
* const enemy = entityManager.createEntity(); // 使用默认名称
|
||||
* ```
|
||||
*/
|
||||
public createEntity(name: string = `Entity_${Date.now()}`): Entity {
|
||||
const id = this._identifierPool.checkOut();
|
||||
const entity = new Entity(name, id);
|
||||
|
||||
this._entities.set(id, entity);
|
||||
this.updateNameIndex(entity, true);
|
||||
this.updateTagIndex(entity, true);
|
||||
|
||||
this._componentIndexManager.addEntity(entity);
|
||||
this._archetypeSystem.addEntity(entity);
|
||||
this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED);
|
||||
|
||||
// 发射实体创建事件
|
||||
this._eventBus.emitEntityCreated({
|
||||
timestamp: Date.now(),
|
||||
source: 'EntityManager',
|
||||
entityId: entity.id,
|
||||
entityName: entity.name,
|
||||
entityTag: entity.tag?.toString()
|
||||
});
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁实体
|
||||
*
|
||||
* 支持通过实体对象、名称或ID来销毁实体。
|
||||
* 会清理所有相关索引并回收ID。
|
||||
*
|
||||
* @param entityOrId 要销毁的实体,可以是实体对象、名称字符串或ID数字
|
||||
* @returns 是否成功销毁实体
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 通过实体对象销毁
|
||||
* entityManager.destroyEntity(player);
|
||||
*
|
||||
* // 通过名称销毁
|
||||
* entityManager.destroyEntity("Enemy_1");
|
||||
*
|
||||
* // 通过ID销毁
|
||||
* entityManager.destroyEntity(123);
|
||||
* ```
|
||||
*/
|
||||
public destroyEntity(entityOrId: Entity | string | number): boolean {
|
||||
let entity: Entity | null = null;
|
||||
|
||||
if (typeof entityOrId === 'string') {
|
||||
entity = this.getEntityByName(entityOrId);
|
||||
} else if (typeof entityOrId === 'number') {
|
||||
entity = this._entities.get(entityOrId) || null;
|
||||
} else {
|
||||
entity = this._entities.get(entityOrId.id) || null;
|
||||
}
|
||||
|
||||
if (!entity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._destroyedEntities.add(entity.id);
|
||||
this.updateNameIndex(entity, false);
|
||||
this.updateTagIndex(entity, false);
|
||||
|
||||
this._componentIndexManager.removeEntity(entity);
|
||||
this._archetypeSystem.removeEntity(entity);
|
||||
this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_REMOVED);
|
||||
|
||||
// 发射实体销毁事件
|
||||
this._eventBus.emitEntityDestroyed({
|
||||
timestamp: Date.now(),
|
||||
source: 'EntityManager',
|
||||
entityId: entity.id,
|
||||
entityName: entity.name,
|
||||
entityTag: entity.tag?.toString()
|
||||
});
|
||||
|
||||
entity.destroy();
|
||||
this._entities.delete(entity.id);
|
||||
this._identifierPool.checkIn(entity.id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有实体
|
||||
*
|
||||
* 返回当前管理的所有实体的副本数组。
|
||||
*
|
||||
* @returns 所有实体的数组
|
||||
*/
|
||||
public getAllEntities(): Entity[] {
|
||||
return Array.from(this._entities.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取实体
|
||||
*
|
||||
* 支持字符串和数字类型的ID。
|
||||
*
|
||||
* @param id 实体ID,可以是字符串或数字
|
||||
* @returns 对应的实体,如果不存在则返回null
|
||||
*/
|
||||
public getEntity(id: string | number): Entity | null {
|
||||
const numId = typeof id === 'string' ? parseInt(id) : id;
|
||||
return this._entities.get(numId) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称获取实体
|
||||
*
|
||||
* 如果存在多个同名实体,返回第一个找到的实体。
|
||||
*
|
||||
* @param name 实体名称
|
||||
* @returns 匹配的实体,如果不存在则返回null
|
||||
*/
|
||||
public getEntityByName(name: string): Entity | null {
|
||||
const entities = this._entitiesByName.get(name);
|
||||
return entities && entities.length > 0 ? entities[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签获取实体列表
|
||||
*
|
||||
* 返回所有具有指定标签的实体。
|
||||
*
|
||||
* @param tag 标签值
|
||||
* @returns 具有指定标签的实体数组
|
||||
*/
|
||||
public getEntitiesByTag(tag: number): Entity[] {
|
||||
return [...(this._entitiesByTag.get(tag) || [])];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取包含指定组件的所有实体
|
||||
*
|
||||
* 遍历所有实体,查找包含指定组件类型的实体。
|
||||
*
|
||||
* @param componentType 组件类型
|
||||
* @returns 包含指定组件的实体数组
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const entitiesWithHealth = entityManager.getEntitiesWithComponent(HealthComponent);
|
||||
* ```
|
||||
*/
|
||||
public getEntitiesWithComponent<T extends Component>(componentType: ComponentType<T>): Entity[] {
|
||||
const indexResult = this._componentIndexManager.query(componentType);
|
||||
return Array.from(indexResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建查询构建器
|
||||
*
|
||||
* 返回一个新的查询构建器实例,用于构建复杂的实体查询。
|
||||
*
|
||||
* @returns 新的查询构建器实例
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const results = entityManager.query()
|
||||
* .withAll(PositionComponent, HealthComponent)
|
||||
* .without(VelocityComponent)
|
||||
* .active()
|
||||
* .execute();
|
||||
* ```
|
||||
*/
|
||||
public query(): EntityQueryBuilder {
|
||||
return new EntityQueryBuilder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用组件索引进行多组件查询
|
||||
*
|
||||
* @param componentTypes 组件类型数组
|
||||
* @param operation 查询操作:'AND' 或 'OR'
|
||||
* @returns 匹配的实体集合
|
||||
*/
|
||||
public queryWithComponentIndex(componentTypes: ComponentType[], operation: 'AND' | 'OR'): Set<Entity> {
|
||||
return this._componentIndexManager.queryMultiple(componentTypes, operation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记实体组件已修改
|
||||
*
|
||||
* @param entity 修改的实体
|
||||
* @param componentTypes 修改的组件类型
|
||||
*/
|
||||
public markEntityDirty(entity: Entity, componentTypes: ComponentType[]): void {
|
||||
this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_MODIFIED, componentTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取性能优化统计信息
|
||||
*/
|
||||
public getOptimizationStats(): any {
|
||||
return {
|
||||
componentIndex: this._componentIndexManager.getStats(),
|
||||
archetypeSystem: this._archetypeSystem.getAllArchetypes().map(a => ({
|
||||
id: a.id,
|
||||
componentTypes: a.componentTypes.map(t => t.name),
|
||||
entityCount: a.entities.length
|
||||
})),
|
||||
dirtyTracking: this._dirtyTrackingSystem.getStats()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取事件总线实例
|
||||
*
|
||||
* 允许外部代码监听和发射ECS相关事件。
|
||||
*
|
||||
* @returns 事件总线实例
|
||||
*/
|
||||
public get eventBus(): EventBus {
|
||||
return this._eventBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新名称索引
|
||||
*
|
||||
* 维护按名称查找实体的索引结构。支持添加和移除操作。
|
||||
*
|
||||
* @param entity 要更新索引的实体
|
||||
* @param isAdd true表示添加到索引,false表示从索引中移除
|
||||
*/
|
||||
private updateNameIndex(entity: Entity, isAdd: boolean): void {
|
||||
if (!entity.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAdd) {
|
||||
let entities = this._entitiesByName.get(entity.name);
|
||||
if (!entities) {
|
||||
entities = [];
|
||||
this._entitiesByName.set(entity.name, entities);
|
||||
}
|
||||
entities.push(entity);
|
||||
} else {
|
||||
const entities = this._entitiesByName.get(entity.name);
|
||||
if (entities) {
|
||||
const index = entities.indexOf(entity);
|
||||
if (index !== -1) {
|
||||
entities.splice(index, 1);
|
||||
if (entities.length === 0) {
|
||||
this._entitiesByName.delete(entity.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新标签索引
|
||||
*
|
||||
* 维护按标签查找实体的索引结构。支持添加和移除操作。
|
||||
*
|
||||
* @param entity 要更新索引的实体
|
||||
* @param isAdd true表示添加到索引,false表示从索引中移除
|
||||
*/
|
||||
private updateTagIndex(entity: Entity, isAdd: boolean): void {
|
||||
if (isAdd) {
|
||||
let entities = this._entitiesByTag.get(entity.tag);
|
||||
if (!entities) {
|
||||
entities = [];
|
||||
this._entitiesByTag.set(entity.tag, entities);
|
||||
}
|
||||
entities.push(entity);
|
||||
} else {
|
||||
const entities = this._entitiesByTag.get(entity.tag);
|
||||
if (entities) {
|
||||
const index = entities.indexOf(entity);
|
||||
if (index !== -1) {
|
||||
entities.splice(index, 1);
|
||||
if (entities.length === 0) {
|
||||
this._entitiesByTag.delete(entity.tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
497
source/src/ECS/Core/EventBus.ts
Normal file
497
source/src/ECS/Core/EventBus.ts
Normal file
@@ -0,0 +1,497 @@
|
||||
import {
|
||||
IEventBus,
|
||||
IEventListenerConfig,
|
||||
IEventStats,
|
||||
IEventData,
|
||||
IEntityEventData,
|
||||
IComponentEventData,
|
||||
ISystemEventData,
|
||||
ISceneEventData,
|
||||
IPerformanceEventData
|
||||
} from '../../Types';
|
||||
import {
|
||||
TypeSafeEventSystem,
|
||||
EventListenerConfig,
|
||||
EventStats
|
||||
} from './EventSystem';
|
||||
import {
|
||||
ECSEventType,
|
||||
EventPriority,
|
||||
EVENT_TYPES,
|
||||
EventTypeValidator
|
||||
} from '../CoreEvents';
|
||||
|
||||
/**
|
||||
* 增强的事件总线实现
|
||||
* 基于TypeSafeEventSystem,提供类型安全的事件发布订阅机制
|
||||
*/
|
||||
export class EventBus implements IEventBus {
|
||||
private eventSystem: TypeSafeEventSystem;
|
||||
private eventIdCounter = 0;
|
||||
private isDebugMode = false;
|
||||
|
||||
constructor(debugMode: boolean = false) {
|
||||
this.eventSystem = new TypeSafeEventSystem();
|
||||
this.isDebugMode = debugMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发射事件
|
||||
* @param eventType 事件类型
|
||||
* @param data 事件数据
|
||||
*/
|
||||
public emit<T>(eventType: string, data: T): void {
|
||||
this.validateEventType(eventType);
|
||||
|
||||
// 增强事件数据
|
||||
const enhancedData = this.enhanceEventData(eventType, data);
|
||||
|
||||
if (this.isDebugMode) {
|
||||
console.log(`[EventBus] Emitting event: ${eventType}`, enhancedData);
|
||||
}
|
||||
|
||||
this.eventSystem.emitSync(eventType, enhancedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步发射事件
|
||||
* @param eventType 事件类型
|
||||
* @param data 事件数据
|
||||
*/
|
||||
public async emitAsync<T>(eventType: string, data: T): Promise<void> {
|
||||
this.validateEventType(eventType);
|
||||
|
||||
// 增强事件数据
|
||||
const enhancedData = this.enhanceEventData(eventType, data);
|
||||
|
||||
if (this.isDebugMode) {
|
||||
console.log(`[EventBus] Emitting async event: ${eventType}`, enhancedData);
|
||||
}
|
||||
|
||||
await this.eventSystem.emit(eventType, enhancedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听事件
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件处理器
|
||||
* @param config 监听器配置
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
public on<T>(
|
||||
eventType: string,
|
||||
handler: (data: T) => void,
|
||||
config: IEventListenerConfig = {}
|
||||
): string {
|
||||
this.validateEventType(eventType);
|
||||
|
||||
const eventConfig: EventListenerConfig = {
|
||||
once: config.once || false,
|
||||
priority: config.priority || EventPriority.NORMAL,
|
||||
async: config.async || false,
|
||||
context: config.context
|
||||
};
|
||||
|
||||
if (this.isDebugMode) {
|
||||
console.log(`[EventBus] Adding listener for: ${eventType}`, eventConfig);
|
||||
}
|
||||
|
||||
return this.eventSystem.on(eventType, handler, eventConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听事件(一次性)
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件处理器
|
||||
* @param config 监听器配置
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
public once<T>(
|
||||
eventType: string,
|
||||
handler: (data: T) => void,
|
||||
config: IEventListenerConfig = {}
|
||||
): string {
|
||||
return this.on(eventType, handler, { ...config, once: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步监听事件
|
||||
* @param eventType 事件类型
|
||||
* @param handler 异步事件处理器
|
||||
* @param config 监听器配置
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
public onAsync<T>(
|
||||
eventType: string,
|
||||
handler: (data: T) => Promise<void>,
|
||||
config: IEventListenerConfig = {}
|
||||
): string {
|
||||
return this.on(eventType, handler as any, { ...config, async: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除事件监听器
|
||||
* @param eventType 事件类型
|
||||
* @param listenerId 监听器ID
|
||||
*/
|
||||
public off(eventType: string, listenerId: string): boolean {
|
||||
if (this.isDebugMode) {
|
||||
console.log(`[EventBus] Removing listener: ${listenerId} for event: ${eventType}`);
|
||||
}
|
||||
|
||||
return this.eventSystem.off(eventType, listenerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定事件类型的所有监听器
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
public offAll(eventType: string): void {
|
||||
if (this.isDebugMode) {
|
||||
console.log(`[EventBus] Removing all listeners for event: ${eventType}`);
|
||||
}
|
||||
|
||||
this.eventSystem.offAll(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有指定事件的监听器
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
public hasListeners(eventType: string): boolean {
|
||||
return this.eventSystem.hasListeners(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取事件统计信息
|
||||
* @param eventType 事件类型(可选)
|
||||
*/
|
||||
public getStats(eventType?: string): IEventStats | Map<string, IEventStats> {
|
||||
const stats = this.eventSystem.getStats(eventType);
|
||||
|
||||
if (stats instanceof Map) {
|
||||
// 转换Map中的每个EventStats为IEventStats
|
||||
const result = new Map<string, IEventStats>();
|
||||
stats.forEach((stat, key) => {
|
||||
result.set(key, this.convertEventStats(stat));
|
||||
});
|
||||
return result;
|
||||
} else {
|
||||
return this.convertEventStats(stats);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有监听器
|
||||
*/
|
||||
public clear(): void {
|
||||
if (this.isDebugMode) {
|
||||
console.log('[EventBus] Clearing all listeners');
|
||||
}
|
||||
|
||||
this.eventSystem.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用或禁用事件系统
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
public setEnabled(enabled: boolean): void {
|
||||
this.eventSystem.setEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置调试模式
|
||||
* @param debug 是否启用调试
|
||||
*/
|
||||
public setDebugMode(debug: boolean): void {
|
||||
this.isDebugMode = debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大监听器数量
|
||||
* @param max 最大数量
|
||||
*/
|
||||
public setMaxListeners(max: number): void {
|
||||
this.eventSystem.setMaxListeners(max);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取监听器数量
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
public getListenerCount(eventType: string): number {
|
||||
return this.eventSystem.getListenerCount(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置事件批处理配置
|
||||
* @param eventType 事件类型
|
||||
* @param batchSize 批处理大小
|
||||
* @param delay 延迟时间(毫秒)
|
||||
*/
|
||||
public setBatchConfig(eventType: string, batchSize: number, delay: number): void {
|
||||
this.eventSystem.setBatchConfig(eventType, {
|
||||
batchSize,
|
||||
delay,
|
||||
enabled: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新指定事件的批处理队列
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
public flushBatch(eventType: string): void {
|
||||
this.eventSystem.flushBatch(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置事件统计
|
||||
* @param eventType 事件类型(可选)
|
||||
*/
|
||||
public resetStats(eventType?: string): void {
|
||||
this.eventSystem.resetStats(eventType);
|
||||
}
|
||||
|
||||
// 便捷方法:发射预定义的ECS事件
|
||||
|
||||
/**
|
||||
* 发射实体创建事件
|
||||
* @param entityData 实体事件数据
|
||||
*/
|
||||
public emitEntityCreated(entityData: IEntityEventData): void {
|
||||
this.emit(ECSEventType.ENTITY_CREATED, entityData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发射实体销毁事件
|
||||
* @param entityData 实体事件数据
|
||||
*/
|
||||
public emitEntityDestroyed(entityData: IEntityEventData): void {
|
||||
this.emit(ECSEventType.ENTITY_DESTROYED, entityData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发射组件添加事件
|
||||
* @param componentData 组件事件数据
|
||||
*/
|
||||
public emitComponentAdded(componentData: IComponentEventData): void {
|
||||
this.emit(ECSEventType.COMPONENT_ADDED, componentData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发射组件移除事件
|
||||
* @param componentData 组件事件数据
|
||||
*/
|
||||
public emitComponentRemoved(componentData: IComponentEventData): void {
|
||||
this.emit(ECSEventType.COMPONENT_REMOVED, componentData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发射系统添加事件
|
||||
* @param systemData 系统事件数据
|
||||
*/
|
||||
public emitSystemAdded(systemData: ISystemEventData): void {
|
||||
this.emit(ECSEventType.SYSTEM_ADDED, systemData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发射系统移除事件
|
||||
* @param systemData 系统事件数据
|
||||
*/
|
||||
public emitSystemRemoved(systemData: ISystemEventData): void {
|
||||
this.emit(ECSEventType.SYSTEM_REMOVED, systemData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发射场景变化事件
|
||||
* @param sceneData 场景事件数据
|
||||
*/
|
||||
public emitSceneChanged(sceneData: ISceneEventData): void {
|
||||
this.emit(EVENT_TYPES.CORE.SCENE_CHANGED, sceneData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发射性能警告事件
|
||||
* @param performanceData 性能事件数据
|
||||
*/
|
||||
public emitPerformanceWarning(performanceData: IPerformanceEventData): void {
|
||||
this.emit(ECSEventType.PERFORMANCE_WARNING, performanceData);
|
||||
}
|
||||
|
||||
// 便捷方法:监听预定义的ECS事件
|
||||
|
||||
/**
|
||||
* 监听实体创建事件
|
||||
* @param handler 事件处理器
|
||||
* @param config 监听器配置
|
||||
*/
|
||||
public onEntityCreated(
|
||||
handler: (data: IEntityEventData) => void,
|
||||
config?: IEventListenerConfig
|
||||
): string {
|
||||
return this.on(ECSEventType.ENTITY_CREATED, handler, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听组件添加事件
|
||||
* @param handler 事件处理器
|
||||
* @param config 监听器配置
|
||||
*/
|
||||
public onComponentAdded(
|
||||
handler: (data: IComponentEventData) => void,
|
||||
config?: IEventListenerConfig
|
||||
): string {
|
||||
return this.on(ECSEventType.COMPONENT_ADDED, handler, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听系统错误事件
|
||||
* @param handler 事件处理器
|
||||
* @param config 监听器配置
|
||||
*/
|
||||
public onSystemError(
|
||||
handler: (data: ISystemEventData) => void,
|
||||
config?: IEventListenerConfig
|
||||
): string {
|
||||
return this.on(ECSEventType.SYSTEM_ERROR, handler, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听性能警告事件
|
||||
* @param handler 事件处理器
|
||||
* @param config 监听器配置
|
||||
*/
|
||||
public onPerformanceWarning(
|
||||
handler: (data: IPerformanceEventData) => void,
|
||||
config?: IEventListenerConfig
|
||||
): string {
|
||||
return this.on(ECSEventType.PERFORMANCE_WARNING, handler, config);
|
||||
}
|
||||
|
||||
// 私有方法
|
||||
|
||||
/**
|
||||
* 验证事件类型
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
private validateEventType(eventType: string): void {
|
||||
if (!EventTypeValidator.isValid(eventType)) {
|
||||
if (this.isDebugMode) {
|
||||
console.warn(`[EventBus] Unknown event type: ${eventType}`);
|
||||
}
|
||||
// 在调试模式下添加自定义事件类型
|
||||
if (this.isDebugMode) {
|
||||
EventTypeValidator.addCustomType(eventType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 增强事件数据
|
||||
* @param eventType 事件类型
|
||||
* @param data 原始数据
|
||||
*/
|
||||
private enhanceEventData<T>(eventType: string, data: T): T & IEventData {
|
||||
const enhanced = data as T & IEventData;
|
||||
|
||||
// 如果数据还没有基础事件属性,添加它们
|
||||
if (!enhanced.timestamp) {
|
||||
enhanced.timestamp = Date.now();
|
||||
}
|
||||
if (!enhanced.eventId) {
|
||||
enhanced.eventId = `${eventType}_${++this.eventIdCounter}`;
|
||||
}
|
||||
if (!enhanced.source) {
|
||||
enhanced.source = 'EventBus';
|
||||
}
|
||||
|
||||
return enhanced;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换EventStats为IEventStats
|
||||
* @param stats EventStats实例
|
||||
*/
|
||||
private convertEventStats(stats: EventStats): IEventStats {
|
||||
return {
|
||||
eventType: stats.eventType,
|
||||
listenerCount: stats.listenerCount,
|
||||
triggerCount: stats.triggerCount,
|
||||
totalExecutionTime: stats.totalExecutionTime,
|
||||
averageExecutionTime: stats.averageExecutionTime,
|
||||
lastTriggerTime: stats.lastTriggerTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局事件总线实例
|
||||
* 提供全局访问的事件总线
|
||||
*/
|
||||
export class GlobalEventBus {
|
||||
private static instance: EventBus;
|
||||
|
||||
/**
|
||||
* 获取全局事件总线实例
|
||||
* @param debugMode 是否启用调试模式
|
||||
*/
|
||||
public static getInstance(debugMode: boolean = false): EventBus {
|
||||
if (!this.instance) {
|
||||
this.instance = new EventBus(debugMode);
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置全局事件总线实例
|
||||
* @param debugMode 是否启用调试模式
|
||||
*/
|
||||
public static reset(debugMode: boolean = false): EventBus {
|
||||
if (this.instance) {
|
||||
this.instance.clear();
|
||||
}
|
||||
this.instance = new EventBus(debugMode);
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件装饰器工厂
|
||||
* 用于自动注册事件监听器
|
||||
*/
|
||||
export function EventHandler(eventType: string, config: IEventListenerConfig = {}) {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
const originalMethod = descriptor.value;
|
||||
|
||||
// 在类实例化时自动注册监听器
|
||||
const initMethod = target.constructor.prototype.initEventListeners || function() {};
|
||||
target.constructor.prototype.initEventListeners = function() {
|
||||
initMethod.call(this);
|
||||
const eventBus = GlobalEventBus.getInstance();
|
||||
eventBus.on(eventType, originalMethod.bind(this), config);
|
||||
};
|
||||
|
||||
return descriptor;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步事件装饰器工厂
|
||||
* 用于自动注册异步事件监听器
|
||||
*/
|
||||
export function AsyncEventHandler(eventType: string, config: IEventListenerConfig = {}) {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
const originalMethod = descriptor.value;
|
||||
|
||||
const initMethod = target.constructor.prototype.initEventListeners || function() {};
|
||||
target.constructor.prototype.initEventListeners = function() {
|
||||
initMethod.call(this);
|
||||
const eventBus = GlobalEventBus.getInstance();
|
||||
eventBus.onAsync(eventType, originalMethod.bind(this), config);
|
||||
};
|
||||
|
||||
return descriptor;
|
||||
};
|
||||
}
|
||||
1
source/src/performance.ts
Normal file
1
source/src/performance.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user