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