使用Lerna 和 monorepo管理项目结构
This commit is contained in:
519
packages/core/src/Core.ts
Normal file
519
packages/core/src/Core.ts
Normal file
@@ -0,0 +1,519 @@
|
||||
import { GlobalManager } from './Utils/GlobalManager';
|
||||
import { TimerManager } from './Utils/Timers/TimerManager';
|
||||
import { ITimer } from './Utils/Timers/ITimer';
|
||||
import { Timer } from './Utils/Timers/Timer';
|
||||
import { Time } from './Utils/Time';
|
||||
import { PerformanceMonitor } from './Utils/PerformanceMonitor';
|
||||
import { PoolManager } from './Utils/Pool';
|
||||
import { ECSFluentAPI, createECSAPI } from './ECS/Core/FluentAPI';
|
||||
import { Scene } from './ECS/Scene';
|
||||
import { DebugManager } from './Utils/Debug';
|
||||
import { ICoreConfig, IECSDebugConfig } from './Types';
|
||||
import { BigIntFactory, EnvironmentInfo } from './ECS/Utils/BigIntCompatibility';
|
||||
|
||||
/**
|
||||
* 游戏引擎核心类
|
||||
*
|
||||
* 负责管理游戏的生命周期、场景切换、全局管理器和定时器系统。
|
||||
* 提供统一的游戏循环管理。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 创建核心实例
|
||||
* const core = Core.create(true);
|
||||
*
|
||||
* // 设置场景
|
||||
* Core.scene = new MyScene();
|
||||
*
|
||||
* // 在游戏循环中更新(Laya引擎示例)
|
||||
* Laya.timer.frameLoop(1, this, () => {
|
||||
* const deltaTime = Laya.timer.delta / 1000;
|
||||
* Core.update(deltaTime);
|
||||
* });
|
||||
*
|
||||
* // 调度定时器
|
||||
* Core.schedule(1.0, false, null, (timer) => {
|
||||
* console.log("1秒后执行");
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export class Core {
|
||||
/**
|
||||
* 游戏暂停状态
|
||||
*
|
||||
* 当设置为true时,游戏循环将暂停执行。
|
||||
*/
|
||||
public static paused = false;
|
||||
|
||||
/**
|
||||
* 全局核心实例
|
||||
*/
|
||||
private static _instance: Core;
|
||||
|
||||
/**
|
||||
* 实体系统启用状态
|
||||
*
|
||||
* 控制是否启用ECS实体系统功能。
|
||||
*/
|
||||
public static entitySystemsEnabled: boolean;
|
||||
|
||||
/**
|
||||
* 调试模式标志
|
||||
*
|
||||
* 在调试模式下会启用额外的性能监控和错误检查。
|
||||
*/
|
||||
public readonly debug: boolean;
|
||||
|
||||
/**
|
||||
* 待切换的场景
|
||||
*
|
||||
* 存储下一帧要切换到的场景实例。
|
||||
*/
|
||||
public _nextScene: Scene | null = null;
|
||||
|
||||
/**
|
||||
* 全局管理器集合
|
||||
*
|
||||
* 存储所有注册的全局管理器实例。
|
||||
*/
|
||||
public _globalManagers: GlobalManager[] = [];
|
||||
|
||||
/**
|
||||
* 定时器管理器
|
||||
*
|
||||
* 负责管理所有的游戏定时器。
|
||||
*/
|
||||
public _timerManager: TimerManager;
|
||||
|
||||
/**
|
||||
* 性能监控器
|
||||
*
|
||||
* 监控游戏性能并提供优化建议。
|
||||
*/
|
||||
public _performanceMonitor: PerformanceMonitor;
|
||||
|
||||
/**
|
||||
* 对象池管理器
|
||||
*
|
||||
* 管理所有对象池的生命周期。
|
||||
*/
|
||||
public _poolManager: PoolManager;
|
||||
|
||||
/**
|
||||
* ECS流式API
|
||||
*
|
||||
* 提供便捷的ECS操作接口。
|
||||
*/
|
||||
public _ecsAPI?: ECSFluentAPI;
|
||||
|
||||
/**
|
||||
* 当前活动场景
|
||||
*/
|
||||
public _scene?: Scene;
|
||||
|
||||
/**
|
||||
* 调试管理器
|
||||
*
|
||||
* 负责收集和发送调试数据。
|
||||
*/
|
||||
public _debugManager?: DebugManager;
|
||||
|
||||
/**
|
||||
* Core配置
|
||||
*/
|
||||
private _config: ICoreConfig;
|
||||
|
||||
/**
|
||||
* 兼容性信息
|
||||
*/
|
||||
private _environmentInfo: EnvironmentInfo;
|
||||
|
||||
/**
|
||||
* 创建核心实例
|
||||
*
|
||||
* @param config - Core配置对象
|
||||
*/
|
||||
private constructor(config: ICoreConfig = {}) {
|
||||
Core._instance = this;
|
||||
|
||||
// 保存配置
|
||||
this._config = {
|
||||
debug: true,
|
||||
enableEntitySystems: true,
|
||||
...config
|
||||
};
|
||||
|
||||
// 检测环境兼容性
|
||||
this._environmentInfo = BigIntFactory.getEnvironmentInfo();
|
||||
|
||||
// 初始化管理器
|
||||
this._timerManager = new TimerManager();
|
||||
Core.registerGlobalManager(this._timerManager);
|
||||
|
||||
// 初始化性能监控器
|
||||
this._performanceMonitor = PerformanceMonitor.instance;
|
||||
|
||||
// 在调试模式下启用性能监控
|
||||
if (this._config.debug) {
|
||||
this._performanceMonitor.enable();
|
||||
}
|
||||
|
||||
// 初始化对象池管理器
|
||||
this._poolManager = PoolManager.getInstance();
|
||||
|
||||
Core.entitySystemsEnabled = this._config.enableEntitySystems ?? true;
|
||||
this.debug = this._config.debug ?? true;
|
||||
|
||||
// 初始化调试管理器
|
||||
if (this._config.debugConfig?.enabled) {
|
||||
this._debugManager = new DebugManager(this, this._config.debugConfig);
|
||||
}
|
||||
|
||||
// 在调试模式下显示兼容性信息
|
||||
if (this._config.debug) {
|
||||
this.logCompatibilityInfo();
|
||||
}
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取核心实例
|
||||
*
|
||||
* @returns 全局核心实例
|
||||
*/
|
||||
public static get Instance() {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前活动的场景
|
||||
*
|
||||
* @returns 当前场景实例,如果没有则返回null
|
||||
*/
|
||||
public static get scene(): Scene | null {
|
||||
if (!this._instance)
|
||||
return null;
|
||||
return this._instance._scene || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前活动的场景
|
||||
*
|
||||
* 如果当前没有场景,会立即切换;否则会在下一帧切换。
|
||||
*
|
||||
* @param value - 要设置的场景实例
|
||||
* @throws {Error} 当场景为空时抛出错误
|
||||
*/
|
||||
public static set scene(value: Scene | null) {
|
||||
if (!value) return;
|
||||
|
||||
if (!value) {
|
||||
throw new Error("场景不能为空");
|
||||
}
|
||||
|
||||
if (this._instance._scene == null) {
|
||||
this._instance._scene = value;
|
||||
this._instance.onSceneChanged();
|
||||
this._instance._scene.begin();
|
||||
} else {
|
||||
this._instance._nextScene = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Core实例
|
||||
*
|
||||
* 如果实例已存在,则返回现有实例。
|
||||
*
|
||||
* @param config - Core配置,也可以直接传入boolean表示debug模式(向后兼容)
|
||||
* @returns Core实例
|
||||
*/
|
||||
public static create(config: ICoreConfig | boolean = true): Core {
|
||||
if (this._instance == null) {
|
||||
// 向后兼容:如果传入boolean,转换为配置对象
|
||||
const coreConfig: ICoreConfig = typeof config === 'boolean'
|
||||
? { debug: config, enableEntitySystems: true }
|
||||
: config;
|
||||
this._instance = new Core(coreConfig);
|
||||
}
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新游戏逻辑
|
||||
*
|
||||
* 此方法应该在游戏引擎的更新循环中调用。
|
||||
*
|
||||
* @param deltaTime - 外部引擎提供的帧时间间隔(秒)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Laya引擎
|
||||
* Laya.timer.frameLoop(1, this, () => {
|
||||
* const deltaTime = Laya.timer.delta / 1000;
|
||||
* Core.update(deltaTime);
|
||||
* });
|
||||
*
|
||||
* // Cocos Creator
|
||||
* update(deltaTime: number) {
|
||||
* Core.update(deltaTime);
|
||||
* }
|
||||
*
|
||||
|
||||
* ```
|
||||
*/
|
||||
public static update(deltaTime: number): void {
|
||||
if (!this._instance) {
|
||||
console.warn("Core实例未创建,请先调用Core.create()");
|
||||
return;
|
||||
}
|
||||
|
||||
this._instance.updateInternal(deltaTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册全局管理器
|
||||
*
|
||||
* 将管理器添加到全局管理器列表中,并启用它。
|
||||
*
|
||||
* @param manager - 要注册的全局管理器
|
||||
*/
|
||||
public static registerGlobalManager(manager: GlobalManager) {
|
||||
this._instance._globalManagers.push(manager);
|
||||
manager.enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销全局管理器
|
||||
*
|
||||
* 从全局管理器列表中移除管理器,并禁用它。
|
||||
*
|
||||
* @param manager - 要注销的全局管理器
|
||||
*/
|
||||
public static unregisterGlobalManager(manager: GlobalManager) {
|
||||
this._instance._globalManagers.splice(this._instance._globalManagers.indexOf(manager), 1);
|
||||
manager.enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的全局管理器
|
||||
*
|
||||
* @param type - 管理器类型构造函数
|
||||
* @returns 管理器实例,如果未找到则返回null
|
||||
*/
|
||||
public static getGlobalManager<T extends GlobalManager>(type: new (...args: unknown[]) => T): T | null {
|
||||
for (const manager of this._instance._globalManagers) {
|
||||
if (manager instanceof type)
|
||||
return manager as T;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调度定时器
|
||||
*
|
||||
* 创建一个定时器,在指定时间后执行回调函数。
|
||||
*
|
||||
* @param timeInSeconds - 延迟时间(秒)
|
||||
* @param repeats - 是否重复执行,默认为false
|
||||
* @param context - 回调函数的上下文,默认为null
|
||||
* @param onTime - 定时器触发时的回调函数
|
||||
* @returns 创建的定时器实例
|
||||
*/
|
||||
public static schedule<TContext = unknown>(timeInSeconds: number, repeats: boolean = false, context: TContext = null as any, onTime: (timer: ITimer<TContext>) => void): Timer<TContext> {
|
||||
return this._instance._timerManager.schedule(timeInSeconds, repeats, context, onTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ECS流式API
|
||||
*
|
||||
* @returns ECS API实例,如果未初始化则返回null
|
||||
*/
|
||||
public static get ecsAPI(): ECSFluentAPI | null {
|
||||
return this._instance?._ecsAPI || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用调试功能
|
||||
*
|
||||
* @param config 调试配置
|
||||
*/
|
||||
public static enableDebug(config: IECSDebugConfig): void {
|
||||
if (!this._instance) {
|
||||
console.warn("Core实例未创建,请先调用Core.create()");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._instance._debugManager) {
|
||||
this._instance._debugManager.updateConfig(config);
|
||||
} else {
|
||||
this._instance._debugManager = new DebugManager(this._instance, config);
|
||||
}
|
||||
|
||||
// 更新Core配置
|
||||
this._instance._config.debugConfig = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用调试功能
|
||||
*/
|
||||
public static disableDebug(): void {
|
||||
if (!this._instance) return;
|
||||
|
||||
if (this._instance._debugManager) {
|
||||
this._instance._debugManager.stop();
|
||||
this._instance._debugManager = undefined;
|
||||
}
|
||||
|
||||
// 更新Core配置
|
||||
if (this._instance._config.debugConfig) {
|
||||
this._instance._config.debugConfig.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取调试数据
|
||||
*
|
||||
* @returns 当前调试数据,如果调试未启用则返回null
|
||||
*/
|
||||
public static getDebugData(): any {
|
||||
if (!this._instance?._debugManager) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this._instance._debugManager.getDebugData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查调试是否启用
|
||||
*
|
||||
* @returns 调试状态
|
||||
*/
|
||||
public static get isDebugEnabled(): boolean {
|
||||
return this._instance?._config.debugConfig?.enabled || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取环境兼容性信息
|
||||
*
|
||||
* @returns 环境兼容性信息
|
||||
*/
|
||||
public static getEnvironmentInfo(): EnvironmentInfo | null {
|
||||
return this._instance?._environmentInfo || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查BigInt是否支持
|
||||
*
|
||||
* @returns 是否支持BigInt
|
||||
*/
|
||||
public static get supportsBigInt(): boolean {
|
||||
return this._instance?._environmentInfo.supportsBigInt || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景切换回调
|
||||
*
|
||||
* 在场景切换时调用,用于重置时间系统等。
|
||||
*/
|
||||
public onSceneChanged() {
|
||||
Time.sceneChanged();
|
||||
|
||||
// 初始化ECS API(如果场景支持)
|
||||
if (this._scene && typeof (this._scene as any).querySystem !== 'undefined') {
|
||||
const scene = this._scene as any;
|
||||
this._ecsAPI = createECSAPI(scene, scene.querySystem, scene.eventSystem);
|
||||
}
|
||||
|
||||
// 通知调试管理器场景已变更
|
||||
if (this._debugManager) {
|
||||
this._debugManager.onSceneChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化核心系统
|
||||
*
|
||||
* 执行核心系统的初始化逻辑。
|
||||
*/
|
||||
protected initialize() {
|
||||
// 核心系统初始化
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录兼容性信息
|
||||
*
|
||||
* 在控制台输出当前环境的兼容性信息和建议。
|
||||
*/
|
||||
private logCompatibilityInfo(): void {
|
||||
const info = this._environmentInfo;
|
||||
|
||||
console.log('ECS Framework 兼容性检测结果:');
|
||||
console.log(` 环境: ${info.environment}`);
|
||||
console.log(` JavaScript引擎: ${info.jsEngine}`);
|
||||
console.log(` BigInt支持: ${info.supportsBigInt ? '支持' : '不支持'}`);
|
||||
|
||||
if (!info.supportsBigInt) {
|
||||
console.warn('BigInt兼容模式已启用');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部更新方法
|
||||
*
|
||||
* @param deltaTime - 帧时间间隔(秒)
|
||||
*/
|
||||
private updateInternal(deltaTime: number): void {
|
||||
if (Core.paused) return;
|
||||
|
||||
// 开始性能监控
|
||||
const frameStartTime = this._performanceMonitor.startMonitoring('Core.update');
|
||||
|
||||
// 更新时间系统
|
||||
Time.update(deltaTime);
|
||||
|
||||
// 更新FPS监控(如果性能监控器支持)
|
||||
if (typeof (this._performanceMonitor as any).updateFPS === 'function') {
|
||||
(this._performanceMonitor as any).updateFPS(Time.deltaTime);
|
||||
}
|
||||
|
||||
// 更新全局管理器
|
||||
const managersStartTime = this._performanceMonitor.startMonitoring('GlobalManagers.update');
|
||||
for (const globalManager of this._globalManagers) {
|
||||
if (globalManager.enabled)
|
||||
globalManager.update();
|
||||
}
|
||||
this._performanceMonitor.endMonitoring('GlobalManagers.update', managersStartTime, this._globalManagers.length);
|
||||
|
||||
// 更新对象池管理器
|
||||
this._poolManager.update();
|
||||
|
||||
// 处理场景切换
|
||||
if (this._nextScene != null) {
|
||||
if (this._scene != null)
|
||||
this._scene.end();
|
||||
|
||||
this._scene = this._nextScene;
|
||||
this._nextScene = null;
|
||||
this.onSceneChanged();
|
||||
this._scene.begin();
|
||||
}
|
||||
|
||||
// 更新当前场景
|
||||
if (this._scene != null && this._scene.update) {
|
||||
const sceneStartTime = this._performanceMonitor.startMonitoring('Scene.update');
|
||||
this._scene.update();
|
||||
const entityCount = (this._scene as any).entities?.count || 0;
|
||||
this._performanceMonitor.endMonitoring('Scene.update', sceneStartTime, entityCount);
|
||||
}
|
||||
|
||||
// 更新调试管理器(基于FPS的数据发送)
|
||||
if (this._debugManager) {
|
||||
this._debugManager.onFrameUpdate(deltaTime);
|
||||
}
|
||||
|
||||
// 结束性能监控
|
||||
this._performanceMonitor.endMonitoring('Core.update', frameStartTime);
|
||||
}
|
||||
}
|
||||
159
packages/core/src/ECS/Component.ts
Normal file
159
packages/core/src/ECS/Component.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import type { IComponent } from '../Types';
|
||||
|
||||
/**
|
||||
* 游戏组件基类
|
||||
*
|
||||
* ECS架构中的组件(Component),用于实现具体的游戏功能。
|
||||
* 组件包含数据和行为,可以被添加到实体上以扩展实体的功能。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* class HealthComponent extends Component {
|
||||
* public health: number = 100;
|
||||
*
|
||||
* public takeDamage(damage: number): void {
|
||||
* this.health -= damage;
|
||||
* if (this.health <= 0) {
|
||||
* this.entity.destroy();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export abstract class Component implements IComponent {
|
||||
/**
|
||||
* 组件ID生成器
|
||||
*
|
||||
* 用于为每个组件分配唯一的ID。
|
||||
*/
|
||||
public static _idGenerator: number = 0;
|
||||
|
||||
/**
|
||||
* 组件唯一标识符
|
||||
*
|
||||
* 在整个游戏生命周期中唯一的数字ID。
|
||||
*/
|
||||
public readonly id: number;
|
||||
|
||||
/**
|
||||
* 组件所属的实体
|
||||
*
|
||||
* 指向拥有此组件的实体实例。
|
||||
*/
|
||||
public entity!: Entity;
|
||||
|
||||
/**
|
||||
* 组件启用状态
|
||||
*
|
||||
* 控制组件是否参与更新循环。
|
||||
*/
|
||||
private _enabled: boolean = true;
|
||||
|
||||
/**
|
||||
* 更新顺序
|
||||
*
|
||||
* 决定组件在更新循环中的执行顺序。
|
||||
*/
|
||||
private _updateOrder: number = 0;
|
||||
|
||||
/**
|
||||
* 创建组件实例
|
||||
*
|
||||
* 自动分配唯一ID给组件。
|
||||
*/
|
||||
constructor() {
|
||||
this.id = Component._idGenerator++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件启用状态
|
||||
*
|
||||
* 组件的实际启用状态取决于自身状态和所属实体的状态。
|
||||
*
|
||||
* @returns 如果组件和所属实体都启用则返回true
|
||||
*/
|
||||
public get enabled(): boolean {
|
||||
return this.entity ? this.entity.enabled && this._enabled : this._enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置组件启用状态
|
||||
*
|
||||
* 当状态改变时会触发相应的生命周期回调。
|
||||
*
|
||||
* @param value - 新的启用状态
|
||||
*/
|
||||
public set enabled(value: boolean) {
|
||||
if (this._enabled !== value) {
|
||||
this._enabled = value;
|
||||
if (this._enabled) {
|
||||
this.onEnabled();
|
||||
} else {
|
||||
this.onDisabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取更新顺序
|
||||
*
|
||||
* @returns 组件的更新顺序值
|
||||
*/
|
||||
public get updateOrder(): number {
|
||||
return this._updateOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置更新顺序
|
||||
*
|
||||
* @param value - 新的更新顺序值
|
||||
*/
|
||||
public set updateOrder(value: number) {
|
||||
this._updateOrder = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件添加到实体时的回调
|
||||
*
|
||||
* 当组件被添加到实体时调用,可以在此方法中进行初始化操作。
|
||||
*/
|
||||
public onAddedToEntity(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件从实体移除时的回调
|
||||
*
|
||||
* 当组件从实体中移除时调用,可以在此方法中进行清理操作。
|
||||
*/
|
||||
public onRemovedFromEntity(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件启用时的回调
|
||||
*
|
||||
* 当组件被启用时调用。
|
||||
*/
|
||||
public onEnabled(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件禁用时的回调
|
||||
*
|
||||
* 当组件被禁用时调用。
|
||||
*/
|
||||
public onDisabled(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新组件
|
||||
*
|
||||
* 每帧调用,用于更新组件的逻辑。
|
||||
* 子类应该重写此方法来实现具体的更新逻辑。
|
||||
*/
|
||||
public update(): void {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 避免循环引用,在文件末尾导入Entity
|
||||
import type { Entity } from './Entity';
|
||||
27
packages/core/src/ECS/Components/IUpdatable.ts
Normal file
27
packages/core/src/ECS/Components/IUpdatable.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 可更新接口
|
||||
* 当添加到组件时,只要组件和实体被启用,就会在每帧调用update方法
|
||||
*/
|
||||
export interface IUpdatable {
|
||||
enabled: boolean;
|
||||
updateOrder: number;
|
||||
update(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于比较组件更新排序的比较器
|
||||
*/
|
||||
export class IUpdatableComparer {
|
||||
public compare(a: IUpdatable, b: IUpdatable): number {
|
||||
return a.updateOrder - b.updateOrder;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查对象是否实现了IUpdatable接口
|
||||
* @param props 要检查的对象
|
||||
* @returns 如果实现了IUpdatable接口返回true,否则返回false
|
||||
*/
|
||||
export function isIUpdatable(props: any): props is IUpdatable {
|
||||
return typeof (props as IUpdatable)['update'] !== 'undefined';
|
||||
}
|
||||
64
packages/core/src/ECS/Components/SceneComponent.ts
Normal file
64
packages/core/src/ECS/Components/SceneComponent.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type { Scene } from '../Scene';
|
||||
|
||||
/**
|
||||
* 场景组件基类
|
||||
* 附加到场景的组件,用于实现场景级别的功能
|
||||
*/
|
||||
export class SceneComponent {
|
||||
/** 组件所属的场景 */
|
||||
public scene!: Scene;
|
||||
/** 更新顺序 */
|
||||
public updateOrder: number = 0;
|
||||
/** 是否启用 */
|
||||
private _enabled: boolean = true;
|
||||
|
||||
/** 获取是否启用 */
|
||||
public get enabled(): boolean {
|
||||
return this._enabled;
|
||||
}
|
||||
|
||||
/** 设置是否启用 */
|
||||
public set enabled(value: boolean) {
|
||||
if (this._enabled !== value) {
|
||||
this._enabled = value;
|
||||
if (this._enabled) {
|
||||
this.onEnabled();
|
||||
} else {
|
||||
this.onDisabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当组件启用时调用
|
||||
*/
|
||||
public onEnabled(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当组件禁用时调用
|
||||
*/
|
||||
public onDisabled(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当组件从场景中移除时调用
|
||||
*/
|
||||
public onRemovedFromScene(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每帧更新
|
||||
*/
|
||||
public update(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较组件的更新顺序
|
||||
* @param other 其他组件
|
||||
* @returns 比较结果
|
||||
*/
|
||||
public compare(other: SceneComponent): number {
|
||||
return this.updateOrder - other.updateOrder;
|
||||
}
|
||||
}
|
||||
261
packages/core/src/ECS/Core/ArchetypeSystem.ts
Normal file
261
packages/core/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();
|
||||
}
|
||||
}
|
||||
513
packages/core/src/ECS/Core/ComponentIndex.ts
Normal file
513
packages/core/src/ECS/Core/ComponentIndex.ts
Normal file
@@ -0,0 +1,513 @@
|
||||
import { Entity } from '../Entity';
|
||||
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();
|
||||
|
||||
private _setPool: Set<Entity>[] = [];
|
||||
private _componentTypeSetPool: Set<ComponentType>[] = [];
|
||||
|
||||
public addEntity(entity: Entity): void {
|
||||
if (entity.components.length === 0) {
|
||||
const componentTypes = this._componentTypeSetPool.pop() || new Set<ComponentType>();
|
||||
componentTypes.clear();
|
||||
this._entityToComponents.set(entity, componentTypes);
|
||||
this._lastUpdated = Date.now();
|
||||
return;
|
||||
}
|
||||
|
||||
const componentTypes = this._componentTypeSetPool.pop() || new Set<ComponentType>();
|
||||
componentTypes.clear();
|
||||
|
||||
for (const component of entity.components) {
|
||||
const componentType = component.constructor as ComponentType;
|
||||
componentTypes.add(componentType);
|
||||
|
||||
let entities = this._componentToEntities.get(componentType);
|
||||
if (!entities) {
|
||||
entities = this._setPool.pop() || new Set<Entity>();
|
||||
entities.clear();
|
||||
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);
|
||||
if (this._setPool.length < 50) {
|
||||
entities.clear();
|
||||
this._setPool.push(entities);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._entityToComponents.delete(entity);
|
||||
|
||||
if (this._componentTypeSetPool.length < 50) {
|
||||
componentTypes.clear();
|
||||
this._componentTypeSetPool.push(componentTypes);
|
||||
}
|
||||
|
||||
this._lastUpdated = Date.now();
|
||||
}
|
||||
|
||||
public query(componentType: ComponentType): Set<Entity> {
|
||||
const startTime = performance.now();
|
||||
const entities = this._componentToEntities.get(componentType);
|
||||
const result = entities ? new Set(entities) : new Set<Entity>();
|
||||
|
||||
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);
|
||||
|
||||
if (this._autoOptimize && this._activeIndex.getStats().queryCount % 100 === 0) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
193
packages/core/src/ECS/Core/ComponentPool.ts
Normal file
193
packages/core/src/ECS/Core/ComponentPool.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
import { Component } from '../Component';
|
||||
|
||||
/**
|
||||
* 组件对象池,用于复用组件实例以减少内存分配
|
||||
*/
|
||||
export class ComponentPool<T extends Component> {
|
||||
private pool: T[] = [];
|
||||
private createFn: () => T;
|
||||
private resetFn?: (component: T) => void;
|
||||
private maxSize: number;
|
||||
|
||||
constructor(
|
||||
createFn: () => T,
|
||||
resetFn?: (component: T) => void,
|
||||
maxSize: number = 1000
|
||||
) {
|
||||
this.createFn = createFn;
|
||||
this.resetFn = resetFn;
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个组件实例
|
||||
*/
|
||||
acquire(): T {
|
||||
if (this.pool.length > 0) {
|
||||
return this.pool.pop()!;
|
||||
}
|
||||
return this.createFn();
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放一个组件实例回池中
|
||||
*/
|
||||
release(component: T): void {
|
||||
if (this.pool.length < this.maxSize) {
|
||||
if (this.resetFn) {
|
||||
this.resetFn(component);
|
||||
}
|
||||
this.pool.push(component);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预填充对象池
|
||||
*/
|
||||
prewarm(count: number): void {
|
||||
for (let i = 0; i < count && this.pool.length < this.maxSize; i++) {
|
||||
this.pool.push(this.createFn());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空对象池
|
||||
*/
|
||||
clear(): void {
|
||||
this.pool.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取池中可用对象数量
|
||||
*/
|
||||
getAvailableCount(): number {
|
||||
return this.pool.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取池的最大容量
|
||||
*/
|
||||
getMaxSize(): number {
|
||||
return this.maxSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局组件池管理器
|
||||
*/
|
||||
export class ComponentPoolManager {
|
||||
private static instance: ComponentPoolManager;
|
||||
private pools = new Map<string, ComponentPool<any>>();
|
||||
|
||||
private constructor() {}
|
||||
|
||||
static getInstance(): ComponentPoolManager {
|
||||
if (!ComponentPoolManager.instance) {
|
||||
ComponentPoolManager.instance = new ComponentPoolManager();
|
||||
}
|
||||
return ComponentPoolManager.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册组件池
|
||||
*/
|
||||
registerPool<T extends Component>(
|
||||
componentName: string,
|
||||
createFn: () => T,
|
||||
resetFn?: (component: T) => void,
|
||||
maxSize?: number
|
||||
): void {
|
||||
this.pools.set(componentName, new ComponentPool(createFn, resetFn, maxSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件实例
|
||||
*/
|
||||
acquireComponent<T extends Component>(componentName: string): T | null {
|
||||
const pool = this.pools.get(componentName);
|
||||
return pool ? pool.acquire() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放组件实例
|
||||
*/
|
||||
releaseComponent<T extends Component>(componentName: string, component: T): void {
|
||||
const pool = this.pools.get(componentName);
|
||||
if (pool) {
|
||||
pool.release(component);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预热所有池
|
||||
*/
|
||||
prewarmAll(count: number = 100): void {
|
||||
for (const pool of this.pools.values()) {
|
||||
pool.prewarm(count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有池
|
||||
*/
|
||||
clearAll(): void {
|
||||
for (const pool of this.pools.values()) {
|
||||
pool.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置管理器,移除所有注册的池
|
||||
*/
|
||||
reset(): void {
|
||||
this.pools.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取池统计信息
|
||||
*/
|
||||
getPoolStats(): Map<string, { available: number; maxSize: number }> {
|
||||
const stats = new Map();
|
||||
for (const [name, pool] of this.pools) {
|
||||
stats.set(name, {
|
||||
available: pool.getAvailableCount(),
|
||||
maxSize: pool.getMaxSize()
|
||||
});
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取池利用率信息(用于调试)
|
||||
*/
|
||||
getPoolUtilization(): Map<string, { used: number; total: number; utilization: number }> {
|
||||
const utilization = new Map();
|
||||
for (const [name, pool] of this.pools) {
|
||||
const available = pool.getAvailableCount();
|
||||
const maxSize = pool.getMaxSize();
|
||||
const used = maxSize - available;
|
||||
const utilRate = maxSize > 0 ? (used / maxSize * 100) : 0;
|
||||
|
||||
utilization.set(name, {
|
||||
used: used,
|
||||
total: maxSize,
|
||||
utilization: utilRate
|
||||
});
|
||||
}
|
||||
return utilization;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定组件的池利用率
|
||||
*/
|
||||
getComponentUtilization(componentName: string): number {
|
||||
const pool = this.pools.get(componentName);
|
||||
if (!pool) return 0;
|
||||
|
||||
const available = pool.getAvailableCount();
|
||||
const maxSize = pool.getMaxSize();
|
||||
const used = maxSize - available;
|
||||
|
||||
return maxSize > 0 ? (used / maxSize * 100) : 0;
|
||||
}
|
||||
}
|
||||
536
packages/core/src/ECS/Core/ComponentStorage.ts
Normal file
536
packages/core/src/ECS/Core/ComponentStorage.ts
Normal file
@@ -0,0 +1,536 @@
|
||||
import { Component } from '../Component';
|
||||
import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
|
||||
import { SoAStorage, EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy } from './SoAStorage';
|
||||
|
||||
// 重新导出装饰器
|
||||
export { EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy };
|
||||
|
||||
/**
|
||||
* 组件类型定义
|
||||
*/
|
||||
export type ComponentType<T extends Component = Component> = new (...args: unknown[]) => T;
|
||||
|
||||
/**
|
||||
* 组件注册表
|
||||
* 管理组件类型的位掩码分配
|
||||
*/
|
||||
export class ComponentRegistry {
|
||||
private static componentTypes = new Map<Function, number>();
|
||||
private static componentNameToType = new Map<string, Function>();
|
||||
private static componentNameToId = new Map<string, number>();
|
||||
private static maskCache = new Map<string, IBigIntLike>();
|
||||
private static nextBitIndex = 0;
|
||||
private static maxComponents = 64; // 支持最多64种组件类型
|
||||
|
||||
/**
|
||||
* 注册组件类型并分配位掩码
|
||||
* @param componentType 组件类型
|
||||
* @returns 分配的位索引
|
||||
*/
|
||||
public static register<T extends Component>(componentType: ComponentType<T>): number {
|
||||
if (this.componentTypes.has(componentType)) {
|
||||
return this.componentTypes.get(componentType)!;
|
||||
}
|
||||
|
||||
if (this.nextBitIndex >= this.maxComponents) {
|
||||
throw new Error(`Maximum number of component types (${this.maxComponents}) exceeded`);
|
||||
}
|
||||
|
||||
const bitIndex = this.nextBitIndex++;
|
||||
this.componentTypes.set(componentType, bitIndex);
|
||||
this.componentNameToType.set(componentType.name, componentType);
|
||||
this.componentNameToId.set(componentType.name, bitIndex);
|
||||
return bitIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件类型的位掩码
|
||||
* @param componentType 组件类型
|
||||
* @returns 位掩码
|
||||
*/
|
||||
public static getBitMask<T extends Component>(componentType: ComponentType<T>): IBigIntLike {
|
||||
const bitIndex = this.componentTypes.get(componentType);
|
||||
if (bitIndex === undefined) {
|
||||
throw new Error(`Component type ${componentType.name} is not registered`);
|
||||
}
|
||||
return BigIntFactory.one().shiftLeft(bitIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件类型的位索引
|
||||
* @param componentType 组件类型
|
||||
* @returns 位索引
|
||||
*/
|
||||
public static getBitIndex<T extends Component>(componentType: ComponentType<T>): number {
|
||||
const bitIndex = this.componentTypes.get(componentType);
|
||||
if (bitIndex === undefined) {
|
||||
throw new Error(`Component type ${componentType.name} is not registered`);
|
||||
}
|
||||
return bitIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查组件类型是否已注册
|
||||
* @param componentType 组件类型
|
||||
* @returns 是否已注册
|
||||
*/
|
||||
public static isRegistered<T extends Component>(componentType: ComponentType<T>): boolean {
|
||||
return this.componentTypes.has(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过名称获取组件类型
|
||||
* @param componentName 组件名称
|
||||
* @returns 组件类型构造函数
|
||||
*/
|
||||
public static getComponentType(componentName: string): Function | null {
|
||||
return this.componentNameToType.get(componentName) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已注册的组件类型
|
||||
* @returns 组件类型映射
|
||||
*/
|
||||
public static getAllRegisteredTypes(): Map<Function, number> {
|
||||
return new Map(this.componentTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有组件名称到类型的映射
|
||||
* @returns 名称到类型的映射
|
||||
*/
|
||||
public static getAllComponentNames(): Map<string, Function> {
|
||||
return new Map(this.componentNameToType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过名称获取组件类型ID
|
||||
* @param componentName 组件名称
|
||||
* @returns 组件类型ID
|
||||
*/
|
||||
public static getComponentId(componentName: string): number | undefined {
|
||||
return this.componentNameToId.get(componentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册组件类型(通过名称)
|
||||
* @param componentName 组件名称
|
||||
* @returns 分配的组件ID
|
||||
*/
|
||||
public static registerComponentByName(componentName: string): number {
|
||||
if (this.componentNameToId.has(componentName)) {
|
||||
return this.componentNameToId.get(componentName)!;
|
||||
}
|
||||
|
||||
if (this.nextBitIndex >= this.maxComponents) {
|
||||
throw new Error(`Maximum number of component types (${this.maxComponents}) exceeded`);
|
||||
}
|
||||
|
||||
const bitIndex = this.nextBitIndex++;
|
||||
this.componentNameToId.set(componentName, bitIndex);
|
||||
return bitIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单个组件的掩码
|
||||
* @param componentName 组件名称
|
||||
* @returns 组件掩码
|
||||
*/
|
||||
public static createSingleComponentMask(componentName: string): IBigIntLike {
|
||||
const cacheKey = `single:${componentName}`;
|
||||
|
||||
if (this.maskCache.has(cacheKey)) {
|
||||
return this.maskCache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
const componentId = this.getComponentId(componentName);
|
||||
if (componentId === undefined) {
|
||||
throw new Error(`Component type ${componentName} is not registered`);
|
||||
}
|
||||
|
||||
const mask = BigIntFactory.one().shiftLeft(componentId);
|
||||
this.maskCache.set(cacheKey, mask);
|
||||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建多个组件的掩码
|
||||
* @param componentNames 组件名称数组
|
||||
* @returns 组合掩码
|
||||
*/
|
||||
public static createComponentMask(componentNames: string[]): IBigIntLike {
|
||||
const sortedNames = [...componentNames].sort();
|
||||
const cacheKey = `multi:${sortedNames.join(',')}`;
|
||||
|
||||
if (this.maskCache.has(cacheKey)) {
|
||||
return this.maskCache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
let mask = BigIntFactory.zero();
|
||||
for (const name of componentNames) {
|
||||
const componentId = this.getComponentId(name);
|
||||
if (componentId !== undefined) {
|
||||
mask = mask.or(BigIntFactory.one().shiftLeft(componentId));
|
||||
}
|
||||
}
|
||||
|
||||
this.maskCache.set(cacheKey, mask);
|
||||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除掩码缓存
|
||||
*/
|
||||
public static clearMaskCache(): void {
|
||||
this.maskCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置注册表(用于测试)
|
||||
*/
|
||||
public static reset(): void {
|
||||
this.componentTypes.clear();
|
||||
this.componentNameToType.clear();
|
||||
this.componentNameToId.clear();
|
||||
this.maskCache.clear();
|
||||
this.nextBitIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 高性能组件存储器
|
||||
* 使用SoA(Structure of Arrays)模式存储组件
|
||||
*/
|
||||
export class ComponentStorage<T extends Component> {
|
||||
private components: (T | null)[] = [];
|
||||
private entityToIndex = new Map<number, number>();
|
||||
private indexToEntity: number[] = [];
|
||||
private freeIndices: number[] = [];
|
||||
private componentType: ComponentType<T>;
|
||||
private _size = 0;
|
||||
|
||||
constructor(componentType: ComponentType<T>) {
|
||||
this.componentType = componentType;
|
||||
|
||||
// 确保组件类型已注册
|
||||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||||
ComponentRegistry.register(componentType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加组件
|
||||
* @param entityId 实体ID
|
||||
* @param component 组件实例
|
||||
*/
|
||||
public addComponent(entityId: number, component: T): void {
|
||||
// 检查实体是否已有此组件
|
||||
if (this.entityToIndex.has(entityId)) {
|
||||
throw new Error(`Entity ${entityId} already has component ${this.componentType.name}`);
|
||||
}
|
||||
|
||||
let index: number;
|
||||
|
||||
if (this.freeIndices.length > 0) {
|
||||
// 重用空闲索引
|
||||
index = this.freeIndices.pop()!;
|
||||
this.components[index] = component;
|
||||
this.indexToEntity[index] = entityId;
|
||||
} else {
|
||||
// 添加到末尾
|
||||
index = this.components.length;
|
||||
this.components.push(component);
|
||||
this.indexToEntity.push(entityId);
|
||||
}
|
||||
|
||||
this.entityToIndex.set(entityId, index);
|
||||
this._size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件
|
||||
* @param entityId 实体ID
|
||||
* @returns 组件实例或null
|
||||
*/
|
||||
public getComponent(entityId: number): T | null {
|
||||
const index = this.entityToIndex.get(entityId);
|
||||
return index !== undefined ? this.components[index] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查实体是否有此组件
|
||||
* @param entityId 实体ID
|
||||
* @returns 是否有组件
|
||||
*/
|
||||
public hasComponent(entityId: number): boolean {
|
||||
return this.entityToIndex.has(entityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除组件
|
||||
* @param entityId 实体ID
|
||||
* @returns 被移除的组件或null
|
||||
*/
|
||||
public removeComponent(entityId: number): T | null {
|
||||
const index = this.entityToIndex.get(entityId);
|
||||
if (index === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const component = this.components[index];
|
||||
this.entityToIndex.delete(entityId);
|
||||
this.components[index] = null;
|
||||
this.freeIndices.push(index);
|
||||
this._size--;
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* 高效遍历所有组件
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
public forEach(callback: (component: T, entityId: number, index: number) => void): void {
|
||||
for (let i = 0; i < this.components.length; i++) {
|
||||
const component = this.components[i];
|
||||
if (component) {
|
||||
callback(component, this.indexToEntity[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有组件(密集数组)
|
||||
* @returns 组件数组
|
||||
*/
|
||||
public getDenseArray(): { components: T[]; entityIds: number[] } {
|
||||
const components: T[] = [];
|
||||
const entityIds: number[] = [];
|
||||
|
||||
for (let i = 0; i < this.components.length; i++) {
|
||||
const component = this.components[i];
|
||||
if (component) {
|
||||
components.push(component);
|
||||
entityIds.push(this.indexToEntity[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return { components, entityIds };
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有组件
|
||||
*/
|
||||
public clear(): void {
|
||||
this.components.length = 0;
|
||||
this.entityToIndex.clear();
|
||||
this.indexToEntity.length = 0;
|
||||
this.freeIndices.length = 0;
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件数量
|
||||
*/
|
||||
public get size(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件类型
|
||||
*/
|
||||
public get type(): ComponentType<T> {
|
||||
return this.componentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩存储(移除空洞)
|
||||
*/
|
||||
public compact(): void {
|
||||
if (this.freeIndices.length === 0) {
|
||||
return; // 没有空洞,无需压缩
|
||||
}
|
||||
|
||||
const newComponents: T[] = [];
|
||||
const newIndexToEntity: number[] = [];
|
||||
const newEntityToIndex = new Map<number, number>();
|
||||
|
||||
let newIndex = 0;
|
||||
for (let i = 0; i < this.components.length; i++) {
|
||||
const component = this.components[i];
|
||||
if (component) {
|
||||
newComponents[newIndex] = component;
|
||||
newIndexToEntity[newIndex] = this.indexToEntity[i];
|
||||
newEntityToIndex.set(this.indexToEntity[i], newIndex);
|
||||
newIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
this.components = newComponents;
|
||||
this.indexToEntity = newIndexToEntity;
|
||||
this.entityToIndex = newEntityToIndex;
|
||||
this.freeIndices.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储统计信息
|
||||
*/
|
||||
public getStats(): {
|
||||
totalSlots: number;
|
||||
usedSlots: number;
|
||||
freeSlots: number;
|
||||
fragmentation: number;
|
||||
} {
|
||||
const totalSlots = this.components.length;
|
||||
const usedSlots = this._size;
|
||||
const freeSlots = this.freeIndices.length;
|
||||
const fragmentation = totalSlots > 0 ? freeSlots / totalSlots : 0;
|
||||
|
||||
return {
|
||||
totalSlots,
|
||||
usedSlots,
|
||||
freeSlots,
|
||||
fragmentation
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件存储管理器
|
||||
* 管理所有组件类型的存储器
|
||||
*/
|
||||
export class ComponentStorageManager {
|
||||
private storages = new Map<Function, ComponentStorage<any> | SoAStorage<any>>();
|
||||
|
||||
/**
|
||||
* 获取或创建组件存储器(默认原始存储)
|
||||
* @param componentType 组件类型
|
||||
* @returns 组件存储器
|
||||
*/
|
||||
public getStorage<T extends Component>(componentType: ComponentType<T>): ComponentStorage<T> | SoAStorage<T> {
|
||||
let storage = this.storages.get(componentType);
|
||||
|
||||
if (!storage) {
|
||||
// 检查是否启用SoA优化
|
||||
const enableSoA = (componentType as any).__enableSoA;
|
||||
|
||||
if (enableSoA) {
|
||||
// 使用SoA优化存储
|
||||
storage = new SoAStorage(componentType);
|
||||
console.log(`[SoA] 为 ${componentType.name} 启用SoA优化(适用于大规模批量操作)`);
|
||||
} else {
|
||||
// 默认使用原始存储
|
||||
storage = new ComponentStorage(componentType);
|
||||
}
|
||||
|
||||
this.storages.set(componentType, storage);
|
||||
}
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加组件
|
||||
* @param entityId 实体ID
|
||||
* @param component 组件实例
|
||||
*/
|
||||
public addComponent<T extends Component>(entityId: number, component: T): void {
|
||||
const componentType = component.constructor as ComponentType<T>;
|
||||
const storage = this.getStorage(componentType);
|
||||
storage.addComponent(entityId, component);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件
|
||||
* @param entityId 实体ID
|
||||
* @param componentType 组件类型
|
||||
* @returns 组件实例或null
|
||||
*/
|
||||
public getComponent<T extends Component>(entityId: number, componentType: ComponentType<T>): T | null {
|
||||
const storage = this.storages.get(componentType);
|
||||
return storage ? storage.getComponent(entityId) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查实体是否有组件
|
||||
* @param entityId 实体ID
|
||||
* @param componentType 组件类型
|
||||
* @returns 是否有组件
|
||||
*/
|
||||
public hasComponent<T extends Component>(entityId: number, componentType: ComponentType<T>): boolean {
|
||||
const storage = this.storages.get(componentType);
|
||||
return storage ? storage.hasComponent(entityId) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除组件
|
||||
* @param entityId 实体ID
|
||||
* @param componentType 组件类型
|
||||
* @returns 被移除的组件或null
|
||||
*/
|
||||
public removeComponent<T extends Component>(entityId: number, componentType: ComponentType<T>): T | null {
|
||||
const storage = this.storages.get(componentType);
|
||||
return storage ? storage.removeComponent(entityId) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除实体的所有组件
|
||||
* @param entityId 实体ID
|
||||
*/
|
||||
public removeAllComponents(entityId: number): void {
|
||||
for (const storage of this.storages.values()) {
|
||||
storage.removeComponent(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体的组件位掩码
|
||||
* @param entityId 实体ID
|
||||
* @returns 组件位掩码
|
||||
*/
|
||||
public getComponentMask(entityId: number): IBigIntLike {
|
||||
let mask = BigIntFactory.zero();
|
||||
|
||||
for (const [componentType, storage] of this.storages.entries()) {
|
||||
if (storage.hasComponent(entityId)) {
|
||||
const componentMask = ComponentRegistry.getBitMask(componentType as ComponentType);
|
||||
mask = mask.or(componentMask);
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩所有存储器
|
||||
*/
|
||||
public compactAll(): void {
|
||||
for (const storage of this.storages.values()) {
|
||||
storage.compact();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有存储器的统计信息
|
||||
*/
|
||||
public getAllStats(): Map<string, any> {
|
||||
const stats = new Map<string, any>();
|
||||
|
||||
for (const [componentType, storage] of this.storages.entries()) {
|
||||
const typeName = (componentType as any).name || 'Unknown';
|
||||
stats.set(typeName, storage.getStats());
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有存储器
|
||||
*/
|
||||
public clear(): void {
|
||||
for (const storage of this.storages.values()) {
|
||||
storage.clear();
|
||||
}
|
||||
this.storages.clear();
|
||||
}
|
||||
}
|
||||
375
packages/core/src/ECS/Core/DirtyTrackingSystem.ts
Normal file
375
packages/core/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('脏数据监听器错误:', 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('脏数据监听器通知错误:', 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;
|
||||
}
|
||||
}
|
||||
759
packages/core/src/ECS/Core/EntityManager.ts
Normal file
759
packages/core/src/ECS/Core/EntityManager.ts
Normal file
@@ -0,0 +1,759 @@
|
||||
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';
|
||||
|
||||
/**
|
||||
* 实体查询构建器
|
||||
*
|
||||
* 提供流式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;
|
||||
|
||||
// 监听组件事件来同步更新索引
|
||||
this._eventBus.on('component:added', (data: any) => {
|
||||
const entity = this._entities.get(data.entityId);
|
||||
if (entity) {
|
||||
this._componentIndexManager.addEntity(entity);
|
||||
this._archetypeSystem.addEntity(entity);
|
||||
}
|
||||
});
|
||||
|
||||
this._eventBus.on('component:removed', (data: any) => {
|
||||
const entity = this._entities.get(data.entityId);
|
||||
if (entity) {
|
||||
this._componentIndexManager.removeEntity(entity);
|
||||
this._archetypeSystem.removeEntity(entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体总数
|
||||
*
|
||||
* @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 {
|
||||
const id = this._identifierPool.checkOut();
|
||||
if (!name) {
|
||||
name = `Entity_${id}`;
|
||||
}
|
||||
const entity = new Entity(name, id);
|
||||
|
||||
this._entities.set(id, entity);
|
||||
this.updateNameIndex(entity, true);
|
||||
this.updateTagIndex(entity, true);
|
||||
|
||||
if (entity.components.length > 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量创建实体
|
||||
*
|
||||
* 为了优化大量实体创建的性能,批量处理索引更新和事件发射。
|
||||
* 适用于需要创建大量实体的场景,如子弹、粒子等。
|
||||
*
|
||||
* @param count 要创建的实体数量
|
||||
* @param namePrefix 实体名称前缀,默认为 Entity
|
||||
* @param skipEvents 是否跳过事件发射以提升性能,默认为 false
|
||||
* @returns 创建的实体数组
|
||||
*
|
||||
* @example
|
||||
* const bullets = entityManager.createEntitiesBatch(100, "Bullet", true);
|
||||
* const particles = entityManager.createEntitiesBatch(500, "Particle");
|
||||
*/
|
||||
public createEntitiesBatch(
|
||||
count: number,
|
||||
namePrefix: string = "Entity",
|
||||
skipEvents: boolean = false
|
||||
): Entity[] {
|
||||
if (count <= 0) return [];
|
||||
|
||||
const entities: Entity[] = [];
|
||||
|
||||
// 批量分配ID和创建Entity对象
|
||||
for (let i = 0; i < count; i++) {
|
||||
const id = this._identifierPool.checkOut();
|
||||
const name = `${namePrefix}_${id}`;
|
||||
const entity = new Entity(name, id);
|
||||
|
||||
entities.push(entity);
|
||||
this._entities.set(id, entity);
|
||||
}
|
||||
|
||||
// 批量更新索引
|
||||
for (const entity of entities) {
|
||||
this.updateNameIndex(entity, true);
|
||||
this.updateTagIndex(entity, true);
|
||||
|
||||
if (entity.components.length > 0) {
|
||||
this._componentIndexManager.addEntity(entity);
|
||||
this._archetypeSystem.addEntity(entity);
|
||||
this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量发射事件
|
||||
if (!skipEvents) {
|
||||
const timestamp = Date.now();
|
||||
for (const entity of entities) {
|
||||
this._eventBus.emitEntityCreated({
|
||||
timestamp,
|
||||
source: 'EntityManager',
|
||||
entityId: entity.id,
|
||||
entityName: entity.name,
|
||||
entityTag: entity.tag?.toString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁实体
|
||||
*
|
||||
* 支持通过实体对象、名称或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 Array.from(this._entities.values())
|
||||
.filter(entity => entity.tag === 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
506
packages/core/src/ECS/Core/EventBus.ts
Normal file
506
packages/core/src/ECS/Core/EventBus.ts
Normal file
@@ -0,0 +1,506 @@
|
||||
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] 发射事件: ${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] 发射异步事件: ${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] 添加监听器: ${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] 移除监听器: ${listenerId} 事件: ${eventType}`);
|
||||
}
|
||||
|
||||
return this.eventSystem.off(eventType, listenerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定事件类型的所有监听器
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
public offAll(eventType: string): void {
|
||||
if (this.isDebugMode) {
|
||||
console.log(`[EventBus] 移除所有监听器: ${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] 清空所有监听器');
|
||||
}
|
||||
|
||||
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(ECSEventType.SCENE_ACTIVATED, 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] 未知事件类型: ${eventType}`);
|
||||
}
|
||||
// 在调试模式下添加自定义事件类型
|
||||
if (this.isDebugMode) {
|
||||
EventTypeValidator.addCustomType(eventType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 增强事件数据
|
||||
* @param eventType 事件类型
|
||||
* @param data 原始数据
|
||||
*/
|
||||
private enhanceEventData<T>(eventType: string, data: T): T & IEventData {
|
||||
// 处理null或undefined数据
|
||||
if (data === null || data === undefined) {
|
||||
return {
|
||||
timestamp: Date.now(),
|
||||
eventId: `${eventType}_${++this.eventIdCounter}`,
|
||||
source: 'EventBus'
|
||||
} as 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;
|
||||
};
|
||||
}
|
||||
611
packages/core/src/ECS/Core/EventSystem.ts
Normal file
611
packages/core/src/ECS/Core/EventSystem.ts
Normal file
@@ -0,0 +1,611 @@
|
||||
/**
|
||||
* 事件处理器函数类型
|
||||
*/
|
||||
export type EventHandler<T = any> = (event: T) => void;
|
||||
|
||||
/**
|
||||
* 异步事件处理器函数类型
|
||||
*/
|
||||
export type AsyncEventHandler<T = any> = (event: T) => Promise<void>;
|
||||
|
||||
/**
|
||||
* 事件监听器配置
|
||||
*/
|
||||
export interface EventListenerConfig {
|
||||
/** 是否只执行一次 */
|
||||
once?: boolean;
|
||||
/** 优先级(数字越大优先级越高) */
|
||||
priority?: number;
|
||||
/** 是否异步执行 */
|
||||
async?: boolean;
|
||||
/** 执行上下文 */
|
||||
context?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部事件监听器
|
||||
*/
|
||||
interface InternalEventListener<T = any> {
|
||||
handler: EventHandler<T> | AsyncEventHandler<T>;
|
||||
config: EventListenerConfig;
|
||||
id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件统计信息
|
||||
*/
|
||||
export interface EventStats {
|
||||
/** 事件类型 */
|
||||
eventType: string;
|
||||
/** 监听器数量 */
|
||||
listenerCount: number;
|
||||
/** 触发次数 */
|
||||
triggerCount: number;
|
||||
/** 总执行时间(毫秒) */
|
||||
totalExecutionTime: number;
|
||||
/** 平均执行时间(毫秒) */
|
||||
averageExecutionTime: number;
|
||||
/** 最后触发时间 */
|
||||
lastTriggerTime: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件批处理配置
|
||||
*/
|
||||
export interface EventBatchConfig {
|
||||
/** 批处理大小 */
|
||||
batchSize: number;
|
||||
/** 批处理延迟(毫秒) */
|
||||
delay: number;
|
||||
/** 是否启用批处理 */
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型安全的高性能事件系统
|
||||
* 支持同步/异步事件、优先级、批处理等功能
|
||||
*/
|
||||
export class TypeSafeEventSystem {
|
||||
private listeners = new Map<string, InternalEventListener[]>();
|
||||
private stats = new Map<string, EventStats>();
|
||||
private batchQueue = new Map<string, any[]>();
|
||||
private batchTimers = new Map<string, number>();
|
||||
private batchConfigs = new Map<string, EventBatchConfig>();
|
||||
private nextListenerId = 0;
|
||||
private isEnabled = true;
|
||||
private maxListeners = 100; // 每个事件类型的最大监听器数量
|
||||
|
||||
/**
|
||||
* 添加事件监听器
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件处理器
|
||||
* @param config 监听器配置
|
||||
* @returns 监听器ID(用于移除)
|
||||
*/
|
||||
public on<T>(
|
||||
eventType: string,
|
||||
handler: EventHandler<T>,
|
||||
config: EventListenerConfig = {}
|
||||
): string {
|
||||
return this.addListener(eventType, handler, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一次性事件监听器
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件处理器
|
||||
* @param config 监听器配置
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
public once<T>(
|
||||
eventType: string,
|
||||
handler: EventHandler<T>,
|
||||
config: EventListenerConfig = {}
|
||||
): string {
|
||||
return this.addListener(eventType, handler, { ...config, once: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加异步事件监听器
|
||||
* @param eventType 事件类型
|
||||
* @param handler 异步事件处理器
|
||||
* @param config 监听器配置
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
public onAsync<T>(
|
||||
eventType: string,
|
||||
handler: AsyncEventHandler<T>,
|
||||
config: EventListenerConfig = {}
|
||||
): string {
|
||||
return this.addListener(eventType, handler, { ...config, async: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除事件监听器
|
||||
* @param eventType 事件类型
|
||||
* @param listenerId 监听器ID
|
||||
* @returns 是否成功移除
|
||||
*/
|
||||
public off(eventType: string, listenerId: string): boolean {
|
||||
const listeners = this.listeners.get(eventType);
|
||||
if (!listeners) return false;
|
||||
|
||||
const index = listeners.findIndex(l => l.id === listenerId);
|
||||
if (index === -1) return false;
|
||||
|
||||
listeners.splice(index, 1);
|
||||
|
||||
// 如果没有监听器了,清理相关数据
|
||||
if (listeners.length === 0) {
|
||||
this.listeners.delete(eventType);
|
||||
this.stats.delete(eventType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定事件类型的所有监听器
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
public offAll(eventType: string): void {
|
||||
this.listeners.delete(eventType);
|
||||
this.stats.delete(eventType);
|
||||
this.clearBatch(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
* @param eventType 事件类型
|
||||
* @param event 事件数据
|
||||
* @returns Promise(如果有异步监听器)
|
||||
*/
|
||||
public async emit<T>(eventType: string, event: T): Promise<void> {
|
||||
if (!this.isEnabled) return;
|
||||
|
||||
// 检查是否启用了批处理
|
||||
const batchConfig = this.batchConfigs.get(eventType);
|
||||
if (batchConfig?.enabled) {
|
||||
this.addToBatch(eventType, event);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.executeEvent(eventType, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步触发事件(忽略异步监听器)
|
||||
* @param eventType 事件类型
|
||||
* @param event 事件数据
|
||||
*/
|
||||
public emitSync<T>(eventType: string, event: T): void {
|
||||
if (!this.isEnabled) return;
|
||||
|
||||
const listeners = this.listeners.get(eventType);
|
||||
if (!listeners || listeners.length === 0) return;
|
||||
|
||||
const startTime = performance.now();
|
||||
const toRemove: string[] = [];
|
||||
|
||||
// 按优先级排序
|
||||
const sortedListeners = this.sortListenersByPriority(listeners);
|
||||
|
||||
for (const listener of sortedListeners) {
|
||||
if (listener.config.async) continue; // 跳过异步监听器
|
||||
|
||||
try {
|
||||
if (listener.config.context) {
|
||||
(listener.handler as EventHandler<T>).call(listener.config.context, event);
|
||||
} else {
|
||||
(listener.handler as EventHandler<T>)(event);
|
||||
}
|
||||
|
||||
if (listener.config.once) {
|
||||
toRemove.push(listener.id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`事件处理器执行错误 ${eventType}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// 移除一次性监听器
|
||||
this.removeListeners(eventType, toRemove);
|
||||
|
||||
// 更新统计信息
|
||||
this.updateStats(eventType, performance.now() - startTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置事件批处理配置
|
||||
* @param eventType 事件类型
|
||||
* @param config 批处理配置
|
||||
*/
|
||||
public setBatchConfig(eventType: string, config: EventBatchConfig): void {
|
||||
this.batchConfigs.set(eventType, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即处理指定事件类型的批处理队列
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
public flushBatch(eventType: string): void {
|
||||
const batch = this.batchQueue.get(eventType);
|
||||
if (!batch || batch.length === 0) return;
|
||||
|
||||
// 清除定时器
|
||||
const timer = this.batchTimers.get(eventType);
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
this.batchTimers.delete(eventType);
|
||||
}
|
||||
|
||||
// 处理批处理事件
|
||||
this.processBatch(eventType, batch);
|
||||
|
||||
// 清空队列
|
||||
this.batchQueue.delete(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取事件统计信息
|
||||
* @param eventType 事件类型(可选)
|
||||
* @returns 统计信息
|
||||
*/
|
||||
public getStats(eventType?: string): EventStats | Map<string, EventStats> {
|
||||
if (eventType) {
|
||||
return this.stats.get(eventType) || this.createEmptyStats(eventType);
|
||||
}
|
||||
return new Map(this.stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置统计信息
|
||||
* @param eventType 事件类型(可选,不指定则重置所有)
|
||||
*/
|
||||
public resetStats(eventType?: string): void {
|
||||
if (eventType) {
|
||||
this.stats.delete(eventType);
|
||||
} else {
|
||||
this.stats.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用/禁用事件系统
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
public setEnabled(enabled: boolean): void {
|
||||
this.isEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有指定事件类型的监听器
|
||||
* @param eventType 事件类型
|
||||
* @returns 是否有监听器
|
||||
*/
|
||||
public hasListeners(eventType: string): boolean {
|
||||
const listeners = this.listeners.get(eventType);
|
||||
return listeners ? listeners.length > 0 : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定事件类型的监听器数量
|
||||
* @param eventType 事件类型
|
||||
* @returns 监听器数量
|
||||
*/
|
||||
public getListenerCount(eventType: string): number {
|
||||
const listeners = this.listeners.get(eventType);
|
||||
return listeners ? listeners.length : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有事件监听器和数据
|
||||
*/
|
||||
public clear(): void {
|
||||
this.listeners.clear();
|
||||
this.stats.clear();
|
||||
this.clearAllBatches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置每个事件类型的最大监听器数量
|
||||
* @param max 最大数量
|
||||
*/
|
||||
public setMaxListeners(max: number): void {
|
||||
this.maxListeners = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加监听器的内部实现
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件处理器
|
||||
* @param config 配置
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
private addListener<T>(
|
||||
eventType: string,
|
||||
handler: EventHandler<T> | AsyncEventHandler<T>,
|
||||
config: EventListenerConfig
|
||||
): string {
|
||||
let listeners = this.listeners.get(eventType);
|
||||
|
||||
if (!listeners) {
|
||||
listeners = [];
|
||||
this.listeners.set(eventType, listeners);
|
||||
}
|
||||
|
||||
// 检查监听器数量限制
|
||||
if (listeners.length >= this.maxListeners) {
|
||||
console.warn(`事件类型 ${eventType} 的监听器数量超过最大限制 (${this.maxListeners})`);
|
||||
return '';
|
||||
}
|
||||
|
||||
const listenerId = `listener_${this.nextListenerId++}`;
|
||||
const listener: InternalEventListener<T> = {
|
||||
handler,
|
||||
config: {
|
||||
priority: 0,
|
||||
...config
|
||||
},
|
||||
id: listenerId
|
||||
};
|
||||
|
||||
listeners.push(listener);
|
||||
|
||||
// 初始化统计信息
|
||||
if (!this.stats.has(eventType)) {
|
||||
this.stats.set(eventType, this.createEmptyStats(eventType));
|
||||
}
|
||||
|
||||
return listenerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行事件的内部实现
|
||||
* @param eventType 事件类型
|
||||
* @param event 事件数据
|
||||
*/
|
||||
private async executeEvent<T>(eventType: string, event: T): Promise<void> {
|
||||
const listeners = this.listeners.get(eventType);
|
||||
if (!listeners || listeners.length === 0) return;
|
||||
|
||||
const startTime = performance.now();
|
||||
const toRemove: string[] = [];
|
||||
|
||||
// 按优先级排序
|
||||
const sortedListeners = this.sortListenersByPriority(listeners);
|
||||
|
||||
// 分离同步和异步监听器
|
||||
const syncListeners = sortedListeners.filter(l => !l.config.async);
|
||||
const asyncListeners = sortedListeners.filter(l => l.config.async);
|
||||
|
||||
// 执行同步监听器
|
||||
for (const listener of syncListeners) {
|
||||
try {
|
||||
if (listener.config.context) {
|
||||
(listener.handler as EventHandler<T>).call(listener.config.context, event);
|
||||
} else {
|
||||
(listener.handler as EventHandler<T>)(event);
|
||||
}
|
||||
|
||||
if (listener.config.once) {
|
||||
toRemove.push(listener.id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`同步事件处理器执行错误 ${eventType}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行异步监听器
|
||||
const asyncPromises = asyncListeners.map(async (listener) => {
|
||||
try {
|
||||
if (listener.config.context) {
|
||||
await (listener.handler as AsyncEventHandler<T>).call(listener.config.context, event);
|
||||
} else {
|
||||
await (listener.handler as AsyncEventHandler<T>)(event);
|
||||
}
|
||||
|
||||
if (listener.config.once) {
|
||||
toRemove.push(listener.id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`异步事件处理器执行错误 ${eventType}:`, error);
|
||||
}
|
||||
});
|
||||
|
||||
// 等待所有异步监听器完成
|
||||
await Promise.all(asyncPromises);
|
||||
|
||||
// 移除一次性监听器
|
||||
this.removeListeners(eventType, toRemove);
|
||||
|
||||
// 更新统计信息
|
||||
this.updateStats(eventType, performance.now() - startTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按优先级排序监听器
|
||||
* @param listeners 监听器数组
|
||||
* @returns 排序后的监听器数组
|
||||
*/
|
||||
private sortListenersByPriority<T>(listeners: InternalEventListener<T>[]): InternalEventListener<T>[] {
|
||||
return listeners.slice().sort((a, b) => (b.config.priority || 0) - (a.config.priority || 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定的监听器
|
||||
* @param eventType 事件类型
|
||||
* @param listenerIds 要移除的监听器ID数组
|
||||
*/
|
||||
private removeListeners(eventType: string, listenerIds: string[]): void {
|
||||
if (listenerIds.length === 0) return;
|
||||
|
||||
const listeners = this.listeners.get(eventType);
|
||||
if (!listeners) return;
|
||||
|
||||
for (const id of listenerIds) {
|
||||
const index = listeners.findIndex(l => l.id === id);
|
||||
if (index !== -1) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有监听器了,清理相关数据
|
||||
if (listeners.length === 0) {
|
||||
this.listeners.delete(eventType);
|
||||
this.stats.delete(eventType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加事件到批处理队列
|
||||
* @param eventType 事件类型
|
||||
* @param event 事件数据
|
||||
*/
|
||||
private addToBatch<T>(eventType: string, event: T): void {
|
||||
let batch = this.batchQueue.get(eventType);
|
||||
if (!batch) {
|
||||
batch = [];
|
||||
this.batchQueue.set(eventType, batch);
|
||||
}
|
||||
|
||||
batch.push(event);
|
||||
|
||||
const config = this.batchConfigs.get(eventType)!;
|
||||
|
||||
// 如果达到批处理大小,立即处理
|
||||
if (batch.length >= config.batchSize) {
|
||||
this.flushBatch(eventType);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置延迟处理定时器
|
||||
if (!this.batchTimers.has(eventType)) {
|
||||
const timer = setTimeout(() => {
|
||||
this.flushBatch(eventType);
|
||||
}, config.delay);
|
||||
|
||||
this.batchTimers.set(eventType, timer as any);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理批处理事件
|
||||
* @param eventType 事件类型
|
||||
* @param batch 批处理事件数组
|
||||
*/
|
||||
private async processBatch<T>(eventType: string, batch: T[]): Promise<void> {
|
||||
// 创建批处理事件对象
|
||||
const batchEvent = {
|
||||
type: eventType,
|
||||
events: batch,
|
||||
count: batch.length,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
// 触发批处理事件
|
||||
await this.executeEvent(`${eventType}:batch`, batchEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定事件类型的批处理
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
private clearBatch(eventType: string): void {
|
||||
this.batchQueue.delete(eventType);
|
||||
|
||||
const timer = this.batchTimers.get(eventType);
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
this.batchTimers.delete(eventType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有批处理
|
||||
*/
|
||||
private clearAllBatches(): void {
|
||||
this.batchQueue.clear();
|
||||
|
||||
for (const timer of this.batchTimers.values()) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
this.batchTimers.clear();
|
||||
this.batchConfigs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新事件统计信息
|
||||
* @param eventType 事件类型
|
||||
* @param executionTime 执行时间
|
||||
*/
|
||||
private updateStats(eventType: string, executionTime: number): void {
|
||||
let stats = this.stats.get(eventType);
|
||||
if (!stats) {
|
||||
stats = this.createEmptyStats(eventType);
|
||||
this.stats.set(eventType, stats);
|
||||
}
|
||||
|
||||
stats.triggerCount++;
|
||||
stats.totalExecutionTime += executionTime;
|
||||
stats.averageExecutionTime = stats.totalExecutionTime / stats.triggerCount;
|
||||
stats.lastTriggerTime = Date.now();
|
||||
stats.listenerCount = this.getListenerCount(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建空的统计信息
|
||||
* @param eventType 事件类型
|
||||
* @returns 空的统计信息
|
||||
*/
|
||||
private createEmptyStats(eventType: string): EventStats {
|
||||
return {
|
||||
eventType,
|
||||
listenerCount: 0,
|
||||
triggerCount: 0,
|
||||
totalExecutionTime: 0,
|
||||
averageExecutionTime: 0,
|
||||
lastTriggerTime: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局事件系统实例
|
||||
*/
|
||||
export const GlobalEventSystem = new TypeSafeEventSystem();
|
||||
|
||||
/**
|
||||
* 事件装饰器 - 用于自动注册事件监听器
|
||||
* @param eventType 事件类型
|
||||
* @param config 监听器配置
|
||||
*/
|
||||
export function EventListener(eventType: string, config: EventListenerConfig = {}) {
|
||||
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);
|
||||
GlobalEventSystem.on(eventType, originalMethod.bind(this), config);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步事件装饰器
|
||||
* @param eventType 事件类型
|
||||
* @param config 监听器配置
|
||||
*/
|
||||
export function AsyncEventListener(eventType: string, config: EventListenerConfig = {}) {
|
||||
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);
|
||||
GlobalEventSystem.onAsync(eventType, originalMethod.bind(this), config);
|
||||
};
|
||||
};
|
||||
}
|
||||
2
packages/core/src/ECS/Core/Events/index.ts
Normal file
2
packages/core/src/ECS/Core/Events/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { EventBus, GlobalEventBus, EventHandler, AsyncEventHandler } from '../EventBus';
|
||||
export { TypeSafeEventSystem, EventListenerConfig, EventStats } from '../EventSystem';
|
||||
638
packages/core/src/ECS/Core/FluentAPI.ts
Normal file
638
packages/core/src/ECS/Core/FluentAPI.ts
Normal file
@@ -0,0 +1,638 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { Scene } from '../Scene';
|
||||
import { ComponentType, ComponentStorageManager } from './ComponentStorage';
|
||||
import { QuerySystem, QueryBuilder } from './QuerySystem';
|
||||
import { TypeSafeEventSystem } from './EventSystem';
|
||||
import { EntitySystem } from '../Systems/EntitySystem';
|
||||
|
||||
/**
|
||||
* 实体构建器 - 提供流式API创建和配置实体
|
||||
*/
|
||||
export class EntityBuilder {
|
||||
private entity: Entity;
|
||||
private scene: Scene;
|
||||
private storageManager: ComponentStorageManager;
|
||||
|
||||
constructor(scene: Scene, storageManager: ComponentStorageManager) {
|
||||
this.scene = scene;
|
||||
this.storageManager = storageManager;
|
||||
this.entity = new Entity("", scene.identifierPool.checkOut());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实体名称
|
||||
* @param name 实体名称
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public named(name: string): EntityBuilder {
|
||||
this.entity.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实体标签
|
||||
* @param tag 标签
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public tagged(tag: number): EntityBuilder {
|
||||
this.entity.tag = tag;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加组件
|
||||
* @param component 组件实例
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public with<T extends Component>(component: T): EntityBuilder {
|
||||
this.entity.addComponent(component);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加多个组件
|
||||
* @param components 组件数组
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public withComponents(...components: Component[]): EntityBuilder {
|
||||
for (const component of components) {
|
||||
this.entity.addComponent(component);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 条件性添加组件
|
||||
* @param condition 条件
|
||||
* @param component 组件实例
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public withIf<T extends Component>(condition: boolean, component: T): EntityBuilder {
|
||||
if (condition) {
|
||||
this.entity.addComponent(component);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用工厂函数创建并添加组件
|
||||
* @param factory 组件工厂函数
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public withFactory<T extends Component>(factory: () => T): EntityBuilder {
|
||||
const component = factory();
|
||||
this.entity.addComponent(component);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置组件属性
|
||||
* @param componentType 组件类型
|
||||
* @param configurator 配置函数
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public configure<T extends Component>(
|
||||
componentType: ComponentType<T>,
|
||||
configurator: (component: T) => void
|
||||
): EntityBuilder {
|
||||
const component = this.entity.getComponent(componentType);
|
||||
if (component) {
|
||||
configurator(component);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实体为启用状态
|
||||
* @param enabled 是否启用
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public enabled(enabled: boolean = true): EntityBuilder {
|
||||
this.entity.enabled = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实体为活跃状态
|
||||
* @param active 是否活跃
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public active(active: boolean = true): EntityBuilder {
|
||||
this.entity.active = active;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加子实体
|
||||
* @param childBuilder 子实体构建器
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public withChild(childBuilder: EntityBuilder): EntityBuilder {
|
||||
const child = childBuilder.build();
|
||||
this.entity.addChild(child);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加子实体
|
||||
* @param childBuilders 子实体构建器数组
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public withChildren(...childBuilders: EntityBuilder[]): EntityBuilder {
|
||||
for (const childBuilder of childBuilders) {
|
||||
const child = childBuilder.build();
|
||||
this.entity.addChild(child);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用工厂函数创建子实体
|
||||
* @param childFactory 子实体工厂函数
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public withChildFactory(childFactory: (parent: Entity) => EntityBuilder): EntityBuilder {
|
||||
const childBuilder = childFactory(this.entity);
|
||||
const child = childBuilder.build();
|
||||
this.entity.addChild(child);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 条件性添加子实体
|
||||
* @param condition 条件
|
||||
* @param childBuilder 子实体构建器
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public withChildIf(condition: boolean, childBuilder: EntityBuilder): EntityBuilder {
|
||||
if (condition) {
|
||||
const child = childBuilder.build();
|
||||
this.entity.addChild(child);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建并返回实体
|
||||
* @returns 构建的实体
|
||||
*/
|
||||
public build(): Entity {
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建实体并添加到场景
|
||||
* @returns 构建的实体
|
||||
*/
|
||||
public spawn(): Entity {
|
||||
this.scene.addEntity(this.entity);
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆当前构建器
|
||||
* @returns 新的实体构建器
|
||||
*/
|
||||
public clone(): EntityBuilder {
|
||||
const newBuilder = new EntityBuilder(this.scene, this.storageManager);
|
||||
// 这里需要深度克隆实体,简化实现
|
||||
newBuilder.entity = this.entity; // 实际应该是深度克隆
|
||||
return newBuilder;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景构建器 - 提供流式API创建和配置场景
|
||||
*/
|
||||
export class SceneBuilder {
|
||||
private scene: Scene;
|
||||
|
||||
constructor() {
|
||||
this.scene = new Scene();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置场景名称
|
||||
* @param name 场景名称
|
||||
* @returns 场景构建器
|
||||
*/
|
||||
public named(name: string): SceneBuilder {
|
||||
this.scene.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加实体
|
||||
* @param entity 实体
|
||||
* @returns 场景构建器
|
||||
*/
|
||||
public withEntity(entity: Entity): SceneBuilder {
|
||||
this.scene.addEntity(entity);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用实体构建器添加实体
|
||||
* @param builderFn 实体构建器函数
|
||||
* @returns 场景构建器
|
||||
*/
|
||||
public withEntityBuilder(builderFn: (builder: EntityBuilder) => EntityBuilder): SceneBuilder {
|
||||
const builder = new EntityBuilder(this.scene, this.scene.componentStorageManager);
|
||||
const configuredBuilder = builderFn(builder);
|
||||
const entity = configuredBuilder.build();
|
||||
this.scene.addEntity(entity);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加实体
|
||||
* @param entities 实体数组
|
||||
* @returns 场景构建器
|
||||
*/
|
||||
public withEntities(...entities: Entity[]): SceneBuilder {
|
||||
for (const entity of entities) {
|
||||
this.scene.addEntity(entity);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加系统
|
||||
* @param system 系统实例
|
||||
* @returns 场景构建器
|
||||
*/
|
||||
public withSystem(system: EntitySystem): SceneBuilder {
|
||||
this.scene.addSystem(system);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加系统
|
||||
* @param systems 系统数组
|
||||
* @returns 场景构建器
|
||||
*/
|
||||
public withSystems(...systems: EntitySystem[]): SceneBuilder {
|
||||
for (const system of systems) {
|
||||
this.scene.addSystem(system);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建并返回场景
|
||||
* @returns 构建的场景
|
||||
*/
|
||||
public build(): Scene {
|
||||
return this.scene;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件构建器 - 提供流式API创建组件
|
||||
*/
|
||||
export class ComponentBuilder<T extends Component> {
|
||||
private component: T;
|
||||
|
||||
constructor(componentClass: new (...args: unknown[]) => T, ...args: unknown[]) {
|
||||
this.component = new componentClass(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置组件属性
|
||||
* @param property 属性名
|
||||
* @param value 属性值
|
||||
* @returns 组件构建器
|
||||
*/
|
||||
public set<K extends keyof T>(property: K, value: T[K]): ComponentBuilder<T> {
|
||||
this.component[property] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用配置函数设置组件
|
||||
* @param configurator 配置函数
|
||||
* @returns 组件构建器
|
||||
*/
|
||||
public configure(configurator: (component: T) => void): ComponentBuilder<T> {
|
||||
configurator(this.component);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 条件性设置属性
|
||||
* @param condition 条件
|
||||
* @param property 属性名
|
||||
* @param value 属性值
|
||||
* @returns 组件构建器
|
||||
*/
|
||||
public setIf<K extends keyof T>(condition: boolean, property: K, value: T[K]): ComponentBuilder<T> {
|
||||
if (condition) {
|
||||
this.component[property] = value;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建并返回组件
|
||||
* @returns 构建的组件
|
||||
*/
|
||||
public build(): T {
|
||||
return this.component;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ECS流式API主入口
|
||||
* 提供统一的流式接口
|
||||
*/
|
||||
export class ECSFluentAPI {
|
||||
private scene: Scene;
|
||||
private querySystem: QuerySystem;
|
||||
private eventSystem: TypeSafeEventSystem;
|
||||
|
||||
constructor(scene: Scene, querySystem: QuerySystem, eventSystem: TypeSafeEventSystem) {
|
||||
this.scene = scene;
|
||||
this.querySystem = querySystem;
|
||||
this.eventSystem = eventSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建实体构建器
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
public createEntity(): EntityBuilder {
|
||||
return new EntityBuilder(this.scene, this.scene.componentStorageManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建场景构建器
|
||||
* @returns 场景构建器
|
||||
*/
|
||||
public createScene(): SceneBuilder {
|
||||
return new SceneBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建组件构建器
|
||||
* @param componentClass 组件类
|
||||
* @param args 构造参数
|
||||
* @returns 组件构建器
|
||||
*/
|
||||
public createComponent<T extends Component>(
|
||||
componentClass: new (...args: unknown[]) => T,
|
||||
...args: unknown[]
|
||||
): ComponentBuilder<T> {
|
||||
return new ComponentBuilder(componentClass, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建查询构建器
|
||||
* @returns 查询构建器
|
||||
*/
|
||||
public query(): QueryBuilder {
|
||||
return new QueryBuilder(this.querySystem);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找实体(简化版)
|
||||
* @param componentTypes 组件类型
|
||||
* @returns 实体数组
|
||||
*/
|
||||
public find(...componentTypes: ComponentType[]): Entity[] {
|
||||
return this.querySystem.queryAll(...componentTypes).entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找第一个匹配的实体
|
||||
* @param componentTypes 组件类型
|
||||
* @returns 实体或null
|
||||
*/
|
||||
public findFirst(...componentTypes: ComponentType[]): Entity | null {
|
||||
const result = this.querySystem.queryAll(...componentTypes);
|
||||
return result.entities.length > 0 ? result.entities[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按名称查找实体
|
||||
* @param name 实体名称
|
||||
* @returns 实体或null
|
||||
*/
|
||||
public findByName(name: string): Entity | null {
|
||||
return this.scene.getEntityByName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按标签查找实体
|
||||
* @param tag 标签
|
||||
* @returns 实体数组
|
||||
*/
|
||||
public findByTag(tag: number): Entity[] {
|
||||
return this.scene.getEntitiesByTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
* @param eventType 事件类型
|
||||
* @param event 事件数据
|
||||
*/
|
||||
public emit<T>(eventType: string, event: T): void {
|
||||
this.eventSystem.emitSync(eventType, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步触发事件
|
||||
* @param eventType 事件类型
|
||||
* @param event 事件数据
|
||||
*/
|
||||
public async emitAsync<T>(eventType: string, event: T): Promise<void> {
|
||||
await this.eventSystem.emit(eventType, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听事件
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件处理器
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
public on<T>(eventType: string, handler: (event: T) => void): string {
|
||||
return this.eventSystem.on(eventType, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性监听事件
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件处理器
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
public once<T>(eventType: string, handler: (event: T) => void): string {
|
||||
return this.eventSystem.once(eventType, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除事件监听器
|
||||
* @param eventType 事件类型
|
||||
* @param listenerId 监听器ID
|
||||
*/
|
||||
public off(eventType: string, listenerId: string): void {
|
||||
this.eventSystem.off(eventType, listenerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作实体
|
||||
* @param entities 实体数组
|
||||
* @returns 批量操作器
|
||||
*/
|
||||
public batch(entities: Entity[]): EntityBatchOperator {
|
||||
return new EntityBatchOperator(entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景统计信息
|
||||
* @returns 统计信息
|
||||
*/
|
||||
public getStats(): {
|
||||
entityCount: number;
|
||||
systemCount: number;
|
||||
componentStats: Map<string, any>;
|
||||
queryStats: unknown;
|
||||
eventStats: Map<string, any>;
|
||||
} {
|
||||
return {
|
||||
entityCount: this.scene.entities.count,
|
||||
systemCount: this.scene.systems.length,
|
||||
componentStats: this.scene.componentStorageManager.getAllStats(),
|
||||
queryStats: this.querySystem.getStats(),
|
||||
eventStats: this.eventSystem.getStats() as Map<string, any>
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体批量操作器
|
||||
* 提供对多个实体的批量操作
|
||||
*/
|
||||
export class EntityBatchOperator {
|
||||
private entities: Entity[];
|
||||
|
||||
constructor(entities: Entity[]) {
|
||||
this.entities = entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加组件
|
||||
* @param component 组件实例
|
||||
* @returns 批量操作器
|
||||
*/
|
||||
public addComponent<T extends Component>(component: T): EntityBatchOperator {
|
||||
for (const entity of this.entities) {
|
||||
entity.addComponent(component);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量移除组件
|
||||
* @param componentType 组件类型
|
||||
* @returns 批量操作器
|
||||
*/
|
||||
public removeComponent<T extends Component>(componentType: ComponentType<T>): EntityBatchOperator {
|
||||
for (const entity of this.entities) {
|
||||
entity.removeComponentByType(componentType);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置活跃状态
|
||||
* @param active 是否活跃
|
||||
* @returns 批量操作器
|
||||
*/
|
||||
public setActive(active: boolean): EntityBatchOperator {
|
||||
for (const entity of this.entities) {
|
||||
entity.active = active;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置标签
|
||||
* @param tag 标签
|
||||
* @returns 批量操作器
|
||||
*/
|
||||
public setTag(tag: number): EntityBatchOperator {
|
||||
for (const entity of this.entities) {
|
||||
entity.tag = tag;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量执行操作
|
||||
* @param operation 操作函数
|
||||
* @returns 批量操作器
|
||||
*/
|
||||
public forEach(operation: (entity: Entity, index: number) => void): EntityBatchOperator {
|
||||
this.entities.forEach(operation);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤实体
|
||||
* @param predicate 过滤条件
|
||||
* @returns 新的批量操作器
|
||||
*/
|
||||
public filter(predicate: (entity: Entity) => boolean): EntityBatchOperator {
|
||||
return new EntityBatchOperator(this.entities.filter(predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体数组
|
||||
* @returns 实体数组
|
||||
*/
|
||||
public toArray(): Entity[] {
|
||||
return this.entities.slice();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体数量
|
||||
* @returns 实体数量
|
||||
*/
|
||||
public count(): number {
|
||||
return this.entities.length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建ECS流式API实例
|
||||
* @param scene 场景
|
||||
* @param querySystem 查询系统
|
||||
* @param eventSystem 事件系统
|
||||
* @returns ECS流式API实例
|
||||
*/
|
||||
export function createECSAPI(
|
||||
scene: Scene,
|
||||
querySystem: QuerySystem,
|
||||
eventSystem: TypeSafeEventSystem
|
||||
): ECSFluentAPI {
|
||||
return new ECSFluentAPI(scene, querySystem, eventSystem);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局ECS流式API实例(需要在使用前初始化)
|
||||
*/
|
||||
export let ECS: ECSFluentAPI;
|
||||
|
||||
/**
|
||||
* 初始化全局ECS API
|
||||
* @param scene 场景
|
||||
* @param querySystem 查询系统
|
||||
* @param eventSystem 事件系统
|
||||
*/
|
||||
export function initializeECS(
|
||||
scene: Scene,
|
||||
querySystem: QuerySystem,
|
||||
eventSystem: TypeSafeEventSystem
|
||||
): void {
|
||||
ECS = createECSAPI(scene, querySystem, eventSystem);
|
||||
}
|
||||
21
packages/core/src/ECS/Core/Performance/index.ts
Normal file
21
packages/core/src/ECS/Core/Performance/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export {
|
||||
ComponentIndexManager,
|
||||
HashComponentIndex,
|
||||
BitmapComponentIndex,
|
||||
IndexType
|
||||
} from '../ComponentIndex';
|
||||
|
||||
export {
|
||||
ArchetypeSystem,
|
||||
Archetype,
|
||||
ArchetypeQueryResult
|
||||
} from '../ArchetypeSystem';
|
||||
|
||||
export {
|
||||
DirtyTrackingSystem,
|
||||
DirtyFlag,
|
||||
DirtyData,
|
||||
DirtyListener
|
||||
} from '../DirtyTrackingSystem';
|
||||
|
||||
|
||||
3
packages/core/src/ECS/Core/Query/index.ts
Normal file
3
packages/core/src/ECS/Core/Query/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { QuerySystem } from '../QuerySystem';
|
||||
export { ECSFluentAPI, createECSAPI } from '../FluentAPI';
|
||||
export { EntityManager, EntityQueryBuilder } from '../EntityManager';
|
||||
1168
packages/core/src/ECS/Core/QuerySystem.ts
Normal file
1168
packages/core/src/ECS/Core/QuerySystem.ts
Normal file
File diff suppressed because it is too large
Load Diff
596
packages/core/src/ECS/Core/SoAStorage.ts
Normal file
596
packages/core/src/ECS/Core/SoAStorage.ts
Normal file
@@ -0,0 +1,596 @@
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType } from './ComponentStorage';
|
||||
|
||||
/**
|
||||
* 启用SoA优化装饰器
|
||||
* 默认关闭SoA,只有在大规模批量操作场景下才建议开启
|
||||
*/
|
||||
export function EnableSoA<T extends ComponentType>(target: T): T {
|
||||
(target as any).__enableSoA = true;
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 高精度数值装饰器
|
||||
* 标记字段需要保持完整精度,存储为复杂对象而非TypedArray
|
||||
*/
|
||||
export function HighPrecision(target: any, propertyKey: string | symbol): void {
|
||||
const key = String(propertyKey);
|
||||
if (!target.constructor.__highPrecisionFields) {
|
||||
target.constructor.__highPrecisionFields = new Set();
|
||||
}
|
||||
target.constructor.__highPrecisionFields.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 64位浮点数装饰器
|
||||
* 标记字段使用Float64Array存储(更高精度但更多内存)
|
||||
*/
|
||||
export function Float64(target: any, propertyKey: string | symbol): void {
|
||||
const key = String(propertyKey);
|
||||
if (!target.constructor.__float64Fields) {
|
||||
target.constructor.__float64Fields = new Set();
|
||||
}
|
||||
target.constructor.__float64Fields.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 32位浮点数装饰器
|
||||
* 标记字段使用Float32Array存储(默认类型,平衡性能和精度)
|
||||
*/
|
||||
export function Float32(target: any, propertyKey: string | symbol): void {
|
||||
const key = String(propertyKey);
|
||||
if (!target.constructor.__float32Fields) {
|
||||
target.constructor.__float32Fields = new Set();
|
||||
}
|
||||
target.constructor.__float32Fields.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 32位整数装饰器
|
||||
* 标记字段使用Int32Array存储(适用于整数值)
|
||||
*/
|
||||
export function Int32(target: any, propertyKey: string | symbol): void {
|
||||
const key = String(propertyKey);
|
||||
if (!target.constructor.__int32Fields) {
|
||||
target.constructor.__int32Fields = new Set();
|
||||
}
|
||||
target.constructor.__int32Fields.add(key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 序列化Map装饰器
|
||||
* 标记Map字段需要序列化/反序列化存储
|
||||
*/
|
||||
export function SerializeMap(target: any, propertyKey: string | symbol): void {
|
||||
const key = String(propertyKey);
|
||||
if (!target.constructor.__serializeMapFields) {
|
||||
target.constructor.__serializeMapFields = new Set();
|
||||
}
|
||||
target.constructor.__serializeMapFields.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化Set装饰器
|
||||
* 标记Set字段需要序列化/反序列化存储
|
||||
*/
|
||||
export function SerializeSet(target: any, propertyKey: string | symbol): void {
|
||||
const key = String(propertyKey);
|
||||
if (!target.constructor.__serializeSetFields) {
|
||||
target.constructor.__serializeSetFields = new Set();
|
||||
}
|
||||
target.constructor.__serializeSetFields.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化Array装饰器
|
||||
* 标记Array字段需要序列化/反序列化存储
|
||||
*/
|
||||
export function SerializeArray(target: any, propertyKey: string | symbol): void {
|
||||
const key = String(propertyKey);
|
||||
if (!target.constructor.__serializeArrayFields) {
|
||||
target.constructor.__serializeArrayFields = new Set();
|
||||
}
|
||||
target.constructor.__serializeArrayFields.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 深拷贝装饰器
|
||||
* 标记字段需要深拷贝处理(适用于嵌套对象)
|
||||
*/
|
||||
export function DeepCopy(target: any, propertyKey: string | symbol): void {
|
||||
const key = String(propertyKey);
|
||||
if (!target.constructor.__deepCopyFields) {
|
||||
target.constructor.__deepCopyFields = new Set();
|
||||
}
|
||||
target.constructor.__deepCopyFields.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* SoA存储器(需要装饰器启用)
|
||||
* 使用Structure of Arrays存储模式,在大规模批量操作时提供优异性能
|
||||
*/
|
||||
export class SoAStorage<T extends Component> {
|
||||
private fields = new Map<string, Float32Array | Float64Array | Int32Array>();
|
||||
private stringFields = new Map<string, string[]>(); // 专门存储字符串
|
||||
private serializedFields = new Map<string, string[]>(); // 序列化存储Map/Set/Array
|
||||
private complexFields = new Map<number, Map<string, any>>(); // 存储复杂对象
|
||||
private entityToIndex = new Map<number, number>();
|
||||
private indexToEntity: number[] = [];
|
||||
private freeIndices: number[] = [];
|
||||
private _size = 0;
|
||||
private _capacity = 1000;
|
||||
public readonly type: ComponentType<T>;
|
||||
|
||||
constructor(componentType: ComponentType<T>) {
|
||||
this.type = componentType;
|
||||
this.initializeFields(componentType);
|
||||
}
|
||||
|
||||
private initializeFields(componentType: ComponentType<T>): void {
|
||||
const instance = new componentType();
|
||||
const highPrecisionFields = (componentType as any).__highPrecisionFields || new Set();
|
||||
const float64Fields = (componentType as any).__float64Fields || new Set();
|
||||
const float32Fields = (componentType as any).__float32Fields || new Set();
|
||||
const int32Fields = (componentType as any).__int32Fields || new Set();
|
||||
const serializeMapFields = (componentType as any).__serializeMapFields || new Set();
|
||||
const serializeSetFields = (componentType as any).__serializeSetFields || new Set();
|
||||
const serializeArrayFields = (componentType as any).__serializeArrayFields || new Set();
|
||||
// const deepCopyFields = (componentType as any).__deepCopyFields || new Set(); // 未使用,但保留供future使用
|
||||
|
||||
for (const key in instance) {
|
||||
if (instance.hasOwnProperty(key) && key !== 'id') {
|
||||
const value = (instance as any)[key];
|
||||
const type = typeof value;
|
||||
|
||||
if (type === 'number') {
|
||||
if (highPrecisionFields.has(key)) {
|
||||
// 标记为高精度,作为复杂对象处理
|
||||
// 不添加到fields,会在updateComponentAtIndex中自动添加到complexFields
|
||||
} else if (float64Fields.has(key)) {
|
||||
// 使用Float64Array存储
|
||||
this.fields.set(key, new Float64Array(this._capacity));
|
||||
} else if (int32Fields.has(key)) {
|
||||
// 使用Int32Array存储
|
||||
this.fields.set(key, new Int32Array(this._capacity));
|
||||
} else if (float32Fields.has(key)) {
|
||||
// 使用Float32Array存储
|
||||
this.fields.set(key, new Float32Array(this._capacity));
|
||||
} else {
|
||||
// 默认使用Float32Array
|
||||
this.fields.set(key, new Float32Array(this._capacity));
|
||||
}
|
||||
} else if (type === 'boolean') {
|
||||
// 布尔值使用Float32Array存储为0/1
|
||||
this.fields.set(key, new Float32Array(this._capacity));
|
||||
} else if (type === 'string') {
|
||||
// 字符串专门处理
|
||||
this.stringFields.set(key, new Array(this._capacity));
|
||||
} else if (type === 'object' && value !== null) {
|
||||
// 处理集合类型
|
||||
if (serializeMapFields.has(key) || serializeSetFields.has(key) || serializeArrayFields.has(key)) {
|
||||
// 序列化存储
|
||||
this.serializedFields.set(key, new Array(this._capacity));
|
||||
}
|
||||
// 其他对象类型会在updateComponentAtIndex中作为复杂对象处理
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public addComponent(entityId: number, component: T): void {
|
||||
if (this.entityToIndex.has(entityId)) {
|
||||
const index = this.entityToIndex.get(entityId)!;
|
||||
this.updateComponentAtIndex(index, component);
|
||||
return;
|
||||
}
|
||||
|
||||
let index: number;
|
||||
if (this.freeIndices.length > 0) {
|
||||
index = this.freeIndices.pop()!;
|
||||
} else {
|
||||
index = this._size;
|
||||
if (index >= this._capacity) {
|
||||
this.resize(this._capacity * 2);
|
||||
}
|
||||
}
|
||||
|
||||
this.entityToIndex.set(entityId, index);
|
||||
this.indexToEntity[index] = entityId;
|
||||
this.updateComponentAtIndex(index, component);
|
||||
this._size++;
|
||||
}
|
||||
|
||||
private updateComponentAtIndex(index: number, component: T): void {
|
||||
const entityId = this.indexToEntity[index];
|
||||
const complexFieldMap = new Map<string, any>();
|
||||
const highPrecisionFields = (this.type as any).__highPrecisionFields || new Set();
|
||||
const serializeMapFields = (this.type as any).__serializeMapFields || new Set();
|
||||
const serializeSetFields = (this.type as any).__serializeSetFields || new Set();
|
||||
const serializeArrayFields = (this.type as any).__serializeArrayFields || new Set();
|
||||
const deepCopyFields = (this.type as any).__deepCopyFields || new Set();
|
||||
|
||||
// 处理所有字段
|
||||
for (const key in component) {
|
||||
if (component.hasOwnProperty(key) && key !== 'id') {
|
||||
const value = (component as any)[key];
|
||||
const type = typeof value;
|
||||
|
||||
if (type === 'number') {
|
||||
if (highPrecisionFields.has(key) || !this.fields.has(key)) {
|
||||
// 标记为高精度或未在TypedArray中的数值作为复杂对象存储
|
||||
complexFieldMap.set(key, value);
|
||||
} else {
|
||||
// 存储到TypedArray
|
||||
const array = this.fields.get(key)!;
|
||||
array[index] = value;
|
||||
}
|
||||
} else if (type === 'boolean' && this.fields.has(key)) {
|
||||
// 布尔值存储到TypedArray
|
||||
const array = this.fields.get(key)!;
|
||||
array[index] = value ? 1 : 0;
|
||||
} else if (this.stringFields.has(key)) {
|
||||
// 字符串字段专门处理
|
||||
const stringArray = this.stringFields.get(key)!;
|
||||
stringArray[index] = String(value);
|
||||
} else if (this.serializedFields.has(key)) {
|
||||
// 序列化字段处理
|
||||
const serializedArray = this.serializedFields.get(key)!;
|
||||
serializedArray[index] = this.serializeValue(value, key, serializeMapFields, serializeSetFields, serializeArrayFields);
|
||||
} else {
|
||||
// 复杂字段单独存储
|
||||
if (deepCopyFields.has(key)) {
|
||||
// 深拷贝处理
|
||||
complexFieldMap.set(key, this.deepClone(value));
|
||||
} else {
|
||||
complexFieldMap.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 存储复杂字段
|
||||
if (complexFieldMap.size > 0) {
|
||||
this.complexFields.set(entityId, complexFieldMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化值为JSON字符串
|
||||
*/
|
||||
private serializeValue(value: any, key: string, mapFields: Set<string>, setFields: Set<string>, arrayFields: Set<string>): string {
|
||||
try {
|
||||
if (mapFields.has(key) && value instanceof Map) {
|
||||
// Map序列化为数组形式
|
||||
return JSON.stringify(Array.from(value.entries()));
|
||||
} else if (setFields.has(key) && value instanceof Set) {
|
||||
// Set序列化为数组形式
|
||||
return JSON.stringify(Array.from(value));
|
||||
} else if (arrayFields.has(key) && Array.isArray(value)) {
|
||||
// Array直接序列化
|
||||
return JSON.stringify(value);
|
||||
} else {
|
||||
// 其他对象直接序列化
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`SoA序列化字段 ${key} 失败:`, error);
|
||||
return '{}';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化JSON字符串为值
|
||||
*/
|
||||
private deserializeValue(serialized: string, key: string, mapFields: Set<string>, setFields: Set<string>, arrayFields: Set<string>): any {
|
||||
try {
|
||||
const parsed = JSON.parse(serialized);
|
||||
|
||||
if (mapFields.has(key)) {
|
||||
// 恢复Map
|
||||
return new Map(parsed);
|
||||
} else if (setFields.has(key)) {
|
||||
// 恢复Set
|
||||
return new Set(parsed);
|
||||
} else if (arrayFields.has(key)) {
|
||||
// 恢复Array
|
||||
return parsed;
|
||||
} else {
|
||||
return parsed;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`SoA反序列化字段 ${key} 失败:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 深拷贝对象
|
||||
*/
|
||||
private deepClone(obj: any): any {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime());
|
||||
}
|
||||
|
||||
if (obj instanceof Array) {
|
||||
return obj.map(item => this.deepClone(item));
|
||||
}
|
||||
|
||||
if (obj instanceof Map) {
|
||||
const cloned = new Map();
|
||||
for (const [key, value] of obj.entries()) {
|
||||
cloned.set(key, this.deepClone(value));
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
if (obj instanceof Set) {
|
||||
const cloned = new Set();
|
||||
for (const value of obj.values()) {
|
||||
cloned.add(this.deepClone(value));
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
const cloned: any = {};
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
cloned[key] = this.deepClone(obj[key]);
|
||||
}
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
public getComponent(entityId: number): T | null {
|
||||
const index = this.entityToIndex.get(entityId);
|
||||
if (index === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 创建真正的组件实例以保持兼容性
|
||||
const component = new this.type() as any;
|
||||
const serializeMapFields = (this.type as any).__serializeMapFields || new Set();
|
||||
const serializeSetFields = (this.type as any).__serializeSetFields || new Set();
|
||||
const serializeArrayFields = (this.type as any).__serializeArrayFields || new Set();
|
||||
|
||||
// 恢复数值字段
|
||||
for (const [fieldName, array] of this.fields.entries()) {
|
||||
const value = array[index];
|
||||
const fieldType = this.getFieldType(fieldName);
|
||||
|
||||
if (fieldType === 'boolean') {
|
||||
component[fieldName] = value === 1;
|
||||
} else {
|
||||
component[fieldName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复字符串字段
|
||||
for (const [fieldName, stringArray] of this.stringFields.entries()) {
|
||||
component[fieldName] = stringArray[index];
|
||||
}
|
||||
|
||||
// 恢复序列化字段
|
||||
for (const [fieldName, serializedArray] of this.serializedFields.entries()) {
|
||||
const serialized = serializedArray[index];
|
||||
if (serialized) {
|
||||
component[fieldName] = this.deserializeValue(serialized, fieldName, serializeMapFields, serializeSetFields, serializeArrayFields);
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复复杂字段
|
||||
const complexFieldMap = this.complexFields.get(entityId);
|
||||
if (complexFieldMap) {
|
||||
for (const [fieldName, value] of complexFieldMap.entries()) {
|
||||
component[fieldName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return component as T;
|
||||
}
|
||||
|
||||
private getFieldType(fieldName: string): string {
|
||||
// 通过创建临时实例检查字段类型
|
||||
const tempInstance = new this.type();
|
||||
const value = (tempInstance as any)[fieldName];
|
||||
return typeof value;
|
||||
}
|
||||
|
||||
public hasComponent(entityId: number): boolean {
|
||||
return this.entityToIndex.has(entityId);
|
||||
}
|
||||
|
||||
public removeComponent(entityId: number): T | null {
|
||||
const index = this.entityToIndex.get(entityId);
|
||||
if (index === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取组件副本以便返回
|
||||
const component = this.getComponent(entityId);
|
||||
|
||||
// 清理复杂字段
|
||||
this.complexFields.delete(entityId);
|
||||
|
||||
this.entityToIndex.delete(entityId);
|
||||
this.freeIndices.push(index);
|
||||
this._size--;
|
||||
return component;
|
||||
}
|
||||
|
||||
private resize(newCapacity: number): void {
|
||||
// 调整数值字段的TypedArray
|
||||
for (const [fieldName, oldArray] of this.fields.entries()) {
|
||||
let newArray: Float32Array | Float64Array | Int32Array;
|
||||
|
||||
if (oldArray instanceof Float32Array) {
|
||||
newArray = new Float32Array(newCapacity);
|
||||
} else if (oldArray instanceof Float64Array) {
|
||||
newArray = new Float64Array(newCapacity);
|
||||
} else {
|
||||
newArray = new Int32Array(newCapacity);
|
||||
}
|
||||
|
||||
newArray.set(oldArray);
|
||||
this.fields.set(fieldName, newArray);
|
||||
}
|
||||
|
||||
// 调整字符串字段的数组
|
||||
for (const [fieldName, oldArray] of this.stringFields.entries()) {
|
||||
const newArray = new Array(newCapacity);
|
||||
for (let i = 0; i < oldArray.length; i++) {
|
||||
newArray[i] = oldArray[i];
|
||||
}
|
||||
this.stringFields.set(fieldName, newArray);
|
||||
}
|
||||
|
||||
// 调整序列化字段的数组
|
||||
for (const [fieldName, oldArray] of this.serializedFields.entries()) {
|
||||
const newArray = new Array(newCapacity);
|
||||
for (let i = 0; i < oldArray.length; i++) {
|
||||
newArray[i] = oldArray[i];
|
||||
}
|
||||
this.serializedFields.set(fieldName, newArray);
|
||||
}
|
||||
|
||||
this._capacity = newCapacity;
|
||||
}
|
||||
|
||||
public getActiveIndices(): number[] {
|
||||
return Array.from(this.entityToIndex.values());
|
||||
}
|
||||
|
||||
public getFieldArray(fieldName: string): Float32Array | Float64Array | Int32Array | null {
|
||||
return this.fields.get(fieldName) || null;
|
||||
}
|
||||
|
||||
public size(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.entityToIndex.clear();
|
||||
this.indexToEntity = [];
|
||||
this.freeIndices = [];
|
||||
this.complexFields.clear();
|
||||
this._size = 0;
|
||||
|
||||
// 重置数值字段数组
|
||||
for (const array of this.fields.values()) {
|
||||
array.fill(0);
|
||||
}
|
||||
|
||||
// 重置字符串字段数组
|
||||
for (const stringArray of this.stringFields.values()) {
|
||||
for (let i = 0; i < stringArray.length; i++) {
|
||||
stringArray[i] = undefined as any;
|
||||
}
|
||||
}
|
||||
|
||||
// 重置序列化字段数组
|
||||
for (const serializedArray of this.serializedFields.values()) {
|
||||
for (let i = 0; i < serializedArray.length; i++) {
|
||||
serializedArray[i] = undefined as any;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public compact(): void {
|
||||
if (this.freeIndices.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeEntries = Array.from(this.entityToIndex.entries())
|
||||
.sort((a, b) => a[1] - b[1]);
|
||||
|
||||
// 重新映射索引
|
||||
const newEntityToIndex = new Map<number, number>();
|
||||
const newIndexToEntity: number[] = [];
|
||||
|
||||
for (let newIndex = 0; newIndex < activeEntries.length; newIndex++) {
|
||||
const [entityId, oldIndex] = activeEntries[newIndex];
|
||||
|
||||
newEntityToIndex.set(entityId, newIndex);
|
||||
newIndexToEntity[newIndex] = entityId;
|
||||
|
||||
// 移动字段数据
|
||||
if (newIndex !== oldIndex) {
|
||||
// 移动数值字段
|
||||
for (const [, array] of this.fields.entries()) {
|
||||
array[newIndex] = array[oldIndex];
|
||||
}
|
||||
|
||||
// 移动字符串字段
|
||||
for (const [, stringArray] of this.stringFields.entries()) {
|
||||
stringArray[newIndex] = stringArray[oldIndex];
|
||||
}
|
||||
|
||||
// 移动序列化字段
|
||||
for (const [, serializedArray] of this.serializedFields.entries()) {
|
||||
serializedArray[newIndex] = serializedArray[oldIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.entityToIndex = newEntityToIndex;
|
||||
this.indexToEntity = newIndexToEntity;
|
||||
this.freeIndices = [];
|
||||
this._size = activeEntries.length;
|
||||
}
|
||||
|
||||
public getStats(): any {
|
||||
let totalMemory = 0;
|
||||
const fieldStats = new Map<string, any>();
|
||||
|
||||
for (const [fieldName, array] of this.fields.entries()) {
|
||||
let bytesPerElement: number;
|
||||
let typeName: string;
|
||||
|
||||
if (array instanceof Float32Array) {
|
||||
bytesPerElement = 4;
|
||||
typeName = 'float32';
|
||||
} else if (array instanceof Float64Array) {
|
||||
bytesPerElement = 8;
|
||||
typeName = 'float64';
|
||||
} else {
|
||||
bytesPerElement = 4;
|
||||
typeName = 'int32';
|
||||
}
|
||||
|
||||
const memory = array.length * bytesPerElement;
|
||||
totalMemory += memory;
|
||||
|
||||
fieldStats.set(fieldName, {
|
||||
size: this._size,
|
||||
capacity: array.length,
|
||||
type: typeName,
|
||||
memory: memory
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
size: this._size,
|
||||
capacity: this._capacity,
|
||||
usedSlots: this._size, // 兼容原测试
|
||||
fragmentation: this.freeIndices.length / this._capacity,
|
||||
memoryUsage: totalMemory,
|
||||
fieldStats: fieldStats
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行向量化批量操作
|
||||
* @param operation 操作函数,接收字段数组和活跃索引
|
||||
*/
|
||||
public performVectorizedOperation(operation: (fieldArrays: Map<string, Float32Array | Float64Array | Int32Array>, activeIndices: number[]) => void): void {
|
||||
const activeIndices = this.getActiveIndices();
|
||||
operation(this.fields, activeIndices);
|
||||
}
|
||||
}
|
||||
2
packages/core/src/ECS/Core/Storage/index.ts
Normal file
2
packages/core/src/ECS/Core/Storage/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { ComponentPool, ComponentPoolManager } from '../ComponentPool';
|
||||
export { ComponentStorage } from '../ComponentStorage';
|
||||
183
packages/core/src/ECS/CoreEvents.ts
Normal file
183
packages/core/src/ECS/CoreEvents.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
|
||||
|
||||
/**
|
||||
* ECS事件类型枚举
|
||||
* 定义实体组件系统中的所有事件类型
|
||||
*/
|
||||
export enum ECSEventType {
|
||||
// 实体相关事件
|
||||
ENTITY_CREATED = 'entity:created',
|
||||
ENTITY_DESTROYED = 'entity:destroyed',
|
||||
ENTITY_ENABLED = 'entity:enabled',
|
||||
ENTITY_DISABLED = 'entity:disabled',
|
||||
ENTITY_TAG_ADDED = 'entity:tag:added',
|
||||
ENTITY_TAG_REMOVED = 'entity:tag:removed',
|
||||
ENTITY_NAME_CHANGED = 'entity:name:changed',
|
||||
|
||||
// 组件相关事件
|
||||
COMPONENT_ADDED = 'component:added',
|
||||
COMPONENT_REMOVED = 'component:removed',
|
||||
COMPONENT_MODIFIED = 'component:modified',
|
||||
COMPONENT_ENABLED = 'component:enabled',
|
||||
COMPONENT_DISABLED = 'component:disabled',
|
||||
|
||||
// 系统相关事件
|
||||
SYSTEM_ADDED = 'system:added',
|
||||
SYSTEM_REMOVED = 'system:removed',
|
||||
SYSTEM_ENABLED = 'system:enabled',
|
||||
SYSTEM_DISABLED = 'system:disabled',
|
||||
SYSTEM_PROCESSING_START = 'system:processing:start',
|
||||
SYSTEM_PROCESSING_END = 'system:processing:end',
|
||||
SYSTEM_ERROR = 'system:error',
|
||||
|
||||
// 场景相关事件
|
||||
SCENE_CREATED = 'scene:created',
|
||||
SCENE_DESTROYED = 'scene:destroyed',
|
||||
SCENE_ACTIVATED = 'scene:activated',
|
||||
SCENE_DEACTIVATED = 'scene:deactivated',
|
||||
SCENE_PAUSED = 'scene:paused',
|
||||
SCENE_RESUMED = 'scene:resumed',
|
||||
|
||||
// 查询相关事件
|
||||
QUERY_EXECUTED = 'query:executed',
|
||||
QUERY_CACHE_HIT = 'query:cache:hit',
|
||||
QUERY_CACHE_MISS = 'query:cache:miss',
|
||||
QUERY_OPTIMIZED = 'query:optimized',
|
||||
|
||||
// 性能相关事件
|
||||
PERFORMANCE_WARNING = 'performance:warning',
|
||||
PERFORMANCE_CRITICAL = 'performance:critical',
|
||||
MEMORY_USAGE_HIGH = 'memory:usage:high',
|
||||
FRAME_RATE_DROP = 'frame:rate:drop',
|
||||
|
||||
// 索引相关事件
|
||||
INDEX_CREATED = 'index:created',
|
||||
INDEX_UPDATED = 'index:updated',
|
||||
INDEX_OPTIMIZED = 'index:optimized',
|
||||
|
||||
// Archetype相关事件
|
||||
ARCHETYPE_CREATED = 'archetype:created',
|
||||
ARCHETYPE_ENTITY_ADDED = 'archetype:entity:added',
|
||||
ARCHETYPE_ENTITY_REMOVED = 'archetype:entity:removed',
|
||||
|
||||
// 脏标记相关事件
|
||||
DIRTY_MARK_ADDED = 'dirty:mark:added',
|
||||
DIRTY_BATCH_PROCESSED = 'dirty:batch:processed',
|
||||
|
||||
// 错误和警告事件
|
||||
ERROR_OCCURRED = 'error:occurred',
|
||||
WARNING_ISSUED = 'warning:issued',
|
||||
|
||||
// 生命周期事件
|
||||
FRAMEWORK_INITIALIZED = 'framework:initialized',
|
||||
FRAMEWORK_SHUTDOWN = 'framework:shutdown',
|
||||
|
||||
// 调试相关事件
|
||||
DEBUG_INFO = 'debug:info',
|
||||
DEBUG_STATS_UPDATED = 'debug:stats:updated'
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件优先级枚举
|
||||
* 定义事件处理的优先级级别
|
||||
*/
|
||||
export enum EventPriority {
|
||||
LOWEST = 0,
|
||||
LOW = 25,
|
||||
NORMAL = 50,
|
||||
HIGH = 75,
|
||||
HIGHEST = 100,
|
||||
CRITICAL = 200
|
||||
}
|
||||
|
||||
/**
|
||||
* 预定义的事件类型常量
|
||||
* 提供类型安全的事件类型字符串
|
||||
*/
|
||||
export const EVENT_TYPES = {
|
||||
// 实体事件
|
||||
ENTITY: {
|
||||
CREATED: ECSEventType.ENTITY_CREATED,
|
||||
DESTROYED: ECSEventType.ENTITY_DESTROYED,
|
||||
ENABLED: ECSEventType.ENTITY_ENABLED,
|
||||
DISABLED: ECSEventType.ENTITY_DISABLED,
|
||||
TAG_ADDED: ECSEventType.ENTITY_TAG_ADDED,
|
||||
TAG_REMOVED: ECSEventType.ENTITY_TAG_REMOVED,
|
||||
NAME_CHANGED: ECSEventType.ENTITY_NAME_CHANGED
|
||||
},
|
||||
|
||||
// 组件事件
|
||||
COMPONENT: {
|
||||
ADDED: ECSEventType.COMPONENT_ADDED,
|
||||
REMOVED: ECSEventType.COMPONENT_REMOVED,
|
||||
MODIFIED: ECSEventType.COMPONENT_MODIFIED,
|
||||
ENABLED: ECSEventType.COMPONENT_ENABLED,
|
||||
DISABLED: ECSEventType.COMPONENT_DISABLED
|
||||
},
|
||||
|
||||
// 系统事件
|
||||
SYSTEM: {
|
||||
ADDED: ECSEventType.SYSTEM_ADDED,
|
||||
REMOVED: ECSEventType.SYSTEM_REMOVED,
|
||||
ENABLED: ECSEventType.SYSTEM_ENABLED,
|
||||
DISABLED: ECSEventType.SYSTEM_DISABLED,
|
||||
PROCESSING_START: ECSEventType.SYSTEM_PROCESSING_START,
|
||||
PROCESSING_END: ECSEventType.SYSTEM_PROCESSING_END,
|
||||
ERROR: ECSEventType.SYSTEM_ERROR
|
||||
},
|
||||
|
||||
// 性能事件
|
||||
PERFORMANCE: {
|
||||
WARNING: ECSEventType.PERFORMANCE_WARNING,
|
||||
CRITICAL: ECSEventType.PERFORMANCE_CRITICAL,
|
||||
MEMORY_HIGH: ECSEventType.MEMORY_USAGE_HIGH,
|
||||
FRAME_DROP: ECSEventType.FRAME_RATE_DROP
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* 事件类型验证器
|
||||
* 验证事件类型是否有效
|
||||
*/
|
||||
export class EventTypeValidator {
|
||||
private static validTypes = new Set<string>([
|
||||
...Object.values(ECSEventType),
|
||||
...Object.values(EVENT_TYPES.ENTITY),
|
||||
...Object.values(EVENT_TYPES.COMPONENT),
|
||||
...Object.values(EVENT_TYPES.SYSTEM),
|
||||
...Object.values(EVENT_TYPES.PERFORMANCE)
|
||||
]);
|
||||
|
||||
/**
|
||||
* 验证事件类型是否有效
|
||||
* @param eventType 事件类型
|
||||
* @returns 是否有效
|
||||
*/
|
||||
public static isValid(eventType: string): boolean {
|
||||
return this.validTypes.has(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有效的事件类型
|
||||
* @returns 事件类型数组
|
||||
*/
|
||||
public static getAllValidTypes(): string[] {
|
||||
return Array.from(this.validTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加自定义事件类型
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
public static addCustomType(eventType: string): void {
|
||||
this.validTypes.add(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除自定义事件类型
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
public static removeCustomType(eventType: string): void {
|
||||
this.validTypes.delete(eventType);
|
||||
}
|
||||
}
|
||||
1006
packages/core/src/ECS/Entity.ts
Normal file
1006
packages/core/src/ECS/Entity.ts
Normal file
File diff suppressed because it is too large
Load Diff
442
packages/core/src/ECS/Scene.ts
Normal file
442
packages/core/src/ECS/Scene.ts
Normal file
@@ -0,0 +1,442 @@
|
||||
import { Entity } from './Entity';
|
||||
import { EntityList } from './Utils/EntityList';
|
||||
import { EntityProcessorList } from './Utils/EntityProcessorList';
|
||||
import { IdentifierPool } from './Utils/IdentifierPool';
|
||||
import { EntitySystem } from './Systems/EntitySystem';
|
||||
import { ComponentStorageManager } from './Core/ComponentStorage';
|
||||
import { QuerySystem } from './Core/QuerySystem';
|
||||
import { TypeSafeEventSystem } from './Core/EventSystem';
|
||||
import { EventBus } from './Core/EventBus';
|
||||
|
||||
/**
|
||||
* 游戏场景类
|
||||
*
|
||||
* 管理游戏场景中的所有实体和系统,提供场景生命周期管理。
|
||||
* 场景是游戏世界的容器,负责协调实体和系统的运行。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* class GameScene extends Scene {
|
||||
* public initialize(): void {
|
||||
* // 创建游戏实体
|
||||
* const player = this.createEntity("Player");
|
||||
*
|
||||
* // 添加系统
|
||||
* this.addEntityProcessor(new MovementSystem());
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class Scene {
|
||||
/**
|
||||
* 场景名称
|
||||
*
|
||||
* 用于标识和调试的友好名称。
|
||||
*/
|
||||
public name: string = "";
|
||||
|
||||
/**
|
||||
* 场景中的实体集合
|
||||
*
|
||||
* 管理场景内所有实体的生命周期。
|
||||
*/
|
||||
public readonly entities: EntityList;
|
||||
|
||||
/**
|
||||
* 实体系统处理器集合
|
||||
*
|
||||
* 管理场景内所有实体系统的执行。
|
||||
*/
|
||||
public readonly entityProcessors: EntityProcessorList;
|
||||
|
||||
/**
|
||||
* 实体ID池
|
||||
*
|
||||
* 用于分配和回收实体的唯一标识符。
|
||||
*/
|
||||
public readonly identifierPool: IdentifierPool;
|
||||
|
||||
/**
|
||||
* 组件存储管理器
|
||||
*
|
||||
* 高性能的组件存储和查询系统。
|
||||
*/
|
||||
public readonly componentStorageManager: ComponentStorageManager;
|
||||
|
||||
/**
|
||||
* 查询系统
|
||||
*
|
||||
* 基于位掩码的高性能实体查询系统。
|
||||
*/
|
||||
public readonly querySystem: QuerySystem;
|
||||
|
||||
/**
|
||||
* 事件系统
|
||||
*
|
||||
* 类型安全的事件系统。
|
||||
*/
|
||||
public readonly eventSystem: TypeSafeEventSystem;
|
||||
|
||||
/**
|
||||
* 场景是否已开始运行
|
||||
*/
|
||||
private _didSceneBegin: boolean = false;
|
||||
|
||||
/**
|
||||
* 获取系统列表(兼容性属性)
|
||||
*/
|
||||
public get systems(): EntitySystem[] {
|
||||
return this.entityProcessors.processors;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建场景实例
|
||||
*/
|
||||
constructor() {
|
||||
this.entities = new EntityList(this);
|
||||
this.entityProcessors = new EntityProcessorList();
|
||||
this.identifierPool = new IdentifierPool();
|
||||
this.componentStorageManager = new ComponentStorageManager();
|
||||
this.querySystem = new QuerySystem();
|
||||
this.eventSystem = new TypeSafeEventSystem();
|
||||
|
||||
if (!Entity.eventBus) {
|
||||
Entity.eventBus = new EventBus(false);
|
||||
}
|
||||
|
||||
if (Entity.eventBus) {
|
||||
Entity.eventBus.onComponentAdded((data: any) => {
|
||||
this.eventSystem.emitSync('component:added', data);
|
||||
});
|
||||
}
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化场景
|
||||
*
|
||||
* 在场景创建时调用,子类可以重写此方法来设置初始实体和组件。
|
||||
*/
|
||||
public initialize(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景开始运行时的回调
|
||||
*
|
||||
* 在场景开始运行时调用,可以在此方法中执行场景启动逻辑。
|
||||
*/
|
||||
public onStart(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景卸载时的回调
|
||||
*
|
||||
* 在场景被销毁时调用,可以在此方法中执行清理工作。
|
||||
*/
|
||||
public unload(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始场景,启动实体处理器等
|
||||
*
|
||||
* 这个方法会启动场景。它将启动实体处理器等,并调用onStart方法。
|
||||
*/
|
||||
public begin() {
|
||||
// 启动实体处理器
|
||||
if (this.entityProcessors != null)
|
||||
this.entityProcessors.begin();
|
||||
|
||||
// 标记场景已开始运行并调用onStart方法
|
||||
this._didSceneBegin = true;
|
||||
this.onStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束场景,清除实体、实体处理器等
|
||||
*
|
||||
* 这个方法会结束场景。它将移除所有实体,结束实体处理器等,并调用unload方法。
|
||||
*/
|
||||
public end() {
|
||||
// 标记场景已结束运行
|
||||
this._didSceneBegin = false;
|
||||
|
||||
// 移除所有实体
|
||||
this.entities.removeAllEntities();
|
||||
|
||||
// 清理查询系统中的实体引用和缓存
|
||||
this.querySystem.setEntities([]);
|
||||
|
||||
// 清空组件存储
|
||||
this.componentStorageManager.clear();
|
||||
|
||||
// 结束实体处理器
|
||||
if (this.entityProcessors)
|
||||
this.entityProcessors.end();
|
||||
|
||||
// 调用卸载方法
|
||||
this.unload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新场景,更新实体组件、实体处理器等
|
||||
*/
|
||||
public update() {
|
||||
// 更新实体列表
|
||||
this.entities.updateLists();
|
||||
|
||||
// 更新实体处理器
|
||||
if (this.entityProcessors != null)
|
||||
this.entityProcessors.update();
|
||||
|
||||
// 更新实体组
|
||||
this.entities.update();
|
||||
|
||||
// 更新实体处理器的后处理方法
|
||||
if (this.entityProcessors != null)
|
||||
this.entityProcessors.lateUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体添加到此场景,并返回它
|
||||
* @param name 实体名称
|
||||
*/
|
||||
public createEntity(name: string) {
|
||||
let entity = new Entity(name, this.identifierPool.checkOut());
|
||||
|
||||
this.eventSystem.emitSync('entity:created', { entityName: name, entity, scene: this });
|
||||
|
||||
return this.addEntity(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在场景的实体列表中添加一个实体
|
||||
* @param entity 要添加的实体
|
||||
* @param deferCacheClear 是否延迟缓存清理(用于批量操作)
|
||||
*/
|
||||
public addEntity(entity: Entity, deferCacheClear: boolean = false) {
|
||||
this.entities.add(entity);
|
||||
entity.scene = this;
|
||||
|
||||
// 将实体添加到查询系统(可延迟缓存清理)
|
||||
this.querySystem.addEntity(entity, deferCacheClear);
|
||||
|
||||
// 触发实体添加事件
|
||||
this.eventSystem.emitSync('entity:added', { entity, scene: this });
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量创建实体(高性能版本)
|
||||
* @param count 要创建的实体数量
|
||||
* @param namePrefix 实体名称前缀
|
||||
* @returns 创建的实体列表
|
||||
*/
|
||||
public createEntities(count: number, namePrefix: string = "Entity"): Entity[] {
|
||||
const entities: Entity[] = [];
|
||||
|
||||
// 批量创建实体对象,不立即添加到系统
|
||||
for (let i = 0; i < count; i++) {
|
||||
const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut());
|
||||
entity.scene = this;
|
||||
entities.push(entity);
|
||||
}
|
||||
|
||||
// 批量添加到实体列表
|
||||
for (const entity of entities) {
|
||||
this.entities.add(entity);
|
||||
}
|
||||
|
||||
// 批量添加到查询系统(无重复检查,性能最优)
|
||||
this.querySystem.addEntitiesUnchecked(entities);
|
||||
|
||||
// 批量触发事件(可选,减少事件开销)
|
||||
this.eventSystem.emitSync('entities:batch_added', { entities, scene: this, count });
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量创建实体
|
||||
* @param count 要创建的实体数量
|
||||
* @param namePrefix 实体名称前缀
|
||||
* @returns 创建的实体列表
|
||||
*/
|
||||
public createEntitiesOld(count: number, namePrefix: string = "Entity"): Entity[] {
|
||||
const entities: Entity[] = [];
|
||||
|
||||
// 批量创建实体,延迟缓存清理
|
||||
for (let i = 0; i < count; i++) {
|
||||
const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut());
|
||||
entities.push(entity);
|
||||
this.addEntity(entity, true); // 延迟缓存清理
|
||||
}
|
||||
|
||||
// 最后统一清理缓存
|
||||
this.querySystem.clearCache();
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从场景中删除所有实体
|
||||
*/
|
||||
public destroyAllEntities() {
|
||||
this.entities.removeAllEntities();
|
||||
|
||||
// 清理查询系统中的实体引用和缓存
|
||||
this.querySystem.setEntities([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索并返回第一个具有名称的实体
|
||||
* @param name 实体名称
|
||||
*/
|
||||
public findEntity(name: string): Entity | null {
|
||||
return this.entities.findEntity(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查找实体
|
||||
* @param id 实体ID
|
||||
*/
|
||||
public findEntityById(id: number): Entity | null {
|
||||
return this.entities.findEntityById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签查找实体
|
||||
* @param tag 实体标签
|
||||
*/
|
||||
public findEntitiesByTag(tag: number): Entity[] {
|
||||
const result: Entity[] = [];
|
||||
for (const entity of this.entities.buffer) {
|
||||
if (entity.tag === tag) {
|
||||
result.push(entity);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称查找实体(别名方法)
|
||||
* @param name 实体名称
|
||||
*/
|
||||
public getEntityByName(name: string): Entity | null {
|
||||
return this.findEntity(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签查找实体(别名方法)
|
||||
* @param tag 实体标签
|
||||
*/
|
||||
public getEntitiesByTag(tag: number): Entity[] {
|
||||
return this.findEntitiesByTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在场景中添加一个EntitySystem处理器
|
||||
* @param processor 处理器
|
||||
*/
|
||||
public addEntityProcessor(processor: EntitySystem) {
|
||||
if (this.entityProcessors.processors.includes(processor)) {
|
||||
return processor;
|
||||
}
|
||||
|
||||
processor.scene = this;
|
||||
this.entityProcessors.add(processor);
|
||||
processor.initialize();
|
||||
processor.setUpdateOrder(this.entityProcessors.count - 1);
|
||||
return processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加系统到场景(addEntityProcessor的别名)
|
||||
* @param system 系统
|
||||
*/
|
||||
public addSystem(system: EntitySystem) {
|
||||
return this.addEntityProcessor(system);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从场景中删除EntitySystem处理器
|
||||
* @param processor 要删除的处理器
|
||||
*/
|
||||
public removeEntityProcessor(processor: EntitySystem) {
|
||||
this.entityProcessors.remove(processor);
|
||||
processor.reset();
|
||||
processor.scene = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的EntitySystem处理器
|
||||
* @param type 处理器类型
|
||||
*/
|
||||
public getEntityProcessor<T extends EntitySystem>(type: new (...args: unknown[]) => T): T | null {
|
||||
return this.entityProcessors.getProcessor(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景统计信息
|
||||
*/
|
||||
public getStats(): {
|
||||
entityCount: number;
|
||||
processorCount: number;
|
||||
componentStorageStats: Map<string, any>;
|
||||
} {
|
||||
return {
|
||||
entityCount: this.entities.count,
|
||||
processorCount: this.entityProcessors.count,
|
||||
componentStorageStats: this.componentStorageManager.getAllStats()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩组件存储(清理碎片)
|
||||
*/
|
||||
public compactComponentStorage(): void {
|
||||
this.componentStorageManager.compactAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景的调试信息
|
||||
*/
|
||||
public getDebugInfo(): {
|
||||
name: string;
|
||||
entityCount: number;
|
||||
processorCount: number;
|
||||
isRunning: boolean;
|
||||
entities: Array<{
|
||||
name: string;
|
||||
id: number;
|
||||
componentCount: number;
|
||||
componentTypes: string[];
|
||||
}>;
|
||||
processors: Array<{
|
||||
name: string;
|
||||
updateOrder: number;
|
||||
entityCount: number;
|
||||
}>;
|
||||
componentStats: Map<string, any>;
|
||||
} {
|
||||
return {
|
||||
name: this.constructor.name,
|
||||
entityCount: this.entities.count,
|
||||
processorCount: this.entityProcessors.count,
|
||||
isRunning: this._didSceneBegin,
|
||||
entities: this.entities.buffer.map(entity => ({
|
||||
name: entity.name,
|
||||
id: entity.id,
|
||||
componentCount: entity.components.length,
|
||||
componentTypes: entity.components.map(c => c.constructor.name)
|
||||
})),
|
||||
processors: this.entityProcessors.processors.map(processor => ({
|
||||
name: processor.constructor.name,
|
||||
updateOrder: processor.updateOrder,
|
||||
entityCount: (processor as any)._entities?.length || 0
|
||||
})),
|
||||
componentStats: this.componentStorageManager.getAllStats()
|
||||
};
|
||||
}
|
||||
}
|
||||
498
packages/core/src/ECS/Systems/EntitySystem.ts
Normal file
498
packages/core/src/ECS/Systems/EntitySystem.ts
Normal file
@@ -0,0 +1,498 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { PerformanceMonitor } from '../../Utils/PerformanceMonitor';
|
||||
import { Matcher } from '../Utils/Matcher';
|
||||
import type { Scene } from '../Scene';
|
||||
import type { ISystemBase } from '../../Types';
|
||||
import type { QuerySystem } from '../Core/QuerySystem';
|
||||
|
||||
/**
|
||||
* 实体系统的基类
|
||||
*
|
||||
* 用于处理一组符合特定条件的实体。系统是ECS架构中的逻辑处理单元,
|
||||
* 负责对拥有特定组件组合的实体执行业务逻辑。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* class MovementSystem extends EntitySystem {
|
||||
* constructor() {
|
||||
* super(Transform, Velocity);
|
||||
* }
|
||||
*
|
||||
* protected process(entities: Entity[]): void {
|
||||
* for (const entity of entities) {
|
||||
* const transform = entity.getComponent(Transform);
|
||||
* const velocity = entity.getComponent(Velocity);
|
||||
* transform.position.add(velocity.value);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export abstract class EntitySystem implements ISystemBase {
|
||||
private _updateOrder: number = 0;
|
||||
private _enabled: boolean = true;
|
||||
private _performanceMonitor = PerformanceMonitor.instance;
|
||||
private _systemName: string;
|
||||
private _initialized: boolean = false;
|
||||
private _matcher: Matcher;
|
||||
private _trackedEntities: Set<Entity> = new Set();
|
||||
private _lastQueryResult: Entity[] = [];
|
||||
|
||||
/**
|
||||
* 获取系统处理的实体列表(动态查询)
|
||||
*/
|
||||
public get entities(): readonly Entity[] {
|
||||
return this.queryEntities();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统的更新时序
|
||||
*/
|
||||
public get updateOrder(): number {
|
||||
return this._updateOrder;
|
||||
}
|
||||
|
||||
public set updateOrder(value: number) {
|
||||
this.setUpdateOrder(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统的启用状态
|
||||
*/
|
||||
public get enabled(): boolean {
|
||||
return this._enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置系统的启用状态
|
||||
*/
|
||||
public set enabled(value: boolean) {
|
||||
this._enabled = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统名称
|
||||
*/
|
||||
public get systemName(): string {
|
||||
return this._systemName;
|
||||
}
|
||||
|
||||
constructor(matcher?: Matcher) {
|
||||
this._matcher = matcher ? matcher : Matcher.empty();
|
||||
this._systemName = this.constructor.name;
|
||||
}
|
||||
|
||||
private _scene: Scene | null = null;
|
||||
|
||||
/**
|
||||
* 这个系统所属的场景
|
||||
*/
|
||||
public get scene(): Scene | null {
|
||||
return this._scene;
|
||||
}
|
||||
|
||||
public set scene(value: Scene | null) {
|
||||
this._scene = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体匹配器
|
||||
*/
|
||||
public get matcher(): Matcher {
|
||||
return this._matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置更新时序
|
||||
* @param order 更新时序
|
||||
*/
|
||||
public setUpdateOrder(order: number): void {
|
||||
this._updateOrder = order;
|
||||
if (this.scene && this.scene.entityProcessors) {
|
||||
this.scene.entityProcessors.setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统初始化(框架调用)
|
||||
*
|
||||
* 在系统创建时调用。框架内部使用,用户不应直接调用。
|
||||
*/
|
||||
public initialize(): void {
|
||||
// 防止重复初始化
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._initialized = true;
|
||||
|
||||
// 调用用户可重写的初始化方法
|
||||
this.onInitialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统初始化回调
|
||||
*
|
||||
* 子类可以重写此方法进行初始化操作。
|
||||
*/
|
||||
protected onInitialize(): void {
|
||||
// 初始化时触发一次实体查询,以便正确跟踪现有实体
|
||||
if (this.scene) {
|
||||
this.queryEntities();
|
||||
}
|
||||
// 子类可以重写此方法进行初始化
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置系统状态
|
||||
*
|
||||
* 当系统从场景中移除时调用,重置初始化状态以便重新添加时能正确初始化。
|
||||
*/
|
||||
public reset(): void {
|
||||
this._initialized = false;
|
||||
this._trackedEntities.clear();
|
||||
this._lastQueryResult = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询匹配的实体
|
||||
*/
|
||||
private queryEntities(): Entity[] {
|
||||
if (!this.scene?.querySystem || !this._matcher) {
|
||||
this._lastQueryResult = [];
|
||||
return [];
|
||||
}
|
||||
|
||||
const condition = this._matcher.getCondition();
|
||||
const querySystem = this.scene.querySystem;
|
||||
let currentEntities: Entity[] = [];
|
||||
|
||||
// 空条件返回所有实体
|
||||
if (this._matcher.isEmpty()) {
|
||||
currentEntities = querySystem.getAllEntities();
|
||||
} else if (this.isSingleCondition(condition)) {
|
||||
// 单一条件优化查询
|
||||
currentEntities = this.executeSingleConditionQuery(condition, querySystem);
|
||||
} else {
|
||||
// 复合查询
|
||||
currentEntities = this.executeComplexQuery(condition, querySystem);
|
||||
}
|
||||
|
||||
// 检查实体变化并触发回调
|
||||
this.updateEntityTracking(currentEntities);
|
||||
|
||||
this._lastQueryResult = currentEntities;
|
||||
return currentEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为单一条件查询
|
||||
*/
|
||||
private isSingleCondition(condition: any): boolean {
|
||||
const conditionCount =
|
||||
(condition.all.length > 0 ? 1 : 0) +
|
||||
(condition.any.length > 0 ? 1 : 0) +
|
||||
(condition.none.length > 0 ? 1 : 0) +
|
||||
(condition.tag !== undefined ? 1 : 0) +
|
||||
(condition.name !== undefined ? 1 : 0) +
|
||||
(condition.component !== undefined ? 1 : 0);
|
||||
|
||||
return conditionCount === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行单一条件查询
|
||||
*/
|
||||
private executeSingleConditionQuery(condition: any, querySystem: any): Entity[] {
|
||||
// 按标签查询
|
||||
if (condition.tag !== undefined) {
|
||||
return querySystem.queryByTag(condition.tag).entities;
|
||||
}
|
||||
|
||||
// 按名称查询
|
||||
if (condition.name !== undefined) {
|
||||
return querySystem.queryByName(condition.name).entities;
|
||||
}
|
||||
|
||||
// 单组件查询
|
||||
if (condition.component !== undefined) {
|
||||
return querySystem.queryByComponent(condition.component).entities;
|
||||
}
|
||||
|
||||
// 基础组件查询
|
||||
if (condition.all.length > 0 && condition.any.length === 0 && condition.none.length === 0) {
|
||||
return querySystem.queryAll(...condition.all).entities;
|
||||
}
|
||||
|
||||
if (condition.all.length === 0 && condition.any.length > 0 && condition.none.length === 0) {
|
||||
return querySystem.queryAny(...condition.any).entities;
|
||||
}
|
||||
|
||||
if (condition.all.length === 0 && condition.any.length === 0 && condition.none.length > 0) {
|
||||
return querySystem.queryNone(...condition.none).entities;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行复合查询
|
||||
*/
|
||||
private executeComplexQuery(condition: any, querySystem: QuerySystem): Entity[] {
|
||||
let result: Set<Entity> | null = null;
|
||||
|
||||
// 1. 应用标签条件作为基础集合
|
||||
if (condition.tag !== undefined) {
|
||||
const tagResult = querySystem.queryByTag(condition.tag);
|
||||
result = new Set(tagResult.entities);
|
||||
}
|
||||
|
||||
// 2. 应用名称条件
|
||||
if (condition.name !== undefined) {
|
||||
const nameResult = querySystem.queryByName(condition.name);
|
||||
const nameSet = new Set(nameResult.entities);
|
||||
|
||||
if (result) {
|
||||
result = new Set([...result].filter(e => nameSet.has(e)));
|
||||
} else {
|
||||
result = nameSet;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 应用单组件条件
|
||||
if (condition.component !== undefined) {
|
||||
const componentResult = querySystem.queryByComponent(condition.component);
|
||||
const componentSet = new Set(componentResult.entities);
|
||||
|
||||
if (result) {
|
||||
result = new Set([...result].filter(e => componentSet.has(e)));
|
||||
} else {
|
||||
result = componentSet;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 应用all条件
|
||||
if (condition.all.length > 0) {
|
||||
const allResult = querySystem.queryAll(...condition.all);
|
||||
const allSet = new Set(allResult.entities);
|
||||
|
||||
if (result) {
|
||||
result = new Set([...result].filter(e => allSet.has(e)));
|
||||
} else {
|
||||
result = allSet;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 应用any条件(求交集)
|
||||
if (condition.any.length > 0) {
|
||||
const anyResult = querySystem.queryAny(...condition.any);
|
||||
const anySet = new Set(anyResult.entities);
|
||||
|
||||
if (result) {
|
||||
result = new Set([...result].filter(e => anySet.has(e)));
|
||||
} else {
|
||||
result = anySet;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 应用none条件(排除)
|
||||
if (condition.none.length > 0) {
|
||||
if (!result) {
|
||||
// 如果没有前置条件,从所有实体开始
|
||||
result = new Set(querySystem.getAllEntities());
|
||||
}
|
||||
|
||||
const noneResult = querySystem.queryAny(...condition.none);
|
||||
const noneSet = new Set(noneResult.entities);
|
||||
result = new Set([...result].filter(e => !noneSet.has(e)));
|
||||
}
|
||||
|
||||
return result ? Array.from(result) : [];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 更新系统
|
||||
*
|
||||
* 在每帧调用,处理系统的主要逻辑。
|
||||
*/
|
||||
public update(): void {
|
||||
if (!this._enabled || !this.onCheckProcessing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startTime = this._performanceMonitor.startMonitoring(this._systemName);
|
||||
let entityCount = 0;
|
||||
|
||||
try {
|
||||
this.onBegin();
|
||||
// 动态查询实体并处理
|
||||
const entities = this.queryEntities();
|
||||
entityCount = entities.length;
|
||||
this.process(entities);
|
||||
} finally {
|
||||
this._performanceMonitor.endMonitoring(this._systemName, startTime, entityCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后期更新系统
|
||||
*
|
||||
* 在所有系统的update方法执行完毕后调用。
|
||||
*/
|
||||
public lateUpdate(): void {
|
||||
if (!this._enabled || !this.onCheckProcessing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startTime = this._performanceMonitor.startMonitoring(`${this._systemName}_Late`);
|
||||
let entityCount = 0;
|
||||
|
||||
try {
|
||||
// 动态查询实体并处理
|
||||
const entities = this.queryEntities();
|
||||
entityCount = entities.length;
|
||||
this.lateProcess(entities);
|
||||
this.onEnd();
|
||||
} finally {
|
||||
this._performanceMonitor.endMonitoring(`${this._systemName}_Late`, startTime, entityCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在系统处理开始前调用
|
||||
*
|
||||
* 子类可以重写此方法进行预处理操作。
|
||||
*/
|
||||
protected onBegin(): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理实体列表
|
||||
*
|
||||
* 系统的核心逻辑,子类必须实现此方法来定义具体的处理逻辑。
|
||||
*
|
||||
* @param entities 要处理的实体列表
|
||||
*/
|
||||
protected process(entities: Entity[]): void {
|
||||
// 子类必须实现此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 后期处理实体列表
|
||||
*
|
||||
* 在主要处理逻辑之后执行,子类可以重写此方法。
|
||||
*
|
||||
* @param entities 要处理的实体列表
|
||||
*/
|
||||
protected lateProcess(entities: Entity[]): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统处理完毕后调用
|
||||
*
|
||||
* 子类可以重写此方法进行后处理操作。
|
||||
*/
|
||||
protected onEnd(): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查系统是否需要处理
|
||||
*
|
||||
* 在启用系统时有用,但仅偶尔需要处理。
|
||||
* 这只影响处理,不影响事件或订阅列表。
|
||||
*
|
||||
* @returns 如果系统应该处理,则为true,如果不处理则为false
|
||||
*/
|
||||
protected onCheckProcessing(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统的性能数据
|
||||
*
|
||||
* @returns 性能数据或undefined
|
||||
*/
|
||||
public getPerformanceData() {
|
||||
return this._performanceMonitor.getSystemData(this._systemName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统的性能统计
|
||||
*
|
||||
* @returns 性能统计或undefined
|
||||
*/
|
||||
public getPerformanceStats() {
|
||||
return this._performanceMonitor.getSystemStats(this._systemName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置系统的性能数据
|
||||
*/
|
||||
public resetPerformanceData(): void {
|
||||
this._performanceMonitor.resetSystem(this._systemName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取系统信息的字符串表示
|
||||
*
|
||||
* @returns 系统信息字符串
|
||||
*/
|
||||
public toString(): string {
|
||||
const entityCount = this.entities.length;
|
||||
const perfData = this.getPerformanceData();
|
||||
const perfInfo = perfData ? ` (${perfData.executionTime.toFixed(2)}ms)` : '';
|
||||
|
||||
return `${this._systemName}[${entityCount} entities]${perfInfo}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新实体跟踪,检查新增和移除的实体
|
||||
*/
|
||||
private updateEntityTracking(currentEntities: Entity[]): void {
|
||||
const currentSet = new Set(currentEntities);
|
||||
|
||||
// 检查新增的实体
|
||||
for (const entity of currentEntities) {
|
||||
if (!this._trackedEntities.has(entity)) {
|
||||
this._trackedEntities.add(entity);
|
||||
this.onAdded(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查移除的实体
|
||||
for (const entity of this._trackedEntities) {
|
||||
if (!currentSet.has(entity)) {
|
||||
this._trackedEntities.delete(entity);
|
||||
this.onRemoved(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当实体被添加到系统时调用
|
||||
*
|
||||
* 子类可以重写此方法来处理实体添加事件。
|
||||
*
|
||||
* @param entity 被添加的实体
|
||||
*/
|
||||
protected onAdded(_entity: Entity): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 当实体从系统中移除时调用
|
||||
*
|
||||
* 子类可以重写此方法来处理实体移除事件。
|
||||
*
|
||||
* @param entity 被移除的实体
|
||||
*/
|
||||
protected onRemoved(_entity: Entity): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
}
|
||||
58
packages/core/src/ECS/Systems/IntervalSystem.ts
Normal file
58
packages/core/src/ECS/Systems/IntervalSystem.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { EntitySystem } from './EntitySystem';
|
||||
import { Matcher } from '../Utils/Matcher';
|
||||
import { Time } from '../../Utils/Time';
|
||||
|
||||
/**
|
||||
* 间隔系统抽象类
|
||||
* 定义一个按时间间隔处理的抽象类,继承自EntitySystem类
|
||||
* 子类需要实现process方法,用于实现具体的处理逻辑
|
||||
*/
|
||||
export abstract class IntervalSystem extends EntitySystem {
|
||||
/** 累积增量以跟踪间隔 */
|
||||
private acc: number = 0;
|
||||
/** 更新之间需要等待多长时间 */
|
||||
private readonly interval: number;
|
||||
/** 时间间隔的余数,用于计算下一次需要等待的时间 */
|
||||
private intervalRemainder: number = 0;
|
||||
|
||||
/**
|
||||
* 构造函数,初始化时间间隔
|
||||
* @param interval 时间间隔
|
||||
* @param matcher 实体匹配器
|
||||
*/
|
||||
constructor(interval: number, matcher?: Matcher) {
|
||||
super(matcher);
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否需要进行处理
|
||||
* 如果需要进行处理,则更新累积增量和时间间隔余数,返回true
|
||||
* 否则返回false
|
||||
*/
|
||||
protected override onCheckProcessing(): boolean {
|
||||
// 更新累积增量
|
||||
this.acc += Time.deltaTime;
|
||||
|
||||
// 如果累积增量超过时间间隔,则进行处理
|
||||
if (this.acc >= this.interval) {
|
||||
// 更新时间间隔余数
|
||||
this.intervalRemainder = this.acc - this.interval;
|
||||
// 重置累积增量
|
||||
this.acc = 0;
|
||||
// 返回true,表示需要进行处理
|
||||
return true;
|
||||
}
|
||||
|
||||
// 返回false,表示不需要进行处理
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本系统上次处理后的实际delta值
|
||||
* 实际delta值等于时间间隔加上时间间隔余数
|
||||
*/
|
||||
protected getIntervalDelta(): number {
|
||||
return this.interval + this.intervalRemainder;
|
||||
}
|
||||
}
|
||||
23
packages/core/src/ECS/Systems/PassiveSystem.ts
Normal file
23
packages/core/src/ECS/Systems/PassiveSystem.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { EntitySystem } from './EntitySystem';
|
||||
import { Entity } from '../Entity';
|
||||
import { Matcher } from '../Utils/Matcher';
|
||||
|
||||
/**
|
||||
* 被动实体系统
|
||||
* 定义一个被动的实体系统,继承自EntitySystem类
|
||||
* 被动的实体系统不会对实体进行任何修改,只会被动地接收实体的变化事件
|
||||
*/
|
||||
export abstract class PassiveSystem extends EntitySystem {
|
||||
|
||||
constructor(matcher?: Matcher) {
|
||||
super(matcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* 不进行任何处理
|
||||
* @param entities 实体数组,未被使用
|
||||
*/
|
||||
protected override process(entities: Entity[]): void {
|
||||
// 被动系统不进行任何处理
|
||||
}
|
||||
}
|
||||
29
packages/core/src/ECS/Systems/ProcessingSystem.ts
Normal file
29
packages/core/src/ECS/Systems/ProcessingSystem.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { EntitySystem } from './EntitySystem';
|
||||
import { Entity } from '../Entity';
|
||||
import { Matcher } from '../Utils/Matcher';
|
||||
|
||||
/**
|
||||
* 处理系统抽象类
|
||||
* 定义一个处理实体的抽象类,继承自EntitySystem类
|
||||
* 子类需要实现processSystem方法,用于实现具体的处理逻辑
|
||||
*/
|
||||
export abstract class ProcessingSystem extends EntitySystem {
|
||||
|
||||
constructor(matcher?: Matcher) {
|
||||
super(matcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理实体,每帧调用processSystem方法进行处理
|
||||
* @param entities 实体数组,未被使用
|
||||
*/
|
||||
protected override process(entities: Entity[]): void {
|
||||
// 调用子类实现的processSystem方法进行实体处理
|
||||
this.processSystem();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理实体的具体方法,由子类实现
|
||||
*/
|
||||
public abstract processSystem(): void;
|
||||
}
|
||||
5
packages/core/src/ECS/Systems/index.ts
Normal file
5
packages/core/src/ECS/Systems/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// ECS系统导出
|
||||
export { EntitySystem } from './EntitySystem';
|
||||
export { ProcessingSystem } from './ProcessingSystem';
|
||||
export { PassiveSystem } from './PassiveSystem';
|
||||
export { IntervalSystem } from './IntervalSystem';
|
||||
746
packages/core/src/ECS/Utils/BigIntCompatibility.ts
Normal file
746
packages/core/src/ECS/Utils/BigIntCompatibility.ts
Normal file
@@ -0,0 +1,746 @@
|
||||
/**
|
||||
* BigInt兼容性抽象层
|
||||
*
|
||||
* 为不支持BigInt的环境提供兼容实现,确保ECS框架在所有平台上都能正常运行。
|
||||
* 自动检测运行时环境的BigInt支持情况,并提供统一的接口。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 创建兼容的BigInt值
|
||||
* const value = BigIntFactory.create(123);
|
||||
*
|
||||
* // 位运算
|
||||
* const result = value.or(BigIntFactory.create(456));
|
||||
*
|
||||
* // 检查兼容性
|
||||
* console.log(BigIntFactory.isNativeSupported()); // true/false
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* BigInt兼容接口
|
||||
*
|
||||
* 定义了BigInt的基本操作接口,支持原生BigInt和兼容实现的统一调用。
|
||||
*/
|
||||
export interface IBigIntLike {
|
||||
/**
|
||||
* 获取数值表示
|
||||
* @returns 数值
|
||||
*/
|
||||
valueOf(): number;
|
||||
|
||||
/**
|
||||
* 转换为字符串
|
||||
* @param radix 进制,支持2、10、16
|
||||
* @returns 字符串表示
|
||||
*/
|
||||
toString(radix?: number): string;
|
||||
|
||||
/**
|
||||
* 位运算:与
|
||||
* @param other 另一个BigInt值
|
||||
* @returns 运算结果
|
||||
*/
|
||||
and(other: IBigIntLike): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 位运算:或
|
||||
* @param other 另一个BigInt值
|
||||
* @returns 运算结果
|
||||
*/
|
||||
or(other: IBigIntLike): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 位运算:异或
|
||||
* @param other 另一个BigInt值
|
||||
* @returns 运算结果
|
||||
*/
|
||||
xor(other: IBigIntLike): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 位运算:非
|
||||
* @param maxBits 最大位数限制
|
||||
* @returns 运算结果
|
||||
*/
|
||||
not(maxBits?: number): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 左移位运算
|
||||
* @param bits 移位数
|
||||
* @returns 运算结果
|
||||
*/
|
||||
shiftLeft(bits: number): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 右移位运算
|
||||
* @param bits 移位数
|
||||
* @returns 运算结果
|
||||
*/
|
||||
shiftRight(bits: number): IBigIntLike;
|
||||
|
||||
/**
|
||||
* 相等比较
|
||||
* @param other 另一个BigInt值
|
||||
* @returns 是否相等
|
||||
*/
|
||||
equals(other: IBigIntLike): boolean;
|
||||
|
||||
/**
|
||||
* 检查是否为零
|
||||
* @returns 是否为零
|
||||
*/
|
||||
isZero(): boolean;
|
||||
|
||||
/**
|
||||
* 创建副本
|
||||
* @returns 新的实例
|
||||
*/
|
||||
clone(): IBigIntLike;
|
||||
}
|
||||
|
||||
/**
|
||||
* 原生BigInt包装器
|
||||
*
|
||||
* 为支持BigInt的环境提供统一接口包装。
|
||||
*/
|
||||
class NativeBigInt implements IBigIntLike {
|
||||
constructor(private value: bigint) {}
|
||||
|
||||
valueOf(): number {
|
||||
return Number(this.value);
|
||||
}
|
||||
|
||||
toString(radix?: number): string {
|
||||
if (radix !== undefined && radix !== 10 && radix !== 16 && radix !== 2) {
|
||||
throw new Error('Only radix 2, 10, and 16 are supported');
|
||||
}
|
||||
const result = this.value.toString(radix);
|
||||
if (radix === 16) {
|
||||
return result.toUpperCase();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
and(other: IBigIntLike): IBigIntLike {
|
||||
const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf());
|
||||
return new NativeBigInt(this.value & otherBigInt);
|
||||
}
|
||||
|
||||
or(other: IBigIntLike): IBigIntLike {
|
||||
const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf());
|
||||
return new NativeBigInt(this.value | otherBigInt);
|
||||
}
|
||||
|
||||
xor(other: IBigIntLike): IBigIntLike {
|
||||
const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf());
|
||||
return new NativeBigInt(this.value ^ otherBigInt);
|
||||
}
|
||||
|
||||
not(maxBits: number = 64): IBigIntLike {
|
||||
const mask = (BigInt(1) << BigInt(maxBits)) - BigInt(1);
|
||||
return new NativeBigInt((~this.value) & mask);
|
||||
}
|
||||
|
||||
shiftLeft(bits: number): IBigIntLike {
|
||||
return new NativeBigInt(this.value << BigInt(bits));
|
||||
}
|
||||
|
||||
shiftRight(bits: number): IBigIntLike {
|
||||
return new NativeBigInt(this.value >> BigInt(bits));
|
||||
}
|
||||
|
||||
equals(other: IBigIntLike): boolean {
|
||||
const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf());
|
||||
return this.value === otherBigInt;
|
||||
}
|
||||
|
||||
isZero(): boolean {
|
||||
return this.value === BigInt(0);
|
||||
}
|
||||
|
||||
clone(): IBigIntLike {
|
||||
return new NativeBigInt(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组模拟BigInt实现
|
||||
*
|
||||
* 为不支持BigInt的环境提供兼容实现,使用32位数组模拟大整数运算。
|
||||
* 性能略低于原生BigInt,但保证功能一致性。
|
||||
*/
|
||||
class ArrayBigInt implements IBigIntLike {
|
||||
private chunks: number[] = []; // 32位块数组
|
||||
private static readonly CHUNK_SIZE = 32;
|
||||
private static readonly CHUNK_MASK = 0xFFFFFFFF;
|
||||
private static readonly CHUNK_MAX = 0x100000000; // 2^32
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param value 初始值,可以是数值、字符串或数组
|
||||
*/
|
||||
constructor(value: number | string | number[] = 0) {
|
||||
if (typeof value === 'number') {
|
||||
this.fromNumber(value);
|
||||
} else if (typeof value === 'string') {
|
||||
this.fromString(value);
|
||||
} else {
|
||||
this.chunks = value.slice();
|
||||
}
|
||||
this.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数值初始化
|
||||
* @param value 数值
|
||||
*/
|
||||
private fromNumber(value: number): void {
|
||||
this.chunks = [];
|
||||
|
||||
// 处理负数(在位运算中通常不会遇到)
|
||||
if (value < 0) {
|
||||
value = Math.abs(value);
|
||||
}
|
||||
|
||||
if (value === 0) {
|
||||
this.chunks = [0];
|
||||
return;
|
||||
}
|
||||
|
||||
while (value > 0) {
|
||||
this.chunks.push(value & ArrayBigInt.CHUNK_MASK);
|
||||
value = Math.floor(value / ArrayBigInt.CHUNK_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串初始化
|
||||
* @param value 字符串(支持十进制、十六进制、二进制)
|
||||
*/
|
||||
private fromString(value: string): void {
|
||||
value = value.trim();
|
||||
|
||||
if (value.startsWith('0x') || value.startsWith('0X')) {
|
||||
// 十六进制
|
||||
this.fromHexString(value.substring(2));
|
||||
} else if (value.startsWith('0b') || value.startsWith('0B')) {
|
||||
// 二进制
|
||||
this.fromBinaryString(value.substring(2));
|
||||
} else {
|
||||
// 十进制
|
||||
this.fromDecimalString(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从十六进制字符串初始化
|
||||
* @param hex 十六进制字符串
|
||||
*/
|
||||
private fromHexString(hex: string): void {
|
||||
this.chunks = [0];
|
||||
|
||||
for (let i = hex.length - 1; i >= 0; i -= 8) {
|
||||
const start = Math.max(0, i - 7);
|
||||
const chunk = parseInt(hex.substring(start, i + 1), 16);
|
||||
this.chunks.push(chunk);
|
||||
}
|
||||
|
||||
this.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从二进制字符串初始化
|
||||
* @param binary 二进制字符串
|
||||
*/
|
||||
private fromBinaryString(binary: string): void {
|
||||
this.chunks = [0];
|
||||
|
||||
for (let i = binary.length - 1; i >= 0; i -= 32) {
|
||||
const start = Math.max(0, i - 31);
|
||||
const chunk = parseInt(binary.substring(start, i + 1), 2);
|
||||
this.chunks.push(chunk);
|
||||
}
|
||||
|
||||
this.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从十进制字符串初始化
|
||||
* @param decimal 十进制字符串
|
||||
*/
|
||||
private fromDecimalString(decimal: string): void {
|
||||
// 简化实现,直接转换为数值(在ECS位运算场景中通常是小数值)
|
||||
const num = parseInt(decimal, 10);
|
||||
this.fromNumber(num);
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化数组,移除前导零
|
||||
*/
|
||||
private normalize(): void {
|
||||
while (this.chunks.length > 1 && this.chunks[this.chunks.length - 1] === 0) {
|
||||
this.chunks.pop();
|
||||
}
|
||||
|
||||
if (this.chunks.length === 0) {
|
||||
this.chunks = [0];
|
||||
}
|
||||
}
|
||||
|
||||
valueOf(): number {
|
||||
let result = 0;
|
||||
let multiplier = 1;
|
||||
|
||||
for (const chunk of this.chunks) {
|
||||
result += chunk * multiplier;
|
||||
multiplier *= ArrayBigInt.CHUNK_MAX;
|
||||
|
||||
// 防止溢出
|
||||
if (multiplier > Number.MAX_SAFE_INTEGER) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
toString(radix: number = 10): string {
|
||||
if (radix !== 10 && radix !== 16 && radix !== 2) {
|
||||
throw new Error('Only radix 2, 10, and 16 are supported');
|
||||
}
|
||||
|
||||
if (this.isZero()) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if (radix === 10) {
|
||||
// 简化实现,转换为数值
|
||||
return this.valueOf().toString(10);
|
||||
} else if (radix === 16) {
|
||||
let result = '';
|
||||
for (let i = this.chunks.length - 1; i >= 0; i--) {
|
||||
const hex = this.chunks[i].toString(16);
|
||||
result += i === this.chunks.length - 1 ? hex : hex.padStart(8, '0');
|
||||
}
|
||||
return result.toUpperCase();
|
||||
} else if (radix === 2) {
|
||||
let result = '';
|
||||
for (let i = this.chunks.length - 1; i >= 0; i--) {
|
||||
const binary = this.chunks[i].toString(2);
|
||||
result += i === this.chunks.length - 1 ? binary : binary.padStart(32, '0');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return this.valueOf().toString(radix);
|
||||
}
|
||||
|
||||
and(other: IBigIntLike): IBigIntLike {
|
||||
const otherArray = other as ArrayBigInt;
|
||||
const maxLength = Math.max(this.chunks.length, otherArray.chunks.length);
|
||||
const result: number[] = [];
|
||||
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const a = i < this.chunks.length ? this.chunks[i] : 0;
|
||||
const b = i < otherArray.chunks.length ? otherArray.chunks[i] : 0;
|
||||
result.push(a & b);
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result);
|
||||
}
|
||||
|
||||
or(other: IBigIntLike): IBigIntLike {
|
||||
const otherArray = other as ArrayBigInt;
|
||||
const maxLength = Math.max(this.chunks.length, otherArray.chunks.length);
|
||||
const result: number[] = [];
|
||||
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const a = i < this.chunks.length ? this.chunks[i] : 0;
|
||||
const b = i < otherArray.chunks.length ? otherArray.chunks[i] : 0;
|
||||
result.push(a | b);
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result);
|
||||
}
|
||||
|
||||
xor(other: IBigIntLike): IBigIntLike {
|
||||
const otherArray = other as ArrayBigInt;
|
||||
const maxLength = Math.max(this.chunks.length, otherArray.chunks.length);
|
||||
const result: number[] = [];
|
||||
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const a = i < this.chunks.length ? this.chunks[i] : 0;
|
||||
const b = i < otherArray.chunks.length ? otherArray.chunks[i] : 0;
|
||||
result.push(a ^ b);
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result);
|
||||
}
|
||||
|
||||
not(maxBits: number = 64): IBigIntLike {
|
||||
const maxChunks = Math.ceil(maxBits / ArrayBigInt.CHUNK_SIZE);
|
||||
const result: number[] = [];
|
||||
|
||||
for (let i = 0; i < maxChunks; i++) {
|
||||
const chunk = i < this.chunks.length ? this.chunks[i] : 0;
|
||||
|
||||
if (i === maxChunks - 1) {
|
||||
// 最后一个块需要处理剩余位数
|
||||
const remainingBits = maxBits % ArrayBigInt.CHUNK_SIZE;
|
||||
if (remainingBits > 0) {
|
||||
const mask = (1 << remainingBits) - 1;
|
||||
result.push((~chunk) & mask);
|
||||
} else {
|
||||
result.push((~chunk) & ArrayBigInt.CHUNK_MASK);
|
||||
}
|
||||
} else {
|
||||
result.push((~chunk) & ArrayBigInt.CHUNK_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result);
|
||||
}
|
||||
|
||||
shiftLeft(bits: number): IBigIntLike {
|
||||
if (bits === 0) {
|
||||
return this.clone();
|
||||
}
|
||||
|
||||
if (bits < 0) {
|
||||
return this.shiftRight(-bits);
|
||||
}
|
||||
|
||||
const chunkShift = Math.floor(bits / ArrayBigInt.CHUNK_SIZE);
|
||||
const bitShift = bits % ArrayBigInt.CHUNK_SIZE;
|
||||
|
||||
const result: number[] = new Array(chunkShift).fill(0);
|
||||
|
||||
if (bitShift === 0) {
|
||||
// 整块移位
|
||||
result.push(...this.chunks);
|
||||
} else {
|
||||
// 部分位移位
|
||||
let carry = 0;
|
||||
for (const chunk of this.chunks) {
|
||||
const shifted = (chunk << bitShift) | carry;
|
||||
result.push(shifted & ArrayBigInt.CHUNK_MASK);
|
||||
carry = chunk >>> (ArrayBigInt.CHUNK_SIZE - bitShift);
|
||||
}
|
||||
|
||||
if (carry > 0) {
|
||||
result.push(carry);
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result);
|
||||
}
|
||||
|
||||
shiftRight(bits: number): IBigIntLike {
|
||||
if (bits === 0) {
|
||||
return this.clone();
|
||||
}
|
||||
|
||||
if (bits < 0) {
|
||||
return this.shiftLeft(-bits);
|
||||
}
|
||||
|
||||
const chunkShift = Math.floor(bits / ArrayBigInt.CHUNK_SIZE);
|
||||
const bitShift = bits % ArrayBigInt.CHUNK_SIZE;
|
||||
|
||||
if (chunkShift >= this.chunks.length) {
|
||||
return new ArrayBigInt(0);
|
||||
}
|
||||
|
||||
const result: number[] = [];
|
||||
|
||||
if (bitShift === 0) {
|
||||
// 整块移位
|
||||
for (let i = chunkShift; i < this.chunks.length; i++) {
|
||||
result.push(this.chunks[i]);
|
||||
}
|
||||
} else {
|
||||
// 部分位移位
|
||||
let carry = 0;
|
||||
for (let i = this.chunks.length - 1; i >= chunkShift; i--) {
|
||||
const chunk = this.chunks[i];
|
||||
const shifted = (carry << (ArrayBigInt.CHUNK_SIZE - bitShift)) | (chunk >>> bitShift);
|
||||
result.unshift(shifted);
|
||||
carry = chunk & ((1 << bitShift) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayBigInt(result.length > 0 ? result : [0]);
|
||||
}
|
||||
|
||||
equals(other: IBigIntLike): boolean {
|
||||
if (!(other instanceof ArrayBigInt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.chunks.length !== other.chunks.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.chunks.length; i++) {
|
||||
if (this.chunks[i] !== other.chunks[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isZero(): boolean {
|
||||
return this.chunks.length === 1 && this.chunks[0] === 0;
|
||||
}
|
||||
|
||||
clone(): IBigIntLike {
|
||||
return new ArrayBigInt(this.chunks.slice());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BigInt工厂类
|
||||
*
|
||||
* 自动检测运行时环境的BigInt支持情况,并提供统一的创建接口。
|
||||
* 在支持BigInt的环境中使用原生实现,在不支持的环境中使用兼容实现。
|
||||
*/
|
||||
export class BigIntFactory {
|
||||
private static _supportsBigInt: boolean | null = null;
|
||||
private static _cachedZero: IBigIntLike | null = null;
|
||||
private static _cachedOne: IBigIntLike | null = null;
|
||||
// 缓存检测结果以避免重复检测
|
||||
|
||||
/**
|
||||
* 检查是否支持原生BigInt
|
||||
* @returns 是否支持原生BigInt
|
||||
*/
|
||||
public static isNativeSupported(): boolean {
|
||||
if (this._supportsBigInt === null) {
|
||||
this._supportsBigInt = this.detectBigIntSupport();
|
||||
}
|
||||
return this._supportsBigInt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测BigInt支持情况
|
||||
* @returns 是否支持BigInt
|
||||
*/
|
||||
private static detectBigIntSupport(): boolean {
|
||||
try {
|
||||
// 检查BigInt构造函数是否存在
|
||||
if (typeof BigInt === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查基本BigInt操作
|
||||
const test1 = BigInt(1);
|
||||
const test2 = BigInt(2);
|
||||
const result = test1 | test2;
|
||||
|
||||
// 检查字面量支持
|
||||
const literal = eval('1n'); // 使用eval避免语法错误
|
||||
|
||||
// 检查类型
|
||||
if (typeof result !== 'bigint' || typeof literal !== 'bigint') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查基本运算
|
||||
const shifted = test1 << BigInt(1);
|
||||
const compared = test1 === BigInt(1);
|
||||
|
||||
return typeof shifted === 'bigint' && compared === true;
|
||||
} catch (error) {
|
||||
// 任何异常都表示不支持
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建BigInt兼容值
|
||||
* @param value 初始值
|
||||
* @returns IBigIntLike实例
|
||||
*/
|
||||
public static create(value: number | string | bigint = 0): IBigIntLike {
|
||||
if (this.isNativeSupported()) {
|
||||
let bigintValue: bigint;
|
||||
|
||||
if (typeof value === 'bigint') {
|
||||
bigintValue = value;
|
||||
} else if (typeof value === 'string') {
|
||||
bigintValue = BigInt(value);
|
||||
} else {
|
||||
bigintValue = BigInt(value);
|
||||
}
|
||||
|
||||
return new NativeBigInt(bigintValue);
|
||||
} else {
|
||||
// 转换bigint类型到兼容类型
|
||||
let compatValue: number | string;
|
||||
|
||||
if (typeof value === 'bigint') {
|
||||
compatValue = value.toString();
|
||||
} else {
|
||||
compatValue = value;
|
||||
}
|
||||
|
||||
return new ArrayBigInt(compatValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建零值
|
||||
* @returns 零值的IBigIntLike实例
|
||||
*/
|
||||
public static zero(): IBigIntLike {
|
||||
if (!this._cachedZero) {
|
||||
this._cachedZero = this.create(0);
|
||||
}
|
||||
return this._cachedZero;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建1值
|
||||
* @returns 1值的IBigIntLike实例
|
||||
*/
|
||||
public static one(): IBigIntLike {
|
||||
if (!this._cachedOne) {
|
||||
this._cachedOne = this.create(1);
|
||||
}
|
||||
return this._cachedOne;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从二进制字符串创建
|
||||
* @param binary 二进制字符串
|
||||
* @returns IBigIntLike实例
|
||||
*/
|
||||
public static fromBinaryString(binary: string): IBigIntLike {
|
||||
if (this.isNativeSupported()) {
|
||||
const value = BigInt('0b' + binary);
|
||||
return new NativeBigInt(value);
|
||||
} else {
|
||||
return new ArrayBigInt('0b' + binary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从十六进制字符串创建
|
||||
* @param hex 十六进制字符串
|
||||
* @returns IBigIntLike实例
|
||||
*/
|
||||
public static fromHexString(hex: string): IBigIntLike {
|
||||
if (this.isNativeSupported()) {
|
||||
const cleanHex = hex.replace(/^0x/i, '');
|
||||
const value = BigInt('0x' + cleanHex);
|
||||
return new NativeBigInt(value);
|
||||
} else {
|
||||
return new ArrayBigInt(hex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取环境信息
|
||||
* @returns 环境信息对象
|
||||
*/
|
||||
public static getEnvironmentInfo(): EnvironmentInfo {
|
||||
return {
|
||||
supportsBigInt: this.isNativeSupported(),
|
||||
environment: this.detectEnvironment(),
|
||||
jsEngine: this.detectJSEngine()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测运行环境
|
||||
* @returns 环境类型
|
||||
*/
|
||||
private static detectEnvironment(): string {
|
||||
if (typeof window !== 'undefined') {
|
||||
// 浏览器环境
|
||||
if (typeof navigator !== 'undefined') {
|
||||
const userAgent = navigator.userAgent;
|
||||
|
||||
if (userAgent.includes('Chrome')) {
|
||||
const match = userAgent.match(/Chrome\/(\d+)/);
|
||||
const version = match ? parseInt(match[1]) : 0;
|
||||
return `Chrome ${version}`;
|
||||
}
|
||||
|
||||
if (userAgent.includes('Firefox')) {
|
||||
const match = userAgent.match(/Firefox\/(\d+)/);
|
||||
const version = match ? parseInt(match[1]) : 0;
|
||||
return `Firefox ${version}`;
|
||||
}
|
||||
|
||||
if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
|
||||
const match = userAgent.match(/Version\/(\d+)/);
|
||||
const version = match ? parseInt(match[1]) : 0;
|
||||
return `Safari ${version}`;
|
||||
}
|
||||
|
||||
return 'Browser (Unknown)';
|
||||
}
|
||||
|
||||
return 'Browser';
|
||||
} else if (typeof global !== 'undefined') {
|
||||
// Node.js环境
|
||||
if (typeof process !== 'undefined' && process.version) {
|
||||
return `Node.js ${process.version}`;
|
||||
}
|
||||
return 'Node.js';
|
||||
} else if (typeof (globalThis as any).wx !== 'undefined') {
|
||||
// 微信小程序
|
||||
return 'WeChat MiniProgram';
|
||||
} else if (typeof (globalThis as any).cc !== 'undefined') {
|
||||
// Cocos Creator
|
||||
return 'Cocos Creator';
|
||||
} else if (typeof (globalThis as any).Laya !== 'undefined') {
|
||||
// Laya引擎
|
||||
return 'Laya Engine';
|
||||
}
|
||||
|
||||
return 'Unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测JavaScript引擎
|
||||
* @returns JS引擎信息
|
||||
*/
|
||||
private static detectJSEngine(): string {
|
||||
try {
|
||||
// V8引擎特征检测
|
||||
if (typeof process !== 'undefined' && process.versions && process.versions.v8) {
|
||||
return `V8 ${process.versions.v8}`;
|
||||
}
|
||||
|
||||
// SpiderMonkey特征检测
|
||||
if (typeof (globalThis as any).Components !== 'undefined') {
|
||||
return 'SpiderMonkey';
|
||||
}
|
||||
|
||||
// JavaScriptCore特征检测
|
||||
if (typeof window !== 'undefined' && typeof (window as any).safari !== 'undefined') {
|
||||
return 'JavaScriptCore';
|
||||
}
|
||||
|
||||
return 'Unknown';
|
||||
} catch {
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 环境信息接口
|
||||
*/
|
||||
export interface EnvironmentInfo {
|
||||
/** 是否支持BigInt */
|
||||
supportsBigInt: boolean;
|
||||
/** 运行环境 */
|
||||
environment: string;
|
||||
/** JavaScript引擎 */
|
||||
jsEngine: string;
|
||||
}
|
||||
321
packages/core/src/ECS/Utils/Bits.ts
Normal file
321
packages/core/src/ECS/Utils/Bits.ts
Normal file
@@ -0,0 +1,321 @@
|
||||
import { IBigIntLike, BigIntFactory } from './BigIntCompatibility';
|
||||
|
||||
/**
|
||||
* 高性能位操作类
|
||||
*
|
||||
* 基于BigInt实现,支持任意数量的位操作。
|
||||
* 自动适配运行环境,在不支持BigInt的环境中使用兼容实现。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const bits = new Bits();
|
||||
* bits.set(0);
|
||||
* bits.set(5);
|
||||
* console.log(bits.get(0)); // true
|
||||
* console.log(bits.get(1)); // false
|
||||
* ```
|
||||
*/
|
||||
export class Bits {
|
||||
private _value: IBigIntLike;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param initialValue 初始值,可以是IBigIntLike或数值
|
||||
*/
|
||||
constructor(initialValue?: IBigIntLike | number | string) {
|
||||
if (initialValue && typeof initialValue === 'object') {
|
||||
this._value = initialValue;
|
||||
} else {
|
||||
this._value = BigIntFactory.create(initialValue || 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置指定位置的位为1
|
||||
* @param index 位索引(从0开始)
|
||||
* @throws {Error} 当索引为负数时抛出错误
|
||||
*/
|
||||
public set(index: number): void {
|
||||
if (index < 0) {
|
||||
throw new Error('Bit index cannot be negative');
|
||||
}
|
||||
const mask = BigIntFactory.one().shiftLeft(index);
|
||||
this._value = this._value.or(mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定位置的位(设为0)
|
||||
* @param index 位索引(从0开始)
|
||||
* @throws {Error} 当索引为负数时抛出错误
|
||||
*/
|
||||
public clear(index: number): void {
|
||||
if (index < 0) {
|
||||
throw new Error('Bit index cannot be negative');
|
||||
}
|
||||
const mask = BigIntFactory.one().shiftLeft(index).not();
|
||||
this._value = this._value.and(mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定位置的位值
|
||||
* @param index 位索引(从0开始)
|
||||
* @returns 位值(true表示1,false表示0)
|
||||
*/
|
||||
public get(index: number): boolean {
|
||||
if (index < 0) {
|
||||
return false;
|
||||
}
|
||||
const mask = BigIntFactory.one().shiftLeft(index);
|
||||
return !this._value.and(mask).isZero();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否包含所有指定的位
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 是否包含所有指定的位
|
||||
*/
|
||||
public containsAll(other: Bits): boolean {
|
||||
const intersection = this._value.and(other._value);
|
||||
return intersection.equals(other._value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否包含任意一个指定的位
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 是否包含任意一个指定的位
|
||||
*/
|
||||
public intersects(other: Bits): boolean {
|
||||
return !this._value.and(other._value).isZero();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否不包含任何指定的位
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 是否不包含任何指定的位
|
||||
*/
|
||||
public excludes(other: Bits): boolean {
|
||||
return !this.intersects(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有位
|
||||
*/
|
||||
public clearAll(): void {
|
||||
this._value = BigIntFactory.zero();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为空(没有设置任何位)
|
||||
* @returns 是否为空
|
||||
*/
|
||||
public isEmpty(): boolean {
|
||||
return this._value.isZero();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设置的位数量
|
||||
* @returns 设置为1的位数量
|
||||
*/
|
||||
public cardinality(): number {
|
||||
let count = 0;
|
||||
let value = this._value.clone();
|
||||
|
||||
while (!value.isZero()) {
|
||||
const one = BigIntFactory.one();
|
||||
if (!value.and(one).isZero()) {
|
||||
count++;
|
||||
}
|
||||
value = value.shiftRight(1);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 位运算:与
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 新的Bits对象,包含与运算结果
|
||||
*/
|
||||
public and(other: Bits): Bits {
|
||||
return new Bits(this._value.and(other._value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 位运算:或
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 新的Bits对象,包含或运算结果
|
||||
*/
|
||||
public or(other: Bits): Bits {
|
||||
return new Bits(this._value.or(other._value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 位运算:异或
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 新的Bits对象,包含异或运算结果
|
||||
*/
|
||||
public xor(other: Bits): Bits {
|
||||
return new Bits(this._value.xor(other._value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 位运算:非
|
||||
* @param maxBits 最大位数限制,默认64位
|
||||
* @returns 新的Bits对象,包含非运算结果
|
||||
*/
|
||||
public not(maxBits: number = 64): Bits {
|
||||
return new Bits(this._value.not(maxBits));
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制另一个Bits对象
|
||||
* @param other 要复制的Bits对象
|
||||
*/
|
||||
public copyFrom(other: Bits): void {
|
||||
this._value = other._value.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建当前Bits的副本
|
||||
* @returns 新的Bits对象副本
|
||||
*/
|
||||
public clone(): Bits {
|
||||
return new Bits(this._value.clone());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始值
|
||||
* @returns 原始的IBigIntLike值
|
||||
*/
|
||||
public getValue(): IBigIntLike {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置原始值
|
||||
* @param value 新的值,可以是IBigIntLike或数值
|
||||
*/
|
||||
public setValue(value: IBigIntLike | number | string): void {
|
||||
if (typeof value === 'object') {
|
||||
this._value = value;
|
||||
} else {
|
||||
this._value = BigIntFactory.create(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取调试信息
|
||||
* @returns 返回显示设置位索引的字符串
|
||||
*/
|
||||
public toString(): string {
|
||||
const bits: string[] = [];
|
||||
let index = 0;
|
||||
let value = this._value.clone();
|
||||
|
||||
while (!value.isZero()) {
|
||||
const one = BigIntFactory.one();
|
||||
if (!value.and(one).isZero()) {
|
||||
bits.push(index.toString());
|
||||
}
|
||||
value = value.shiftRight(1);
|
||||
index++;
|
||||
}
|
||||
|
||||
return `Bits[${bits.join(', ')}]`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二进制表示
|
||||
* @param maxBits 最大位数,默认64位
|
||||
* @returns 二进制字符串表示
|
||||
*/
|
||||
public toBinaryString(maxBits: number = 64): string {
|
||||
let result = '';
|
||||
for (let i = maxBits - 1; i >= 0; i--) {
|
||||
result += this.get(i) ? '1' : '0';
|
||||
if (i % 8 === 0 && i > 0) {
|
||||
result += ' ';
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取十六进制表示
|
||||
* @returns 十六进制字符串表示
|
||||
*/
|
||||
public toHexString(): string {
|
||||
return '0x' + this._value.toString(16).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从二进制字符串创建Bits
|
||||
* @param binaryString 二进制字符串
|
||||
* @returns 新的Bits对象
|
||||
*/
|
||||
public static fromBinaryString(binaryString: string): Bits {
|
||||
const cleanString = binaryString.replace(/\s/g, '');
|
||||
const value = BigIntFactory.fromBinaryString(cleanString);
|
||||
return new Bits(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从十六进制字符串创建Bits
|
||||
* @param hexString 十六进制字符串
|
||||
* @returns 新的Bits对象
|
||||
*/
|
||||
public static fromHexString(hexString: string): Bits {
|
||||
const value = BigIntFactory.fromHexString(hexString);
|
||||
return new Bits(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个Bits对象是否相等
|
||||
* @param other 另一个Bits对象
|
||||
* @returns 是否相等
|
||||
*/
|
||||
public equals(other: Bits): boolean {
|
||||
return this._value.equals(other._value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最高位的索引
|
||||
* @returns 最高位的索引,如果为空则返回-1
|
||||
*/
|
||||
public getHighestBitIndex(): number {
|
||||
if (this._value.isZero()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
let value = this._value.clone();
|
||||
|
||||
while (!value.shiftRight(1).isZero()) {
|
||||
value = value.shiftRight(1);
|
||||
index++;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最低位的索引
|
||||
* @returns 最低位的索引,如果为空则返回-1
|
||||
*/
|
||||
public getLowestBitIndex(): number {
|
||||
if (this._value.isZero()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
let value = this._value.clone();
|
||||
const one = BigIntFactory.one();
|
||||
|
||||
while (value.and(one).isZero()) {
|
||||
value = value.shiftRight(1);
|
||||
index++;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
99
packages/core/src/ECS/Utils/ComponentTypeManager.ts
Normal file
99
packages/core/src/ECS/Utils/ComponentTypeManager.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { Component } from '../Component';
|
||||
import { Bits } from './Bits';
|
||||
|
||||
/**
|
||||
* 组件类型管理器
|
||||
* 负责管理组件类型的注册和ID分配
|
||||
*/
|
||||
export class ComponentTypeManager {
|
||||
private static _instance: ComponentTypeManager;
|
||||
private _componentTypes = new Map<Function, number>();
|
||||
private _typeNames = new Map<number, string>();
|
||||
private _nextTypeId = 0;
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
public static get instance(): ComponentTypeManager {
|
||||
if (!ComponentTypeManager._instance) {
|
||||
ComponentTypeManager._instance = new ComponentTypeManager();
|
||||
}
|
||||
return ComponentTypeManager._instance;
|
||||
}
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* 获取组件类型的ID
|
||||
* @param componentType 组件类型构造函数
|
||||
* @returns 组件类型ID
|
||||
*/
|
||||
public getTypeId<T extends Component>(componentType: new (...args: unknown[]) => T): number {
|
||||
let typeId = this._componentTypes.get(componentType);
|
||||
|
||||
if (typeId === undefined) {
|
||||
typeId = this._nextTypeId++;
|
||||
this._componentTypes.set(componentType, typeId);
|
||||
this._typeNames.set(typeId, componentType.name);
|
||||
}
|
||||
|
||||
return typeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件类型名称
|
||||
* @param typeId 组件类型ID
|
||||
* @returns 组件类型名称
|
||||
*/
|
||||
public getTypeName(typeId: number): string {
|
||||
return this._typeNames.get(typeId) || 'Unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建包含指定组件类型的Bits对象
|
||||
* @param componentTypes 组件类型构造函数数组
|
||||
* @returns Bits对象
|
||||
*/
|
||||
public createBits(...componentTypes: (new (...args: unknown[]) => Component)[]): Bits {
|
||||
const bits = new Bits();
|
||||
|
||||
for (const componentType of componentTypes) {
|
||||
const typeId = this.getTypeId(componentType);
|
||||
bits.set(typeId);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体的组件位掩码
|
||||
* @param components 组件数组
|
||||
* @returns Bits对象
|
||||
*/
|
||||
public getEntityBits(components: Component[]): Bits {
|
||||
const bits = new Bits();
|
||||
|
||||
for (const component of components) {
|
||||
const typeId = this.getTypeId(component.constructor as new (...args: unknown[]) => Component);
|
||||
bits.set(typeId);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置管理器(主要用于测试)
|
||||
*/
|
||||
public reset(): void {
|
||||
this._componentTypes.clear();
|
||||
this._typeNames.clear();
|
||||
this._nextTypeId = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已注册的组件类型数量
|
||||
*/
|
||||
public get registeredTypeCount(): number {
|
||||
return this._componentTypes.size;
|
||||
}
|
||||
}
|
||||
304
packages/core/src/ECS/Utils/EntityList.ts
Normal file
304
packages/core/src/ECS/Utils/EntityList.ts
Normal file
@@ -0,0 +1,304 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
|
||||
/**
|
||||
* 高性能实体列表管理器
|
||||
* 管理场景中的所有实体,支持快速查找和批量操作
|
||||
*/
|
||||
export class EntityList {
|
||||
public buffer: Entity[] = [];
|
||||
private _scene: any; // 临时使用any,避免循环依赖
|
||||
|
||||
// 索引映射,提升查找性能
|
||||
private _idToEntity = new Map<number, Entity>();
|
||||
private _nameToEntities = new Map<string, Entity[]>();
|
||||
|
||||
// 延迟操作队列
|
||||
private _entitiesToAdd: Entity[] = [];
|
||||
private _entitiesToRemove: Entity[] = [];
|
||||
private _isUpdating = false;
|
||||
|
||||
public get count(): number {
|
||||
return this.buffer.length;
|
||||
}
|
||||
|
||||
constructor(scene: any) {
|
||||
this._scene = scene;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加实体(立即添加或延迟添加)
|
||||
* @param entity 要添加的实体
|
||||
*/
|
||||
public add(entity: Entity): void {
|
||||
if (this._isUpdating) {
|
||||
// 如果正在更新中,延迟添加
|
||||
this._entitiesToAdd.push(entity);
|
||||
} else {
|
||||
this.addImmediate(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即添加实体
|
||||
* @param entity 要添加的实体
|
||||
*/
|
||||
private addImmediate(entity: Entity): void {
|
||||
// 检查是否已存在
|
||||
if (this._idToEntity.has(entity.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.buffer.push(entity);
|
||||
this._idToEntity.set(entity.id, entity);
|
||||
|
||||
// 更新名称索引
|
||||
this.updateNameIndex(entity, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除实体(立即移除或延迟移除)
|
||||
* @param entity 要移除的实体
|
||||
*/
|
||||
public remove(entity: Entity): void {
|
||||
if (this._isUpdating) {
|
||||
// 如果正在更新中,延迟移除
|
||||
this._entitiesToRemove.push(entity);
|
||||
} else {
|
||||
this.removeImmediate(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即移除实体
|
||||
* @param entity 要移除的实体
|
||||
*/
|
||||
private removeImmediate(entity: Entity): void {
|
||||
const index = this.buffer.indexOf(entity);
|
||||
if (index !== -1) {
|
||||
this.buffer.splice(index, 1);
|
||||
this._idToEntity.delete(entity.id);
|
||||
|
||||
// 更新名称索引
|
||||
this.updateNameIndex(entity, false);
|
||||
|
||||
// 回收实体ID到ID池
|
||||
if (this._scene && this._scene.identifierPool) {
|
||||
this._scene.identifierPool.checkIn(entity.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除所有实体
|
||||
*/
|
||||
public removeAllEntities(): void {
|
||||
// 收集所有实体ID用于回收
|
||||
const idsToRecycle: number[] = [];
|
||||
|
||||
for (let i = this.buffer.length - 1; i >= 0; i--) {
|
||||
idsToRecycle.push(this.buffer[i].id);
|
||||
this.buffer[i].destroy();
|
||||
}
|
||||
|
||||
// 批量回收ID
|
||||
if (this._scene && this._scene.identifierPool) {
|
||||
for (const id of idsToRecycle) {
|
||||
this._scene.identifierPool.checkIn(id);
|
||||
}
|
||||
}
|
||||
|
||||
this.buffer.length = 0;
|
||||
this._idToEntity.clear();
|
||||
this._nameToEntities.clear();
|
||||
this._entitiesToAdd.length = 0;
|
||||
this._entitiesToRemove.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新实体列表,处理延迟操作
|
||||
*/
|
||||
public updateLists(): void {
|
||||
// 处理延迟添加的实体
|
||||
if (this._entitiesToAdd.length > 0) {
|
||||
for (const entity of this._entitiesToAdd) {
|
||||
this.addImmediate(entity);
|
||||
}
|
||||
this._entitiesToAdd.length = 0;
|
||||
}
|
||||
|
||||
// 处理延迟移除的实体
|
||||
if (this._entitiesToRemove.length > 0) {
|
||||
for (const entity of this._entitiesToRemove) {
|
||||
this.removeImmediate(entity);
|
||||
}
|
||||
this._entitiesToRemove.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新所有实体
|
||||
*/
|
||||
public update(): void {
|
||||
this._isUpdating = true;
|
||||
|
||||
try {
|
||||
for (let i = 0; i < this.buffer.length; i++) {
|
||||
const entity = this.buffer[i];
|
||||
if (entity.enabled && !entity.isDestroyed) {
|
||||
entity.update();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this._isUpdating = false;
|
||||
}
|
||||
|
||||
// 处理延迟操作
|
||||
this.updateLists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称查找实体(使用索引,O(1)复杂度)
|
||||
* @param name 实体名称
|
||||
* @returns 找到的第一个实体或null
|
||||
*/
|
||||
public findEntity(name: string): Entity | null {
|
||||
const entities = this._nameToEntities.get(name);
|
||||
return entities && entities.length > 0 ? entities[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称查找所有实体
|
||||
* @param name 实体名称
|
||||
* @returns 找到的所有实体数组
|
||||
*/
|
||||
public findEntitiesByName(name: string): Entity[] {
|
||||
return this._nameToEntities.get(name) || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查找实体(使用索引,O(1)复杂度)
|
||||
* @param id 实体ID
|
||||
* @returns 找到的实体或null
|
||||
*/
|
||||
public findEntityById(id: number): Entity | null {
|
||||
return this._idToEntity.get(id) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签查找实体
|
||||
* @param tag 标签
|
||||
* @returns 找到的所有实体数组
|
||||
*/
|
||||
public findEntitiesByTag(tag: number): Entity[] {
|
||||
const result: Entity[] = [];
|
||||
|
||||
for (const entity of this.buffer) {
|
||||
if (entity.tag === tag) {
|
||||
result.push(entity);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据组件类型查找实体
|
||||
* @param componentType 组件类型
|
||||
* @returns 找到的所有实体数组
|
||||
*/
|
||||
public findEntitiesWithComponent<T extends Component>(componentType: new (...args: unknown[]) => T): Entity[] {
|
||||
const result: Entity[] = [];
|
||||
|
||||
for (const entity of this.buffer) {
|
||||
if (entity.hasComponent(componentType)) {
|
||||
result.push(entity);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作:对所有实体执行指定操作
|
||||
* @param action 要执行的操作
|
||||
*/
|
||||
public forEach(action: (entity: Entity) => void): void {
|
||||
for (const entity of this.buffer) {
|
||||
action(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作:对符合条件的实体执行指定操作
|
||||
* @param predicate 筛选条件
|
||||
* @param action 要执行的操作
|
||||
*/
|
||||
public forEachWhere(predicate: (entity: Entity) => boolean, action: (entity: Entity) => void): void {
|
||||
for (const entity of this.buffer) {
|
||||
if (predicate(entity)) {
|
||||
action(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新名称索引
|
||||
* @param entity 实体
|
||||
* @param isAdd 是否为添加操作
|
||||
*/
|
||||
private updateNameIndex(entity: Entity, isAdd: boolean): void {
|
||||
if (!entity.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAdd) {
|
||||
let entities = this._nameToEntities.get(entity.name);
|
||||
if (!entities) {
|
||||
entities = [];
|
||||
this._nameToEntities.set(entity.name, entities);
|
||||
}
|
||||
entities.push(entity);
|
||||
} else {
|
||||
const entities = this._nameToEntities.get(entity.name);
|
||||
if (entities) {
|
||||
const index = entities.indexOf(entity);
|
||||
if (index !== -1) {
|
||||
entities.splice(index, 1);
|
||||
|
||||
// 如果数组为空,删除映射
|
||||
if (entities.length === 0) {
|
||||
this._nameToEntities.delete(entity.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体列表的统计信息
|
||||
* @returns 统计信息
|
||||
*/
|
||||
public getStats(): {
|
||||
totalEntities: number;
|
||||
activeEntities: number;
|
||||
pendingAdd: number;
|
||||
pendingRemove: number;
|
||||
nameIndexSize: number;
|
||||
} {
|
||||
let activeCount = 0;
|
||||
for (const entity of this.buffer) {
|
||||
if (entity.enabled && !entity.isDestroyed) {
|
||||
activeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalEntities: this.buffer.length,
|
||||
activeEntities: activeCount,
|
||||
pendingAdd: this._entitiesToAdd.length,
|
||||
pendingRemove: this._entitiesToRemove.length,
|
||||
nameIndexSize: this._nameToEntities.size
|
||||
};
|
||||
}
|
||||
}
|
||||
111
packages/core/src/ECS/Utils/EntityProcessorList.ts
Normal file
111
packages/core/src/ECS/Utils/EntityProcessorList.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { EntitySystem } from '../Systems/EntitySystem';
|
||||
|
||||
/**
|
||||
* 实体处理器列表管理器
|
||||
* 管理场景中的所有实体系统
|
||||
*/
|
||||
export class EntityProcessorList {
|
||||
private _processors: EntitySystem[] = [];
|
||||
private _isDirty = false;
|
||||
|
||||
/**
|
||||
* 设置为脏状态,需要重新排序
|
||||
*/
|
||||
public setDirty(): void {
|
||||
this._isDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加实体处理器
|
||||
* @param processor 要添加的处理器
|
||||
*/
|
||||
public add(processor: EntitySystem): void {
|
||||
this._processors.push(processor);
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除实体处理器
|
||||
* @param processor 要移除的处理器
|
||||
*/
|
||||
public remove(processor: EntitySystem): void {
|
||||
const index = this._processors.indexOf(processor);
|
||||
if (index !== -1) {
|
||||
this._processors.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的处理器
|
||||
* @param type 处理器类型
|
||||
*/
|
||||
public getProcessor<T extends EntitySystem>(type: new (...args: unknown[]) => T): T | null {
|
||||
for (const processor of this._processors) {
|
||||
if (processor instanceof type) {
|
||||
return processor as T;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始处理
|
||||
*
|
||||
* 对所有处理器进行排序以确保正确的执行顺序。
|
||||
*/
|
||||
public begin(): void {
|
||||
this.sortProcessors();
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束处理
|
||||
*/
|
||||
public end(): void {
|
||||
// 清理处理器
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新所有处理器
|
||||
*/
|
||||
public update(): void {
|
||||
this.sortProcessors();
|
||||
for (const processor of this._processors) {
|
||||
try {
|
||||
processor.update();
|
||||
} catch (error) {
|
||||
console.error(`Error in processor ${processor.constructor.name}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后期更新所有处理器
|
||||
*/
|
||||
public lateUpdate(): void {
|
||||
for (const processor of this._processors) {
|
||||
processor.lateUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序处理器
|
||||
*/
|
||||
private sortProcessors(): void {
|
||||
if (this._isDirty) {
|
||||
this._processors.sort((a, b) => a.updateOrder - b.updateOrder);
|
||||
this._isDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取处理器列表 */
|
||||
public get processors() {
|
||||
return this._processors;
|
||||
}
|
||||
|
||||
/** 获取处理器数量 */
|
||||
public get count() {
|
||||
return this._processors.length;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
405
packages/core/src/ECS/Utils/IdentifierPool.ts
Normal file
405
packages/core/src/ECS/Utils/IdentifierPool.ts
Normal file
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* 世代式ID池管理器
|
||||
*
|
||||
* 用于管理实体ID的分配和回收,支持世代版本控制以防止悬空引用问题。
|
||||
* 世代式ID由索引和版本组成,当ID被回收时版本会递增,确保旧引用失效。
|
||||
*
|
||||
* 支持动态扩展,理论上可以支持到65535个索引(16位),每个索引65535个版本(16位)。
|
||||
* 总计可以处理超过42亿个独特的ID组合,完全满足ECS大规模实体需求。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const pool = new IdentifierPool();
|
||||
*
|
||||
* // 分配ID
|
||||
* const id = pool.checkOut(); // 例如: 65536 (版本1,索引0)
|
||||
*
|
||||
* // 回收ID
|
||||
* pool.checkIn(id);
|
||||
*
|
||||
* // 验证ID是否有效
|
||||
* const isValid = pool.isValid(id); // false,因为版本已递增
|
||||
* ```
|
||||
*/
|
||||
export class IdentifierPool {
|
||||
/**
|
||||
* 下一个可用的索引
|
||||
*/
|
||||
private _nextAvailableIndex = 0;
|
||||
|
||||
/**
|
||||
* 空闲的索引列表
|
||||
*/
|
||||
private _freeIndices: number[] = [];
|
||||
|
||||
/**
|
||||
* 每个索引对应的世代版本
|
||||
* 动态扩展的Map,按需分配内存
|
||||
*/
|
||||
private _generations = new Map<number, number>();
|
||||
|
||||
/**
|
||||
* 延迟回收队列
|
||||
* 防止在同一帧内立即重用ID,避免时序问题
|
||||
*/
|
||||
private _pendingRecycle: Array<{
|
||||
index: number;
|
||||
generation: number;
|
||||
timestamp: number;
|
||||
}> = [];
|
||||
|
||||
/**
|
||||
* 延迟回收时间(毫秒)
|
||||
*/
|
||||
private _recycleDelay: number = 100;
|
||||
|
||||
/**
|
||||
* 最大索引限制(16位)
|
||||
* 这是框架设计选择:16位索引 + 16位版本 = 32位ID,确保高效位操作
|
||||
* 不是硬件限制,而是性能和内存效率的权衡
|
||||
*/
|
||||
private static readonly MAX_INDEX = 0xFFFF; // 65535
|
||||
|
||||
/**
|
||||
* 最大世代限制(16位)
|
||||
*/
|
||||
private static readonly MAX_GENERATION = 0xFFFF; // 65535
|
||||
|
||||
/**
|
||||
* 内存扩展块大小
|
||||
* 当需要更多内存时,一次性预分配的索引数量
|
||||
*/
|
||||
private _expansionBlockSize: number;
|
||||
|
||||
/**
|
||||
* 统计信息
|
||||
*/
|
||||
private _stats = {
|
||||
totalAllocated: 0,
|
||||
totalRecycled: 0,
|
||||
currentActive: 0,
|
||||
memoryExpansions: 0
|
||||
};
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param recycleDelay 延迟回收时间(毫秒),默认为100ms
|
||||
* @param expansionBlockSize 内存扩展块大小,默认为1024
|
||||
*/
|
||||
constructor(recycleDelay: number = 100, expansionBlockSize: number = 1024) {
|
||||
this._recycleDelay = recycleDelay;
|
||||
this._expansionBlockSize = expansionBlockSize;
|
||||
|
||||
// 预分配第一个块的世代信息
|
||||
this._preAllocateGenerations(0, this._expansionBlockSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个可用的ID
|
||||
*
|
||||
* 返回一个32位ID,高16位为世代版本,低16位为索引。
|
||||
*
|
||||
* @returns 新分配的实体ID
|
||||
* @throws {Error} 当达到索引限制时抛出错误
|
||||
*/
|
||||
public checkOut(): number {
|
||||
// 处理延迟回收队列
|
||||
this._processDelayedRecycle();
|
||||
|
||||
let index: number;
|
||||
|
||||
if (this._freeIndices.length > 0) {
|
||||
// 重用回收的索引
|
||||
index = this._freeIndices.pop()!;
|
||||
} else {
|
||||
// 分配新索引
|
||||
if (this._nextAvailableIndex > IdentifierPool.MAX_INDEX) {
|
||||
throw new Error(
|
||||
`实体索引已达到框架设计限制 (${IdentifierPool.MAX_INDEX})。` +
|
||||
`这意味着您已经分配了超过65535个不同的实体索引。` +
|
||||
`这是16位索引设计的限制,考虑优化实体回收策略或升级到64位ID设计。`
|
||||
);
|
||||
}
|
||||
|
||||
index = this._nextAvailableIndex++;
|
||||
|
||||
// 按需扩展世代存储
|
||||
this._ensureGenerationCapacity(index);
|
||||
}
|
||||
|
||||
const generation = this._generations.get(index) || 1;
|
||||
this._stats.totalAllocated++;
|
||||
this._stats.currentActive++;
|
||||
|
||||
return this._packId(index, generation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回收一个ID
|
||||
*
|
||||
* 验证ID的有效性后,将其加入延迟回收队列。
|
||||
* ID不会立即可重用,而是在延迟时间后才真正回收。
|
||||
*
|
||||
* @param id 要回收的实体ID
|
||||
* @returns 是否成功回收(ID是否有效且未被重复回收)
|
||||
*/
|
||||
public checkIn(id: number): boolean {
|
||||
const index = this._unpackIndex(id);
|
||||
const generation = this._unpackGeneration(id);
|
||||
|
||||
// 验证ID有效性
|
||||
if (!this._isValidId(index, generation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否已经在待回收队列中
|
||||
const alreadyPending = this._pendingRecycle.some(
|
||||
item => item.index === index && item.generation === generation
|
||||
);
|
||||
|
||||
if (alreadyPending) {
|
||||
return false; // 已经在回收队列中,拒绝重复回收
|
||||
}
|
||||
|
||||
// 加入延迟回收队列
|
||||
this._pendingRecycle.push({
|
||||
index,
|
||||
generation,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
this._stats.currentActive--;
|
||||
this._stats.totalRecycled++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证ID是否有效
|
||||
*
|
||||
* 检查ID的索引和世代版本是否匹配当前状态。
|
||||
*
|
||||
* @param id 要验证的实体ID
|
||||
* @returns ID是否有效
|
||||
*/
|
||||
public isValid(id: number): boolean {
|
||||
const index = this._unpackIndex(id);
|
||||
const generation = this._unpackGeneration(id);
|
||||
return this._isValidId(index, generation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取统计信息
|
||||
*
|
||||
* @returns 池的当前状态统计
|
||||
*/
|
||||
public getStats(): {
|
||||
/** 已分配的总索引数 */
|
||||
totalAllocated: number;
|
||||
/** 总计回收次数 */
|
||||
totalRecycled: number;
|
||||
/** 当前活跃实体数 */
|
||||
currentActive: number;
|
||||
/** 当前空闲的索引数 */
|
||||
currentlyFree: number;
|
||||
/** 等待回收的ID数 */
|
||||
pendingRecycle: number;
|
||||
/** 理论最大实体数(设计限制) */
|
||||
maxPossibleEntities: number;
|
||||
/** 当前使用的最大索引 */
|
||||
maxUsedIndex: number;
|
||||
/** 内存使用(字节) */
|
||||
memoryUsage: number;
|
||||
/** 内存扩展次数 */
|
||||
memoryExpansions: number;
|
||||
/** 平均世代版本 */
|
||||
averageGeneration: number;
|
||||
/** 世代存储大小 */
|
||||
generationStorageSize: number;
|
||||
} {
|
||||
// 计算平均世代版本
|
||||
let totalGeneration = 0;
|
||||
let generationCount = 0;
|
||||
|
||||
for (const [index, generation] of this._generations) {
|
||||
if (index < this._nextAvailableIndex) {
|
||||
totalGeneration += generation;
|
||||
generationCount++;
|
||||
}
|
||||
}
|
||||
|
||||
const averageGeneration = generationCount > 0
|
||||
? totalGeneration / generationCount
|
||||
: 1;
|
||||
|
||||
return {
|
||||
totalAllocated: this._stats.totalAllocated,
|
||||
totalRecycled: this._stats.totalRecycled,
|
||||
currentActive: this._stats.currentActive,
|
||||
currentlyFree: this._freeIndices.length,
|
||||
pendingRecycle: this._pendingRecycle.length,
|
||||
maxPossibleEntities: IdentifierPool.MAX_INDEX + 1,
|
||||
maxUsedIndex: this._nextAvailableIndex - 1,
|
||||
memoryUsage: this._calculateMemoryUsage(),
|
||||
memoryExpansions: this._stats.memoryExpansions,
|
||||
averageGeneration: Math.round(averageGeneration * 100) / 100,
|
||||
generationStorageSize: this._generations.size
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制执行延迟回收处理
|
||||
*
|
||||
* 在某些情况下可能需要立即处理延迟回收队列,
|
||||
* 比如内存压力大或者需要精确的统计信息时。
|
||||
*/
|
||||
public forceProcessDelayedRecycle(): void {
|
||||
this._processDelayedRecycle(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期的延迟回收项
|
||||
*
|
||||
* 将超过延迟时间的回收项真正回收到空闲列表中。
|
||||
*
|
||||
* @param forceAll 是否强制处理所有延迟回收项
|
||||
* @private
|
||||
*/
|
||||
private _processDelayedRecycle(forceAll: boolean = false): void {
|
||||
if (this._pendingRecycle.length === 0) return;
|
||||
|
||||
const now = Date.now();
|
||||
const readyToRecycle: typeof this._pendingRecycle = [];
|
||||
const stillPending: typeof this._pendingRecycle = [];
|
||||
|
||||
// 分离已到期和未到期的项
|
||||
for (const item of this._pendingRecycle) {
|
||||
if (forceAll || now - item.timestamp >= this._recycleDelay) {
|
||||
readyToRecycle.push(item);
|
||||
} else {
|
||||
stillPending.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理到期的回收项
|
||||
for (const item of readyToRecycle) {
|
||||
// 再次验证ID有效性(防止重复回收)
|
||||
if (this._isValidId(item.index, item.generation)) {
|
||||
// 递增世代版本
|
||||
let newGeneration = item.generation + 1;
|
||||
|
||||
// 防止世代版本溢出
|
||||
if (newGeneration > IdentifierPool.MAX_GENERATION) {
|
||||
newGeneration = 1; // 重置为1而不是0
|
||||
}
|
||||
|
||||
this._generations.set(item.index, newGeneration);
|
||||
|
||||
// 添加到空闲列表
|
||||
this._freeIndices.push(item.index);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新待回收队列
|
||||
this._pendingRecycle = stillPending;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预分配世代信息
|
||||
*
|
||||
* @param startIndex 起始索引
|
||||
* @param count 分配数量
|
||||
* @private
|
||||
*/
|
||||
private _preAllocateGenerations(startIndex: number, count: number): void {
|
||||
for (let i = 0; i < count; i++) {
|
||||
const index = startIndex + i;
|
||||
if (index <= IdentifierPool.MAX_INDEX) {
|
||||
this._generations.set(index, 1);
|
||||
}
|
||||
}
|
||||
this._stats.memoryExpansions++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保指定索引的世代信息存在
|
||||
*
|
||||
* @param index 索引
|
||||
* @private
|
||||
*/
|
||||
private _ensureGenerationCapacity(index: number): void {
|
||||
if (!this._generations.has(index)) {
|
||||
// 计算需要扩展的起始位置
|
||||
const expansionStart = Math.floor(index / this._expansionBlockSize) * this._expansionBlockSize;
|
||||
|
||||
// 预分配一个块
|
||||
this._preAllocateGenerations(expansionStart, this._expansionBlockSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算内存使用量
|
||||
*
|
||||
* @returns 内存使用字节数
|
||||
* @private
|
||||
*/
|
||||
private _calculateMemoryUsage(): number {
|
||||
const generationMapSize = this._generations.size * 16; // Map overhead + number pair
|
||||
const freeIndicesSize = this._freeIndices.length * 8;
|
||||
const pendingRecycleSize = this._pendingRecycle.length * 32;
|
||||
|
||||
return generationMapSize + freeIndicesSize + pendingRecycleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包索引和世代为32位ID
|
||||
*
|
||||
* @param index 索引(16位)
|
||||
* @param generation 世代版本(16位)
|
||||
* @returns 打包后的32位ID
|
||||
* @private
|
||||
*/
|
||||
private _packId(index: number, generation: number): number {
|
||||
return (generation << 16) | index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从ID中解包索引
|
||||
*
|
||||
* @param id 32位ID
|
||||
* @returns 索引部分(16位)
|
||||
* @private
|
||||
*/
|
||||
private _unpackIndex(id: number): number {
|
||||
return id & 0xFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从ID中解包世代版本
|
||||
*
|
||||
* @param id 32位ID
|
||||
* @returns 世代版本部分(16位)
|
||||
* @private
|
||||
*/
|
||||
private _unpackGeneration(id: number): number {
|
||||
return (id >>> 16) & 0xFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部ID有效性检查
|
||||
*
|
||||
* @param index 索引
|
||||
* @param generation 世代版本
|
||||
* @returns 是否有效
|
||||
* @private
|
||||
*/
|
||||
private _isValidId(index: number, generation: number): boolean {
|
||||
if (index < 0 || index >= this._nextAvailableIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const currentGeneration = this._generations.get(index);
|
||||
return currentGeneration !== undefined && currentGeneration === generation;
|
||||
}
|
||||
}
|
||||
295
packages/core/src/ECS/Utils/Matcher.ts
Normal file
295
packages/core/src/ECS/Utils/Matcher.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
import { ComponentType } from '../Core/ComponentStorage';
|
||||
|
||||
/**
|
||||
* 查询条件类型
|
||||
*/
|
||||
interface QueryCondition {
|
||||
all: ComponentType[];
|
||||
any: ComponentType[];
|
||||
none: ComponentType[];
|
||||
tag?: number; // 按标签查询
|
||||
name?: string; // 按名称查询
|
||||
component?: ComponentType; // 单组件查询
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体匹配条件描述符
|
||||
*
|
||||
* 用于描述实体查询条件,不执行实际查询
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const matcher = Matcher.all(Position, Velocity)
|
||||
* .any(Health, Shield)
|
||||
* .none(Dead);
|
||||
*
|
||||
* // 获取查询条件
|
||||
* const condition = matcher.getCondition();
|
||||
* ```
|
||||
*/
|
||||
export class Matcher {
|
||||
private readonly condition: QueryCondition = {
|
||||
all: [],
|
||||
any: [],
|
||||
none: []
|
||||
};
|
||||
|
||||
private constructor() {
|
||||
// 私有构造函数,只能通过静态方法创建
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建匹配器,要求所有指定的组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public static all(...types: ComponentType[]): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.all(...types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建匹配器,要求至少一个指定的组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public static any(...types: ComponentType[]): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.any(...types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建匹配器,排除指定的组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public static none(...types: ComponentType[]): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.none(...types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建按标签查询的匙配器
|
||||
* @param tag 标签值
|
||||
*/
|
||||
public static byTag(tag: number): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.withTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建按名称查询的匙配器
|
||||
* @param name 实体名称
|
||||
*/
|
||||
public static byName(name: string): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.withName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单组件查询的匙配器
|
||||
* @param componentType 组件类型
|
||||
*/
|
||||
public static byComponent(componentType: ComponentType): Matcher {
|
||||
const matcher = new Matcher();
|
||||
return matcher.withComponent(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建复杂查询构建器
|
||||
*/
|
||||
public static complex(): Matcher {
|
||||
return new Matcher();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建空匙配器(向后兼容)
|
||||
*/
|
||||
public static empty(): Matcher {
|
||||
return new Matcher();
|
||||
}
|
||||
|
||||
/**
|
||||
* 必须包含所有指定组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public all(...types: ComponentType[]): Matcher {
|
||||
this.condition.all.push(...types);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 必须包含至少一个指定组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public any(...types: ComponentType[]): Matcher {
|
||||
this.condition.any.push(...types);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 不能包含任何指定组件
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public none(...types: ComponentType[]): Matcher {
|
||||
this.condition.none.push(...types);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 排除指定组件(别名方法)
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public exclude(...types: ComponentType[]): Matcher {
|
||||
return this.none(...types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 至少包含其中之一(别名方法)
|
||||
* @param types 组件类型
|
||||
*/
|
||||
public one(...types: ComponentType[]): Matcher {
|
||||
return this.any(...types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按标签查询
|
||||
* @param tag 标签值
|
||||
*/
|
||||
public withTag(tag: number): Matcher {
|
||||
this.condition.tag = tag;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按名称查询
|
||||
* @param name 实体名称
|
||||
*/
|
||||
public withName(name: string): Matcher {
|
||||
this.condition.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单组件查询
|
||||
* @param componentType 组件类型
|
||||
*/
|
||||
public withComponent(componentType: ComponentType): Matcher {
|
||||
this.condition.component = componentType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除标签条件
|
||||
*/
|
||||
public withoutTag(): Matcher {
|
||||
delete this.condition.tag;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除名称条件
|
||||
*/
|
||||
public withoutName(): Matcher {
|
||||
delete this.condition.name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除单组件条件
|
||||
*/
|
||||
public withoutComponent(): Matcher {
|
||||
delete this.condition.component;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询条件(只读)
|
||||
*/
|
||||
public getCondition(): Readonly<QueryCondition> {
|
||||
return {
|
||||
all: [...this.condition.all],
|
||||
any: [...this.condition.any],
|
||||
none: [...this.condition.none],
|
||||
tag: this.condition.tag,
|
||||
name: this.condition.name,
|
||||
component: this.condition.component
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为空条件
|
||||
*/
|
||||
public isEmpty(): boolean {
|
||||
return this.condition.all.length === 0 &&
|
||||
this.condition.any.length === 0 &&
|
||||
this.condition.none.length === 0 &&
|
||||
this.condition.tag === undefined &&
|
||||
this.condition.name === undefined &&
|
||||
this.condition.component === undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置所有条件
|
||||
*/
|
||||
public reset(): Matcher {
|
||||
this.condition.all.length = 0;
|
||||
this.condition.any.length = 0;
|
||||
this.condition.none.length = 0;
|
||||
delete this.condition.tag;
|
||||
delete this.condition.name;
|
||||
delete this.condition.component;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆匹配器
|
||||
*/
|
||||
public clone(): Matcher {
|
||||
const cloned = new Matcher();
|
||||
cloned.condition.all.push(...this.condition.all);
|
||||
cloned.condition.any.push(...this.condition.any);
|
||||
cloned.condition.none.push(...this.condition.none);
|
||||
if (this.condition.tag !== undefined) {
|
||||
cloned.condition.tag = this.condition.tag;
|
||||
}
|
||||
if (this.condition.name !== undefined) {
|
||||
cloned.condition.name = this.condition.name;
|
||||
}
|
||||
if (this.condition.component !== undefined) {
|
||||
cloned.condition.component = this.condition.component;
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串表示
|
||||
*/
|
||||
public toString(): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
if (this.condition.all.length > 0) {
|
||||
parts.push(`all(${this.condition.all.map(t => t.name).join(', ')})`);
|
||||
}
|
||||
|
||||
if (this.condition.any.length > 0) {
|
||||
parts.push(`any(${this.condition.any.map(t => t.name).join(', ')})`);
|
||||
}
|
||||
|
||||
if (this.condition.none.length > 0) {
|
||||
parts.push(`none(${this.condition.none.map(t => t.name).join(', ')})`);
|
||||
}
|
||||
|
||||
if (this.condition.tag !== undefined) {
|
||||
parts.push(`tag(${this.condition.tag})`);
|
||||
}
|
||||
|
||||
if (this.condition.name !== undefined) {
|
||||
parts.push(`name(${this.condition.name})`);
|
||||
}
|
||||
|
||||
if (this.condition.component !== undefined) {
|
||||
parts.push(`component(${this.condition.component.name})`);
|
||||
}
|
||||
|
||||
return `Matcher[${parts.join(' & ')}]`;
|
||||
}
|
||||
|
||||
}
|
||||
8
packages/core/src/ECS/Utils/index.ts
Normal file
8
packages/core/src/ECS/Utils/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// ECS工具类导出
|
||||
export { EntityList } from './EntityList';
|
||||
export { EntityProcessorList } from './EntityProcessorList';
|
||||
export { IdentifierPool } from './IdentifierPool';
|
||||
export { Matcher } from './Matcher';
|
||||
export { Bits } from './Bits';
|
||||
export { ComponentTypeManager } from './ComponentTypeManager';
|
||||
export { BigIntFactory } from './BigIntCompatibility';
|
||||
11
packages/core/src/ECS/index.ts
Normal file
11
packages/core/src/ECS/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export { Entity } from './Entity';
|
||||
export { Component } from './Component';
|
||||
export { ECSEventType, EventPriority, EVENT_TYPES, EventTypeValidator } from './CoreEvents';
|
||||
export * from './Systems';
|
||||
export * from './Utils';
|
||||
export { Scene } from './Scene';
|
||||
export { EntityManager, EntityQueryBuilder } from './Core/EntityManager';
|
||||
export * from './Core/Events';
|
||||
export * from './Core/Query';
|
||||
export * from './Core/Performance';
|
||||
export * from './Core/Storage';
|
||||
502
packages/core/src/Types/index.ts
Normal file
502
packages/core/src/Types/index.ts
Normal file
@@ -0,0 +1,502 @@
|
||||
/**
|
||||
* 框架核心类型定义
|
||||
*/
|
||||
|
||||
/**
|
||||
* 组件接口
|
||||
*
|
||||
* 定义组件的基本契约,所有组件都应该实现此接口
|
||||
*/
|
||||
export interface IComponent {
|
||||
/** 组件唯一标识符 */
|
||||
readonly id: number;
|
||||
/** 组件所属的实体ID */
|
||||
entityId?: string | number;
|
||||
/** 组件启用状态 */
|
||||
enabled: boolean;
|
||||
/** 更新顺序 */
|
||||
updateOrder: number;
|
||||
|
||||
/** 组件添加到实体时的回调 */
|
||||
onAddedToEntity(): void;
|
||||
/** 组件从实体移除时的回调 */
|
||||
onRemovedFromEntity(): void;
|
||||
/** 组件启用时的回调 */
|
||||
onEnabled(): void;
|
||||
/** 组件禁用时的回调 */
|
||||
onDisabled(): void;
|
||||
/** 更新组件 */
|
||||
update(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统基础接口
|
||||
*
|
||||
* 为现有的EntitySystem类提供类型定义
|
||||
*/
|
||||
export interface ISystemBase {
|
||||
/** 系统名称 */
|
||||
readonly systemName: string;
|
||||
/** 更新顺序/优先级 */
|
||||
updateOrder: number;
|
||||
/** 系统启用状态 */
|
||||
enabled: boolean;
|
||||
|
||||
/** 系统初始化 */
|
||||
initialize(): void;
|
||||
/** 更新系统(主要处理阶段) */
|
||||
update(): void;
|
||||
/** 延迟更新系统 */
|
||||
lateUpdate?(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件类型定义
|
||||
*
|
||||
* 用于类型安全的组件操作
|
||||
*/
|
||||
export type ComponentType<T extends IComponent = IComponent> = new (...args: unknown[]) => T;
|
||||
|
||||
/**
|
||||
* 事件总线接口
|
||||
* 提供类型安全的事件发布订阅机制
|
||||
*/
|
||||
export interface IEventBus {
|
||||
/**
|
||||
* 发射事件
|
||||
* @param eventType 事件类型
|
||||
* @param data 事件数据
|
||||
*/
|
||||
emit<T>(eventType: string, data: T): void;
|
||||
|
||||
/**
|
||||
* 异步发射事件
|
||||
* @param eventType 事件类型
|
||||
* @param data 事件数据
|
||||
*/
|
||||
emitAsync<T>(eventType: string, data: T): Promise<void>;
|
||||
|
||||
/**
|
||||
* 监听事件
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件处理器
|
||||
* @param config 监听器配置
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
on<T>(eventType: string, handler: (data: T) => void, config?: IEventListenerConfig): string;
|
||||
|
||||
/**
|
||||
* 监听事件(一次性)
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件处理器
|
||||
* @param config 监听器配置
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
once<T>(eventType: string, handler: (data: T) => void, config?: IEventListenerConfig): string;
|
||||
|
||||
/**
|
||||
* 异步监听事件
|
||||
* @param eventType 事件类型
|
||||
* @param handler 异步事件处理器
|
||||
* @param config 监听器配置
|
||||
* @returns 监听器ID
|
||||
*/
|
||||
onAsync<T>(eventType: string, handler: (data: T) => Promise<void>, config?: IEventListenerConfig): string;
|
||||
|
||||
/**
|
||||
* 移除事件监听器
|
||||
* @param eventType 事件类型
|
||||
* @param listenerId 监听器ID
|
||||
*/
|
||||
off(eventType: string, listenerId: string): boolean;
|
||||
|
||||
/**
|
||||
* 移除指定事件类型的所有监听器
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
offAll(eventType: string): void;
|
||||
|
||||
/**
|
||||
* 检查是否有指定事件的监听器
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
hasListeners(eventType: string): boolean;
|
||||
|
||||
/**
|
||||
* 获取事件统计信息
|
||||
* @param eventType 事件类型(可选)
|
||||
*/
|
||||
getStats(eventType?: string): IEventStats | Map<string, IEventStats>;
|
||||
|
||||
/**
|
||||
* 清空所有监听器
|
||||
*/
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件监听器配置接口
|
||||
*/
|
||||
export interface IEventListenerConfig {
|
||||
/** 是否只执行一次 */
|
||||
once?: boolean;
|
||||
/** 优先级(数字越大优先级越高) */
|
||||
priority?: number;
|
||||
/** 是否异步执行 */
|
||||
async?: boolean;
|
||||
/** 执行上下文 */
|
||||
context?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件统计信息接口
|
||||
*/
|
||||
export interface IEventStats {
|
||||
/** 事件类型 */
|
||||
eventType: string;
|
||||
/** 监听器数量 */
|
||||
listenerCount: number;
|
||||
/** 触发次数 */
|
||||
triggerCount: number;
|
||||
/** 总执行时间(毫秒) */
|
||||
totalExecutionTime: number;
|
||||
/** 平均执行时间(毫秒) */
|
||||
averageExecutionTime: number;
|
||||
/** 最后触发时间 */
|
||||
lastTriggerTime: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件数据基类接口
|
||||
*/
|
||||
export interface IEventData {
|
||||
/** 事件时间戳 */
|
||||
timestamp: number;
|
||||
/** 事件来源 */
|
||||
source?: string;
|
||||
/** 事件ID */
|
||||
eventId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体事件数据接口
|
||||
*/
|
||||
export interface IEntityEventData extends IEventData {
|
||||
/** 实体ID */
|
||||
entityId: number;
|
||||
/** 实体名称 */
|
||||
entityName?: string;
|
||||
/** 实体标签 */
|
||||
entityTag?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件事件数据接口
|
||||
*/
|
||||
export interface IComponentEventData extends IEntityEventData {
|
||||
/** 组件类型名称 */
|
||||
componentType: string;
|
||||
/** 组件实例 */
|
||||
component?: IComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统事件数据接口
|
||||
*/
|
||||
export interface ISystemEventData extends IEventData {
|
||||
/** 系统名称 */
|
||||
systemName: string;
|
||||
/** 系统类型 */
|
||||
systemType: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景事件数据接口
|
||||
*/
|
||||
export interface ISceneEventData extends IEventData {
|
||||
/** 场景名称 */
|
||||
sceneName: string;
|
||||
/** 前一个场景名称 */
|
||||
previousSceneName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 性能事件数据接口
|
||||
*/
|
||||
export interface IPerformanceEventData extends IEventData {
|
||||
/** 操作类型 */
|
||||
operation: string;
|
||||
/** 执行时间(毫秒) */
|
||||
executionTime: number;
|
||||
/** 内存使用量 */
|
||||
memoryUsage?: number;
|
||||
/** 额外数据 */
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* ECS调试配置接口
|
||||
*/
|
||||
export interface IECSDebugConfig {
|
||||
/** 是否启用调试 */
|
||||
enabled: boolean;
|
||||
/** WebSocket服务器URL */
|
||||
websocketUrl: string;
|
||||
/** 是否自动重连 */
|
||||
autoReconnect?: boolean;
|
||||
/** 数据更新间隔(毫秒)- 已弃用,使用debugFrameRate替代 */
|
||||
updateInterval?: number;
|
||||
/** 调试数据发送帧率 (60fps, 30fps, 15fps) */
|
||||
debugFrameRate?: 60 | 30 | 15;
|
||||
/** 数据通道配置 */
|
||||
channels: {
|
||||
entities: boolean;
|
||||
systems: boolean;
|
||||
performance: boolean;
|
||||
components: boolean;
|
||||
scenes: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Core配置接口
|
||||
*/
|
||||
export interface ICoreConfig {
|
||||
/** 是否启用调试模式 */
|
||||
debug?: boolean;
|
||||
/** 是否启用实体系统 */
|
||||
enableEntitySystems?: boolean;
|
||||
/** 调试配置 */
|
||||
debugConfig?: IECSDebugConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* ECS调试数据接口
|
||||
*/
|
||||
export interface IECSDebugData {
|
||||
/** 时间戳 */
|
||||
timestamp: number;
|
||||
/** 框架版本 */
|
||||
frameworkVersion?: string;
|
||||
/** 是否正在运行 */
|
||||
isRunning: boolean;
|
||||
/** 框架是否已加载 */
|
||||
frameworkLoaded: boolean;
|
||||
/** 当前场景名称 */
|
||||
currentScene: string;
|
||||
/** 实体数据 */
|
||||
entities?: IEntityDebugData;
|
||||
/** 系统数据 */
|
||||
systems?: ISystemDebugData;
|
||||
/** 性能数据 */
|
||||
performance?: IPerformanceDebugData;
|
||||
/** 组件数据 */
|
||||
components?: IComponentDebugData;
|
||||
/** 场景数据 */
|
||||
scenes?: ISceneDebugData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体层次结构节点接口
|
||||
*/
|
||||
export interface IEntityHierarchyNode {
|
||||
id: number;
|
||||
name: string;
|
||||
active: boolean;
|
||||
enabled: boolean;
|
||||
activeInHierarchy: boolean;
|
||||
componentCount: number;
|
||||
componentTypes: string[];
|
||||
parentId: number | null;
|
||||
children: IEntityHierarchyNode[];
|
||||
depth: number;
|
||||
tag: number;
|
||||
updateOrder: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体调试数据接口
|
||||
*/
|
||||
export interface IEntityDebugData {
|
||||
/** 总实体数 */
|
||||
totalEntities: number;
|
||||
/** 激活实体数 */
|
||||
activeEntities: number;
|
||||
/** 待添加实体数 */
|
||||
pendingAdd: number;
|
||||
/** 待移除实体数 */
|
||||
pendingRemove: number;
|
||||
/** 按Archetype分组的实体分布 */
|
||||
entitiesPerArchetype: Array<{
|
||||
signature: string;
|
||||
count: number;
|
||||
memory: number;
|
||||
}>;
|
||||
/** 组件数量最多的前几个实体 */
|
||||
topEntitiesByComponents: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
componentCount: number;
|
||||
memory: number;
|
||||
}>;
|
||||
/** 实体详情列表 */
|
||||
entityDetails?: Array<{
|
||||
id: string | number;
|
||||
name?: string;
|
||||
tag?: string;
|
||||
enabled: boolean;
|
||||
componentCount: number;
|
||||
components: string[];
|
||||
}>;
|
||||
/** 实体层次结构(根实体) */
|
||||
entityHierarchy?: Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
active: boolean;
|
||||
enabled: boolean;
|
||||
activeInHierarchy: boolean;
|
||||
componentCount: number;
|
||||
componentTypes: string[];
|
||||
parentId: number | null;
|
||||
children: IEntityHierarchyNode[];
|
||||
depth: number;
|
||||
tag: number;
|
||||
updateOrder: number;
|
||||
}>;
|
||||
/** 实体详细信息映射 */
|
||||
entityDetailsMap?: Record<number, {
|
||||
id: number;
|
||||
name: string;
|
||||
active: boolean;
|
||||
enabled: boolean;
|
||||
activeInHierarchy: boolean;
|
||||
destroyed: boolean;
|
||||
tag: number;
|
||||
updateOrder: number;
|
||||
componentMask: string;
|
||||
parentId: number | null;
|
||||
parentName: string | null;
|
||||
childCount: number;
|
||||
childIds: number[];
|
||||
depth: number;
|
||||
components: Array<{
|
||||
typeName: string;
|
||||
properties: Record<string, unknown>;
|
||||
}>;
|
||||
componentCount: number;
|
||||
componentTypes: string[];
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统调试数据接口
|
||||
*/
|
||||
export interface ISystemDebugData {
|
||||
/** 总系统数 */
|
||||
totalSystems: number;
|
||||
/** 系统信息列表 */
|
||||
systemsInfo: Array<{
|
||||
name: string;
|
||||
type: string;
|
||||
entityCount: number;
|
||||
executionTime?: number;
|
||||
averageExecutionTime?: number;
|
||||
minExecutionTime?: number;
|
||||
maxExecutionTime?: number;
|
||||
executionTimeHistory?: number[];
|
||||
memoryUsage?: number;
|
||||
updateOrder: number;
|
||||
enabled: boolean;
|
||||
lastUpdateTime?: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 性能调试数据接口
|
||||
*/
|
||||
export interface IPerformanceDebugData {
|
||||
/** ECS框架执行时间(毫秒) */
|
||||
frameTime: number;
|
||||
/** 引擎总帧时间(毫秒) */
|
||||
engineFrameTime?: number;
|
||||
/** ECS占总帧时间百分比 */
|
||||
ecsPercentage?: number;
|
||||
/** 内存使用量(MB) */
|
||||
memoryUsage: number;
|
||||
/** FPS */
|
||||
fps: number;
|
||||
/** 平均ECS执行时间(毫秒) */
|
||||
averageFrameTime: number;
|
||||
/** 最小ECS执行时间(毫秒) */
|
||||
minFrameTime: number;
|
||||
/** 最大ECS执行时间(毫秒) */
|
||||
maxFrameTime: number;
|
||||
/** ECS执行时间历史记录 */
|
||||
frameTimeHistory: number[];
|
||||
/** 系统性能详情 */
|
||||
systemPerformance: Array<{
|
||||
systemName: string;
|
||||
averageTime: number;
|
||||
maxTime: number;
|
||||
minTime: number;
|
||||
samples: number;
|
||||
percentage?: number; // 系统占ECS总时间的百分比
|
||||
}>;
|
||||
/** 系统占比分析数据 */
|
||||
systemBreakdown?: Array<{
|
||||
systemName: string;
|
||||
executionTime: number;
|
||||
percentage: number;
|
||||
}>;
|
||||
/** 内存分配详情 */
|
||||
memoryDetails?: {
|
||||
entities: number;
|
||||
components: number;
|
||||
systems: number;
|
||||
pooled: number;
|
||||
totalMemory: number;
|
||||
usedMemory: number;
|
||||
freeMemory: number;
|
||||
gcCollections: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件调试数据接口
|
||||
*/
|
||||
export interface IComponentDebugData {
|
||||
/** 组件类型数 */
|
||||
componentTypes: number;
|
||||
/** 组件实例总数 */
|
||||
componentInstances: number;
|
||||
/** 组件分布统计 */
|
||||
componentStats: Array<{
|
||||
typeName: string;
|
||||
instanceCount: number;
|
||||
memoryPerInstance: number;
|
||||
totalMemory: number;
|
||||
poolSize: number;
|
||||
poolUtilization: number;
|
||||
averagePerEntity?: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景调试数据接口
|
||||
*/
|
||||
export interface ISceneDebugData {
|
||||
/** 当前场景名称 */
|
||||
currentSceneName: string;
|
||||
/** 场景是否已初始化 */
|
||||
isInitialized: boolean;
|
||||
/** 场景运行时间(秒) */
|
||||
sceneRunTime: number;
|
||||
/** 场景实体数 */
|
||||
sceneEntityCount: number;
|
||||
/** 场景系统数 */
|
||||
sceneSystemCount: number;
|
||||
/** 场景内存使用量 */
|
||||
sceneMemory: number;
|
||||
/** 场景启动时间 */
|
||||
sceneUptime: number;
|
||||
}
|
||||
271
packages/core/src/Utils/Debug/ComponentDataCollector.ts
Normal file
271
packages/core/src/Utils/Debug/ComponentDataCollector.ts
Normal file
@@ -0,0 +1,271 @@
|
||||
import { IComponentDebugData } from '../../Types';
|
||||
import { Core } from '../../Core';
|
||||
|
||||
/**
|
||||
* 组件数据收集器
|
||||
*/
|
||||
export class ComponentDataCollector {
|
||||
private static componentSizeCache = new Map<string, number>();
|
||||
|
||||
/**
|
||||
* 收集组件数据(轻量版,不计算实际内存大小)
|
||||
*/
|
||||
public collectComponentData(): IComponentDebugData {
|
||||
const scene = Core.scene;
|
||||
if (!scene) {
|
||||
return {
|
||||
componentTypes: 0,
|
||||
componentInstances: 0,
|
||||
componentStats: []
|
||||
};
|
||||
}
|
||||
|
||||
const entityList = (scene as any).entities;
|
||||
if (!entityList?.buffer) {
|
||||
return {
|
||||
componentTypes: 0,
|
||||
componentInstances: 0,
|
||||
componentStats: []
|
||||
};
|
||||
}
|
||||
|
||||
const componentStats = new Map<string, { count: number; entities: number }>();
|
||||
let totalInstances = 0;
|
||||
|
||||
entityList.buffer.forEach((entity: any) => {
|
||||
if (entity.components) {
|
||||
entity.components.forEach((component: any) => {
|
||||
const typeName = component.constructor.name;
|
||||
const stats = componentStats.get(typeName) || { count: 0, entities: 0 };
|
||||
stats.count++;
|
||||
totalInstances++;
|
||||
componentStats.set(typeName, stats);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取池利用率信息
|
||||
let poolUtilizations = new Map<string, number>();
|
||||
let poolSizes = new Map<string, number>();
|
||||
|
||||
try {
|
||||
const { ComponentPoolManager } = require('../../ECS/Core/ComponentPool');
|
||||
const poolManager = ComponentPoolManager.getInstance();
|
||||
const poolStats = poolManager.getPoolStats();
|
||||
const utilizations = poolManager.getPoolUtilization();
|
||||
|
||||
for (const [typeName, stats] of poolStats.entries()) {
|
||||
poolSizes.set(typeName, stats.maxSize);
|
||||
}
|
||||
|
||||
for (const [typeName, util] of utilizations.entries()) {
|
||||
poolUtilizations.set(typeName, util.utilization);
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果无法获取池信息,使用默认值
|
||||
}
|
||||
|
||||
return {
|
||||
componentTypes: componentStats.size,
|
||||
componentInstances: totalInstances,
|
||||
componentStats: Array.from(componentStats.entries()).map(([typeName, stats]) => {
|
||||
const poolSize = poolSizes.get(typeName) || 0;
|
||||
const poolUtilization = poolUtilizations.get(typeName) || 0;
|
||||
// 使用预估的基础内存大小,避免每帧计算
|
||||
const memoryPerInstance = this.getEstimatedComponentSize(typeName);
|
||||
|
||||
return {
|
||||
typeName,
|
||||
instanceCount: stats.count,
|
||||
memoryPerInstance: memoryPerInstance,
|
||||
totalMemory: stats.count * memoryPerInstance,
|
||||
poolSize: poolSize,
|
||||
poolUtilization: poolUtilization,
|
||||
averagePerEntity: stats.count / entityList.buffer.length
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件类型的估算内存大小(基于预设值,不进行实际计算)
|
||||
*/
|
||||
private getEstimatedComponentSize(typeName: string): number {
|
||||
if (ComponentDataCollector.componentSizeCache.has(typeName)) {
|
||||
return ComponentDataCollector.componentSizeCache.get(typeName)!;
|
||||
}
|
||||
|
||||
const scene = Core.scene;
|
||||
if (!scene) return 64;
|
||||
|
||||
const entityList = (scene as any).entities;
|
||||
if (!entityList?.buffer) return 64;
|
||||
|
||||
let calculatedSize = 64;
|
||||
|
||||
try {
|
||||
for (const entity of entityList.buffer) {
|
||||
if (entity.components) {
|
||||
const component = entity.components.find((c: any) => c.constructor.name === typeName);
|
||||
if (component) {
|
||||
calculatedSize = this.calculateQuickObjectSize(component);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
calculatedSize = 64;
|
||||
}
|
||||
|
||||
ComponentDataCollector.componentSizeCache.set(typeName, calculatedSize);
|
||||
return calculatedSize;
|
||||
}
|
||||
|
||||
private calculateQuickObjectSize(obj: any): number {
|
||||
if (!obj || typeof obj !== 'object') return 8;
|
||||
|
||||
let size = 32;
|
||||
const visited = new WeakSet();
|
||||
|
||||
const calculate = (item: any, depth: number = 0): number => {
|
||||
if (!item || typeof item !== 'object' || visited.has(item) || depth > 3) {
|
||||
return 0;
|
||||
}
|
||||
visited.add(item);
|
||||
|
||||
let itemSize = 0;
|
||||
|
||||
try {
|
||||
const keys = Object.keys(item);
|
||||
for (let i = 0; i < Math.min(keys.length, 20); i++) {
|
||||
const key = keys[i];
|
||||
if (key === 'entity' || key === '_entity' || key === 'constructor') continue;
|
||||
|
||||
const value = item[key];
|
||||
itemSize += key.length * 2;
|
||||
|
||||
if (typeof value === 'string') {
|
||||
itemSize += Math.min(value.length * 2, 200);
|
||||
} else if (typeof value === 'number') {
|
||||
itemSize += 8;
|
||||
} else if (typeof value === 'boolean') {
|
||||
itemSize += 4;
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
itemSize += calculate(value, depth + 1);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return 32;
|
||||
}
|
||||
|
||||
return itemSize;
|
||||
};
|
||||
|
||||
size += calculate(obj);
|
||||
return Math.max(size, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为内存快照功能提供的详细内存计算
|
||||
* 只在用户主动请求内存快照时调用
|
||||
*/
|
||||
public calculateDetailedComponentMemory(typeName: string): number {
|
||||
const scene = Core.scene;
|
||||
if (!scene) return this.getEstimatedComponentSize(typeName);
|
||||
|
||||
const entityList = (scene as any).entities;
|
||||
if (!entityList?.buffer) return this.getEstimatedComponentSize(typeName);
|
||||
|
||||
try {
|
||||
// 找到第一个包含此组件的实体,分析组件大小
|
||||
for (const entity of entityList.buffer) {
|
||||
if (entity.components) {
|
||||
const component = entity.components.find((c: any) => c.constructor.name === typeName);
|
||||
if (component) {
|
||||
return this.estimateObjectSize(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 忽略错误,使用估算值
|
||||
}
|
||||
|
||||
return this.getEstimatedComponentSize(typeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 估算对象内存大小(仅用于内存快照)
|
||||
* 优化版本:减少递归深度,提高性能
|
||||
*/
|
||||
private estimateObjectSize(obj: any, visited = new WeakSet(), depth = 0): number {
|
||||
if (obj === null || obj === undefined || depth > 10) return 0;
|
||||
if (visited.has(obj)) return 0;
|
||||
|
||||
let size = 0;
|
||||
const type = typeof obj;
|
||||
|
||||
switch (type) {
|
||||
case 'boolean':
|
||||
size = 4;
|
||||
break;
|
||||
case 'number':
|
||||
size = 8;
|
||||
break;
|
||||
case 'string':
|
||||
size = 24 + Math.min(obj.length * 2, 1000);
|
||||
break;
|
||||
case 'object':
|
||||
visited.add(obj);
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
size = 40 + (obj.length * 8);
|
||||
const maxElements = Math.min(obj.length, 50);
|
||||
for (let i = 0; i < maxElements; i++) {
|
||||
size += this.estimateObjectSize(obj[i], visited, depth + 1);
|
||||
}
|
||||
} else {
|
||||
size = 32;
|
||||
|
||||
try {
|
||||
const ownKeys = Object.getOwnPropertyNames(obj);
|
||||
const maxProps = Math.min(ownKeys.length, 30);
|
||||
|
||||
for (let i = 0; i < maxProps; i++) {
|
||||
const key = ownKeys[i];
|
||||
|
||||
if (key === 'constructor' ||
|
||||
key === '__proto__' ||
|
||||
key === 'entity' ||
|
||||
key === '_entity' ||
|
||||
key.startsWith('_cc_') ||
|
||||
key.startsWith('__')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
size += 16 + (key.length * 2);
|
||||
|
||||
const value = obj[key];
|
||||
if (value !== undefined && value !== null) {
|
||||
size += this.estimateObjectSize(value, visited, depth + 1);
|
||||
}
|
||||
} catch (error) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
size = 128;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
size = 8;
|
||||
}
|
||||
|
||||
return Math.ceil(size / 8) * 8;
|
||||
}
|
||||
|
||||
public static clearCache(): void {
|
||||
ComponentDataCollector.componentSizeCache.clear();
|
||||
}
|
||||
}
|
||||
124
packages/core/src/Utils/Debug/DebugDataFormatter.ts
Normal file
124
packages/core/src/Utils/Debug/DebugDataFormatter.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { Component } from '../../ECS/Component';
|
||||
|
||||
/**
|
||||
* 调试数据格式化工具
|
||||
*/
|
||||
export class DebugDataFormatter {
|
||||
/**
|
||||
* 格式化属性值
|
||||
*/
|
||||
public static formatPropertyValue(value: any, depth: number = 0): any {
|
||||
// 防止无限递归,限制最大深度
|
||||
if (depth > 5) {
|
||||
return value?.toString() || 'null';
|
||||
}
|
||||
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (Array.isArray(value)) {
|
||||
// 对于数组,总是返回完整数组,让前端决定如何显示
|
||||
return value.map(item => this.formatPropertyValue(item, depth + 1));
|
||||
} else {
|
||||
// 通用对象处理:提取所有可枚举属性,不限制数量
|
||||
try {
|
||||
const keys = Object.keys(value);
|
||||
if (keys.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const result: any = {};
|
||||
keys.forEach(key => {
|
||||
const propValue = value[key];
|
||||
// 避免循环引用和函数属性
|
||||
if (propValue !== value && typeof propValue !== 'function') {
|
||||
try {
|
||||
result[key] = this.formatPropertyValue(propValue, depth + 1);
|
||||
} catch (error) {
|
||||
// 如果属性访问失败,记录错误信息
|
||||
result[key] = `[访问失败: ${error instanceof Error ? error.message : String(error)}]`;
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
return `[对象解析失败: ${error instanceof Error ? error.message : String(error)}]`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取组件详细信息
|
||||
*/
|
||||
public static extractComponentDetails(components: Component[]): Array<{
|
||||
typeName: string;
|
||||
properties: Record<string, any>;
|
||||
}> {
|
||||
return components.map((component: Component) => {
|
||||
const componentDetail = {
|
||||
typeName: component.constructor.name,
|
||||
properties: {} as Record<string, any>
|
||||
};
|
||||
|
||||
// 安全地提取组件属性
|
||||
try {
|
||||
const propertyKeys = Object.keys(component);
|
||||
propertyKeys.forEach(propertyKey => {
|
||||
// 跳过私有属性和实体引用,避免循环引用
|
||||
if (!propertyKey.startsWith('_') && propertyKey !== 'entity') {
|
||||
const propertyValue = (component as any)[propertyKey];
|
||||
if (propertyValue !== undefined && propertyValue !== null) {
|
||||
componentDetail.properties[propertyKey] = this.formatPropertyValue(propertyValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
componentDetail.properties['_extractionError'] = '属性提取失败';
|
||||
}
|
||||
|
||||
return componentDetail;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算对象大小
|
||||
*/
|
||||
public static calculateObjectSize(obj: any, excludeKeys: string[] = []): number {
|
||||
if (!obj || typeof obj !== 'object') return 0;
|
||||
|
||||
let size = 0;
|
||||
const visited = new WeakSet();
|
||||
|
||||
const calculate = (item: any): number => {
|
||||
if (!item || typeof item !== 'object' || visited.has(item)) return 0;
|
||||
visited.add(item);
|
||||
|
||||
let itemSize = 0;
|
||||
|
||||
try {
|
||||
for (const key in item) {
|
||||
if (excludeKeys.includes(key)) continue;
|
||||
|
||||
const value = item[key];
|
||||
itemSize += key.length * 2; // key size
|
||||
|
||||
if (typeof value === 'string') {
|
||||
itemSize += value.length * 2;
|
||||
} else if (typeof value === 'number') {
|
||||
itemSize += 8;
|
||||
} else if (typeof value === 'boolean') {
|
||||
itemSize += 4;
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
itemSize += calculate(value);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 忽略无法访问的属性
|
||||
}
|
||||
|
||||
return itemSize;
|
||||
};
|
||||
|
||||
return calculate(obj);
|
||||
}
|
||||
}
|
||||
732
packages/core/src/Utils/Debug/DebugManager.ts
Normal file
732
packages/core/src/Utils/Debug/DebugManager.ts
Normal file
@@ -0,0 +1,732 @@
|
||||
import { IECSDebugConfig, IECSDebugData } from '../../Types';
|
||||
import { EntityDataCollector } from './EntityDataCollector';
|
||||
import { SystemDataCollector } from './SystemDataCollector';
|
||||
import { PerformanceDataCollector } from './PerformanceDataCollector';
|
||||
import { ComponentDataCollector } from './ComponentDataCollector';
|
||||
import { SceneDataCollector } from './SceneDataCollector';
|
||||
import { WebSocketManager } from './WebSocketManager';
|
||||
import { Core } from '../../Core';
|
||||
|
||||
/**
|
||||
* 调试管理器
|
||||
*
|
||||
* 整合所有调试数据收集器,负责收集和发送调试数据
|
||||
*/
|
||||
export class DebugManager {
|
||||
private config: IECSDebugConfig;
|
||||
private webSocketManager: WebSocketManager;
|
||||
private entityCollector: EntityDataCollector;
|
||||
private systemCollector: SystemDataCollector;
|
||||
private performanceCollector: PerformanceDataCollector;
|
||||
private componentCollector: ComponentDataCollector;
|
||||
private sceneCollector: SceneDataCollector;
|
||||
|
||||
private frameCounter: number = 0;
|
||||
private lastSendTime: number = 0;
|
||||
private sendInterval: number;
|
||||
private isRunning: boolean = false;
|
||||
|
||||
constructor(core: Core, config: IECSDebugConfig) {
|
||||
this.config = config;
|
||||
|
||||
// 初始化数据收集器
|
||||
this.entityCollector = new EntityDataCollector();
|
||||
this.systemCollector = new SystemDataCollector();
|
||||
this.performanceCollector = new PerformanceDataCollector();
|
||||
this.componentCollector = new ComponentDataCollector();
|
||||
this.sceneCollector = new SceneDataCollector();
|
||||
|
||||
// 初始化WebSocket管理器
|
||||
this.webSocketManager = new WebSocketManager(
|
||||
config.websocketUrl,
|
||||
config.autoReconnect !== false
|
||||
);
|
||||
|
||||
// 设置消息处理回调
|
||||
this.webSocketManager.setMessageHandler(this.handleMessage.bind(this));
|
||||
|
||||
// 计算发送间隔(基于帧率)
|
||||
const debugFrameRate = config.debugFrameRate || 30;
|
||||
this.sendInterval = 1000 / debugFrameRate;
|
||||
|
||||
this.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动调试管理器
|
||||
*/
|
||||
public start(): void {
|
||||
if (this.isRunning) return;
|
||||
|
||||
this.isRunning = true;
|
||||
this.connectWebSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止调试管理器
|
||||
*/
|
||||
public stop(): void {
|
||||
if (!this.isRunning) return;
|
||||
|
||||
this.isRunning = false;
|
||||
this.webSocketManager.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新配置
|
||||
*/
|
||||
public updateConfig(config: IECSDebugConfig): void {
|
||||
this.config = config;
|
||||
|
||||
// 更新发送间隔
|
||||
const debugFrameRate = config.debugFrameRate || 30;
|
||||
this.sendInterval = 1000 / debugFrameRate;
|
||||
|
||||
// 重新连接WebSocket(如果URL变化)
|
||||
if (this.webSocketManager && config.websocketUrl) {
|
||||
this.webSocketManager.disconnect();
|
||||
this.webSocketManager = new WebSocketManager(
|
||||
config.websocketUrl,
|
||||
config.autoReconnect !== false
|
||||
);
|
||||
this.webSocketManager.setMessageHandler(this.handleMessage.bind(this));
|
||||
this.connectWebSocket();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 帧更新回调
|
||||
*/
|
||||
public onFrameUpdate(deltaTime: number): void {
|
||||
if (!this.isRunning || !this.config.enabled) return;
|
||||
|
||||
this.frameCounter++;
|
||||
const currentTime = Date.now();
|
||||
|
||||
// 基于配置的帧率发送数据
|
||||
if (currentTime - this.lastSendTime >= this.sendInterval) {
|
||||
this.sendDebugData();
|
||||
this.lastSendTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景变更回调
|
||||
*/
|
||||
public onSceneChanged(): void {
|
||||
// 场景变更时立即发送一次数据
|
||||
if (this.isRunning && this.config.enabled) {
|
||||
this.sendDebugData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理来自调试面板的消息
|
||||
*/
|
||||
private handleMessage(message: any): void {
|
||||
try {
|
||||
switch (message.type) {
|
||||
case 'capture_memory_snapshot':
|
||||
this.handleMemorySnapshotRequest();
|
||||
break;
|
||||
|
||||
case 'config_update':
|
||||
if (message.config) {
|
||||
this.updateConfig({ ...this.config, ...message.config });
|
||||
}
|
||||
break;
|
||||
|
||||
case 'expand_lazy_object':
|
||||
this.handleExpandLazyObjectRequest(message);
|
||||
break;
|
||||
|
||||
case 'get_component_properties':
|
||||
this.handleGetComponentPropertiesRequest(message);
|
||||
break;
|
||||
|
||||
case 'get_raw_entity_list':
|
||||
this.handleGetRawEntityListRequest(message);
|
||||
break;
|
||||
|
||||
case 'get_entity_details':
|
||||
this.handleGetEntityDetailsRequest(message);
|
||||
break;
|
||||
|
||||
case 'ping':
|
||||
this.webSocketManager.send({
|
||||
type: 'pong',
|
||||
timestamp: Date.now()
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
// 未知消息类型,忽略
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
// console.error('[ECS Debug] 处理消息失败:', error);
|
||||
if (message.requestId) {
|
||||
this.webSocketManager.send({
|
||||
type: 'error_response',
|
||||
requestId: message.requestId,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理展开懒加载对象请求
|
||||
*/
|
||||
private handleExpandLazyObjectRequest(message: any): void {
|
||||
try {
|
||||
const { entityId, componentIndex, propertyPath, requestId } = message;
|
||||
|
||||
if (entityId === undefined || componentIndex === undefined || !propertyPath) {
|
||||
this.webSocketManager.send({
|
||||
type: 'expand_lazy_object_response',
|
||||
requestId,
|
||||
error: '缺少必要参数'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const expandedData = this.entityCollector.expandLazyObject(entityId, componentIndex, propertyPath);
|
||||
|
||||
this.webSocketManager.send({
|
||||
type: 'expand_lazy_object_response',
|
||||
requestId,
|
||||
data: expandedData
|
||||
});
|
||||
} catch (error) {
|
||||
this.webSocketManager.send({
|
||||
type: 'expand_lazy_object_response',
|
||||
requestId: message.requestId,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理获取组件属性请求
|
||||
*/
|
||||
private handleGetComponentPropertiesRequest(message: any): void {
|
||||
try {
|
||||
const { entityId, componentIndex, requestId } = message;
|
||||
|
||||
if (entityId === undefined || componentIndex === undefined) {
|
||||
this.webSocketManager.send({
|
||||
type: 'get_component_properties_response',
|
||||
requestId,
|
||||
error: '缺少必要参数'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const properties = this.entityCollector.getComponentProperties(entityId, componentIndex);
|
||||
|
||||
this.webSocketManager.send({
|
||||
type: 'get_component_properties_response',
|
||||
requestId,
|
||||
data: properties
|
||||
});
|
||||
} catch (error) {
|
||||
this.webSocketManager.send({
|
||||
type: 'get_component_properties_response',
|
||||
requestId: message.requestId,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理获取原始实体列表请求
|
||||
*/
|
||||
private handleGetRawEntityListRequest(message: any): void {
|
||||
try {
|
||||
const { requestId } = message;
|
||||
|
||||
const rawEntityList = this.entityCollector.getRawEntityList();
|
||||
|
||||
this.webSocketManager.send({
|
||||
type: 'get_raw_entity_list_response',
|
||||
requestId,
|
||||
data: rawEntityList
|
||||
});
|
||||
} catch (error) {
|
||||
this.webSocketManager.send({
|
||||
type: 'get_raw_entity_list_response',
|
||||
requestId: message.requestId,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理获取实体详情请求
|
||||
*/
|
||||
private handleGetEntityDetailsRequest(message: any): void {
|
||||
try {
|
||||
const { entityId, requestId } = message;
|
||||
|
||||
if (entityId === undefined) {
|
||||
this.webSocketManager.send({
|
||||
type: 'get_entity_details_response',
|
||||
requestId,
|
||||
error: '缺少实体ID参数'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const entityDetails = this.entityCollector.getEntityDetails(entityId);
|
||||
|
||||
this.webSocketManager.send({
|
||||
type: 'get_entity_details_response',
|
||||
requestId,
|
||||
data: entityDetails
|
||||
});
|
||||
} catch (error) {
|
||||
this.webSocketManager.send({
|
||||
type: 'get_entity_details_response',
|
||||
requestId: message.requestId,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 处理内存快照请求
|
||||
*/
|
||||
private handleMemorySnapshotRequest(): void {
|
||||
try {
|
||||
const memorySnapshot = this.captureMemorySnapshot();
|
||||
this.webSocketManager.send({
|
||||
type: 'memory_snapshot_response',
|
||||
data: memorySnapshot
|
||||
});
|
||||
} catch (error) {
|
||||
this.webSocketManager.send({
|
||||
type: 'memory_snapshot_error',
|
||||
error: error instanceof Error ? error.message : '内存快照捕获失败'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获内存快照
|
||||
*/
|
||||
private captureMemorySnapshot(): any {
|
||||
const timestamp = Date.now();
|
||||
|
||||
// 使用专门的内存计算方法收集实体数据
|
||||
const entityData = this.entityCollector.collectEntityDataWithMemory();
|
||||
|
||||
// 收集其他内存统计
|
||||
const baseMemoryInfo = this.collectBaseMemoryInfo();
|
||||
const componentMemoryStats = this.collectComponentMemoryStats((Core.scene as any)?.entities);
|
||||
const systemMemoryStats = this.collectSystemMemoryStats();
|
||||
const poolMemoryStats = this.collectPoolMemoryStats();
|
||||
const performanceStats = this.collectPerformanceStats();
|
||||
|
||||
// 计算总内存使用量
|
||||
const totalEntityMemory = entityData.entitiesPerArchetype.reduce((sum, arch) => sum + arch.memory, 0);
|
||||
|
||||
return {
|
||||
timestamp,
|
||||
version: '2.0',
|
||||
summary: {
|
||||
totalEntities: entityData.totalEntities,
|
||||
totalMemoryUsage: baseMemoryInfo.usedMemory,
|
||||
totalMemoryLimit: baseMemoryInfo.totalMemory,
|
||||
memoryUtilization: (baseMemoryInfo.usedMemory / baseMemoryInfo.totalMemory * 100),
|
||||
gcCollections: baseMemoryInfo.gcCollections,
|
||||
entityMemory: totalEntityMemory,
|
||||
componentMemory: componentMemoryStats.totalMemory,
|
||||
systemMemory: systemMemoryStats.totalMemory,
|
||||
poolMemory: poolMemoryStats.totalMemory
|
||||
},
|
||||
baseMemory: baseMemoryInfo,
|
||||
entities: {
|
||||
totalMemory: totalEntityMemory,
|
||||
entityCount: entityData.totalEntities,
|
||||
archetypes: entityData.entitiesPerArchetype,
|
||||
largestEntities: entityData.topEntitiesByComponents
|
||||
},
|
||||
components: componentMemoryStats,
|
||||
systems: systemMemoryStats,
|
||||
pools: poolMemoryStats,
|
||||
performance: performanceStats
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集基础内存信息
|
||||
*/
|
||||
private collectBaseMemoryInfo(): any {
|
||||
const memoryInfo: any = {
|
||||
totalMemory: 0,
|
||||
usedMemory: 0,
|
||||
freeMemory: 0,
|
||||
gcCollections: 0,
|
||||
heapInfo: null
|
||||
};
|
||||
|
||||
try {
|
||||
if ((performance as any).memory) {
|
||||
const perfMemory = (performance as any).memory;
|
||||
memoryInfo.totalMemory = perfMemory.jsHeapSizeLimit || 512 * 1024 * 1024;
|
||||
memoryInfo.usedMemory = perfMemory.usedJSHeapSize || 0;
|
||||
memoryInfo.freeMemory = memoryInfo.totalMemory - memoryInfo.usedMemory;
|
||||
memoryInfo.heapInfo = {
|
||||
totalJSHeapSize: perfMemory.totalJSHeapSize || 0,
|
||||
usedJSHeapSize: perfMemory.usedJSHeapSize || 0,
|
||||
jsHeapSizeLimit: perfMemory.jsHeapSizeLimit || 0
|
||||
};
|
||||
} else {
|
||||
memoryInfo.totalMemory = 512 * 1024 * 1024;
|
||||
memoryInfo.freeMemory = 512 * 1024 * 1024;
|
||||
}
|
||||
|
||||
// 尝试获取GC信息
|
||||
if ((performance as any).measureUserAgentSpecificMemory) {
|
||||
// 这是一个实验性API,可能不可用
|
||||
(performance as any).measureUserAgentSpecificMemory().then((result: any) => {
|
||||
memoryInfo.detailedMemory = result;
|
||||
}).catch(() => {
|
||||
// 忽略错误
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// 使用默认值
|
||||
}
|
||||
|
||||
return memoryInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 收集组件内存统计(仅用于内存快照)
|
||||
*/
|
||||
private collectComponentMemoryStats(entityList: any): any {
|
||||
const componentStats = new Map<string, { count: number; totalMemory: number; instances: any[] }>();
|
||||
let totalComponentMemory = 0;
|
||||
|
||||
// 首先统计组件类型和数量
|
||||
const componentTypeCounts = new Map<string, number>();
|
||||
for (const entity of entityList.buffer) {
|
||||
if (!entity || entity.destroyed || !entity.components) continue;
|
||||
|
||||
for (const component of entity.components) {
|
||||
const typeName = component.constructor.name;
|
||||
componentTypeCounts.set(typeName, (componentTypeCounts.get(typeName) || 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 为每种组件类型计算详细内存(只计算一次,然后乘以数量)
|
||||
for (const [typeName, count] of componentTypeCounts.entries()) {
|
||||
const detailedMemoryPerInstance = this.componentCollector.calculateDetailedComponentMemory(typeName);
|
||||
const totalMemoryForType = detailedMemoryPerInstance * count;
|
||||
totalComponentMemory += totalMemoryForType;
|
||||
|
||||
// 收集该类型组件的实例信息(用于显示最大的几个实例)
|
||||
const instances: any[] = [];
|
||||
let instanceCount = 0;
|
||||
|
||||
for (const entity of entityList.buffer) {
|
||||
if (!entity || entity.destroyed || !entity.components) continue;
|
||||
|
||||
for (const component of entity.components) {
|
||||
if (component.constructor.name === typeName) {
|
||||
instances.push({
|
||||
entityId: entity.id,
|
||||
entityName: entity.name || `Entity_${entity.id}`,
|
||||
memory: detailedMemoryPerInstance // 使用统一的详细计算结果
|
||||
});
|
||||
instanceCount++;
|
||||
|
||||
// 限制收集的实例数量,避免过多数据
|
||||
if (instanceCount >= 100) break;
|
||||
}
|
||||
}
|
||||
if (instanceCount >= 100) break;
|
||||
}
|
||||
|
||||
componentStats.set(typeName, {
|
||||
count: count,
|
||||
totalMemory: totalMemoryForType,
|
||||
instances: instances.slice(0, 10) // 只保留前10个实例用于显示
|
||||
});
|
||||
}
|
||||
|
||||
const componentBreakdown = Array.from(componentStats.entries()).map(([typeName, stats]) => ({
|
||||
typeName,
|
||||
instanceCount: stats.count,
|
||||
totalMemory: stats.totalMemory,
|
||||
averageMemory: stats.totalMemory / stats.count,
|
||||
percentage: totalComponentMemory > 0 ? (stats.totalMemory / totalComponentMemory * 100) : 0,
|
||||
largestInstances: stats.instances.sort((a, b) => b.memory - a.memory).slice(0, 3)
|
||||
})).sort((a, b) => b.totalMemory - a.totalMemory);
|
||||
|
||||
return {
|
||||
totalMemory: totalComponentMemory,
|
||||
componentTypes: componentStats.size,
|
||||
totalInstances: Array.from(componentStats.values()).reduce((sum, stats) => sum + stats.count, 0),
|
||||
breakdown: componentBreakdown
|
||||
};
|
||||
}
|
||||
|
||||
private collectSystemMemoryStats(): any {
|
||||
const scene = Core.scene;
|
||||
let totalSystemMemory = 0;
|
||||
const systemBreakdown: any[] = [];
|
||||
|
||||
try {
|
||||
const entityProcessors = (scene as any).entityProcessors;
|
||||
if (entityProcessors && entityProcessors.processors) {
|
||||
const systemTypeMemoryCache = new Map<string, number>();
|
||||
|
||||
for (const system of entityProcessors.processors) {
|
||||
const systemTypeName = system.constructor.name;
|
||||
|
||||
let systemMemory: number;
|
||||
if (systemTypeMemoryCache.has(systemTypeName)) {
|
||||
systemMemory = systemTypeMemoryCache.get(systemTypeName)!;
|
||||
} else {
|
||||
systemMemory = this.calculateQuickSystemSize(system);
|
||||
systemTypeMemoryCache.set(systemTypeName, systemMemory);
|
||||
}
|
||||
|
||||
totalSystemMemory += systemMemory;
|
||||
|
||||
systemBreakdown.push({
|
||||
name: systemTypeName,
|
||||
memory: systemMemory,
|
||||
enabled: system.enabled !== false,
|
||||
updateOrder: system.updateOrder || 0
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
return {
|
||||
totalMemory: totalSystemMemory,
|
||||
systemCount: systemBreakdown.length,
|
||||
breakdown: systemBreakdown.sort((a, b) => b.memory - a.memory)
|
||||
};
|
||||
}
|
||||
|
||||
private calculateQuickSystemSize(system: any): number {
|
||||
if (!system || typeof system !== 'object') return 64;
|
||||
|
||||
let size = 128;
|
||||
|
||||
try {
|
||||
const keys = Object.keys(system);
|
||||
for (let i = 0; i < Math.min(keys.length, 15); i++) {
|
||||
const key = keys[i];
|
||||
if (key === 'entities' || key === 'scene' || key === 'constructor') continue;
|
||||
|
||||
const value = system[key];
|
||||
size += key.length * 2;
|
||||
|
||||
if (typeof value === 'string') {
|
||||
size += Math.min(value.length * 2, 100);
|
||||
} else if (typeof value === 'number') {
|
||||
size += 8;
|
||||
} else if (typeof value === 'boolean') {
|
||||
size += 4;
|
||||
} else if (Array.isArray(value)) {
|
||||
size += 40 + Math.min(value.length * 8, 200);
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
size += 64;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return 128;
|
||||
}
|
||||
|
||||
return Math.max(size, 64);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集对象池内存统计
|
||||
*/
|
||||
private collectPoolMemoryStats(): any {
|
||||
let totalPoolMemory = 0;
|
||||
const poolBreakdown: any[] = [];
|
||||
|
||||
try {
|
||||
// 尝试获取组件池统计
|
||||
const { ComponentPoolManager } = require('../../ECS/Core/ComponentPool');
|
||||
const poolManager = ComponentPoolManager.getInstance();
|
||||
const poolStats = poolManager.getPoolStats();
|
||||
|
||||
for (const [typeName, stats] of poolStats.entries()) {
|
||||
const poolData = stats as any; // 类型断言
|
||||
const poolMemory = poolData.maxSize * 32; // 估算每个对象32字节
|
||||
totalPoolMemory += poolMemory;
|
||||
|
||||
poolBreakdown.push({
|
||||
typeName,
|
||||
maxSize: poolData.maxSize,
|
||||
currentSize: poolData.currentSize || 0,
|
||||
estimatedMemory: poolMemory,
|
||||
utilization: poolData.currentSize ? (poolData.currentSize / poolData.maxSize * 100) : 0
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果无法获取池信息,使用默认值
|
||||
}
|
||||
|
||||
try {
|
||||
// 尝试获取通用对象池统计
|
||||
const { Pool } = require('../../Utils/Pool');
|
||||
const poolStats = Pool.getStats();
|
||||
|
||||
for (const [typeName, stats] of Object.entries(poolStats)) {
|
||||
const poolData = stats as any; // 类型断言
|
||||
totalPoolMemory += poolData.estimatedMemoryUsage;
|
||||
poolBreakdown.push({
|
||||
typeName: `Pool_${typeName}`,
|
||||
maxSize: poolData.maxSize,
|
||||
currentSize: poolData.size,
|
||||
estimatedMemory: poolData.estimatedMemoryUsage,
|
||||
utilization: poolData.size / poolData.maxSize * 100,
|
||||
hitRate: poolData.hitRate * 100
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
return {
|
||||
totalMemory: totalPoolMemory,
|
||||
poolCount: poolBreakdown.length,
|
||||
breakdown: poolBreakdown.sort((a, b) => b.estimatedMemory - a.estimatedMemory)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集性能统计信息
|
||||
*/
|
||||
private collectPerformanceStats(): any {
|
||||
try {
|
||||
const performanceMonitor = (Core.Instance as any)._performanceMonitor;
|
||||
if (!performanceMonitor) {
|
||||
return { enabled: false };
|
||||
}
|
||||
|
||||
const stats = performanceMonitor.getAllSystemStats();
|
||||
const warnings = performanceMonitor.getPerformanceWarnings();
|
||||
|
||||
return {
|
||||
enabled: performanceMonitor.enabled,
|
||||
systemCount: stats.size,
|
||||
warnings: warnings.slice(0, 10), // 最多10个警告
|
||||
topSystems: Array.from(stats.entries() as any).map((entry: any) => {
|
||||
const [name, stat] = entry;
|
||||
return {
|
||||
name,
|
||||
averageTime: stat.averageTime,
|
||||
maxTime: stat.maxTime,
|
||||
samples: stat.executionCount
|
||||
};
|
||||
}).sort((a: any, b: any) => b.averageTime - a.averageTime).slice(0, 5)
|
||||
};
|
||||
} catch (error: any) {
|
||||
return { enabled: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内存大小分类
|
||||
*/
|
||||
private getMemorySizeCategory(memoryBytes: number): string {
|
||||
if (memoryBytes < 1024) return '< 1KB';
|
||||
if (memoryBytes < 10 * 1024) return '1-10KB';
|
||||
if (memoryBytes < 100 * 1024) return '10-100KB';
|
||||
if (memoryBytes < 1024 * 1024) return '100KB-1MB';
|
||||
return '> 1MB';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取调试数据
|
||||
*/
|
||||
public getDebugData(): IECSDebugData {
|
||||
const currentTime = Date.now();
|
||||
const scene = Core.scene;
|
||||
|
||||
const debugData: IECSDebugData = {
|
||||
timestamp: currentTime,
|
||||
frameworkVersion: '1.0.0', // 可以从package.json读取
|
||||
isRunning: this.isRunning,
|
||||
frameworkLoaded: true,
|
||||
currentScene: scene?.name || 'Unknown'
|
||||
};
|
||||
|
||||
// 根据配置收集各种数据
|
||||
if (this.config.channels.entities) {
|
||||
debugData.entities = this.entityCollector.collectEntityData();
|
||||
}
|
||||
|
||||
if (this.config.channels.systems) {
|
||||
const performanceMonitor = (Core.Instance as any)._performanceMonitor;
|
||||
debugData.systems = this.systemCollector.collectSystemData(performanceMonitor);
|
||||
}
|
||||
|
||||
if (this.config.channels.performance) {
|
||||
const performanceMonitor = (Core.Instance as any)._performanceMonitor;
|
||||
debugData.performance = this.performanceCollector.collectPerformanceData(performanceMonitor);
|
||||
}
|
||||
|
||||
if (this.config.channels.components) {
|
||||
debugData.components = this.componentCollector.collectComponentData();
|
||||
}
|
||||
|
||||
if (this.config.channels.scenes) {
|
||||
debugData.scenes = this.sceneCollector.collectSceneData();
|
||||
}
|
||||
|
||||
return debugData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接WebSocket
|
||||
*/
|
||||
private async connectWebSocket(): Promise<void> {
|
||||
try {
|
||||
await this.webSocketManager.connect();
|
||||
// console.log('[ECS Debug] 调试管理器已连接到调试服务器');
|
||||
} catch (error) {
|
||||
// console.warn('[ECS Debug] 无法连接到调试服务器:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送调试数据
|
||||
*/
|
||||
private sendDebugData(): void {
|
||||
if (!this.webSocketManager.getConnectionStatus()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const debugData = this.getDebugData();
|
||||
// 包装成调试面板期望的消息格式
|
||||
const message = {
|
||||
type: 'debug_data',
|
||||
data: debugData
|
||||
};
|
||||
this.webSocketManager.send(message);
|
||||
} catch (error) {
|
||||
// console.error('[ECS Debug] 发送调试数据失败:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
1014
packages/core/src/Utils/Debug/EntityDataCollector.ts
Normal file
1014
packages/core/src/Utils/Debug/EntityDataCollector.ts
Normal file
File diff suppressed because it is too large
Load Diff
234
packages/core/src/Utils/Debug/PerformanceDataCollector.ts
Normal file
234
packages/core/src/Utils/Debug/PerformanceDataCollector.ts
Normal file
@@ -0,0 +1,234 @@
|
||||
import { IPerformanceDebugData } from '../../Types';
|
||||
import { Time } from '../Time';
|
||||
|
||||
/**
|
||||
* 性能数据收集器
|
||||
*/
|
||||
export class PerformanceDataCollector {
|
||||
private frameTimeHistory: number[] = [];
|
||||
private maxHistoryLength: number = 60;
|
||||
private lastGCCount: number = 0;
|
||||
private gcCollections: number = 0;
|
||||
private lastMemoryCheck: number = 0;
|
||||
|
||||
/**
|
||||
* 收集性能数据
|
||||
*/
|
||||
public collectPerformanceData(performanceMonitor: any): IPerformanceDebugData {
|
||||
const frameTimeSeconds = Time.deltaTime;
|
||||
const engineFrameTimeMs = frameTimeSeconds * 1000;
|
||||
const currentFps = frameTimeSeconds > 0 ? Math.round(1 / frameTimeSeconds) : 0;
|
||||
|
||||
const ecsPerformanceData = this.getECSPerformanceData(performanceMonitor);
|
||||
const ecsExecutionTimeMs = ecsPerformanceData.totalExecutionTime;
|
||||
const ecsPercentage = engineFrameTimeMs > 0 ? (ecsExecutionTimeMs / engineFrameTimeMs * 100) : 0;
|
||||
|
||||
let memoryUsage = 0;
|
||||
if ((performance as any).memory) {
|
||||
memoryUsage = (performance as any).memory.usedJSHeapSize / 1024 / 1024;
|
||||
}
|
||||
|
||||
// 更新ECS执行时间历史记录
|
||||
this.frameTimeHistory.push(ecsExecutionTimeMs);
|
||||
if (this.frameTimeHistory.length > this.maxHistoryLength) {
|
||||
this.frameTimeHistory.shift();
|
||||
}
|
||||
|
||||
// 计算ECS执行时间统计
|
||||
const history = this.frameTimeHistory.filter(t => t >= 0);
|
||||
const averageECSTime = history.length > 0 ? history.reduce((a, b) => a + b, 0) / history.length : ecsExecutionTimeMs;
|
||||
const minECSTime = history.length > 0 ? Math.min(...history) : ecsExecutionTimeMs;
|
||||
const maxECSTime = history.length > 0 ? Math.max(...history) : ecsExecutionTimeMs;
|
||||
|
||||
return {
|
||||
frameTime: ecsExecutionTimeMs,
|
||||
engineFrameTime: engineFrameTimeMs,
|
||||
ecsPercentage: ecsPercentage,
|
||||
memoryUsage: memoryUsage,
|
||||
fps: currentFps,
|
||||
averageFrameTime: averageECSTime,
|
||||
minFrameTime: minECSTime,
|
||||
maxFrameTime: maxECSTime,
|
||||
frameTimeHistory: [...this.frameTimeHistory],
|
||||
systemPerformance: this.getSystemPerformance(performanceMonitor),
|
||||
systemBreakdown: ecsPerformanceData.systemBreakdown,
|
||||
memoryDetails: this.getMemoryDetails()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ECS框架整体性能数据
|
||||
*/
|
||||
private getECSPerformanceData(performanceMonitor: any): { totalExecutionTime: number; systemBreakdown: Array<any> } {
|
||||
// 检查性能监视器是否存在
|
||||
if (!performanceMonitor) {
|
||||
// 尝试从Core实例获取性能监视器
|
||||
try {
|
||||
const { Core } = require('../../Core');
|
||||
const coreInstance = Core.Instance;
|
||||
if (coreInstance && (coreInstance as any)._performanceMonitor) {
|
||||
performanceMonitor = (coreInstance as any)._performanceMonitor;
|
||||
} else {
|
||||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||||
}
|
||||
} catch (error) {
|
||||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||||
}
|
||||
}
|
||||
|
||||
if (!performanceMonitor.enabled) {
|
||||
// 尝试启用性能监视器
|
||||
try {
|
||||
performanceMonitor.enabled = true;
|
||||
} catch (error) {
|
||||
// 如果无法启用,返回默认值
|
||||
}
|
||||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||||
}
|
||||
|
||||
try {
|
||||
let totalTime = 0;
|
||||
const systemBreakdown = [];
|
||||
|
||||
const stats = performanceMonitor.getAllSystemStats();
|
||||
|
||||
if (stats.size === 0) {
|
||||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||||
}
|
||||
|
||||
// 计算各系统的执行时间
|
||||
for (const [systemName, stat] of stats.entries()) {
|
||||
// 使用最近的执行时间而不是平均时间,这样更能反映当前状态
|
||||
const systemTime = stat.recentTimes && stat.recentTimes.length > 0 ?
|
||||
stat.recentTimes[stat.recentTimes.length - 1] :
|
||||
(stat.averageTime || 0);
|
||||
|
||||
totalTime += systemTime;
|
||||
systemBreakdown.push({
|
||||
systemName: systemName,
|
||||
executionTime: systemTime,
|
||||
percentage: 0 // 后面计算
|
||||
});
|
||||
}
|
||||
|
||||
// 计算各系统占ECS总时间的百分比
|
||||
systemBreakdown.forEach(system => {
|
||||
system.percentage = totalTime > 0 ? (system.executionTime / totalTime * 100) : 0;
|
||||
});
|
||||
|
||||
// 按执行时间排序
|
||||
systemBreakdown.sort((a, b) => b.executionTime - a.executionTime);
|
||||
|
||||
return {
|
||||
totalExecutionTime: totalTime,
|
||||
systemBreakdown: systemBreakdown
|
||||
};
|
||||
} catch (error) {
|
||||
return { totalExecutionTime: 0, systemBreakdown: [] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统性能数据
|
||||
*/
|
||||
private getSystemPerformance(performanceMonitor: any): Array<any> {
|
||||
if (!performanceMonitor) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const stats = performanceMonitor.getAllSystemStats();
|
||||
const systemData = performanceMonitor.getAllSystemData();
|
||||
|
||||
return Array.from(stats.entries() as Iterable<[string, any]>).map(([systemName, stat]) => {
|
||||
const data = systemData.get(systemName);
|
||||
return {
|
||||
systemName: systemName,
|
||||
averageTime: stat.averageTime || 0,
|
||||
maxTime: stat.maxTime || 0,
|
||||
minTime: stat.minTime === Number.MAX_VALUE ? 0 : (stat.minTime || 0),
|
||||
samples: stat.executionCount || 0,
|
||||
percentage: 0,
|
||||
entityCount: data?.entityCount || 0,
|
||||
lastExecutionTime: data?.executionTime || 0
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内存详情
|
||||
*/
|
||||
private getMemoryDetails(): any {
|
||||
const memoryInfo: any = {
|
||||
entities: 0,
|
||||
components: 0,
|
||||
systems: 0,
|
||||
pooled: 0,
|
||||
totalMemory: 0,
|
||||
usedMemory: 0,
|
||||
freeMemory: 0,
|
||||
gcCollections: this.updateGCCount()
|
||||
};
|
||||
|
||||
try {
|
||||
if ((performance as any).memory) {
|
||||
const perfMemory = (performance as any).memory;
|
||||
memoryInfo.totalMemory = perfMemory.jsHeapSizeLimit || 512 * 1024 * 1024;
|
||||
memoryInfo.usedMemory = perfMemory.usedJSHeapSize || 0;
|
||||
memoryInfo.freeMemory = memoryInfo.totalMemory - memoryInfo.usedMemory;
|
||||
|
||||
// 检测GC:如果使用的内存突然大幅减少,可能发生了GC
|
||||
if (this.lastMemoryCheck > 0) {
|
||||
const memoryDrop = this.lastMemoryCheck - memoryInfo.usedMemory;
|
||||
if (memoryDrop > 1024 * 1024) { // 内存减少超过1MB
|
||||
this.gcCollections++;
|
||||
}
|
||||
}
|
||||
this.lastMemoryCheck = memoryInfo.usedMemory;
|
||||
} else {
|
||||
memoryInfo.totalMemory = 512 * 1024 * 1024;
|
||||
memoryInfo.freeMemory = 512 * 1024 * 1024;
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
totalMemory: 0,
|
||||
usedMemory: 0,
|
||||
freeMemory: 0,
|
||||
entityMemory: 0,
|
||||
componentMemory: 0,
|
||||
systemMemory: 0,
|
||||
pooledMemory: 0,
|
||||
gcCollections: this.gcCollections
|
||||
};
|
||||
}
|
||||
|
||||
return memoryInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新GC计数
|
||||
*/
|
||||
private updateGCCount(): number {
|
||||
try {
|
||||
// 尝试使用PerformanceObserver来检测GC
|
||||
if (typeof PerformanceObserver !== 'undefined') {
|
||||
// 这是一个简化的GC检测方法
|
||||
// 实际的GC检测需要更复杂的逻辑
|
||||
return this.gcCollections;
|
||||
}
|
||||
|
||||
// 如果有其他GC检测API,可以在这里添加
|
||||
if ((performance as any).measureUserAgentSpecificMemory) {
|
||||
// 实验性API,可能不可用
|
||||
return this.gcCollections;
|
||||
}
|
||||
|
||||
return this.gcCollections;
|
||||
} catch (error) {
|
||||
return this.gcCollections;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
packages/core/src/Utils/Debug/SceneDataCollector.ts
Normal file
50
packages/core/src/Utils/Debug/SceneDataCollector.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { ISceneDebugData } from '../../Types';
|
||||
import { Core } from '../../Core';
|
||||
|
||||
/**
|
||||
* 场景数据收集器
|
||||
*/
|
||||
export class SceneDataCollector {
|
||||
private sceneStartTime: number = Date.now();
|
||||
|
||||
/**
|
||||
* 收集场景数据
|
||||
*/
|
||||
public collectSceneData(): ISceneDebugData {
|
||||
const scene = Core.scene;
|
||||
if (!scene) {
|
||||
return {
|
||||
currentSceneName: 'No Scene',
|
||||
isInitialized: false,
|
||||
sceneRunTime: 0,
|
||||
sceneEntityCount: 0,
|
||||
sceneSystemCount: 0,
|
||||
sceneMemory: 0,
|
||||
sceneUptime: 0
|
||||
};
|
||||
}
|
||||
|
||||
const currentTime = Date.now();
|
||||
const runTime = (currentTime - this.sceneStartTime) / 1000;
|
||||
|
||||
const entityList = (scene as any).entities;
|
||||
const entityProcessors = (scene as any).entityProcessors;
|
||||
|
||||
return {
|
||||
currentSceneName: (scene as any).name || 'Unnamed Scene',
|
||||
isInitialized: (scene as any)._didSceneBegin || false,
|
||||
sceneRunTime: runTime,
|
||||
sceneEntityCount: entityList?.buffer?.length || 0,
|
||||
sceneSystemCount: entityProcessors?.processors?.length || 0,
|
||||
sceneMemory: 0, // TODO: 计算实际场景内存
|
||||
sceneUptime: runTime
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置场景开始时间
|
||||
*/
|
||||
public setSceneStartTime(time: number): void {
|
||||
this.sceneStartTime = time;
|
||||
}
|
||||
}
|
||||
65
packages/core/src/Utils/Debug/SystemDataCollector.ts
Normal file
65
packages/core/src/Utils/Debug/SystemDataCollector.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { ISystemDebugData } from '../../Types';
|
||||
import { Core } from '../../Core';
|
||||
|
||||
/**
|
||||
* 系统数据收集器
|
||||
*/
|
||||
export class SystemDataCollector {
|
||||
/**
|
||||
* 收集系统数据
|
||||
*/
|
||||
public collectSystemData(performanceMonitor: any): ISystemDebugData {
|
||||
const scene = Core.scene;
|
||||
if (!scene) {
|
||||
return {
|
||||
totalSystems: 0,
|
||||
systemsInfo: []
|
||||
};
|
||||
}
|
||||
|
||||
const entityProcessors = (scene as any).entityProcessors;
|
||||
if (!entityProcessors) {
|
||||
return {
|
||||
totalSystems: 0,
|
||||
systemsInfo: []
|
||||
};
|
||||
}
|
||||
|
||||
const systems = entityProcessors.processors || [];
|
||||
|
||||
// 获取性能监控数据
|
||||
let systemStats: Map<string, any> = new Map();
|
||||
let systemData: Map<string, any> = new Map();
|
||||
|
||||
if (performanceMonitor) {
|
||||
try {
|
||||
systemStats = performanceMonitor.getAllSystemStats();
|
||||
systemData = performanceMonitor.getAllSystemData();
|
||||
} catch (error) {
|
||||
// 忽略错误,使用空的Map
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalSystems: systems.length,
|
||||
systemsInfo: systems.map((system: any) => {
|
||||
const systemName = system.systemName || system.constructor.name;
|
||||
const stats = systemStats.get(systemName);
|
||||
const data = systemData.get(systemName);
|
||||
|
||||
return {
|
||||
name: systemName,
|
||||
type: system.constructor.name,
|
||||
entityCount: system.entities?.length || 0,
|
||||
executionTime: stats?.averageTime || data?.executionTime || 0,
|
||||
minExecutionTime: stats?.minTime === Number.MAX_VALUE ? 0 : (stats?.minTime || 0),
|
||||
maxExecutionTime: stats?.maxTime || 0,
|
||||
executionTimeHistory: stats?.recentTimes || [],
|
||||
updateOrder: system.updateOrder || 0,
|
||||
enabled: system.enabled !== false,
|
||||
lastUpdateTime: data?.lastUpdateTime || 0
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
177
packages/core/src/Utils/Debug/WebSocketManager.ts
Normal file
177
packages/core/src/Utils/Debug/WebSocketManager.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* WebSocket连接管理器
|
||||
*/
|
||||
export class WebSocketManager {
|
||||
private ws?: WebSocket;
|
||||
private isConnected: boolean = false;
|
||||
private reconnectAttempts: number = 0;
|
||||
private maxReconnectAttempts: number = 5;
|
||||
private reconnectInterval: number = 2000;
|
||||
private url: string;
|
||||
private autoReconnect: boolean;
|
||||
private reconnectTimer?: NodeJS.Timeout;
|
||||
private onOpen?: (event: Event) => void;
|
||||
private onClose?: (event: CloseEvent) => void;
|
||||
private onError?: (error: Event | any) => void;
|
||||
private messageHandler?: (message: any) => void;
|
||||
|
||||
constructor(url: string, autoReconnect: boolean = true) {
|
||||
this.url = url;
|
||||
this.autoReconnect = autoReconnect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置消息处理回调
|
||||
*/
|
||||
public setMessageHandler(handler: (message: any) => void): void {
|
||||
this.messageHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接WebSocket
|
||||
*/
|
||||
public connect(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
this.ws = new WebSocket(this.url);
|
||||
|
||||
this.ws.onopen = (event) => {
|
||||
this.handleOpen(event);
|
||||
resolve();
|
||||
};
|
||||
|
||||
this.ws.onclose = (event) => {
|
||||
this.handleClose(event);
|
||||
};
|
||||
|
||||
this.ws.onerror = (error) => {
|
||||
this.handleError(error);
|
||||
reject(error);
|
||||
};
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
this.handleMessage(event);
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
this.handleConnectionFailure(error);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开连接
|
||||
*/
|
||||
public disconnect(): void {
|
||||
if (this.ws) {
|
||||
this.autoReconnect = false; // 主动断开时不自动重连
|
||||
this.ws.close();
|
||||
this.ws = undefined;
|
||||
}
|
||||
this.isConnected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送数据
|
||||
*/
|
||||
public send(data: any): void {
|
||||
if (!this.isConnected || !this.ws) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const message = typeof data === 'string' ? data : JSON.stringify(data);
|
||||
this.ws.send(message);
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接状态
|
||||
*/
|
||||
public getConnectionStatus(): boolean {
|
||||
return this.isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大重连次数
|
||||
*/
|
||||
public setMaxReconnectAttempts(attempts: number): void {
|
||||
this.maxReconnectAttempts = attempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置重连间隔
|
||||
*/
|
||||
public setReconnectInterval(interval: number): void {
|
||||
this.reconnectInterval = interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计划重连
|
||||
*/
|
||||
private scheduleReconnect(): void {
|
||||
if (this.reconnectTimer) {
|
||||
clearTimeout(this.reconnectTimer);
|
||||
}
|
||||
|
||||
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
||||
this.reconnectAttempts++;
|
||||
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
this.connect().catch(error => {
|
||||
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
this.scheduleReconnect();
|
||||
}
|
||||
});
|
||||
}, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理接收到的消息
|
||||
*/
|
||||
private handleMessage(event: MessageEvent): void {
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
// 调用消息处理回调
|
||||
if (this.messageHandler) {
|
||||
this.messageHandler(message);
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
|
||||
private handleOpen(event: Event): void {
|
||||
this.isConnected = true;
|
||||
this.reconnectAttempts = 0;
|
||||
|
||||
if (this.onOpen) {
|
||||
this.onOpen(event);
|
||||
}
|
||||
}
|
||||
|
||||
private handleClose(event: CloseEvent): void {
|
||||
this.isConnected = false;
|
||||
|
||||
if (this.onClose) {
|
||||
this.onClose(event);
|
||||
}
|
||||
|
||||
if (this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
this.scheduleReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private handleError(error: Event): void {
|
||||
if (this.onError) {
|
||||
this.onError(error);
|
||||
}
|
||||
}
|
||||
|
||||
private handleConnectionFailure(error: any): void {
|
||||
if (this.onError) {
|
||||
this.onError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
packages/core/src/Utils/Debug/index.ts
Normal file
7
packages/core/src/Utils/Debug/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export { EntityDataCollector } from './EntityDataCollector';
|
||||
export { SystemDataCollector } from './SystemDataCollector';
|
||||
export { PerformanceDataCollector } from './PerformanceDataCollector';
|
||||
export { ComponentDataCollector } from './ComponentDataCollector';
|
||||
export { SceneDataCollector } from './SceneDataCollector';
|
||||
export { WebSocketManager } from './WebSocketManager';
|
||||
export { DebugManager } from './DebugManager';
|
||||
81
packages/core/src/Utils/Emitter.ts
Normal file
81
packages/core/src/Utils/Emitter.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 用于包装事件的一个小类
|
||||
*/
|
||||
export class FuncPack<TContext = unknown> {
|
||||
/** 函数 */
|
||||
public func: Function;
|
||||
/** 上下文 */
|
||||
public context: TContext;
|
||||
|
||||
constructor(func: Function, context: TContext) {
|
||||
this.func = func;
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于事件管理
|
||||
*/
|
||||
export class Emitter<T, TContext = unknown> {
|
||||
private _messageTable: Map<T, FuncPack<TContext>[]>;
|
||||
|
||||
constructor() {
|
||||
this._messageTable = new Map<T, FuncPack<TContext>[]>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始监听项
|
||||
* @param eventType 监听类型
|
||||
* @param handler 监听函数
|
||||
* @param context 监听上下文
|
||||
*/
|
||||
public addObserver(eventType: T, handler: Function, context: TContext) {
|
||||
let list = this._messageTable.get(eventType);
|
||||
if (!list) {
|
||||
list = [];
|
||||
this._messageTable.set(eventType, list);
|
||||
}
|
||||
|
||||
if (!this.hasObserver(eventType, handler)) {
|
||||
list.push(new FuncPack<TContext>(handler, context));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除监听项
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件函数
|
||||
*/
|
||||
public removeObserver(eventType: T, handler: Function) {
|
||||
let messageData = this._messageTable.get(eventType);
|
||||
if (messageData) {
|
||||
let index = messageData.findIndex(data => data.func == handler);
|
||||
if (index != -1)
|
||||
messageData.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发该事件
|
||||
* @param eventType 事件类型
|
||||
* @param data 事件数据
|
||||
*/
|
||||
public emit<TData = unknown>(eventType: T, ...data: TData[]) {
|
||||
let list = this._messageTable.get(eventType);
|
||||
if (list) {
|
||||
for (let observer of list) {
|
||||
observer.func.call(observer.context, ...data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在该类型的观察者
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件函数
|
||||
*/
|
||||
public hasObserver(eventType: T, handler: Function): boolean {
|
||||
let list = this._messageTable.get(eventType);
|
||||
return list ? list.some(observer => observer.func === handler) : false;
|
||||
}
|
||||
}
|
||||
15
packages/core/src/Utils/Extensions/NumberExtension.ts
Normal file
15
packages/core/src/Utils/Extensions/NumberExtension.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* 数字扩展工具类
|
||||
* 提供数字转换的实用方法
|
||||
*/
|
||||
export class NumberExtension {
|
||||
/**
|
||||
* 将值转换为数字
|
||||
* @param value 要转换的值
|
||||
* @returns 转换后的数字,如果值为undefined则返回0
|
||||
*/
|
||||
public static toNumber(value: unknown): number {
|
||||
if (value == undefined) return 0;
|
||||
return Number(value);
|
||||
}
|
||||
}
|
||||
14
packages/core/src/Utils/Extensions/TypeUtils.ts
Normal file
14
packages/core/src/Utils/Extensions/TypeUtils.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 类型工具类
|
||||
* 提供类型相关的实用方法
|
||||
*/
|
||||
export class TypeUtils {
|
||||
/**
|
||||
* 获取对象的类型
|
||||
* @param obj 对象
|
||||
* @returns 对象的构造函数
|
||||
*/
|
||||
public static getType(obj: any) {
|
||||
return obj.constructor;
|
||||
}
|
||||
}
|
||||
3
packages/core/src/Utils/Extensions/index.ts
Normal file
3
packages/core/src/Utils/Extensions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// 扩展工具类导出
|
||||
export { TypeUtils } from './TypeUtils';
|
||||
export { NumberExtension } from './NumberExtension';
|
||||
55
packages/core/src/Utils/GlobalManager.ts
Normal file
55
packages/core/src/Utils/GlobalManager.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 全局管理器的基类。所有全局管理器都应该从此类继承。
|
||||
*/
|
||||
export class GlobalManager {
|
||||
/**
|
||||
* 表示管理器是否启用
|
||||
*/
|
||||
public _enabled: boolean = false;
|
||||
|
||||
/**
|
||||
* 获取或设置管理器是否启用
|
||||
*/
|
||||
public get enabled() {
|
||||
return this._enabled;
|
||||
}
|
||||
|
||||
public set enabled(value: boolean) {
|
||||
this.setEnabled(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置管理器是否启用
|
||||
* @param isEnabled 如果为true,则启用管理器;否则禁用管理器
|
||||
*/
|
||||
public setEnabled(isEnabled: boolean) {
|
||||
if (this._enabled != isEnabled) {
|
||||
this._enabled = isEnabled;
|
||||
if (this._enabled) {
|
||||
// 如果启用了管理器,则调用onEnabled方法
|
||||
this.onEnabled();
|
||||
} else {
|
||||
// 如果禁用了管理器,则调用onDisabled方法
|
||||
this.onDisabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在启用管理器时调用的回调方法
|
||||
*/
|
||||
protected onEnabled() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 在禁用管理器时调用的回调方法
|
||||
*/
|
||||
protected onDisabled() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新管理器状态的方法
|
||||
*/
|
||||
public update() {
|
||||
}
|
||||
}
|
||||
403
packages/core/src/Utils/PerformanceMonitor.ts
Normal file
403
packages/core/src/Utils/PerformanceMonitor.ts
Normal file
@@ -0,0 +1,403 @@
|
||||
/**
|
||||
* 性能监控数据
|
||||
*/
|
||||
export interface PerformanceData {
|
||||
/** 系统名称 */
|
||||
name: string;
|
||||
/** 执行时间(毫秒) */
|
||||
executionTime: number;
|
||||
/** 处理的实体数量 */
|
||||
entityCount: number;
|
||||
/** 平均每个实体的处理时间 */
|
||||
averageTimePerEntity: number;
|
||||
/** 最后更新时间戳 */
|
||||
lastUpdateTime: number;
|
||||
/** 内存使用量(字节) */
|
||||
memoryUsage?: number;
|
||||
/** CPU使用率(百分比) */
|
||||
cpuUsage?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 性能统计信息
|
||||
*/
|
||||
export interface PerformanceStats {
|
||||
/** 总执行时间 */
|
||||
totalTime: number;
|
||||
/** 平均执行时间 */
|
||||
averageTime: number;
|
||||
/** 最小执行时间 */
|
||||
minTime: number;
|
||||
/** 最大执行时间 */
|
||||
maxTime: number;
|
||||
/** 执行次数 */
|
||||
executionCount: number;
|
||||
/** 最近的执行时间列表 */
|
||||
recentTimes: number[];
|
||||
/** 标准差 */
|
||||
standardDeviation: number;
|
||||
/** 95百分位数 */
|
||||
percentile95: number;
|
||||
/** 99百分位数 */
|
||||
percentile99: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 性能警告类型
|
||||
*/
|
||||
export enum PerformanceWarningType {
|
||||
HIGH_EXECUTION_TIME = 'high_execution_time',
|
||||
HIGH_MEMORY_USAGE = 'high_memory_usage',
|
||||
HIGH_CPU_USAGE = 'high_cpu_usage',
|
||||
FREQUENT_GC = 'frequent_gc',
|
||||
LOW_FPS = 'low_fps',
|
||||
HIGH_ENTITY_COUNT = 'high_entity_count'
|
||||
}
|
||||
|
||||
/**
|
||||
* 性能警告
|
||||
*/
|
||||
export interface PerformanceWarning {
|
||||
type: PerformanceWarningType;
|
||||
systemName: string;
|
||||
message: string;
|
||||
severity: 'low' | 'medium' | 'high' | 'critical';
|
||||
timestamp: number;
|
||||
value: number;
|
||||
threshold: number;
|
||||
suggestion?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 性能阈值配置
|
||||
*/
|
||||
export interface PerformanceThresholds {
|
||||
/** 执行时间阈值(毫秒) */
|
||||
executionTime: {
|
||||
warning: number;
|
||||
critical: number;
|
||||
};
|
||||
/** 内存使用阈值(MB) */
|
||||
memoryUsage: {
|
||||
warning: number;
|
||||
critical: number;
|
||||
};
|
||||
/** CPU使用率阈值(百分比) */
|
||||
cpuUsage: {
|
||||
warning: number;
|
||||
critical: number;
|
||||
};
|
||||
/** FPS阈值 */
|
||||
fps: {
|
||||
warning: number;
|
||||
critical: number;
|
||||
};
|
||||
/** 实体数量阈值 */
|
||||
entityCount: {
|
||||
warning: number;
|
||||
critical: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 高性能监控器
|
||||
* 用于监控ECS系统的性能表现,提供详细的分析和优化建议
|
||||
*/
|
||||
export class PerformanceMonitor {
|
||||
private static _instance: PerformanceMonitor;
|
||||
|
||||
private _systemData = new Map<string, PerformanceData>();
|
||||
private _systemStats = new Map<string, PerformanceStats>();
|
||||
private _warnings: PerformanceWarning[] = [];
|
||||
private _isEnabled = false;
|
||||
private _maxRecentSamples = 60; // 保留最近60帧的数据
|
||||
private _maxWarnings = 100; // 最大警告数量
|
||||
|
||||
// 性能阈值配置
|
||||
private _thresholds: PerformanceThresholds = {
|
||||
executionTime: { warning: 16.67, critical: 33.33 }, // 60fps和30fps对应的帧时间
|
||||
memoryUsage: { warning: 100, critical: 200 }, // MB
|
||||
cpuUsage: { warning: 70, critical: 90 }, // 百分比
|
||||
fps: { warning: 45, critical: 30 },
|
||||
entityCount: { warning: 1000, critical: 5000 }
|
||||
};
|
||||
|
||||
// FPS监控
|
||||
private _fpsHistory: number[] = [];
|
||||
private _lastFrameTime = 0;
|
||||
private _frameCount = 0;
|
||||
private _fpsUpdateInterval = 1000; // 1秒更新一次FPS
|
||||
private _lastFpsUpdate = 0;
|
||||
private _currentFps = 60;
|
||||
|
||||
// 内存监控
|
||||
private _memoryCheckInterval = 5000; // 5秒检查一次内存
|
||||
private _lastMemoryCheck = 0;
|
||||
private _memoryHistory: number[] = [];
|
||||
|
||||
// GC监控
|
||||
private _gcCount = 0;
|
||||
private _lastGcCheck = 0;
|
||||
private _gcCheckInterval = 1000;
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
public static get instance(): PerformanceMonitor {
|
||||
if (!PerformanceMonitor._instance) {
|
||||
PerformanceMonitor._instance = new PerformanceMonitor();
|
||||
}
|
||||
return PerformanceMonitor._instance;
|
||||
}
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* 启用性能监控
|
||||
*/
|
||||
public enable(): void {
|
||||
this._isEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用性能监控
|
||||
*/
|
||||
public disable(): void {
|
||||
this._isEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否启用了性能监控
|
||||
*/
|
||||
public get isEnabled(): boolean {
|
||||
return this._isEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始监控系统性能
|
||||
* @param systemName 系统名称
|
||||
* @returns 开始时间戳
|
||||
*/
|
||||
public startMonitoring(systemName: string): number {
|
||||
if (!this._isEnabled) {
|
||||
return 0;
|
||||
}
|
||||
return performance.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束监控并记录性能数据
|
||||
* @param systemName 系统名称
|
||||
* @param startTime 开始时间戳
|
||||
* @param entityCount 处理的实体数量
|
||||
*/
|
||||
public endMonitoring(systemName: string, startTime: number, entityCount: number = 0): void {
|
||||
if (!this._isEnabled || startTime === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const endTime = performance.now();
|
||||
const executionTime = endTime - startTime;
|
||||
const averageTimePerEntity = entityCount > 0 ? executionTime / entityCount : 0;
|
||||
|
||||
// 更新当前性能数据
|
||||
const data: PerformanceData = {
|
||||
name: systemName,
|
||||
executionTime,
|
||||
entityCount,
|
||||
averageTimePerEntity,
|
||||
lastUpdateTime: endTime
|
||||
};
|
||||
|
||||
this._systemData.set(systemName, data);
|
||||
|
||||
// 更新统计信息
|
||||
this.updateStats(systemName, executionTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新系统统计信息
|
||||
* @param systemName 系统名称
|
||||
* @param executionTime 执行时间
|
||||
*/
|
||||
private updateStats(systemName: string, executionTime: number): void {
|
||||
let stats = this._systemStats.get(systemName);
|
||||
|
||||
if (!stats) {
|
||||
stats = {
|
||||
totalTime: 0,
|
||||
averageTime: 0,
|
||||
minTime: Number.MAX_VALUE,
|
||||
maxTime: 0,
|
||||
executionCount: 0,
|
||||
recentTimes: [],
|
||||
standardDeviation: 0,
|
||||
percentile95: 0,
|
||||
percentile99: 0
|
||||
};
|
||||
this._systemStats.set(systemName, stats);
|
||||
}
|
||||
|
||||
// 更新基本统计
|
||||
stats.totalTime += executionTime;
|
||||
stats.executionCount++;
|
||||
stats.averageTime = stats.totalTime / stats.executionCount;
|
||||
stats.minTime = Math.min(stats.minTime, executionTime);
|
||||
stats.maxTime = Math.max(stats.maxTime, executionTime);
|
||||
|
||||
// 更新最近时间列表
|
||||
stats.recentTimes.push(executionTime);
|
||||
if (stats.recentTimes.length > this._maxRecentSamples) {
|
||||
stats.recentTimes.shift();
|
||||
}
|
||||
|
||||
// 计算高级统计信息
|
||||
this.calculateAdvancedStats(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算高级统计信息
|
||||
* @param stats 统计信息对象
|
||||
*/
|
||||
private calculateAdvancedStats(stats: PerformanceStats): void {
|
||||
if (stats.recentTimes.length === 0) return;
|
||||
|
||||
// 计算标准差
|
||||
const mean = stats.recentTimes.reduce((a, b) => a + b, 0) / stats.recentTimes.length;
|
||||
const variance = stats.recentTimes.reduce((acc, time) => acc + Math.pow(time - mean, 2), 0) / stats.recentTimes.length;
|
||||
stats.standardDeviation = Math.sqrt(variance);
|
||||
|
||||
// 计算百分位数
|
||||
const sortedTimes = [...stats.recentTimes].sort((a, b) => a - b);
|
||||
const len = sortedTimes.length;
|
||||
|
||||
stats.percentile95 = sortedTimes[Math.floor(len * 0.95)] || 0;
|
||||
stats.percentile99 = sortedTimes[Math.floor(len * 0.99)] || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统的当前性能数据
|
||||
* @param systemName 系统名称
|
||||
* @returns 性能数据或undefined
|
||||
*/
|
||||
public getSystemData(systemName: string): PerformanceData | undefined {
|
||||
return this._systemData.get(systemName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统的统计信息
|
||||
* @param systemName 系统名称
|
||||
* @returns 统计信息或undefined
|
||||
*/
|
||||
public getSystemStats(systemName: string): PerformanceStats | undefined {
|
||||
return this._systemStats.get(systemName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有系统的性能数据
|
||||
* @returns 所有系统的性能数据
|
||||
*/
|
||||
public getAllSystemData(): Map<string, PerformanceData> {
|
||||
return new Map(this._systemData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有系统的统计信息
|
||||
* @returns 所有系统的统计信息
|
||||
*/
|
||||
public getAllSystemStats(): Map<string, PerformanceStats> {
|
||||
return new Map(this._systemStats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取性能报告
|
||||
* @returns 格式化的性能报告字符串
|
||||
*/
|
||||
public getPerformanceReport(): string {
|
||||
if (!this._isEnabled) {
|
||||
return "Performance monitoring is disabled.";
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push("=== ECS Performance Report ===");
|
||||
lines.push("");
|
||||
|
||||
// 按平均执行时间排序
|
||||
const sortedSystems = Array.from(this._systemStats.entries())
|
||||
.sort((a, b) => b[1].averageTime - a[1].averageTime);
|
||||
|
||||
for (const [systemName, stats] of sortedSystems) {
|
||||
const data = this._systemData.get(systemName);
|
||||
|
||||
lines.push(`System: ${systemName}`);
|
||||
lines.push(` Current: ${data?.executionTime.toFixed(2)}ms (${data?.entityCount} entities)`);
|
||||
lines.push(` Average: ${stats.averageTime.toFixed(2)}ms`);
|
||||
lines.push(` Min/Max: ${stats.minTime.toFixed(2)}ms / ${stats.maxTime.toFixed(2)}ms`);
|
||||
lines.push(` Total: ${stats.totalTime.toFixed(2)}ms (${stats.executionCount} calls)`);
|
||||
|
||||
if (data?.averageTimePerEntity && data.averageTimePerEntity > 0) {
|
||||
lines.push(` Per Entity: ${data.averageTimePerEntity.toFixed(4)}ms`);
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
// 总体统计
|
||||
const totalCurrentTime = Array.from(this._systemData.values())
|
||||
.reduce((sum, data) => sum + data.executionTime, 0);
|
||||
|
||||
lines.push(`Total Frame Time: ${totalCurrentTime.toFixed(2)}ms`);
|
||||
lines.push(`Systems Count: ${this._systemData.size}`);
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置所有性能数据
|
||||
*/
|
||||
public reset(): void {
|
||||
this._systemData.clear();
|
||||
this._systemStats.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置指定系统的性能数据
|
||||
* @param systemName 系统名称
|
||||
*/
|
||||
public resetSystem(systemName: string): void {
|
||||
this._systemData.delete(systemName);
|
||||
this._systemStats.delete(systemName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取性能警告
|
||||
* @param thresholdMs 警告阈值(毫秒)
|
||||
* @returns 超过阈值的系统列表
|
||||
*/
|
||||
public getPerformanceWarnings(thresholdMs: number = 16.67): string[] {
|
||||
const warnings: string[] = [];
|
||||
|
||||
for (const [systemName, data] of this._systemData.entries()) {
|
||||
if (data.executionTime > thresholdMs) {
|
||||
warnings.push(`${systemName}: ${data.executionTime.toFixed(2)}ms (>${thresholdMs}ms)`);
|
||||
}
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大保留样本数
|
||||
* @param maxSamples 最大样本数
|
||||
*/
|
||||
public setMaxRecentSamples(maxSamples: number): void {
|
||||
this._maxRecentSamples = maxSamples;
|
||||
|
||||
// 裁剪现有数据
|
||||
for (const stats of this._systemStats.values()) {
|
||||
while (stats.recentTimes.length > maxSamples) {
|
||||
stats.recentTimes.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
565
packages/core/src/Utils/Pool.ts
Normal file
565
packages/core/src/Utils/Pool.ts
Normal file
@@ -0,0 +1,565 @@
|
||||
/**
|
||||
* 可池化对象接口
|
||||
*/
|
||||
export interface IPoolable {
|
||||
/**
|
||||
* 重置对象状态,准备重用
|
||||
*/
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象池统计信息
|
||||
*/
|
||||
export interface PoolStats {
|
||||
/** 池中对象数量 */
|
||||
size: number;
|
||||
/** 池的最大大小 */
|
||||
maxSize: number;
|
||||
/** 总共创建的对象数量 */
|
||||
totalCreated: number;
|
||||
/** 总共获取的次数 */
|
||||
totalObtained: number;
|
||||
/** 总共释放的次数 */
|
||||
totalReleased: number;
|
||||
/** 命中率(从池中获取的比例) */
|
||||
hitRate: number;
|
||||
/** 内存使用估算(字节) */
|
||||
estimatedMemoryUsage: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 高性能通用对象池
|
||||
* 支持任意类型的对象池化,包含详细的统计信息
|
||||
*/
|
||||
export class Pool<T extends IPoolable> {
|
||||
private static _pools = new Map<Function, Pool<any>>();
|
||||
|
||||
private _objects: T[] = [];
|
||||
private _createFn: () => T;
|
||||
private _maxSize: number;
|
||||
private _stats: PoolStats;
|
||||
private _objectSize: number; // 估算的单个对象大小
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param createFn 创建对象的函数
|
||||
* @param maxSize 池的最大大小,默认100
|
||||
* @param estimatedObjectSize 估算的单个对象大小(字节),默认1024
|
||||
*/
|
||||
constructor(createFn: () => T, maxSize: number = 100, estimatedObjectSize: number = 1024) {
|
||||
this._createFn = createFn;
|
||||
this._maxSize = maxSize;
|
||||
this._objectSize = estimatedObjectSize;
|
||||
this._stats = {
|
||||
size: 0,
|
||||
maxSize,
|
||||
totalCreated: 0,
|
||||
totalObtained: 0,
|
||||
totalReleased: 0,
|
||||
hitRate: 0,
|
||||
estimatedMemoryUsage: 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的对象池
|
||||
* @param type 对象类型
|
||||
* @param maxSize 池的最大大小
|
||||
* @param estimatedObjectSize 估算的单个对象大小
|
||||
* @returns 对象池实例
|
||||
*/
|
||||
public static getPool<T extends IPoolable>(
|
||||
type: new (...args: unknown[]) => T,
|
||||
maxSize: number = 100,
|
||||
estimatedObjectSize: number = 1024
|
||||
): Pool<T> {
|
||||
let pool = this._pools.get(type);
|
||||
|
||||
if (!pool) {
|
||||
pool = new Pool<T>(() => new type(), maxSize, estimatedObjectSize);
|
||||
this._pools.set(type, pool);
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从池中获取对象
|
||||
* @returns 对象实例
|
||||
*/
|
||||
public obtain(): T {
|
||||
this._stats.totalObtained++;
|
||||
|
||||
if (this._objects.length > 0) {
|
||||
const obj = this._objects.pop()!;
|
||||
this._stats.size--;
|
||||
this._updateHitRate();
|
||||
this._updateMemoryUsage();
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 池中没有对象,创建新的
|
||||
const obj = this._createFn();
|
||||
this._stats.totalCreated++;
|
||||
this._updateHitRate();
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象归还到池中
|
||||
* @param obj 要归还的对象
|
||||
*/
|
||||
public free(obj: T): void {
|
||||
if (this._objects.length < this._maxSize) {
|
||||
obj.reset();
|
||||
this._objects.push(obj);
|
||||
this._stats.size++;
|
||||
this._stats.totalReleased++;
|
||||
this._updateMemoryUsage();
|
||||
}
|
||||
// 如果池已满,对象会被丢弃(由GC回收)
|
||||
}
|
||||
|
||||
/**
|
||||
* 预热池,创建指定数量的对象
|
||||
* @param count 要创建的对象数量
|
||||
*/
|
||||
public warmUp(count: number): void {
|
||||
const targetSize = Math.min(count, this._maxSize);
|
||||
|
||||
while (this._objects.length < targetSize) {
|
||||
const obj = this._createFn();
|
||||
this._stats.totalCreated++;
|
||||
this._objects.push(obj);
|
||||
this._stats.size++;
|
||||
}
|
||||
|
||||
this._updateMemoryUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空池
|
||||
*/
|
||||
public clear(): void {
|
||||
this._objects.length = 0;
|
||||
this._stats.size = 0;
|
||||
this._updateMemoryUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取池中对象数量
|
||||
*/
|
||||
public get size(): number {
|
||||
return this._objects.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取池的最大大小
|
||||
*/
|
||||
public get maxSize(): number {
|
||||
return this._maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置池的最大大小
|
||||
*/
|
||||
public set maxSize(value: number) {
|
||||
this._maxSize = value;
|
||||
this._stats.maxSize = value;
|
||||
|
||||
// 如果当前池大小超过新的最大值,则移除多余的对象
|
||||
while (this._objects.length > this._maxSize) {
|
||||
this._objects.pop();
|
||||
this._stats.size--;
|
||||
}
|
||||
|
||||
this._updateMemoryUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取池的统计信息
|
||||
*/
|
||||
public getStats(): PoolStats {
|
||||
return { ...this._stats };
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置统计信息
|
||||
*/
|
||||
public resetStats(): void {
|
||||
this._stats.totalCreated = 0;
|
||||
this._stats.totalObtained = 0;
|
||||
this._stats.totalReleased = 0;
|
||||
this._stats.hitRate = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新命中率
|
||||
*/
|
||||
private _updateHitRate(): void {
|
||||
if (this._stats.totalObtained > 0) {
|
||||
const hits = this._stats.totalObtained - this._stats.totalCreated;
|
||||
this._stats.hitRate = hits / this._stats.totalObtained;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新内存使用估算
|
||||
*/
|
||||
private _updateMemoryUsage(): void {
|
||||
this._stats.estimatedMemoryUsage = this._stats.size * this._objectSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法:从指定类型的池中获取对象
|
||||
* @param type 对象类型
|
||||
* @returns 对象实例
|
||||
*/
|
||||
public static obtain<T extends IPoolable>(type: new (...args: unknown[]) => T): T {
|
||||
return this.getPool(type).obtain();
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法:将对象归还到对应类型的池中
|
||||
* @param type 对象类型
|
||||
* @param obj 要归还的对象
|
||||
*/
|
||||
public static free<T extends IPoolable>(type: new (...args: unknown[]) => T, obj: T): void {
|
||||
this.getPool(type).free(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法:预热指定类型的池
|
||||
* @param type 对象类型
|
||||
* @param count 要创建的对象数量
|
||||
*/
|
||||
public static warmUp<T extends IPoolable>(type: new (...args: unknown[]) => T, count: number): void {
|
||||
this.getPool(type).warmUp(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法:清空指定类型的池
|
||||
* @param type 对象类型
|
||||
*/
|
||||
public static clearPool<T extends IPoolable>(type: new (...args: unknown[]) => T): void {
|
||||
const pool = this._pools.get(type);
|
||||
if (pool) {
|
||||
pool.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法:清空所有池
|
||||
*/
|
||||
public static clearAllPools(): void {
|
||||
for (const pool of this._pools.values()) {
|
||||
pool.clear();
|
||||
}
|
||||
this._pools.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法:获取池的统计信息
|
||||
* @returns 池的统计信息
|
||||
*/
|
||||
public static getStats(): { [typeName: string]: PoolStats } {
|
||||
const stats: { [typeName: string]: PoolStats } = {};
|
||||
|
||||
for (const [type, pool] of this._pools.entries()) {
|
||||
const typeName = (type as any).name || 'Unknown';
|
||||
stats[typeName] = pool.getStats();
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法:获取所有池的总内存使用量
|
||||
* @returns 总内存使用量(字节)
|
||||
*/
|
||||
public static getTotalMemoryUsage(): number {
|
||||
let total = 0;
|
||||
for (const pool of this._pools.values()) {
|
||||
total += pool.getStats().estimatedMemoryUsage;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法:获取性能报告
|
||||
* @returns 格式化的性能报告
|
||||
*/
|
||||
public static getPerformanceReport(): string {
|
||||
const stats = this.getStats();
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push('=== Object Pool Performance Report ===');
|
||||
lines.push(`Total Memory Usage: ${(this.getTotalMemoryUsage() / 1024 / 1024).toFixed(2)} MB`);
|
||||
lines.push('');
|
||||
|
||||
for (const [typeName, stat] of Object.entries(stats)) {
|
||||
lines.push(`${typeName}:`);
|
||||
lines.push(` Size: ${stat.size}/${stat.maxSize}`);
|
||||
lines.push(` Hit Rate: ${(stat.hitRate * 100).toFixed(1)}%`);
|
||||
lines.push(` Total Created: ${stat.totalCreated}`);
|
||||
lines.push(` Total Obtained: ${stat.totalObtained}`);
|
||||
lines.push(` Memory: ${(stat.estimatedMemoryUsage / 1024).toFixed(1)} KB`);
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分层对象池
|
||||
* 使用多个不同大小的池来优化内存使用
|
||||
*/
|
||||
export class TieredObjectPool<T extends IPoolable> {
|
||||
private pools: Pool<T>[] = [];
|
||||
private createFn: () => T;
|
||||
private resetFn: (obj: T) => void;
|
||||
private tierSizes: number[];
|
||||
private totalObtained = 0;
|
||||
private totalReleased = 0;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param createFn 创建对象的函数
|
||||
* @param resetFn 重置对象的函数
|
||||
* @param tierSizes 各层级的大小,默认[10, 50, 200]
|
||||
* @param estimatedObjectSize 估算的单个对象大小
|
||||
*/
|
||||
constructor(
|
||||
createFn: () => T,
|
||||
resetFn: (obj: T) => void,
|
||||
tierSizes: number[] = [10, 50, 200],
|
||||
estimatedObjectSize: number = 1024
|
||||
) {
|
||||
this.createFn = createFn;
|
||||
this.resetFn = resetFn;
|
||||
this.tierSizes = tierSizes;
|
||||
|
||||
// 初始化不同层级的池
|
||||
for (const size of tierSizes) {
|
||||
this.pools.push(new Pool(createFn, size, estimatedObjectSize));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
* @returns 对象实例
|
||||
*/
|
||||
public obtain(): T {
|
||||
this.totalObtained++;
|
||||
|
||||
// 从最小的池开始尝试获取
|
||||
for (const pool of this.pools) {
|
||||
if (pool.size > 0) {
|
||||
return pool.obtain();
|
||||
}
|
||||
}
|
||||
|
||||
// 所有池都空了,创建新对象
|
||||
return this.createFn();
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放对象
|
||||
* @param obj 要释放的对象
|
||||
*/
|
||||
public release(obj: T): void {
|
||||
this.totalReleased++;
|
||||
this.resetFn(obj);
|
||||
|
||||
// 放入第一个有空间的池
|
||||
for (const pool of this.pools) {
|
||||
if (pool.size < pool.maxSize) {
|
||||
pool.free(obj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 所有池都满了,直接丢弃
|
||||
}
|
||||
|
||||
/**
|
||||
* 预热所有池
|
||||
* @param totalCount 总预热数量
|
||||
*/
|
||||
public warmUp(totalCount: number): void {
|
||||
let remaining = totalCount;
|
||||
|
||||
for (const pool of this.pools) {
|
||||
const warmUpCount = Math.min(remaining, pool.maxSize);
|
||||
pool.warmUp(warmUpCount);
|
||||
remaining -= warmUpCount;
|
||||
|
||||
if (remaining <= 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有池
|
||||
*/
|
||||
public clear(): void {
|
||||
for (const pool of this.pools) {
|
||||
pool.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取统计信息
|
||||
*/
|
||||
public getStats(): {
|
||||
totalSize: number;
|
||||
totalMaxSize: number;
|
||||
totalMemoryUsage: number;
|
||||
tierStats: PoolStats[];
|
||||
hitRate: number;
|
||||
} {
|
||||
let totalSize = 0;
|
||||
let totalMaxSize = 0;
|
||||
let totalMemoryUsage = 0;
|
||||
const tierStats: PoolStats[] = [];
|
||||
|
||||
for (const pool of this.pools) {
|
||||
const stats = pool.getStats();
|
||||
tierStats.push(stats);
|
||||
totalSize += stats.size;
|
||||
totalMaxSize += stats.maxSize;
|
||||
totalMemoryUsage += stats.estimatedMemoryUsage;
|
||||
}
|
||||
|
||||
const hitRate = this.totalObtained > 0 ?
|
||||
(this.totalObtained - this.getTotalCreated()) / this.totalObtained : 0;
|
||||
|
||||
return {
|
||||
totalSize,
|
||||
totalMaxSize,
|
||||
totalMemoryUsage,
|
||||
tierStats,
|
||||
hitRate
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取总创建数量
|
||||
*/
|
||||
private getTotalCreated(): number {
|
||||
return this.pools.reduce((total, pool) => total + pool.getStats().totalCreated, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 池管理器
|
||||
* 统一管理所有对象池
|
||||
*/
|
||||
export class PoolManager {
|
||||
private static instance: PoolManager;
|
||||
private pools = new Map<string, Pool<any> | TieredObjectPool<any>>();
|
||||
private autoCompactInterval = 60000; // 60秒
|
||||
private lastCompactTime = 0;
|
||||
|
||||
public static getInstance(): PoolManager {
|
||||
if (!PoolManager.instance) {
|
||||
PoolManager.instance = new PoolManager();
|
||||
}
|
||||
return PoolManager.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册池
|
||||
* @param name 池名称
|
||||
* @param pool 池实例
|
||||
*/
|
||||
public registerPool<T extends IPoolable>(name: string, pool: Pool<T> | TieredObjectPool<T>): void {
|
||||
this.pools.set(name, pool);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取池
|
||||
* @param name 池名称
|
||||
* @returns 池实例
|
||||
*/
|
||||
public getPool<T extends IPoolable>(name: string): Pool<T> | TieredObjectPool<T> | null {
|
||||
return this.pools.get(name) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新池管理器(应在游戏循环中调用)
|
||||
*/
|
||||
public update(): void {
|
||||
const now = Date.now();
|
||||
|
||||
if (now - this.lastCompactTime > this.autoCompactInterval) {
|
||||
this.compactAllPools();
|
||||
this.lastCompactTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩所有池(清理碎片)
|
||||
*/
|
||||
public compactAllPools(): void {
|
||||
// 对于标准池,可以考虑清理一些长时间未使用的对象
|
||||
// 这里简单实现为重置统计信息
|
||||
for (const pool of this.pools.values()) {
|
||||
if (pool instanceof Pool) {
|
||||
pool.resetStats();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有池的统计信息
|
||||
*/
|
||||
public getAllStats(): Map<string, any> {
|
||||
const stats = new Map<string, any>();
|
||||
|
||||
for (const [name, pool] of this.pools.entries()) {
|
||||
if (pool instanceof Pool) {
|
||||
stats.set(name, pool.getStats());
|
||||
} else if (pool instanceof TieredObjectPool) {
|
||||
stats.set(name, pool.getStats());
|
||||
}
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成性能报告
|
||||
*/
|
||||
public generateReport(): string {
|
||||
const lines: string[] = [];
|
||||
lines.push('=== Pool Manager Report ===');
|
||||
|
||||
let totalMemory = 0;
|
||||
|
||||
for (const [name, pool] of this.pools.entries()) {
|
||||
lines.push(`\n${name}:`);
|
||||
|
||||
if (pool instanceof Pool) {
|
||||
const stats = pool.getStats();
|
||||
lines.push(` Type: Standard Pool`);
|
||||
lines.push(` Size: ${stats.size}/${stats.maxSize}`);
|
||||
lines.push(` Hit Rate: ${(stats.hitRate * 100).toFixed(1)}%`);
|
||||
lines.push(` Memory: ${(stats.estimatedMemoryUsage / 1024).toFixed(1)} KB`);
|
||||
totalMemory += stats.estimatedMemoryUsage;
|
||||
} else if (pool instanceof TieredObjectPool) {
|
||||
const stats = pool.getStats();
|
||||
lines.push(` Type: Tiered Pool`);
|
||||
lines.push(` Total Size: ${stats.totalSize}/${stats.totalMaxSize}`);
|
||||
lines.push(` Hit Rate: ${(stats.hitRate * 100).toFixed(1)}%`);
|
||||
lines.push(` Memory: ${(stats.totalMemoryUsage / 1024).toFixed(1)} KB`);
|
||||
totalMemory += stats.totalMemoryUsage;
|
||||
}
|
||||
}
|
||||
|
||||
lines.push(`\nTotal Memory Usage: ${(totalMemory / 1024 / 1024).toFixed(2)} MB`);
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
}
|
||||
73
packages/core/src/Utils/Time.ts
Normal file
73
packages/core/src/Utils/Time.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 时间管理工具类
|
||||
* 提供游戏时间相关的功能,包括帧时间、总时间、时间缩放等
|
||||
*/
|
||||
export class Time {
|
||||
/**
|
||||
* 上一帧到当前帧的时间间隔(秒)
|
||||
*/
|
||||
public static deltaTime: number = 0;
|
||||
|
||||
/**
|
||||
* 未缩放的帧时间间隔(秒)
|
||||
*/
|
||||
public static unscaledDeltaTime: number = 0;
|
||||
|
||||
/**
|
||||
* 游戏开始以来的总时间(秒)
|
||||
*/
|
||||
public static totalTime: number = 0;
|
||||
|
||||
/**
|
||||
* 未缩放的总时间(秒)
|
||||
*/
|
||||
public static unscaledTotalTime: number = 0;
|
||||
|
||||
/**
|
||||
* 时间缩放比例
|
||||
*/
|
||||
public static timeScale: number = 1;
|
||||
|
||||
/**
|
||||
* 当前帧数
|
||||
*/
|
||||
public static frameCount: number = 0;
|
||||
|
||||
/**
|
||||
* 使用外部引擎提供的deltaTime更新时间信息
|
||||
* @param deltaTime 外部引擎提供的帧时间间隔(秒)
|
||||
*/
|
||||
public static update(deltaTime: number): void {
|
||||
// 设置未缩放的帧时间
|
||||
this.unscaledDeltaTime = deltaTime;
|
||||
this.deltaTime = deltaTime * this.timeScale;
|
||||
|
||||
// 更新总时间
|
||||
this.unscaledTotalTime += this.unscaledDeltaTime;
|
||||
this.totalTime += this.deltaTime;
|
||||
|
||||
// 更新帧数
|
||||
this.frameCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景改变时重置时间
|
||||
*/
|
||||
public static sceneChanged(): void {
|
||||
this.frameCount = 0;
|
||||
this.totalTime = 0;
|
||||
this.unscaledTotalTime = 0;
|
||||
this.deltaTime = 0;
|
||||
this.unscaledDeltaTime = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定的时间间隔是否已经过去
|
||||
* @param interval 时间间隔(秒)
|
||||
* @param lastTime 上次检查的时间
|
||||
* @returns 是否已经过去指定时间
|
||||
*/
|
||||
public static checkEvery(interval: number, lastTime: number): boolean {
|
||||
return this.totalTime - lastTime >= interval;
|
||||
}
|
||||
}
|
||||
18
packages/core/src/Utils/Timers/ITimer.ts
Normal file
18
packages/core/src/Utils/Timers/ITimer.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export interface ITimer<TContext = unknown> {
|
||||
context: TContext;
|
||||
|
||||
/**
|
||||
* 调用stop以停止此计时器再次运行。这对非重复计时器没有影响。
|
||||
*/
|
||||
stop(): void;
|
||||
|
||||
/**
|
||||
* 将计时器的运行时间重置为0
|
||||
*/
|
||||
reset(): void;
|
||||
|
||||
/**
|
||||
* 返回投向T的上下文,作为方便
|
||||
*/
|
||||
getContext<T>(): T;
|
||||
}
|
||||
70
packages/core/src/Utils/Timers/Timer.ts
Normal file
70
packages/core/src/Utils/Timers/Timer.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { ITimer } from './ITimer';
|
||||
import { Time } from '../Time';
|
||||
|
||||
/**
|
||||
* 私有类隐藏ITimer的实现
|
||||
*/
|
||||
export class Timer<TContext = unknown> implements ITimer<TContext>{
|
||||
public context!: TContext;
|
||||
public _timeInSeconds: number = 0;
|
||||
public _repeats: boolean = false;
|
||||
public _onTime!: (timer: ITimer<TContext>) => void;
|
||||
public _isDone: boolean = false;
|
||||
public _elapsedTime: number = 0;
|
||||
|
||||
public getContext<T>(): T {
|
||||
return this.context as unknown as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时器是否已完成
|
||||
*/
|
||||
public get isDone(): boolean {
|
||||
return this._isDone;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时器已运行的时间
|
||||
*/
|
||||
public get elapsedTime(): number {
|
||||
return this._elapsedTime;
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this._elapsedTime = 0;
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
this._isDone = true;
|
||||
}
|
||||
|
||||
public tick(){
|
||||
// 如果stop在tick之前被调用,那么isDone将为true,我们不应该再做任何事情
|
||||
if (!this._isDone && this._elapsedTime > this._timeInSeconds){
|
||||
this._elapsedTime -= this._timeInSeconds;
|
||||
this._onTime(this);
|
||||
|
||||
if (!this._isDone && !this._repeats)
|
||||
this._isDone = true;
|
||||
}
|
||||
|
||||
this._elapsedTime += Time.deltaTime;
|
||||
|
||||
return this._isDone;
|
||||
}
|
||||
|
||||
public initialize(timeInsSeconds: number, repeats: boolean, context: TContext, onTime: (timer: ITimer<TContext>)=>void){
|
||||
this._timeInSeconds = timeInsSeconds;
|
||||
this._repeats = repeats;
|
||||
this.context = context;
|
||||
this._onTime = onTime.bind(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 空出对象引用,以便在js需要时GC可以清理它们的引用
|
||||
*/
|
||||
public unload(){
|
||||
this.context = null as unknown as TContext;
|
||||
this._onTime = null!;
|
||||
}
|
||||
}
|
||||
34
packages/core/src/Utils/Timers/TimerManager.ts
Normal file
34
packages/core/src/Utils/Timers/TimerManager.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { GlobalManager } from '../GlobalManager';
|
||||
import { Timer } from './Timer';
|
||||
import { ITimer } from './ITimer';
|
||||
|
||||
/**
|
||||
* 允许动作的延迟和重复执行
|
||||
*/
|
||||
export class TimerManager extends GlobalManager {
|
||||
public _timers: Array<Timer<unknown>> = [];
|
||||
|
||||
public override update() {
|
||||
for (let i = this._timers.length - 1; i >= 0; i --){
|
||||
if (this._timers[i].tick()){
|
||||
this._timers[i].unload();
|
||||
this._timers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调度一个一次性或重复的计时器,该计时器将调用已传递的动作
|
||||
* @param timeInSeconds
|
||||
* @param repeats
|
||||
* @param context
|
||||
* @param onTime
|
||||
*/
|
||||
public schedule<TContext = unknown>(timeInSeconds: number, repeats: boolean, context: TContext, onTime: (timer: ITimer<TContext>)=>void): Timer<TContext> {
|
||||
let timer = new Timer<TContext>();
|
||||
timer.initialize(timeInSeconds, repeats, context, onTime);
|
||||
this._timers.push(timer as Timer<unknown>);
|
||||
|
||||
return timer;
|
||||
}
|
||||
}
|
||||
7
packages/core/src/Utils/index.ts
Normal file
7
packages/core/src/Utils/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from './Extensions';
|
||||
export * from './Pool';
|
||||
export * from './Emitter';
|
||||
export * from './GlobalManager';
|
||||
export * from './PerformanceMonitor';
|
||||
export { Time } from './Time';
|
||||
export * from './Debug';
|
||||
21
packages/core/src/index.ts
Normal file
21
packages/core/src/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* ECS Framework - 轻量级实体组件系统框架
|
||||
* 适用于Laya、Cocos Creator等JavaScript游戏引擎和H5小游戏开发
|
||||
*/
|
||||
|
||||
// 核心模块
|
||||
export { Core } from './Core';
|
||||
|
||||
// 核心管理器
|
||||
export { Emitter, FuncPack } from './Utils/Emitter';
|
||||
export { GlobalManager } from './Utils/GlobalManager';
|
||||
export { TimerManager } from './Utils/Timers/TimerManager';
|
||||
export { ITimer } from './Utils/Timers/ITimer';
|
||||
export { Timer } from './Utils/Timers/Timer';
|
||||
|
||||
// ECS核心组件
|
||||
export * from './ECS';
|
||||
|
||||
// 工具类和类型定义
|
||||
export * from './Utils';
|
||||
export * from './Types';
|
||||
Reference in New Issue
Block a user