依赖注入引入DI容器
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
import { GlobalManager } from './Utils/GlobalManager';
|
|
||||||
import { TimerManager } from './Utils/Timers/TimerManager';
|
import { TimerManager } from './Utils/Timers/TimerManager';
|
||||||
import { ITimer } from './Utils/Timers/ITimer';
|
import { ITimer } from './Utils/Timers/ITimer';
|
||||||
import { Timer } from './Utils/Timers/Timer';
|
import { Timer } from './Utils/Timers/Timer';
|
||||||
@@ -6,7 +5,7 @@ import { Time } from './Utils/Time';
|
|||||||
import { PerformanceMonitor } from './Utils/PerformanceMonitor';
|
import { PerformanceMonitor } from './Utils/PerformanceMonitor';
|
||||||
import { PoolManager } from './Utils/Pool/PoolManager';
|
import { PoolManager } from './Utils/Pool/PoolManager';
|
||||||
import { DebugManager } from './Utils/Debug';
|
import { DebugManager } from './Utils/Debug';
|
||||||
import { ICoreConfig, IECSDebugConfig } from './Types';
|
import { ICoreConfig, IECSDebugConfig, IUpdatable, isUpdatable } from './Types';
|
||||||
import { createLogger } from './Utils/Logger';
|
import { createLogger } from './Utils/Logger';
|
||||||
import { SceneManager } from './ECS/SceneManager';
|
import { SceneManager } from './ECS/SceneManager';
|
||||||
import { IScene } from './ECS/IScene';
|
import { IScene } from './ECS/IScene';
|
||||||
@@ -55,8 +54,10 @@ export class Core {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 全局核心实例
|
* 全局核心实例
|
||||||
|
*
|
||||||
|
* 可能为null表示Core尚未初始化或已被销毁
|
||||||
*/
|
*/
|
||||||
private static _instance: Core;
|
private static _instance: Core | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core专用日志器
|
* Core专用日志器
|
||||||
@@ -84,13 +85,6 @@ export class Core {
|
|||||||
*/
|
*/
|
||||||
private _serviceContainer: ServiceContainer;
|
private _serviceContainer: ServiceContainer;
|
||||||
|
|
||||||
/**
|
|
||||||
* 全局管理器集合
|
|
||||||
*
|
|
||||||
* 存储所有注册的全局管理器实例。
|
|
||||||
*/
|
|
||||||
public _globalManagers: GlobalManager[] = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定时器管理器
|
* 定时器管理器
|
||||||
*
|
*
|
||||||
@@ -151,7 +145,6 @@ export class Core {
|
|||||||
|
|
||||||
// 初始化定时器管理器
|
// 初始化定时器管理器
|
||||||
this._timerManager = new TimerManager();
|
this._timerManager = new TimerManager();
|
||||||
Core.registerGlobalManager(this._timerManager);
|
|
||||||
this._serviceContainer.registerInstance(TimerManager, this._timerManager);
|
this._serviceContainer.registerInstance(TimerManager, this._timerManager);
|
||||||
|
|
||||||
// 初始化性能监控器
|
// 初始化性能监控器
|
||||||
@@ -175,7 +168,13 @@ export class Core {
|
|||||||
|
|
||||||
// 初始化调试管理器
|
// 初始化调试管理器
|
||||||
if (this._config.debugConfig?.enabled) {
|
if (this._config.debugConfig?.enabled) {
|
||||||
this._debugManager = new DebugManager(this, this._config.debugConfig);
|
// 使用DI容器创建DebugManager(前两个参数从容器解析,config手动传入)
|
||||||
|
const config = this._config.debugConfig;
|
||||||
|
this._debugManager = new DebugManager(
|
||||||
|
this._serviceContainer.resolve(SceneManager),
|
||||||
|
this._serviceContainer.resolve(PerformanceMonitor),
|
||||||
|
config
|
||||||
|
);
|
||||||
this._serviceContainer.registerInstance(DebugManager, this._debugManager);
|
this._serviceContainer.registerInstance(DebugManager, this._debugManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +196,7 @@ export class Core {
|
|||||||
* 用于注册和解析自定义服务。
|
* 用于注册和解析自定义服务。
|
||||||
*
|
*
|
||||||
* @returns 服务容器实例
|
* @returns 服务容器实例
|
||||||
|
* @throws 如果Core实例未创建
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
@@ -368,43 +368,6 @@ export class Core {
|
|||||||
this._instance.updateInternal(deltaTime);
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调度定时器
|
* 调度定时器
|
||||||
@@ -416,6 +379,7 @@ export class Core {
|
|||||||
* @param context - 回调函数的上下文,默认为null
|
* @param context - 回调函数的上下文,默认为null
|
||||||
* @param onTime - 定时器触发时的回调函数
|
* @param onTime - 定时器触发时的回调函数
|
||||||
* @returns 创建的定时器实例
|
* @returns 创建的定时器实例
|
||||||
|
* @throws 如果Core实例未创建或onTime回调未提供
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
@@ -431,6 +395,9 @@ export class Core {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
public static schedule<TContext = unknown>(timeInSeconds: number, repeats: boolean = false, context?: TContext, onTime?: (timer: ITimer<TContext>) => void): Timer<TContext> {
|
public static schedule<TContext = unknown>(timeInSeconds: number, repeats: boolean = false, context?: TContext, onTime?: (timer: ITimer<TContext>) => void): Timer<TContext> {
|
||||||
|
if (!this._instance) {
|
||||||
|
throw new Error('Core实例未创建,请先调用Core.create()');
|
||||||
|
}
|
||||||
if (!onTime) {
|
if (!onTime) {
|
||||||
throw new Error('onTime callback is required');
|
throw new Error('onTime callback is required');
|
||||||
}
|
}
|
||||||
@@ -451,7 +418,13 @@ export class Core {
|
|||||||
if (this._instance._debugManager) {
|
if (this._instance._debugManager) {
|
||||||
this._instance._debugManager.updateConfig(config);
|
this._instance._debugManager.updateConfig(config);
|
||||||
} else {
|
} else {
|
||||||
this._instance._debugManager = new DebugManager(this._instance, config);
|
// 使用DI容器创建DebugManager
|
||||||
|
this._instance._debugManager = new DebugManager(
|
||||||
|
this._instance._serviceContainer.resolve(SceneManager),
|
||||||
|
this._instance._serviceContainer.resolve(PerformanceMonitor),
|
||||||
|
config
|
||||||
|
);
|
||||||
|
this._instance._serviceContainer.registerInstance(DebugManager, this._instance._debugManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新Core配置
|
// 更新Core配置
|
||||||
@@ -530,13 +503,10 @@ export class Core {
|
|||||||
this._performanceMonitor.updateFPS(Time.deltaTime);
|
this._performanceMonitor.updateFPS(Time.deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新全局管理器
|
// 更新所有可更新的服务
|
||||||
const managersStartTime = this._performanceMonitor.startMonitoring('GlobalManagers.update');
|
const servicesStartTime = this._performanceMonitor.startMonitoring('Services.update');
|
||||||
for (const globalManager of this._globalManagers) {
|
this._serviceContainer.updateAll(deltaTime);
|
||||||
if (globalManager.enabled)
|
this._performanceMonitor.endMonitoring('Services.update', servicesStartTime, this._serviceContainer.getUpdatableCount());
|
||||||
globalManager.update();
|
|
||||||
}
|
|
||||||
this._performanceMonitor.endMonitoring('GlobalManagers.update', managersStartTime, this._globalManagers.length);
|
|
||||||
|
|
||||||
// 更新对象池管理器
|
// 更新对象池管理器
|
||||||
this._poolManager.update();
|
this._poolManager.update();
|
||||||
@@ -566,15 +536,12 @@ export class Core {
|
|||||||
this._instance._debugManager.stop();
|
this._instance._debugManager.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理全局管理器
|
// 清理所有服务
|
||||||
for (const manager of this._instance._globalManagers) {
|
this._instance._serviceContainer.clear();
|
||||||
manager.enabled = false;
|
|
||||||
}
|
|
||||||
this._instance._globalManagers = [];
|
|
||||||
|
|
||||||
Core._logger.info('Core destroyed');
|
Core._logger.info('Core destroyed');
|
||||||
|
|
||||||
// @ts-ignore - 清空实例引用
|
// 清空实例引用,允许重新创建Core实例
|
||||||
this._instance = null;
|
this._instance = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
317
packages/core/src/Core/DI/Decorators.ts
Normal file
317
packages/core/src/Core/DI/Decorators.ts
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
/**
|
||||||
|
* 依赖注入装饰器
|
||||||
|
*
|
||||||
|
* 提供 @Injectable、@Inject 和 @Updatable 装饰器,用于标记可注入的类和依赖注入点
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ServiceContainer } from '../ServiceContainer';
|
||||||
|
import type { IService, ServiceType } from '../ServiceContainer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 依赖注入元数据键
|
||||||
|
*/
|
||||||
|
const INJECTABLE_METADATA_KEY = Symbol('injectable');
|
||||||
|
const INJECT_METADATA_KEY = Symbol('inject');
|
||||||
|
const INJECT_PARAMS_METADATA_KEY = Symbol('inject:params');
|
||||||
|
const UPDATABLE_METADATA_KEY = Symbol('updatable');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 依赖注入元数据存储
|
||||||
|
*/
|
||||||
|
const injectableMetadata = new WeakMap<any, InjectableMetadata>();
|
||||||
|
const injectMetadata = new WeakMap<any, Map<number, ServiceType<any> | string | symbol>>();
|
||||||
|
const updatableMetadata = new WeakMap<any, UpdatableMetadata>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可注入元数据接口
|
||||||
|
*/
|
||||||
|
export interface InjectableMetadata {
|
||||||
|
/**
|
||||||
|
* 是否可注入
|
||||||
|
*/
|
||||||
|
injectable: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 依赖列表
|
||||||
|
*/
|
||||||
|
dependencies: Array<ServiceType<any> | string | symbol>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可更新元数据接口
|
||||||
|
*/
|
||||||
|
export interface UpdatableMetadata {
|
||||||
|
/**
|
||||||
|
* 是否可更新
|
||||||
|
*/
|
||||||
|
updatable: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新优先级(数值越小越先执行,默认0)
|
||||||
|
*/
|
||||||
|
priority: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Injectable() 装饰器
|
||||||
|
*
|
||||||
|
* 标记类为可注入的服务,使其可以通过ServiceContainer进行依赖注入
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* @Injectable()
|
||||||
|
* class TimeService implements IService {
|
||||||
|
* constructor() {}
|
||||||
|
* dispose() {}
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Injectable()
|
||||||
|
* class PhysicsSystem extends EntitySystem {
|
||||||
|
* constructor(
|
||||||
|
* @Inject(TimeService) private timeService: TimeService
|
||||||
|
* ) {
|
||||||
|
* super();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function Injectable(): ClassDecorator {
|
||||||
|
return function <T extends Function>(target: T): T {
|
||||||
|
// 标记为可注入
|
||||||
|
injectableMetadata.set(target, {
|
||||||
|
injectable: true,
|
||||||
|
dependencies: []
|
||||||
|
});
|
||||||
|
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Updatable() 装饰器
|
||||||
|
*
|
||||||
|
* 标记服务类为可更新的,使其在每帧自动被ServiceContainer调用update方法。
|
||||||
|
* 使用此装饰器的类必须实现IUpdatable接口(包含update方法)。
|
||||||
|
*
|
||||||
|
* @param priority - 更新优先级(数值越小越先执行,默认0)
|
||||||
|
* @throws 如果类没有实现update方法,将在运行时抛出错误
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* @Injectable()
|
||||||
|
* @Updatable()
|
||||||
|
* class TimerManager implements IService, IUpdatable {
|
||||||
|
* update(deltaTime?: number) {
|
||||||
|
* // 每帧更新逻辑
|
||||||
|
* }
|
||||||
|
* dispose() {}
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // 指定优先级
|
||||||
|
* @Injectable()
|
||||||
|
* @Updatable(10)
|
||||||
|
* class PhysicsManager implements IService, IUpdatable {
|
||||||
|
* update() { }
|
||||||
|
* dispose() {}
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function Updatable(priority: number = 0): ClassDecorator {
|
||||||
|
return function <T extends Function>(target: T): T {
|
||||||
|
// 验证类原型上是否有update方法
|
||||||
|
const prototype = (target as any).prototype;
|
||||||
|
if (!prototype || typeof prototype.update !== 'function') {
|
||||||
|
throw new Error(
|
||||||
|
`@Updatable() decorator requires class ${target.name} to implement IUpdatable interface with update() method. ` +
|
||||||
|
`Please add 'implements IUpdatable' and define update(deltaTime?: number): void method.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记为可更新
|
||||||
|
updatableMetadata.set(target, {
|
||||||
|
updatable: true,
|
||||||
|
priority
|
||||||
|
});
|
||||||
|
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Inject() 装饰器
|
||||||
|
*
|
||||||
|
* 标记构造函数参数需要注入的服务类型
|
||||||
|
*
|
||||||
|
* @param serviceType 服务类型标识符(类、字符串或Symbol)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* @Injectable()
|
||||||
|
* class MySystem extends EntitySystem {
|
||||||
|
* constructor(
|
||||||
|
* @Inject(TimeService) private timeService: TimeService,
|
||||||
|
* @Inject(PhysicsService) private physics: PhysicsService
|
||||||
|
* ) {
|
||||||
|
* super();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function Inject(serviceType: ServiceType<any> | string | symbol): ParameterDecorator {
|
||||||
|
return function (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) {
|
||||||
|
// 获取或创建注入元数据
|
||||||
|
let params = injectMetadata.get(target);
|
||||||
|
if (!params) {
|
||||||
|
params = new Map();
|
||||||
|
injectMetadata.set(target, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录参数索引和服务类型的映射
|
||||||
|
params.set(parameterIndex, serviceType);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查类是否标记为可注入
|
||||||
|
*
|
||||||
|
* @param target 目标类
|
||||||
|
* @returns 是否可注入
|
||||||
|
*/
|
||||||
|
export function isInjectable(target: any): boolean {
|
||||||
|
const metadata = injectableMetadata.get(target);
|
||||||
|
return metadata?.injectable ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类的依赖注入元数据
|
||||||
|
*
|
||||||
|
* @param target 目标类
|
||||||
|
* @returns 依赖注入元数据
|
||||||
|
*/
|
||||||
|
export function getInjectableMetadata(target: any): InjectableMetadata | undefined {
|
||||||
|
return injectableMetadata.get(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取构造函数参数的注入元数据
|
||||||
|
*
|
||||||
|
* @param target 目标类
|
||||||
|
* @returns 参数索引到服务类型的映射
|
||||||
|
*/
|
||||||
|
export function getInjectMetadata(target: any): Map<number, ServiceType<any> | string | symbol> {
|
||||||
|
return injectMetadata.get(target) || new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建实例并自动注入依赖
|
||||||
|
*
|
||||||
|
* @param constructor 构造函数
|
||||||
|
* @param container 服务容器
|
||||||
|
* @returns 创建的实例
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const instance = createInstance(MySystem, container);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function createInstance<T>(
|
||||||
|
constructor: new (...args: any[]) => T,
|
||||||
|
container: ServiceContainer
|
||||||
|
): T {
|
||||||
|
// 获取参数注入元数据
|
||||||
|
const injectParams = getInjectMetadata(constructor);
|
||||||
|
|
||||||
|
// 解析依赖
|
||||||
|
const dependencies: any[] = [];
|
||||||
|
|
||||||
|
// 获取构造函数参数数量
|
||||||
|
const paramCount = constructor.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < paramCount; i++) {
|
||||||
|
const serviceType = injectParams.get(i);
|
||||||
|
|
||||||
|
if (serviceType) {
|
||||||
|
// 如果有显式的@Inject标记,使用标记的类型
|
||||||
|
if (typeof serviceType === 'string' || typeof serviceType === 'symbol') {
|
||||||
|
// 字符串或Symbol类型的服务标识
|
||||||
|
throw new Error(
|
||||||
|
`String and Symbol service identifiers are not yet supported in constructor injection. ` +
|
||||||
|
`Please use class types for ${constructor.name} parameter ${i}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 类类型
|
||||||
|
dependencies.push(container.resolve(serviceType as ServiceType<any>));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有@Inject标记,传入undefined
|
||||||
|
dependencies.push(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建实例
|
||||||
|
return new constructor(...dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查类是否标记为可更新
|
||||||
|
*
|
||||||
|
* @param target 目标类
|
||||||
|
* @returns 是否可更新
|
||||||
|
*/
|
||||||
|
export function isUpdatable(target: any): boolean {
|
||||||
|
const metadata = updatableMetadata.get(target);
|
||||||
|
return metadata?.updatable ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类的可更新元数据
|
||||||
|
*
|
||||||
|
* @param target 目标类
|
||||||
|
* @returns 可更新元数据
|
||||||
|
*/
|
||||||
|
export function getUpdatableMetadata(target: any): UpdatableMetadata | undefined {
|
||||||
|
return updatableMetadata.get(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册可注入的服务到容器
|
||||||
|
*
|
||||||
|
* 自动检测@Injectable装饰器并注册服务
|
||||||
|
*
|
||||||
|
* @param container 服务容器
|
||||||
|
* @param serviceType 服务类型
|
||||||
|
* @param singleton 是否注册为单例(默认true)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* @Injectable()
|
||||||
|
* class MyService implements IService {
|
||||||
|
* dispose() {}
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // 自动注册
|
||||||
|
* registerInjectable(Core.services, MyService);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function registerInjectable<T extends IService>(
|
||||||
|
container: ServiceContainer,
|
||||||
|
serviceType: ServiceType<T>,
|
||||||
|
singleton: boolean = true
|
||||||
|
): void {
|
||||||
|
if (!isInjectable(serviceType)) {
|
||||||
|
throw new Error(
|
||||||
|
`${serviceType.name} is not marked as @Injectable(). ` +
|
||||||
|
`Please add @Injectable() decorator to the class.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建工厂函数,使用createInstance自动解析依赖
|
||||||
|
const factory = (c: ServiceContainer) => createInstance(serviceType, c);
|
||||||
|
|
||||||
|
// 注册到容器
|
||||||
|
if (singleton) {
|
||||||
|
container.registerSingleton(serviceType, factory);
|
||||||
|
} else {
|
||||||
|
container.registerTransient(serviceType, factory);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
packages/core/src/Core/DI/index.ts
Normal file
20
packages/core/src/Core/DI/index.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 依赖注入模块
|
||||||
|
*
|
||||||
|
* 提供装饰器和工具函数,用于实现依赖注入模式
|
||||||
|
*/
|
||||||
|
|
||||||
|
export {
|
||||||
|
Injectable,
|
||||||
|
Inject,
|
||||||
|
Updatable,
|
||||||
|
isInjectable,
|
||||||
|
getInjectableMetadata,
|
||||||
|
getInjectMetadata,
|
||||||
|
isUpdatable,
|
||||||
|
getUpdatableMetadata,
|
||||||
|
createInstance,
|
||||||
|
registerInjectable
|
||||||
|
} from './Decorators';
|
||||||
|
|
||||||
|
export type { InjectableMetadata, UpdatableMetadata } from './Decorators';
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createLogger } from '../Utils/Logger';
|
import { createLogger } from '../Utils/Logger';
|
||||||
|
import { isUpdatable as checkUpdatable, getUpdatableMetadata } from './DI';
|
||||||
|
|
||||||
const logger = createLogger('ServiceContainer');
|
const logger = createLogger('ServiceContainer');
|
||||||
|
|
||||||
@@ -16,8 +17,10 @@ export interface IService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 服务类型
|
* 服务类型
|
||||||
|
*
|
||||||
|
* 支持任意构造函数签名,以便与依赖注入装饰器配合使用
|
||||||
*/
|
*/
|
||||||
export type ServiceType<T extends IService> = new (...args: unknown[]) => T;
|
export type ServiceType<T extends IService> = new (...args: any[]) => T;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 服务生命周期
|
* 服务生命周期
|
||||||
@@ -99,6 +102,14 @@ export class ServiceContainer {
|
|||||||
*/
|
*/
|
||||||
private _resolving: Set<ServiceType<IService>> = new Set();
|
private _resolving: Set<ServiceType<IService>> = new Set();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可更新的服务列表
|
||||||
|
*
|
||||||
|
* 自动收集所有使用@Updatable装饰器标记的服务,供Core统一更新
|
||||||
|
* 按优先级排序(数值越小越先执行)
|
||||||
|
*/
|
||||||
|
private _updatableServices: Array<{ instance: any; priority: number }> = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册单例服务
|
* 注册单例服务
|
||||||
*
|
*
|
||||||
@@ -192,6 +203,18 @@ export class ServiceContainer {
|
|||||||
lifetime: ServiceLifetime.Singleton
|
lifetime: ServiceLifetime.Singleton
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 如果使用了@Updatable装饰器,添加到可更新列表
|
||||||
|
if (checkUpdatable(type)) {
|
||||||
|
const metadata = getUpdatableMetadata(type);
|
||||||
|
const priority = metadata?.priority ?? 0;
|
||||||
|
this._updatableServices.push({ instance, priority });
|
||||||
|
|
||||||
|
// 按优先级排序(数值越小越先执行)
|
||||||
|
this._updatableServices.sort((a, b) => a.priority - b.priority);
|
||||||
|
|
||||||
|
logger.debug(`Service ${type.name} is updatable (priority: ${priority}), added to update list`);
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug(`Registered service instance: ${type.name}`);
|
logger.debug(`Registered service instance: ${type.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,6 +266,18 @@ export class ServiceContainer {
|
|||||||
// 如果是单例,缓存实例
|
// 如果是单例,缓存实例
|
||||||
if (registration.lifetime === ServiceLifetime.Singleton) {
|
if (registration.lifetime === ServiceLifetime.Singleton) {
|
||||||
registration.instance = instance;
|
registration.instance = instance;
|
||||||
|
|
||||||
|
// 如果使用了@Updatable装饰器,添加到可更新列表
|
||||||
|
if (checkUpdatable(registration.type)) {
|
||||||
|
const metadata = getUpdatableMetadata(registration.type);
|
||||||
|
const priority = metadata?.priority ?? 0;
|
||||||
|
this._updatableServices.push({ instance, priority });
|
||||||
|
|
||||||
|
// 按优先级排序(数值越小越先执行)
|
||||||
|
this._updatableServices.sort((a, b) => a.priority - b.priority);
|
||||||
|
|
||||||
|
logger.debug(`Service ${type.name} is updatable (priority: ${priority}), added to update list`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return instance as T;
|
return instance as T;
|
||||||
@@ -300,6 +335,12 @@ export class ServiceContainer {
|
|||||||
|
|
||||||
// 如果有单例实例,调用 dispose
|
// 如果有单例实例,调用 dispose
|
||||||
if (registration.instance) {
|
if (registration.instance) {
|
||||||
|
// 从可更新列表中移除
|
||||||
|
const index = this._updatableServices.findIndex(item => item.instance === registration.instance);
|
||||||
|
if (index !== -1) {
|
||||||
|
this._updatableServices.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
registration.instance.dispose();
|
registration.instance.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,6 +361,7 @@ export class ServiceContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._services.clear();
|
this._services.clear();
|
||||||
|
this._updatableServices = [];
|
||||||
logger.debug('Cleared all services');
|
logger.debug('Cleared all services');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,4 +373,51 @@ export class ServiceContainer {
|
|||||||
public getRegisteredServices(): ServiceType<IService>[] {
|
public getRegisteredServices(): ServiceType<IService>[] {
|
||||||
return Array.from(this._services.keys());
|
return Array.from(this._services.keys());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新所有使用@Updatable装饰器标记的服务
|
||||||
|
*
|
||||||
|
* 此方法会按优先级顺序遍历所有可更新的服务并调用它们的update方法。
|
||||||
|
* 所有服务在注册时已经由@Updatable装饰器验证过必须实现IUpdatable接口。
|
||||||
|
* 通常在Core的主更新循环中调用。
|
||||||
|
*
|
||||||
|
* @param deltaTime - 可选的帧时间间隔(秒)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // 在Core的update方法中
|
||||||
|
* this._serviceContainer.updateAll(deltaTime);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public updateAll(deltaTime?: number): void {
|
||||||
|
for (const { instance } of this._updatableServices) {
|
||||||
|
instance.update(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有可更新的服务数量
|
||||||
|
*
|
||||||
|
* @returns 可更新服务的数量
|
||||||
|
*/
|
||||||
|
public getUpdatableCount(): number {
|
||||||
|
return this._updatableServices.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有已实例化的服务实例
|
||||||
|
*
|
||||||
|
* @returns 所有服务实例的数组
|
||||||
|
*/
|
||||||
|
public getAll(): IService[] {
|
||||||
|
const instances: IService[] = [];
|
||||||
|
|
||||||
|
for (const descriptor of this._services.values()) {
|
||||||
|
if (descriptor.instance) {
|
||||||
|
instances.push(descriptor.instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return instances;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,22 +39,48 @@ export function ECSComponent(typeName: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System元数据配置
|
||||||
|
*/
|
||||||
|
export interface SystemMetadata {
|
||||||
|
/**
|
||||||
|
* 更新顺序(数值越小越先执行,默认0)
|
||||||
|
*/
|
||||||
|
updateOrder?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否默认启用(默认true)
|
||||||
|
*/
|
||||||
|
enabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统类型装饰器
|
* 系统类型装饰器
|
||||||
* 用于为系统类指定固定的类型名称,避免在代码混淆后失效
|
* 用于为系统类指定固定的类型名称,避免在代码混淆后失效
|
||||||
*
|
*
|
||||||
* @param typeName 系统类型名称
|
* @param typeName 系统类型名称
|
||||||
|
* @param metadata 系统元数据配置
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
|
* // 基本使用
|
||||||
* @ECSSystem('Movement')
|
* @ECSSystem('Movement')
|
||||||
* class MovementSystem extends EntitySystem {
|
* class MovementSystem extends EntitySystem {
|
||||||
* protected process(entities: Entity[]): void {
|
* protected process(entities: Entity[]): void {
|
||||||
* // 系统逻辑
|
* // 系统逻辑
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
|
*
|
||||||
|
* // 配置更新顺序
|
||||||
|
* @Injectable()
|
||||||
|
* @ECSSystem('Physics', { updateOrder: 10 })
|
||||||
|
* class PhysicsSystem extends EntitySystem {
|
||||||
|
* constructor(@Inject(CollisionSystem) private collision: CollisionSystem) {
|
||||||
|
* super(Matcher.of(Transform, RigidBody));
|
||||||
|
* }
|
||||||
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function ECSSystem(typeName: string) {
|
export function ECSSystem(typeName: string, metadata?: SystemMetadata) {
|
||||||
return function <T extends new (...args: any[]) => EntitySystem>(target: T): T {
|
return function <T extends new (...args: any[]) => EntitySystem>(target: T): T {
|
||||||
if (!typeName || typeof typeName !== 'string') {
|
if (!typeName || typeof typeName !== 'string') {
|
||||||
throw new Error('ECSSystem装饰器必须提供有效的类型名称');
|
throw new Error('ECSSystem装饰器必须提供有效的类型名称');
|
||||||
@@ -63,10 +89,22 @@ export function ECSSystem(typeName: string) {
|
|||||||
// 在构造函数上存储类型名称
|
// 在构造函数上存储类型名称
|
||||||
(target as any)[SYSTEM_TYPE_NAME] = typeName;
|
(target as any)[SYSTEM_TYPE_NAME] = typeName;
|
||||||
|
|
||||||
|
// 存储元数据
|
||||||
|
if (metadata) {
|
||||||
|
(target as any).__systemMetadata__ = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取System的元数据
|
||||||
|
*/
|
||||||
|
export function getSystemMetadata(systemType: new (...args: any[]) => EntitySystem): SystemMetadata | undefined {
|
||||||
|
return (systemType as any).__systemMetadata__;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取组件类型的名称,优先使用装饰器指定的名称
|
* 获取组件类型的名称,优先使用装饰器指定的名称
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ export {
|
|||||||
getSystemTypeName,
|
getSystemTypeName,
|
||||||
getComponentInstanceTypeName,
|
getComponentInstanceTypeName,
|
||||||
getSystemInstanceTypeName,
|
getSystemInstanceTypeName,
|
||||||
|
getSystemMetadata,
|
||||||
COMPONENT_TYPE_NAME,
|
COMPONENT_TYPE_NAME,
|
||||||
SYSTEM_TYPE_NAME
|
SYSTEM_TYPE_NAME
|
||||||
} from './TypeDecorators';
|
} from './TypeDecorators';
|
||||||
|
|
||||||
|
export type { SystemMetadata } from './TypeDecorators';
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Entity } from './Entity';
|
import { Entity } from './Entity';
|
||||||
import { EntityList } from './Utils/EntityList';
|
import { EntityList } from './Utils/EntityList';
|
||||||
import { EntityProcessorList } from './Utils/EntityProcessorList';
|
|
||||||
import { IdentifierPool } from './Utils/IdentifierPool';
|
import { IdentifierPool } from './Utils/IdentifierPool';
|
||||||
import { EntitySystem } from './Systems/EntitySystem';
|
import { EntitySystem } from './Systems/EntitySystem';
|
||||||
import { ComponentStorageManager } from './Core/ComponentStorage';
|
import { ComponentStorageManager } from './Core/ComponentStorage';
|
||||||
@@ -42,11 +41,6 @@ export interface IScene {
|
|||||||
*/
|
*/
|
||||||
readonly entities: EntityList;
|
readonly entities: EntityList;
|
||||||
|
|
||||||
/**
|
|
||||||
* 实体系统处理器集合
|
|
||||||
*/
|
|
||||||
readonly entityProcessors: EntityProcessorList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标识符池
|
* 标识符池
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Entity } from './Entity';
|
import { Entity } from './Entity';
|
||||||
import { EntityList } from './Utils/EntityList';
|
import { EntityList } from './Utils/EntityList';
|
||||||
import { EntityProcessorList } from './Utils/EntityProcessorList';
|
|
||||||
import { IdentifierPool } from './Utils/IdentifierPool';
|
import { IdentifierPool } from './Utils/IdentifierPool';
|
||||||
import { EntitySystem } from './Systems/EntitySystem';
|
import { EntitySystem } from './Systems/EntitySystem';
|
||||||
import { ComponentStorageManager, ComponentRegistry } from './Core/ComponentStorage';
|
import { ComponentStorageManager, ComponentRegistry } from './Core/ComponentStorage';
|
||||||
@@ -8,13 +7,17 @@ import { QuerySystem } from './Core/QuerySystem';
|
|||||||
import { TypeSafeEventSystem } from './Core/EventSystem';
|
import { TypeSafeEventSystem } from './Core/EventSystem';
|
||||||
import { EventBus } from './Core/EventBus';
|
import { EventBus } from './Core/EventBus';
|
||||||
import { IScene, ISceneConfig } from './IScene';
|
import { IScene, ISceneConfig } from './IScene';
|
||||||
import { getComponentInstanceTypeName, getSystemInstanceTypeName } from './Decorators';
|
import { getComponentInstanceTypeName, getSystemInstanceTypeName, getSystemMetadata } from "./Decorators";
|
||||||
import { TypedQueryBuilder } from './Core/Query/TypedQuery';
|
import { TypedQueryBuilder } from './Core/Query/TypedQuery';
|
||||||
import { SceneSerializer, SceneSerializationOptions, SceneDeserializationOptions } from './Serialization/SceneSerializer';
|
import { SceneSerializer, SceneSerializationOptions, SceneDeserializationOptions } from './Serialization/SceneSerializer';
|
||||||
import { IncrementalSerializer, IncrementalSnapshot, IncrementalSerializationOptions } from './Serialization/IncrementalSerializer';
|
import { IncrementalSerializer, IncrementalSnapshot, IncrementalSerializationOptions } from './Serialization/IncrementalSerializer';
|
||||||
import { ComponentPoolManager } from './Core/ComponentPool';
|
import { ComponentPoolManager } from './Core/ComponentPool';
|
||||||
import { PerformanceMonitor } from '../Utils/PerformanceMonitor';
|
import { PerformanceMonitor } from '../Utils/PerformanceMonitor';
|
||||||
import { Core } from '../Core';
|
import { Core } from '../Core';
|
||||||
|
import { ServiceContainer, type ServiceType } from '../Core/ServiceContainer';
|
||||||
|
import { createInstance, isInjectable } from '../Core/DI';
|
||||||
|
import { isUpdatable, getUpdatableMetadata } from '../Core/DI/Decorators';
|
||||||
|
import { createLogger } from '../Utils/Logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 游戏场景默认实现类
|
* 游戏场景默认实现类
|
||||||
@@ -44,12 +47,6 @@ export class Scene implements IScene {
|
|||||||
*/
|
*/
|
||||||
public readonly entities: EntityList;
|
public readonly entities: EntityList;
|
||||||
|
|
||||||
/**
|
|
||||||
* 实体系统处理器集合
|
|
||||||
*
|
|
||||||
* 管理场景内所有实体系统的执行。
|
|
||||||
*/
|
|
||||||
public readonly entityProcessors: EntityProcessorList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体ID池
|
* 实体ID池
|
||||||
@@ -79,31 +76,97 @@ export class Scene implements IScene {
|
|||||||
*/
|
*/
|
||||||
public readonly eventSystem: TypeSafeEventSystem;
|
public readonly eventSystem: TypeSafeEventSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务容器
|
||||||
|
*
|
||||||
|
* 场景级别的依赖注入容器,用于管理EntitySystem和其他服务的生命周期。
|
||||||
|
* 每个Scene拥有独立的服务容器,实现场景间的隔离。
|
||||||
|
*/
|
||||||
|
private readonly _services: ServiceContainer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志记录器
|
||||||
|
*/
|
||||||
|
private readonly logger: ReturnType<typeof createLogger>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 场景是否已开始运行
|
* 场景是否已开始运行
|
||||||
*/
|
*/
|
||||||
private _didSceneBegin: boolean = false;
|
private _didSceneBegin: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取系统列表(兼容性属性)
|
* 获取场景中所有已注册的EntitySystem
|
||||||
|
*
|
||||||
|
* 按updateOrder排序。
|
||||||
|
*
|
||||||
|
* @returns 系统列表
|
||||||
*/
|
*/
|
||||||
public get systems(): EntitySystem[] {
|
public get systems(): EntitySystem[] {
|
||||||
return this.entityProcessors.processors;
|
// 从ServiceContainer获取所有EntitySystem实例
|
||||||
|
const services = this._services.getAll();
|
||||||
|
const systems: EntitySystem[] = [];
|
||||||
|
|
||||||
|
for (const service of services) {
|
||||||
|
if (service instanceof EntitySystem) {
|
||||||
|
systems.push(service);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按updateOrder排序
|
||||||
|
systems.sort((a, b) => a.updateOrder - b.updateOrder);
|
||||||
|
|
||||||
|
return systems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过类型获取System实例
|
||||||
|
*
|
||||||
|
* @param systemType System类型
|
||||||
|
* @returns System实例,如果未找到则返回null
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const physics = scene.getSystem(PhysicsSystem);
|
||||||
|
* if (physics) {
|
||||||
|
* physics.doSomething();
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public getSystem<T extends EntitySystem>(systemType: ServiceType<T>): T | null {
|
||||||
|
return this._services.tryResolve(systemType) as T | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取场景的服务容器
|
||||||
|
*
|
||||||
|
* 用于注册和解析场景级别的服务(如EntitySystem)。
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // 注册服务
|
||||||
|
* scene.services.registerSingleton(PhysicsSystem);
|
||||||
|
*
|
||||||
|
* // 解析服务
|
||||||
|
* const physics = scene.services.resolve(PhysicsSystem);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public get services(): ServiceContainer {
|
||||||
|
return this._services;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建场景实例
|
* 创建场景实例
|
||||||
*/
|
*/
|
||||||
constructor(config?: ISceneConfig) {
|
constructor(config?: ISceneConfig) {
|
||||||
this.entities = new EntityList(this);
|
this.entities = new EntityList(this);
|
||||||
this.entityProcessors = new EntityProcessorList();
|
|
||||||
this.identifierPool = new IdentifierPool();
|
this.identifierPool = new IdentifierPool();
|
||||||
this.componentStorageManager = new ComponentStorageManager();
|
this.componentStorageManager = new ComponentStorageManager();
|
||||||
this.querySystem = new QuerySystem();
|
this.querySystem = new QuerySystem();
|
||||||
this.eventSystem = new TypeSafeEventSystem();
|
this.eventSystem = new TypeSafeEventSystem();
|
||||||
|
this._services = new ServiceContainer();
|
||||||
|
this.logger = createLogger('Scene');
|
||||||
|
|
||||||
// 应用配置
|
|
||||||
if (config?.name) {
|
if (config?.name) {
|
||||||
this.name = config.name;
|
this.name = config.name;
|
||||||
}
|
}
|
||||||
@@ -149,10 +212,6 @@ export class Scene implements IScene {
|
|||||||
* 这个方法会启动场景。它将启动实体处理器等,并调用onStart方法。
|
* 这个方法会启动场景。它将启动实体处理器等,并调用onStart方法。
|
||||||
*/
|
*/
|
||||||
public begin() {
|
public begin() {
|
||||||
// 启动实体处理器
|
|
||||||
if (this.entityProcessors != null)
|
|
||||||
this.entityProcessors.begin();
|
|
||||||
|
|
||||||
// 标记场景已开始运行并调用onStart方法
|
// 标记场景已开始运行并调用onStart方法
|
||||||
this._didSceneBegin = true;
|
this._didSceneBegin = true;
|
||||||
this.onStart();
|
this.onStart();
|
||||||
@@ -176,9 +235,8 @@ export class Scene implements IScene {
|
|||||||
// 清空组件存储
|
// 清空组件存储
|
||||||
this.componentStorageManager.clear();
|
this.componentStorageManager.clear();
|
||||||
|
|
||||||
// 结束实体处理器
|
// 清空服务容器(会调用所有服务的dispose方法,包括所有EntitySystem)
|
||||||
if (this.entityProcessors)
|
this._services.clear();
|
||||||
this.entityProcessors.end();
|
|
||||||
|
|
||||||
// 调用卸载方法
|
// 调用卸载方法
|
||||||
this.unload();
|
this.unload();
|
||||||
@@ -192,11 +250,28 @@ export class Scene implements IScene {
|
|||||||
|
|
||||||
this.entities.updateLists();
|
this.entities.updateLists();
|
||||||
|
|
||||||
if (this.entityProcessors != null)
|
// 更新所有EntitySystem
|
||||||
this.entityProcessors.update();
|
const systems = this.systems;
|
||||||
|
for (const system of systems) {
|
||||||
|
if (system.enabled) {
|
||||||
|
try {
|
||||||
|
system.update();
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error in system ${system.constructor.name}.update():`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.entityProcessors != null)
|
// LateUpdate
|
||||||
this.entityProcessors.lateUpdate();
|
for (const system of systems) {
|
||||||
|
if (system.enabled) {
|
||||||
|
try {
|
||||||
|
system.lateUpdate();
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error in system ${system.constructor.name}.lateUpdate():`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -216,7 +291,7 @@ export class Scene implements IScene {
|
|||||||
* 当实体或组件发生变化时调用
|
* 当实体或组件发生变化时调用
|
||||||
*/
|
*/
|
||||||
public clearSystemEntityCaches(): void {
|
public clearSystemEntityCaches(): void {
|
||||||
for (const system of this.entityProcessors.processors) {
|
for (const system of this.systems) {
|
||||||
system.clearEntityCache();
|
system.clearEntityCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,23 +494,121 @@ export class Scene implements IScene {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 在场景中添加一个EntitySystem处理器
|
* 在场景中添加一个EntitySystem处理器
|
||||||
* @param processor 处理器
|
*
|
||||||
|
* 支持两种使用方式:
|
||||||
|
* 1. 传入类型(推荐):自动使用DI创建实例,支持@Injectable和@Inject装饰器
|
||||||
|
* 2. 传入实例:直接使用提供的实例
|
||||||
|
*
|
||||||
|
* @param systemTypeOrInstance 系统类型或系统实例
|
||||||
|
* @returns 添加的处理器实例
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // 方式1:传入类型,自动DI(推荐)
|
||||||
|
* @Injectable()
|
||||||
|
* class PhysicsSystem extends EntitySystem {
|
||||||
|
* constructor(@Inject(CollisionSystem) private collision: CollisionSystem) {
|
||||||
|
* super(Matcher.of(Transform));
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* scene.addEntityProcessor(PhysicsSystem);
|
||||||
|
*
|
||||||
|
* // 方式2:传入实例
|
||||||
|
* const system = new MySystem();
|
||||||
|
* scene.addEntityProcessor(system);
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
public addEntityProcessor(processor: EntitySystem) {
|
public addEntityProcessor<T extends EntitySystem>(
|
||||||
if (this.entityProcessors.processors.includes(processor)) {
|
systemTypeOrInstance: ServiceType<T> | T
|
||||||
return processor;
|
): T {
|
||||||
|
let system: T;
|
||||||
|
let constructor: any;
|
||||||
|
|
||||||
|
if (typeof systemTypeOrInstance === 'function') {
|
||||||
|
constructor = systemTypeOrInstance;
|
||||||
|
|
||||||
|
if (this._services.isRegistered(constructor)) {
|
||||||
|
return this._services.resolve(constructor) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
processor.scene = this;
|
if (isInjectable(constructor)) {
|
||||||
|
system = createInstance(constructor, this._services) as T;
|
||||||
|
} else {
|
||||||
|
system = new (constructor as any)() as T;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
system = systemTypeOrInstance;
|
||||||
|
constructor = system.constructor;
|
||||||
|
|
||||||
|
if (this._services.isRegistered(constructor)) {
|
||||||
|
return system;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
system.scene = this;
|
||||||
|
|
||||||
// 从Core获取PerformanceMonitor并注入到System
|
|
||||||
const perfMonitor = Core.services.resolve(PerformanceMonitor);
|
const perfMonitor = Core.services.resolve(PerformanceMonitor);
|
||||||
processor.setPerformanceMonitor(perfMonitor);
|
system.setPerformanceMonitor(perfMonitor);
|
||||||
|
|
||||||
this.entityProcessors.add(processor);
|
const metadata = getSystemMetadata(constructor);
|
||||||
processor.initialize();
|
if (metadata?.updateOrder !== undefined) {
|
||||||
processor.setUpdateOrder(this.entityProcessors.count - 1);
|
system.setUpdateOrder(metadata.updateOrder);
|
||||||
return processor;
|
}
|
||||||
|
if (metadata?.enabled !== undefined) {
|
||||||
|
system.enabled = metadata.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._services.registerInstance(constructor, system);
|
||||||
|
|
||||||
|
system.initialize();
|
||||||
|
|
||||||
|
return system;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量注册EntitySystem到场景(使用DI)
|
||||||
|
*
|
||||||
|
* 自动按照依赖顺序注册多个System。
|
||||||
|
* 所有System必须使用@Injectable装饰器标记。
|
||||||
|
*
|
||||||
|
* @param systemTypes System类型数组
|
||||||
|
* @returns 注册的System实例数组
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* @Injectable()
|
||||||
|
* @ECSSystem('Collision', { updateOrder: 5 })
|
||||||
|
* class CollisionSystem extends EntitySystem implements IService {
|
||||||
|
* constructor() { super(Matcher.of(Collider)); }
|
||||||
|
* dispose() {}
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Injectable()
|
||||||
|
* @ECSSystem('Physics', { updateOrder: 10 })
|
||||||
|
* class PhysicsSystem extends EntitySystem implements IService {
|
||||||
|
* constructor(@Inject(CollisionSystem) private collision: CollisionSystem) {
|
||||||
|
* super(Matcher.of(Transform, RigidBody));
|
||||||
|
* }
|
||||||
|
* dispose() {}
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // 批量注册(自动解析依赖顺序)
|
||||||
|
* scene.registerSystems([
|
||||||
|
* CollisionSystem,
|
||||||
|
* PhysicsSystem, // 自动注入CollisionSystem
|
||||||
|
* RenderSystem
|
||||||
|
* ]);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public registerSystems(systemTypes: Array<ServiceType<EntitySystem>>): EntitySystem[] {
|
||||||
|
const registeredSystems: EntitySystem[] = [];
|
||||||
|
|
||||||
|
for (const systemType of systemTypes) {
|
||||||
|
const system = this.addEntityProcessor(systemType);
|
||||||
|
registeredSystems.push(system);
|
||||||
|
}
|
||||||
|
|
||||||
|
return registeredSystems;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -450,8 +623,13 @@ export class Scene implements IScene {
|
|||||||
* 从场景中删除EntitySystem处理器
|
* 从场景中删除EntitySystem处理器
|
||||||
* @param processor 要删除的处理器
|
* @param processor 要删除的处理器
|
||||||
*/
|
*/
|
||||||
public removeEntityProcessor(processor: EntitySystem) {
|
public removeEntityProcessor(processor: EntitySystem): void {
|
||||||
this.entityProcessors.remove(processor);
|
const constructor = processor.constructor as any;
|
||||||
|
|
||||||
|
// 从ServiceContainer移除
|
||||||
|
this._services.unregister(constructor);
|
||||||
|
|
||||||
|
// 重置System状态
|
||||||
processor.reset();
|
processor.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,10 +643,24 @@ export class Scene implements IScene {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定类型的EntitySystem处理器
|
* 获取指定类型的EntitySystem处理器
|
||||||
|
*
|
||||||
|
* @deprecated 推荐使用依赖注入代替此方法。使用 `scene.services.resolve(SystemType)` 或在System构造函数中使用 `@Inject(SystemType)` 装饰器。
|
||||||
|
*
|
||||||
* @param type 处理器类型
|
* @param type 处理器类型
|
||||||
|
* @returns 处理器实例,如果未找到则返回null
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* @Injectable()
|
||||||
|
* class MySystem extends EntitySystem {
|
||||||
|
* constructor(@Inject(PhysicsSystem) private physics: PhysicsSystem) {
|
||||||
|
* super();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
public getEntityProcessor<T extends EntitySystem>(type: new (...args: unknown[]) => T): T | null {
|
public getEntityProcessor<T extends EntitySystem>(type: new (...args: unknown[]) => T): T | null {
|
||||||
return this.entityProcessors.getProcessor(type);
|
return this._services.tryResolve(type as any) as T | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -481,7 +673,7 @@ export class Scene implements IScene {
|
|||||||
} {
|
} {
|
||||||
return {
|
return {
|
||||||
entityCount: this.entities.count,
|
entityCount: this.entities.count,
|
||||||
processorCount: this.entityProcessors.count,
|
processorCount: this.systems.length,
|
||||||
componentStorageStats: this.componentStorageManager.getAllStats()
|
componentStorageStats: this.componentStorageManager.getAllStats()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -508,10 +700,11 @@ export class Scene implements IScene {
|
|||||||
}>;
|
}>;
|
||||||
componentStats: Map<string, any>;
|
componentStats: Map<string, any>;
|
||||||
} {
|
} {
|
||||||
|
const systems = this.systems;
|
||||||
return {
|
return {
|
||||||
name: this.name || this.constructor.name,
|
name: this.name || this.constructor.name,
|
||||||
entityCount: this.entities.count,
|
entityCount: this.entities.count,
|
||||||
processorCount: this.entityProcessors.count,
|
processorCount: systems.length,
|
||||||
isRunning: this._didSceneBegin,
|
isRunning: this._didSceneBegin,
|
||||||
entities: this.entities.buffer.map(entity => ({
|
entities: this.entities.buffer.map(entity => ({
|
||||||
name: entity.name,
|
name: entity.name,
|
||||||
@@ -519,7 +712,7 @@ export class Scene implements IScene {
|
|||||||
componentCount: entity.components.length,
|
componentCount: entity.components.length,
|
||||||
componentTypes: entity.components.map(c => getComponentInstanceTypeName(c))
|
componentTypes: entity.components.map(c => getComponentInstanceTypeName(c))
|
||||||
})),
|
})),
|
||||||
processors: this.entityProcessors.processors.map(processor => ({
|
processors: systems.map(processor => ({
|
||||||
name: getSystemInstanceTypeName(processor),
|
name: getSystemInstanceTypeName(processor),
|
||||||
updateOrder: processor.updateOrder,
|
updateOrder: processor.updateOrder,
|
||||||
entityCount: (processor as any)._entities?.length || 0
|
entityCount: (processor as any)._entities?.length || 0
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { getSystemInstanceTypeName } from '../Decorators';
|
|||||||
import { createLogger } from '../../Utils/Logger';
|
import { createLogger } from '../../Utils/Logger';
|
||||||
import type { EventListenerConfig, TypeSafeEventSystem, EventHandler } from '../Core/EventSystem';
|
import type { EventListenerConfig, TypeSafeEventSystem, EventHandler } from '../Core/EventSystem';
|
||||||
import type { ComponentConstructor, ComponentInstance } from '../../Types/TypeHelpers';
|
import type { ComponentConstructor, ComponentInstance } from '../../Types/TypeHelpers';
|
||||||
|
import type { IService } from '../../Core/ServiceContainer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 事件监听器记录
|
* 事件监听器记录
|
||||||
@@ -65,7 +66,7 @@ interface EventListenerRecord {
|
|||||||
*/
|
*/
|
||||||
export abstract class EntitySystem<
|
export abstract class EntitySystem<
|
||||||
TComponents extends readonly ComponentConstructor[] = []
|
TComponents extends readonly ComponentConstructor[] = []
|
||||||
> implements ISystemBase {
|
> implements ISystemBase, IService {
|
||||||
private _updateOrder: number;
|
private _updateOrder: number;
|
||||||
private _enabled: boolean;
|
private _enabled: boolean;
|
||||||
private _performanceMonitor: PerformanceMonitor | null;
|
private _performanceMonitor: PerformanceMonitor | null;
|
||||||
@@ -222,9 +223,6 @@ export abstract class EntitySystem<
|
|||||||
*/
|
*/
|
||||||
public setUpdateOrder(order: number): void {
|
public setUpdateOrder(order: number): void {
|
||||||
this._updateOrder = order;
|
this._updateOrder = order;
|
||||||
if (this.scene && this.scene.entityProcessors) {
|
|
||||||
this.scene.entityProcessors.setDirty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -734,6 +732,34 @@ export abstract class EntitySystem<
|
|||||||
// 子类可以重写此方法
|
// 子类可以重写此方法
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放系统资源
|
||||||
|
*
|
||||||
|
* 实现IService接口要求的dispose方法。
|
||||||
|
* 当系统从Scene中移除或Scene销毁时调用。
|
||||||
|
*
|
||||||
|
* 默认行为:
|
||||||
|
* - 移除所有事件监听器
|
||||||
|
* - 清空所有缓存
|
||||||
|
* - 重置初始化状态
|
||||||
|
*
|
||||||
|
* 子类可以重写此方法来清理自定义资源,但应该调用super.dispose()。
|
||||||
|
*/
|
||||||
|
public dispose(): void {
|
||||||
|
// 移除所有事件监听器
|
||||||
|
this.cleanupManualEventListeners();
|
||||||
|
|
||||||
|
// 清空所有缓存
|
||||||
|
this._entityCache.clearAll();
|
||||||
|
this._entityIdMap = null;
|
||||||
|
|
||||||
|
// 重置状态
|
||||||
|
this._initialized = false;
|
||||||
|
this._scene = null;
|
||||||
|
|
||||||
|
this.logger.debug(`System ${this._systemName} disposed`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加事件监听器
|
* 添加事件监听器
|
||||||
*
|
*
|
||||||
|
|||||||
20
packages/core/src/Types/IUpdatable.ts
Normal file
20
packages/core/src/Types/IUpdatable.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 可更新接口
|
||||||
|
*
|
||||||
|
* 实现此接口的服务将在每帧被Core自动调用update方法
|
||||||
|
*/
|
||||||
|
export interface IUpdatable {
|
||||||
|
/**
|
||||||
|
* 每帧更新方法
|
||||||
|
*
|
||||||
|
* @param deltaTime - 帧时间间隔(秒),可选参数
|
||||||
|
*/
|
||||||
|
update(deltaTime?: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查对象是否实现了IUpdatable接口
|
||||||
|
*/
|
||||||
|
export function isUpdatable(obj: any): obj is IUpdatable {
|
||||||
|
return obj && typeof obj.update === 'function';
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
// 导出TypeScript类型增强工具
|
// 导出TypeScript类型增强工具
|
||||||
export * from './TypeHelpers';
|
export * from './TypeHelpers';
|
||||||
|
export * from './IUpdatable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件接口
|
* 组件接口
|
||||||
|
|||||||
@@ -10,11 +10,15 @@ import { ComponentPoolManager } from '../../ECS/Core/ComponentPool';
|
|||||||
import { Pool } from '../../Utils/Pool';
|
import { Pool } from '../../Utils/Pool';
|
||||||
import { getComponentInstanceTypeName, getSystemInstanceTypeName } from '../../ECS/Decorators';
|
import { getComponentInstanceTypeName, getSystemInstanceTypeName } from '../../ECS/Decorators';
|
||||||
import type { IService } from '../../Core/ServiceContainer';
|
import type { IService } from '../../Core/ServiceContainer';
|
||||||
|
import { SceneManager } from '../../ECS/SceneManager';
|
||||||
|
import { PerformanceMonitor } from '../PerformanceMonitor';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调试管理器
|
* 调试管理器
|
||||||
*
|
*
|
||||||
* 整合所有调试数据收集器,负责收集和发送调试数据
|
* 整合所有调试数据收集器,负责收集和发送调试数据
|
||||||
|
*
|
||||||
|
* 通过构造函数接收SceneManager和PerformanceMonitor,避免直接依赖Core实例
|
||||||
*/
|
*/
|
||||||
export class DebugManager implements IService {
|
export class DebugManager implements IService {
|
||||||
private config: IECSDebugConfig;
|
private config: IECSDebugConfig;
|
||||||
@@ -24,8 +28,8 @@ export class DebugManager implements IService {
|
|||||||
private performanceCollector: PerformanceDataCollector;
|
private performanceCollector: PerformanceDataCollector;
|
||||||
private componentCollector: ComponentDataCollector;
|
private componentCollector: ComponentDataCollector;
|
||||||
private sceneCollector: SceneDataCollector;
|
private sceneCollector: SceneDataCollector;
|
||||||
private sceneProvider: () => any;
|
private sceneManager: SceneManager;
|
||||||
private performanceMonitorProvider: () => any;
|
private performanceMonitor: PerformanceMonitor;
|
||||||
|
|
||||||
private frameCounter: number = 0;
|
private frameCounter: number = 0;
|
||||||
private lastSendTime: number = 0;
|
private lastSendTime: number = 0;
|
||||||
@@ -34,15 +38,18 @@ export class DebugManager implements IService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造调试管理器
|
* 构造调试管理器
|
||||||
* @param core Core实例
|
* @param sceneManager 场景管理器
|
||||||
|
* @param performanceMonitor 性能监控器
|
||||||
* @param config 调试配置
|
* @param config 调试配置
|
||||||
*/
|
*/
|
||||||
constructor(core: any, config: IECSDebugConfig) {
|
constructor(
|
||||||
|
sceneManager: SceneManager,
|
||||||
|
performanceMonitor: PerformanceMonitor,
|
||||||
|
config: IECSDebugConfig
|
||||||
|
) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
this.sceneManager = sceneManager;
|
||||||
// 设置提供器函数
|
this.performanceMonitor = performanceMonitor;
|
||||||
this.sceneProvider = () => (core as any).scene || (core.constructor as any).scene;
|
|
||||||
this.performanceMonitorProvider = () => core._performanceMonitor;
|
|
||||||
|
|
||||||
// 初始化数据收集器
|
// 初始化数据收集器
|
||||||
this.entityCollector = new EntityDataCollector();
|
this.entityCollector = new EntityDataCollector();
|
||||||
@@ -339,7 +346,7 @@ export class DebugManager implements IService {
|
|||||||
|
|
||||||
// 收集其他内存统计
|
// 收集其他内存统计
|
||||||
const baseMemoryInfo = this.collectBaseMemoryInfo();
|
const baseMemoryInfo = this.collectBaseMemoryInfo();
|
||||||
const scene = this.sceneProvider();
|
const scene = this.sceneManager.currentScene;
|
||||||
|
|
||||||
// 使用专门的内存计算方法收集实体数据
|
// 使用专门的内存计算方法收集实体数据
|
||||||
const entityData = this.entityCollector.collectEntityDataWithMemory(scene);
|
const entityData = this.entityCollector.collectEntityDataWithMemory(scene);
|
||||||
@@ -453,7 +460,7 @@ export class DebugManager implements IService {
|
|||||||
/**
|
/**
|
||||||
* 收集组件内存统计(仅用于内存快照)
|
* 收集组件内存统计(仅用于内存快照)
|
||||||
*/
|
*/
|
||||||
private collectComponentMemoryStats(entityList: { buffer: Array<{ id: number; name?: string; destroyed?: boolean; components?: Component[] }> }): {
|
private collectComponentMemoryStats(entityList: { buffer: Array<{ id: number; name?: string; destroyed?: boolean; components?: readonly Component[] }> }): {
|
||||||
totalMemory: number;
|
totalMemory: number;
|
||||||
componentTypes: number;
|
componentTypes: number;
|
||||||
totalInstances: number;
|
totalInstances: number;
|
||||||
@@ -547,7 +554,7 @@ export class DebugManager implements IService {
|
|||||||
updateOrder: number;
|
updateOrder: number;
|
||||||
}>;
|
}>;
|
||||||
} {
|
} {
|
||||||
const scene = this.sceneProvider();
|
const scene = this.sceneManager.currentScene;
|
||||||
let totalSystemMemory = 0;
|
let totalSystemMemory = 0;
|
||||||
const systemBreakdown: Array<{
|
const systemBreakdown: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
@@ -557,11 +564,11 @@ export class DebugManager implements IService {
|
|||||||
}> = [];
|
}> = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const entityProcessors = scene?.entityProcessors;
|
const systems = scene?.systems;
|
||||||
if (entityProcessors && entityProcessors.processors) {
|
if (systems) {
|
||||||
const systemTypeMemoryCache = new Map<string, number>();
|
const systemTypeMemoryCache = new Map<string, number>();
|
||||||
|
|
||||||
for (const system of entityProcessors.processors) {
|
for (const system of systems) {
|
||||||
const systemTypeName = getSystemInstanceTypeName(system);
|
const systemTypeName = getSystemInstanceTypeName(system);
|
||||||
|
|
||||||
let systemMemory: number;
|
let systemMemory: number;
|
||||||
@@ -721,16 +728,15 @@ export class DebugManager implements IService {
|
|||||||
error?: string;
|
error?: string;
|
||||||
} {
|
} {
|
||||||
try {
|
try {
|
||||||
const performanceMonitor = this.performanceMonitorProvider();
|
if (!this.performanceMonitor) {
|
||||||
if (!performanceMonitor) {
|
|
||||||
return { enabled: false };
|
return { enabled: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
const stats = performanceMonitor.getAllSystemStats();
|
const stats = this.performanceMonitor.getAllSystemStats();
|
||||||
const warnings = performanceMonitor.getPerformanceWarnings();
|
const warnings = this.performanceMonitor.getPerformanceWarnings();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enabled: (performanceMonitor as { enabled?: boolean }).enabled ?? false,
|
enabled: (this.performanceMonitor as { enabled?: boolean }).enabled ?? false,
|
||||||
systemCount: stats.size,
|
systemCount: stats.size,
|
||||||
warnings: warnings.slice(0, 10), // 最多10个警告
|
warnings: warnings.slice(0, 10), // 最多10个警告
|
||||||
topSystems: Array.from(stats.entries()).map((entry) => {
|
topSystems: Array.from(stats.entries()).map((entry) => {
|
||||||
@@ -754,7 +760,7 @@ export class DebugManager implements IService {
|
|||||||
*/
|
*/
|
||||||
public getDebugData(): IECSDebugData {
|
public getDebugData(): IECSDebugData {
|
||||||
const currentTime = Date.now();
|
const currentTime = Date.now();
|
||||||
const scene = this.sceneProvider();
|
const scene = this.sceneManager.currentScene;
|
||||||
|
|
||||||
const debugData: IECSDebugData = {
|
const debugData: IECSDebugData = {
|
||||||
timestamp: currentTime,
|
timestamp: currentTime,
|
||||||
@@ -770,13 +776,11 @@ export class DebugManager implements IService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.channels.systems) {
|
if (this.config.channels.systems) {
|
||||||
const performanceMonitor = this.performanceMonitorProvider();
|
debugData.systems = this.systemCollector.collectSystemData(this.performanceMonitor, scene);
|
||||||
debugData.systems = this.systemCollector.collectSystemData(performanceMonitor, scene);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.channels.performance) {
|
if (this.config.channels.performance) {
|
||||||
const performanceMonitor = this.performanceMonitorProvider();
|
debugData.performance = this.performanceCollector.collectPerformanceData(this.performanceMonitor);
|
||||||
debugData.performance = this.performanceCollector.collectPerformanceData(performanceMonitor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.channels.components) {
|
if (this.config.channels.components) {
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
import { GlobalManager } from '../GlobalManager';
|
|
||||||
import { Timer } from './Timer';
|
import { Timer } from './Timer';
|
||||||
import { ITimer } from './ITimer';
|
import { ITimer } from './ITimer';
|
||||||
import type { IService } from '../../Core/ServiceContainer';
|
import type { IService } from '../../Core/ServiceContainer';
|
||||||
|
import type { IUpdatable } from '../../Types/IUpdatable';
|
||||||
|
import { Updatable } from '../../Core/DI';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 定时器管理器
|
||||||
|
*
|
||||||
* 允许动作的延迟和重复执行
|
* 允许动作的延迟和重复执行
|
||||||
*/
|
*/
|
||||||
export class TimerManager extends GlobalManager implements IService {
|
@Updatable()
|
||||||
|
export class TimerManager implements IService, IUpdatable {
|
||||||
public _timers: Array<Timer<unknown>> = [];
|
public _timers: Array<Timer<unknown>> = [];
|
||||||
|
|
||||||
public override update() {
|
public update() {
|
||||||
for (let i = this._timers.length - 1; i >= 0; i --){
|
for (let i = this._timers.length - 1; i >= 0; i --){
|
||||||
if (this._timers[i].tick()){
|
if (this._timers[i].tick()){
|
||||||
this._timers[i].unload();
|
this._timers[i].unload();
|
||||||
|
|||||||
@@ -8,6 +8,18 @@ export { Core } from './Core';
|
|||||||
export { ServiceContainer, ServiceLifetime } from './Core/ServiceContainer';
|
export { ServiceContainer, ServiceLifetime } from './Core/ServiceContainer';
|
||||||
export type { IService, ServiceType } from './Core/ServiceContainer';
|
export type { IService, ServiceType } from './Core/ServiceContainer';
|
||||||
|
|
||||||
|
// 依赖注入
|
||||||
|
export {
|
||||||
|
Injectable,
|
||||||
|
Inject,
|
||||||
|
Updatable,
|
||||||
|
registerInjectable,
|
||||||
|
createInstance,
|
||||||
|
isUpdatable,
|
||||||
|
getUpdatableMetadata
|
||||||
|
} from './Core/DI';
|
||||||
|
export type { InjectableMetadata, UpdatableMetadata } from './Core/DI';
|
||||||
|
|
||||||
// 核心管理器
|
// 核心管理器
|
||||||
export { Emitter, FuncPack } from './Utils/Emitter';
|
export { Emitter, FuncPack } from './Utils/Emitter';
|
||||||
export { GlobalManager } from './Utils/GlobalManager';
|
export { GlobalManager } from './Utils/GlobalManager';
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import { Scene } from '../src/ECS/Scene';
|
|||||||
import { SceneManager } from '../src/ECS/SceneManager';
|
import { SceneManager } from '../src/ECS/SceneManager';
|
||||||
import { Entity } from '../src/ECS/Entity';
|
import { Entity } from '../src/ECS/Entity';
|
||||||
import { Component } from '../src/ECS/Component';
|
import { Component } from '../src/ECS/Component';
|
||||||
import { GlobalManager } from '../src/Utils/GlobalManager';
|
|
||||||
import { ITimer } from '../src/Utils/Timers/ITimer';
|
import { ITimer } from '../src/Utils/Timers/ITimer';
|
||||||
|
import { Updatable } from '../src/Core/DI';
|
||||||
|
import type { IService } from '../src/Core/ServiceContainer';
|
||||||
|
import type { IUpdatable } from '../src/Types/IUpdatable';
|
||||||
|
|
||||||
// 测试组件
|
// 测试组件
|
||||||
class TestComponent extends Component {
|
class TestComponent extends Component {
|
||||||
@@ -41,22 +43,18 @@ class TestScene extends Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 测试全局管理器
|
// 测试可更新服务
|
||||||
class TestGlobalManager extends GlobalManager {
|
@Updatable()
|
||||||
|
class TestUpdatableService implements IService, IUpdatable {
|
||||||
public updateCallCount = 0;
|
public updateCallCount = 0;
|
||||||
public override _enabled = false;
|
|
||||||
|
|
||||||
public override get enabled(): boolean {
|
public update(): void {
|
||||||
return this._enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override set enabled(value: boolean) {
|
|
||||||
this._enabled = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override update(): void {
|
|
||||||
this.updateCallCount++;
|
this.updateCallCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public dispose(): void {
|
||||||
|
// 清理资源
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Core - 核心管理系统测试', () => {
|
describe('Core - 核心管理系统测试', () => {
|
||||||
@@ -129,96 +127,75 @@ describe('Core - 核心管理系统测试', () => {
|
|||||||
// 注意:场景管理功能已移至SceneManager
|
// 注意:场景管理功能已移至SceneManager
|
||||||
// 相关测试请查看 SceneManager.test.ts
|
// 相关测试请查看 SceneManager.test.ts
|
||||||
|
|
||||||
describe('更新循环 - 全局服务', () => {
|
describe('更新循环 - 可更新服务', () => {
|
||||||
let core: Core;
|
let core: Core;
|
||||||
let globalManager: TestGlobalManager;
|
let updatableService: TestUpdatableService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
core = Core.create(true);
|
core = Core.create(true);
|
||||||
globalManager = new TestGlobalManager();
|
updatableService = new TestUpdatableService();
|
||||||
Core.registerGlobalManager(globalManager);
|
Core.services.registerInstance(TestUpdatableService, updatableService);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('应该能够更新全局管理器', () => {
|
test('应该能够更新可更新服务', () => {
|
||||||
Core.update(0.016);
|
Core.update(0.016);
|
||||||
expect(globalManager.updateCallCount).toBe(1);
|
expect(updatableService.updateCallCount).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('暂停状态下不应该执行更新', () => {
|
test('暂停状态下不应该执行更新', () => {
|
||||||
Core.paused = true;
|
Core.paused = true;
|
||||||
Core.update(0.016);
|
Core.update(0.016);
|
||||||
expect(globalManager.updateCallCount).toBe(0);
|
expect(updatableService.updateCallCount).toBe(0);
|
||||||
|
|
||||||
// 恢复状态
|
// 恢复状态
|
||||||
Core.paused = false;
|
Core.paused = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('禁用的全局管理器不应该被更新', () => {
|
|
||||||
globalManager.enabled = false;
|
|
||||||
Core.update(0.016);
|
|
||||||
expect(globalManager.updateCallCount).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('多次更新应该累积调用', () => {
|
test('多次更新应该累积调用', () => {
|
||||||
Core.update(0.016);
|
Core.update(0.016);
|
||||||
Core.update(0.016);
|
Core.update(0.016);
|
||||||
Core.update(0.016);
|
Core.update(0.016);
|
||||||
expect(globalManager.updateCallCount).toBe(3);
|
expect(updatableService.updateCallCount).toBe(3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('全局管理器管理', () => {
|
describe('服务容器集成', () => {
|
||||||
let core: Core;
|
let core: Core;
|
||||||
let manager1: TestGlobalManager;
|
let service1: TestUpdatableService;
|
||||||
let manager2: TestGlobalManager;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
core = Core.create(true);
|
core = Core.create(true);
|
||||||
manager1 = new TestGlobalManager();
|
service1 = new TestUpdatableService();
|
||||||
manager2 = new TestGlobalManager();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('应该能够注册全局管理器', () => {
|
test('应该能够通过ServiceContainer注册可更新服务', () => {
|
||||||
Core.registerGlobalManager(manager1);
|
Core.services.registerInstance(TestUpdatableService, service1);
|
||||||
|
|
||||||
expect(manager1.enabled).toBe(true);
|
|
||||||
|
|
||||||
// 测试更新是否被调用
|
// 测试更新是否被调用
|
||||||
Core.update(0.016);
|
Core.update(0.016);
|
||||||
expect(manager1.updateCallCount).toBe(1);
|
expect(service1.updateCallCount).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('应该能够注销全局管理器', () => {
|
test('应该能够注销服务', () => {
|
||||||
Core.registerGlobalManager(manager1);
|
Core.services.registerInstance(TestUpdatableService, service1);
|
||||||
Core.unregisterGlobalManager(manager1);
|
Core.services.unregister(TestUpdatableService);
|
||||||
|
|
||||||
expect(manager1.enabled).toBe(false);
|
|
||||||
|
|
||||||
// 测试更新不应该被调用
|
// 测试更新不应该被调用
|
||||||
Core.update(0.016);
|
Core.update(0.016);
|
||||||
expect(manager1.updateCallCount).toBe(0);
|
expect(service1.updateCallCount).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('应该能够获取指定类型的全局管理器', () => {
|
test('应该能够通过ServiceContainer解析服务', () => {
|
||||||
Core.registerGlobalManager(manager1);
|
Core.services.registerInstance(TestUpdatableService, service1);
|
||||||
|
|
||||||
const retrieved = Core.getGlobalManager(TestGlobalManager);
|
const retrieved = Core.services.resolve(TestUpdatableService);
|
||||||
expect(retrieved).toBe(manager1);
|
expect(retrieved).toBe(service1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('获取不存在的管理器应该返回null', () => {
|
test('解析不存在的服务应该抛出错误', () => {
|
||||||
const retrieved = Core.getGlobalManager(TestGlobalManager);
|
expect(() => {
|
||||||
expect(retrieved).toBeNull();
|
Core.services.resolve(TestUpdatableService);
|
||||||
});
|
}).toThrow();
|
||||||
|
|
||||||
test('应该能够管理多个全局管理器', () => {
|
|
||||||
Core.registerGlobalManager(manager1);
|
|
||||||
Core.registerGlobalManager(manager2);
|
|
||||||
|
|
||||||
Core.update(0.016);
|
|
||||||
|
|
||||||
expect(manager1.updateCallCount).toBe(1);
|
|
||||||
expect(manager2.updateCallCount).toBe(1);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
206
packages/core/tests/Core/DI.test.ts
Normal file
206
packages/core/tests/Core/DI.test.ts
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
import { Injectable, Inject, isInjectable, getInjectMetadata, createInstance, registerInjectable } from '../../src/Core/DI';
|
||||||
|
import { ServiceContainer } from '../../src/Core/ServiceContainer';
|
||||||
|
import type { IService } from '../../src/Core/ServiceContainer';
|
||||||
|
|
||||||
|
// 测试服务类
|
||||||
|
@Injectable()
|
||||||
|
class SimpleService implements IService {
|
||||||
|
public value: string = 'simple';
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
// 清理资源
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
class DependentService implements IService {
|
||||||
|
constructor(
|
||||||
|
@Inject(SimpleService) public simpleService: SimpleService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
// 清理资源
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
class MultiDependencyService implements IService {
|
||||||
|
constructor(
|
||||||
|
@Inject(SimpleService) public service1: SimpleService,
|
||||||
|
@Inject(DependentService) public service2: DependentService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
// 清理资源
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非Injectable类(用于测试错误情况)
|
||||||
|
class NonInjectableService implements IService {
|
||||||
|
dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('DI - 依赖注入装饰器测试', () => {
|
||||||
|
let container: ServiceContainer;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
container = new ServiceContainer();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@Injectable 装饰器', () => {
|
||||||
|
test('应该正确标记类为可注入', () => {
|
||||||
|
expect(isInjectable(SimpleService)).toBe(true);
|
||||||
|
expect(isInjectable(DependentService)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('未标记的类不应该是可注入的', () => {
|
||||||
|
expect(isInjectable(NonInjectableService)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@Inject 装饰器', () => {
|
||||||
|
test('应该记录参数注入元数据', () => {
|
||||||
|
const metadata = getInjectMetadata(DependentService);
|
||||||
|
expect(metadata.size).toBe(1);
|
||||||
|
expect(metadata.get(0)).toBe(SimpleService);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该记录多个参数的注入元数据', () => {
|
||||||
|
const metadata = getInjectMetadata(MultiDependencyService);
|
||||||
|
expect(metadata.size).toBe(2);
|
||||||
|
expect(metadata.get(0)).toBe(SimpleService);
|
||||||
|
expect(metadata.get(1)).toBe(DependentService);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createInstance', () => {
|
||||||
|
test('应该创建无依赖的实例', () => {
|
||||||
|
container.registerSingleton(SimpleService);
|
||||||
|
const instance = createInstance(SimpleService, container);
|
||||||
|
|
||||||
|
expect(instance).toBeInstanceOf(SimpleService);
|
||||||
|
expect(instance.value).toBe('simple');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该创建有依赖的实例', () => {
|
||||||
|
container.registerSingleton(SimpleService);
|
||||||
|
container.registerSingleton(DependentService, () =>
|
||||||
|
createInstance(DependentService, container)
|
||||||
|
);
|
||||||
|
|
||||||
|
const instance = createInstance(DependentService, container);
|
||||||
|
|
||||||
|
expect(instance).toBeInstanceOf(DependentService);
|
||||||
|
expect(instance.simpleService).toBeInstanceOf(SimpleService);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该创建有多个依赖的实例', () => {
|
||||||
|
container.registerSingleton(SimpleService);
|
||||||
|
container.registerSingleton(DependentService, () =>
|
||||||
|
createInstance(DependentService, container)
|
||||||
|
);
|
||||||
|
container.registerSingleton(MultiDependencyService, () =>
|
||||||
|
createInstance(MultiDependencyService, container)
|
||||||
|
);
|
||||||
|
|
||||||
|
const instance = createInstance(MultiDependencyService, container);
|
||||||
|
|
||||||
|
expect(instance).toBeInstanceOf(MultiDependencyService);
|
||||||
|
expect(instance.service1).toBeInstanceOf(SimpleService);
|
||||||
|
expect(instance.service2).toBeInstanceOf(DependentService);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('依赖应该正确解析为单例', () => {
|
||||||
|
container.registerSingleton(SimpleService);
|
||||||
|
container.registerSingleton(DependentService, () =>
|
||||||
|
createInstance(DependentService, container)
|
||||||
|
);
|
||||||
|
|
||||||
|
const simple1 = container.resolve(SimpleService);
|
||||||
|
const dependent = createInstance(DependentService, container);
|
||||||
|
|
||||||
|
expect(dependent.simpleService).toBe(simple1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('registerInjectable', () => {
|
||||||
|
test('应该注册可注入的服务', () => {
|
||||||
|
registerInjectable(container, SimpleService);
|
||||||
|
|
||||||
|
expect(container.isRegistered(SimpleService)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该自动解析依赖', () => {
|
||||||
|
registerInjectable(container, SimpleService);
|
||||||
|
registerInjectable(container, DependentService);
|
||||||
|
|
||||||
|
const instance = container.resolve(DependentService);
|
||||||
|
|
||||||
|
expect(instance).toBeInstanceOf(DependentService);
|
||||||
|
expect(instance.simpleService).toBeInstanceOf(SimpleService);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该正确处理多层依赖', () => {
|
||||||
|
registerInjectable(container, SimpleService);
|
||||||
|
registerInjectable(container, DependentService);
|
||||||
|
registerInjectable(container, MultiDependencyService);
|
||||||
|
|
||||||
|
const instance = container.resolve(MultiDependencyService);
|
||||||
|
|
||||||
|
expect(instance).toBeInstanceOf(MultiDependencyService);
|
||||||
|
expect(instance.service1).toBeInstanceOf(SimpleService);
|
||||||
|
expect(instance.service2).toBeInstanceOf(DependentService);
|
||||||
|
expect(instance.service2.simpleService).toBeInstanceOf(SimpleService);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('依赖应该是单例的', () => {
|
||||||
|
registerInjectable(container, SimpleService);
|
||||||
|
registerInjectable(container, DependentService);
|
||||||
|
|
||||||
|
const instance1 = container.resolve(DependentService);
|
||||||
|
const instance2 = container.resolve(DependentService);
|
||||||
|
const simple = container.resolve(SimpleService);
|
||||||
|
|
||||||
|
expect(instance1).toBe(instance2);
|
||||||
|
expect(instance1.simpleService).toBe(simple);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('注册瞬时服务应该每次创建新实例', () => {
|
||||||
|
registerInjectable(container, SimpleService);
|
||||||
|
registerInjectable(container, DependentService, false); // 瞬时
|
||||||
|
|
||||||
|
const instance1 = container.resolve(DependentService);
|
||||||
|
const instance2 = container.resolve(DependentService);
|
||||||
|
|
||||||
|
expect(instance1).not.toBe(instance2);
|
||||||
|
expect(instance1.simpleService).toBe(instance2.simpleService); // 依赖仍然是单例
|
||||||
|
});
|
||||||
|
|
||||||
|
test('注册非Injectable类应该抛出错误', () => {
|
||||||
|
expect(() => {
|
||||||
|
registerInjectable(container, NonInjectableService as any);
|
||||||
|
}).toThrow(/not marked as @Injectable/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('集成测试', () => {
|
||||||
|
test('完整的DI流程应该正常工作', () => {
|
||||||
|
// 1. 注册所有服务
|
||||||
|
registerInjectable(container, SimpleService);
|
||||||
|
registerInjectable(container, DependentService);
|
||||||
|
registerInjectable(container, MultiDependencyService);
|
||||||
|
|
||||||
|
// 2. 解析服务
|
||||||
|
const multi = container.resolve(MultiDependencyService);
|
||||||
|
|
||||||
|
// 3. 验证依赖树
|
||||||
|
expect(multi).toBeInstanceOf(MultiDependencyService);
|
||||||
|
expect(multi.service1).toBeInstanceOf(SimpleService);
|
||||||
|
expect(multi.service2).toBeInstanceOf(DependentService);
|
||||||
|
expect(multi.service2.simpleService).toBe(multi.service1); // 同一个实例
|
||||||
|
|
||||||
|
// 4. 验证服务功能
|
||||||
|
expect(multi.service1.value).toBe('simple');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -335,7 +335,7 @@ describe('FluentAPI - 流式API测试', () => {
|
|||||||
.withSystems(system1, system2)
|
.withSystems(system1, system2)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
expect(scene.systems.length).toBe(2);
|
expect(scene.systems.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('流式调用应该工作正常', () => {
|
test('流式调用应该工作正常', () => {
|
||||||
|
|||||||
426
packages/core/tests/ECS/EntitySystemDI.test.ts
Normal file
426
packages/core/tests/ECS/EntitySystemDI.test.ts
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
import { Scene } from '../../src/ECS/Scene';
|
||||||
|
import { EntitySystem } from '../../src/ECS/Systems/EntitySystem';
|
||||||
|
import { Entity } from '../../src/ECS/Entity';
|
||||||
|
import { Component } from '../../src/ECS/Component';
|
||||||
|
import { Matcher } from '../../src/ECS/Utils/Matcher';
|
||||||
|
import { Injectable, Inject } from '../../src/Core/DI';
|
||||||
|
import { Core } from '../../src/Core';
|
||||||
|
import type { IService } from '../../src/Core/ServiceContainer';
|
||||||
|
import { ECSSystem } from '../../src/ECS/Decorators';
|
||||||
|
|
||||||
|
class Transform extends Component {
|
||||||
|
constructor(public x: number = 0, public y: number = 0) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Velocity extends Component {
|
||||||
|
constructor(public vx: number = 0, public vy: number = 0) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Health extends Component {
|
||||||
|
constructor(public value: number = 100) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('EntitySystem - 依赖注入测试', () => {
|
||||||
|
let scene: Scene;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
Core.create();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
scene = new Scene();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
scene.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
Core.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('基本DI功能', () => {
|
||||||
|
test('应该支持无依赖的System通过类型添加', () => {
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('Movement')
|
||||||
|
class MovementSystem extends EntitySystem implements IService {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty().all(Transform, Velocity));
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const system = scene.addEntityProcessor(MovementSystem);
|
||||||
|
|
||||||
|
expect(system).toBeInstanceOf(MovementSystem);
|
||||||
|
expect(scene.systems.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该支持有依赖的System自动注入', () => {
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('Collision')
|
||||||
|
class CollisionSystem extends EntitySystem implements IService {
|
||||||
|
public checkCount = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty().all(Transform));
|
||||||
|
}
|
||||||
|
|
||||||
|
public checkCollisions() {
|
||||||
|
this.checkCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('Physics')
|
||||||
|
class PhysicsSystem extends EntitySystem implements IService {
|
||||||
|
constructor(
|
||||||
|
@Inject(CollisionSystem) public collision: CollisionSystem
|
||||||
|
) {
|
||||||
|
super(Matcher.empty().all(Transform, Velocity));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override process(entities: readonly Entity[]): void {
|
||||||
|
this.collision.checkCollisions();
|
||||||
|
}
|
||||||
|
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.addEntityProcessor(CollisionSystem);
|
||||||
|
const physics = scene.addEntityProcessor(PhysicsSystem);
|
||||||
|
|
||||||
|
expect(physics).toBeInstanceOf(PhysicsSystem);
|
||||||
|
expect(physics.collision).toBeInstanceOf(CollisionSystem);
|
||||||
|
expect(scene.systems.length).toBe(2);
|
||||||
|
|
||||||
|
const entity = scene.createEntity('test');
|
||||||
|
entity.addComponent(new Transform());
|
||||||
|
entity.addComponent(new Velocity());
|
||||||
|
|
||||||
|
scene.update();
|
||||||
|
|
||||||
|
expect(physics.collision.checkCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该支持多层级依赖注入', () => {
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('A')
|
||||||
|
class SystemA extends EntitySystem implements IService {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty());
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('B')
|
||||||
|
class SystemB extends EntitySystem implements IService {
|
||||||
|
constructor(@Inject(SystemA) public systemA: SystemA) {
|
||||||
|
super(Matcher.empty());
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('C')
|
||||||
|
class SystemC extends EntitySystem implements IService {
|
||||||
|
constructor(
|
||||||
|
@Inject(SystemA) public systemA: SystemA,
|
||||||
|
@Inject(SystemB) public systemB: SystemB
|
||||||
|
) {
|
||||||
|
super(Matcher.empty());
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.addEntityProcessor(SystemA);
|
||||||
|
scene.addEntityProcessor(SystemB);
|
||||||
|
const systemC = scene.addEntityProcessor(SystemC);
|
||||||
|
|
||||||
|
expect(systemC.systemA).toBeInstanceOf(SystemA);
|
||||||
|
expect(systemC.systemB).toBeInstanceOf(SystemB);
|
||||||
|
expect(systemC.systemB.systemA).toBe(systemC.systemA);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('批量注册', () => {
|
||||||
|
test('应该支持批量注册System并自动解析依赖', () => {
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('Collision')
|
||||||
|
class CollisionSystem extends EntitySystem implements IService {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty().all(Transform));
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('Physics', { updateOrder: 10 })
|
||||||
|
class PhysicsSystem extends EntitySystem implements IService {
|
||||||
|
constructor(@Inject(CollisionSystem) public collision: CollisionSystem) {
|
||||||
|
super(Matcher.empty().all(Transform, Velocity));
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('Render', { updateOrder: 20 })
|
||||||
|
class RenderSystem extends EntitySystem implements IService {
|
||||||
|
constructor(@Inject(PhysicsSystem) public physics: PhysicsSystem) {
|
||||||
|
super(Matcher.empty().all(Transform));
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const systems = scene.registerSystems([
|
||||||
|
CollisionSystem,
|
||||||
|
PhysicsSystem,
|
||||||
|
RenderSystem
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(systems.length).toBe(3);
|
||||||
|
expect(scene.systems.length).toBe(3);
|
||||||
|
|
||||||
|
const [collision, physics, render] = systems;
|
||||||
|
expect(collision).toBeInstanceOf(CollisionSystem);
|
||||||
|
expect(physics).toBeInstanceOf(PhysicsSystem);
|
||||||
|
expect(render).toBeInstanceOf(RenderSystem);
|
||||||
|
|
||||||
|
expect((physics as any).collision).toBe(collision);
|
||||||
|
expect((render as any).physics).toBe(physics);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('批量注册的System应该按updateOrder排序', () => {
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('C', { updateOrder: 30 })
|
||||||
|
class SystemC extends EntitySystem implements IService {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty());
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('A', { updateOrder: 10 })
|
||||||
|
class SystemA extends EntitySystem implements IService {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty());
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('B', { updateOrder: 20 })
|
||||||
|
class SystemB extends EntitySystem implements IService {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty());
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.registerSystems([SystemC, SystemA, SystemB]);
|
||||||
|
|
||||||
|
const systems = scene.systems;
|
||||||
|
expect(systems[0]).toBeInstanceOf(SystemA);
|
||||||
|
expect(systems[1]).toBeInstanceOf(SystemB);
|
||||||
|
expect(systems[2]).toBeInstanceOf(SystemC);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('场景隔离', () => {
|
||||||
|
test('不同Scene的System实例应该相互独立', () => {
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('Counter')
|
||||||
|
class CounterSystem extends EntitySystem implements IService {
|
||||||
|
public count = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override process(): void {
|
||||||
|
this.count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const scene1 = new Scene();
|
||||||
|
const scene2 = new Scene();
|
||||||
|
|
||||||
|
const counter1 = scene1.addEntityProcessor(CounterSystem);
|
||||||
|
const counter2 = scene2.addEntityProcessor(CounterSystem);
|
||||||
|
|
||||||
|
expect(counter1).not.toBe(counter2);
|
||||||
|
|
||||||
|
scene1.update();
|
||||||
|
expect(counter1.count).toBe(1);
|
||||||
|
expect(counter2.count).toBe(0);
|
||||||
|
|
||||||
|
scene2.update();
|
||||||
|
expect(counter1.count).toBe(1);
|
||||||
|
expect(counter2.count).toBe(1);
|
||||||
|
|
||||||
|
scene1.end();
|
||||||
|
scene2.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSystem方法', () => {
|
||||||
|
test('应该能通过getSystem获取已注册的System', () => {
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('Test')
|
||||||
|
class TestSystem extends EntitySystem implements IService {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty());
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.addEntityProcessor(TestSystem);
|
||||||
|
|
||||||
|
const system = scene.getSystem(TestSystem);
|
||||||
|
expect(system).toBeInstanceOf(TestSystem);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('获取未注册的System应该返回null', () => {
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('Test')
|
||||||
|
class TestSystem extends EntitySystem implements IService {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty());
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const system = scene.getSystem(TestSystem);
|
||||||
|
expect(system).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('向后兼容性', () => {
|
||||||
|
test('应该继续支持手动创建实例的方式', () => {
|
||||||
|
class LegacySystem extends EntitySystem {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty().all(Transform));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const system = new LegacySystem();
|
||||||
|
scene.addEntityProcessor(system);
|
||||||
|
|
||||||
|
expect(scene.systems.length).toBe(1);
|
||||||
|
expect(scene.systems[0]).toBe(system);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('混合使用DI和手动创建应该正常工作', () => {
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('DI')
|
||||||
|
class DISystem extends EntitySystem implements IService {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty().all(Transform));
|
||||||
|
}
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ManualSystem extends EntitySystem {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty().all(Velocity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.addEntityProcessor(DISystem);
|
||||||
|
scene.addEntityProcessor(new ManualSystem());
|
||||||
|
|
||||||
|
expect(scene.systems.length).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Issue #76 场景验证', () => {
|
||||||
|
test('应该消除硬编码依赖,使用构造函数注入', () => {
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('TimeService')
|
||||||
|
class TimeService extends EntitySystem implements IService {
|
||||||
|
public getDeltaTime(): number {
|
||||||
|
return 0.016;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('CollisionService')
|
||||||
|
class CollisionService extends EntitySystem implements IService {
|
||||||
|
public detectCollisions(): string[] {
|
||||||
|
return ['collision1', 'collision2'];
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@ECSSystem('Physics')
|
||||||
|
class PhysicsSystem extends EntitySystem implements IService {
|
||||||
|
constructor(
|
||||||
|
@Inject(TimeService) private time: TimeService,
|
||||||
|
@Inject(CollisionService) private collision: CollisionService
|
||||||
|
) {
|
||||||
|
super(Matcher.empty().all(Transform, Velocity));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override process(entities: readonly Entity[]): void {
|
||||||
|
const dt = this.time.getDeltaTime();
|
||||||
|
const collisions = this.collision.detectCollisions();
|
||||||
|
|
||||||
|
for (const entity of entities) {
|
||||||
|
const transform = entity.getComponent(Transform)!;
|
||||||
|
const velocity = entity.getComponent(Velocity)!;
|
||||||
|
|
||||||
|
transform.x += velocity.vx * dt;
|
||||||
|
transform.y += velocity.vy * dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override dispose() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.registerSystems([
|
||||||
|
TimeService,
|
||||||
|
CollisionService,
|
||||||
|
PhysicsSystem
|
||||||
|
]);
|
||||||
|
|
||||||
|
const entity = scene.createEntity('player');
|
||||||
|
entity.addComponent(new Transform(0, 0));
|
||||||
|
entity.addComponent(new Velocity(100, 50));
|
||||||
|
|
||||||
|
const physics = scene.getSystem(PhysicsSystem);
|
||||||
|
expect(physics).not.toBeNull();
|
||||||
|
expect((physics as any).time).toBeInstanceOf(TimeService);
|
||||||
|
expect((physics as any).collision).toBeInstanceOf(CollisionService);
|
||||||
|
|
||||||
|
scene.update();
|
||||||
|
|
||||||
|
const transform = entity.getComponent(Transform)!;
|
||||||
|
expect(transform.x).toBeCloseTo(1.6, 1);
|
||||||
|
expect(transform.y).toBeCloseTo(0.8, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -129,7 +129,7 @@ describe('Scene - 场景管理系统测试', () => {
|
|||||||
expect(scene).toBeInstanceOf(Scene);
|
expect(scene).toBeInstanceOf(Scene);
|
||||||
expect(scene.name).toBe("");
|
expect(scene.name).toBe("");
|
||||||
expect(scene.entities).toBeDefined();
|
expect(scene.entities).toBeDefined();
|
||||||
expect(scene.entityProcessors).toBeDefined();
|
expect(scene.systems).toBeDefined();
|
||||||
expect(scene.identifierPool).toBeDefined();
|
expect(scene.identifierPool).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ describe('Scene - 场景管理系统测试', () => {
|
|||||||
|
|
||||||
test('场景应该有正确的初始状态', () => {
|
test('场景应该有正确的初始状态', () => {
|
||||||
expect(scene.entities.count).toBe(0);
|
expect(scene.entities.count).toBe(0);
|
||||||
expect(scene.entityProcessors.count).toBe(0);
|
expect(scene.systems.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('应该能够使用配置创建场景', () => {
|
test('应该能够使用配置创建场景', () => {
|
||||||
@@ -249,7 +249,7 @@ describe('Scene - 场景管理系统测试', () => {
|
|||||||
test('应该能够添加实体系统', () => {
|
test('应该能够添加实体系统', () => {
|
||||||
scene.addEntityProcessor(movementSystem);
|
scene.addEntityProcessor(movementSystem);
|
||||||
|
|
||||||
expect(scene.entityProcessors.count).toBe(1);
|
expect(scene.systems.length).toBe(1);
|
||||||
expect(movementSystem.scene).toBe(scene);
|
expect(movementSystem.scene).toBe(scene);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -257,7 +257,7 @@ describe('Scene - 场景管理系统测试', () => {
|
|||||||
scene.addEntityProcessor(movementSystem);
|
scene.addEntityProcessor(movementSystem);
|
||||||
scene.removeEntityProcessor(movementSystem);
|
scene.removeEntityProcessor(movementSystem);
|
||||||
|
|
||||||
expect(scene.entityProcessors.count).toBe(0);
|
expect(scene.systems.length).toBe(0);
|
||||||
expect(movementSystem.scene).toBeNull();
|
expect(movementSystem.scene).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -271,7 +271,7 @@ describe('Scene - 场景管理系统测试', () => {
|
|||||||
scene.addEntityProcessor(movementSystem);
|
scene.addEntityProcessor(movementSystem);
|
||||||
scene.addEntityProcessor(renderSystem);
|
scene.addEntityProcessor(renderSystem);
|
||||||
|
|
||||||
expect(scene.entityProcessors.count).toBe(2);
|
expect(scene.systems.length).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('系统应该按更新顺序执行', () => {
|
test('系统应该按更新顺序执行', () => {
|
||||||
@@ -538,10 +538,11 @@ describe('Scene - 场景管理系统测试', () => {
|
|||||||
test('重复添加同一个系统应该安全处理', () => {
|
test('重复添加同一个系统应该安全处理', () => {
|
||||||
const system = new MovementSystem();
|
const system = new MovementSystem();
|
||||||
|
|
||||||
scene.addEntityProcessor(system);
|
|
||||||
scene.addEntityProcessor(system); // 重复添加
|
|
||||||
|
|
||||||
expect(scene.entityProcessors.count).toBe(1);
|
scene.addEntityProcessor(system);
|
||||||
|
scene.addEntityProcessor(system);
|
||||||
|
|
||||||
|
expect(scene.systems.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('系统处理过程中的异常应该被正确处理', () => {
|
test('系统处理过程中的异常应该被正确处理', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user