From b13132b259824465996c839ff033d045b572f12b Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Fri, 10 Oct 2025 21:52:43 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BE=9D=E8=B5=96=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E5=BC=95=E5=85=A5DI=E5=AE=B9=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/Core.ts | 93 ++-- packages/core/src/Core/DI/Decorators.ts | 317 +++++++++++++ packages/core/src/Core/DI/index.ts | 20 + packages/core/src/Core/ServiceContainer.ts | 91 +++- .../core/src/ECS/Decorators/TypeDecorators.ts | 46 +- packages/core/src/ECS/Decorators/index.ts | 5 +- packages/core/src/ECS/IScene.ts | 8 +- packages/core/src/ECS/Scene.ts | 281 ++++++++++-- packages/core/src/ECS/Systems/EntitySystem.ts | 38 +- packages/core/src/Types/IUpdatable.ts | 20 + packages/core/src/Types/index.ts | 1 + packages/core/src/Utils/Debug/DebugManager.ts | 52 ++- .../core/src/Utils/Timers/TimerManager.ts | 10 +- packages/core/src/index.ts | 12 + packages/core/tests/Core.test.ts | 103 ++--- packages/core/tests/Core/DI.test.ts | 206 +++++++++ .../core/tests/ECS/Core/FluentAPI.test.ts | 6 +- .../core/tests/ECS/EntitySystemDI.test.ts | 426 ++++++++++++++++++ packages/core/tests/ECS/Scene.test.ts | 25 +- 19 files changed, 1529 insertions(+), 231 deletions(-) create mode 100644 packages/core/src/Core/DI/Decorators.ts create mode 100644 packages/core/src/Core/DI/index.ts create mode 100644 packages/core/src/Types/IUpdatable.ts create mode 100644 packages/core/tests/Core/DI.test.ts create mode 100644 packages/core/tests/ECS/EntitySystemDI.test.ts diff --git a/packages/core/src/Core.ts b/packages/core/src/Core.ts index c66f7c1b..628485ec 100644 --- a/packages/core/src/Core.ts +++ b/packages/core/src/Core.ts @@ -1,4 +1,3 @@ -import { GlobalManager } from './Utils/GlobalManager'; import { TimerManager } from './Utils/Timers/TimerManager'; import { ITimer } from './Utils/Timers/ITimer'; import { Timer } from './Utils/Timers/Timer'; @@ -6,7 +5,7 @@ import { Time } from './Utils/Time'; import { PerformanceMonitor } from './Utils/PerformanceMonitor'; import { PoolManager } from './Utils/Pool/PoolManager'; import { DebugManager } from './Utils/Debug'; -import { ICoreConfig, IECSDebugConfig } from './Types'; +import { ICoreConfig, IECSDebugConfig, IUpdatable, isUpdatable } from './Types'; import { createLogger } from './Utils/Logger'; import { SceneManager } from './ECS/SceneManager'; 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专用日志器 @@ -84,13 +85,6 @@ export class Core { */ private _serviceContainer: ServiceContainer; - /** - * 全局管理器集合 - * - * 存储所有注册的全局管理器实例。 - */ - public _globalManagers: GlobalManager[] = []; - /** * 定时器管理器 * @@ -151,7 +145,6 @@ export class Core { // 初始化定时器管理器 this._timerManager = new TimerManager(); - Core.registerGlobalManager(this._timerManager); this._serviceContainer.registerInstance(TimerManager, this._timerManager); // 初始化性能监控器 @@ -175,7 +168,13 @@ export class Core { // 初始化调试管理器 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); } @@ -197,6 +196,7 @@ export class Core { * 用于注册和解析自定义服务。 * * @returns 服务容器实例 + * @throws 如果Core实例未创建 * * @example * ```typescript @@ -368,43 +368,6 @@ export class Core { 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(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 onTime - 定时器触发时的回调函数 * @returns 创建的定时器实例 + * @throws 如果Core实例未创建或onTime回调未提供 * * @example * ```typescript @@ -431,6 +395,9 @@ export class Core { * ``` */ public static schedule(timeInSeconds: number, repeats: boolean = false, context?: TContext, onTime?: (timer: ITimer) => void): Timer { + if (!this._instance) { + throw new Error('Core实例未创建,请先调用Core.create()'); + } if (!onTime) { throw new Error('onTime callback is required'); } @@ -451,7 +418,13 @@ export class Core { if (this._instance._debugManager) { this._instance._debugManager.updateConfig(config); } 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配置 @@ -530,13 +503,10 @@ export class Core { this._performanceMonitor.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); + // 更新所有可更新的服务 + const servicesStartTime = this._performanceMonitor.startMonitoring('Services.update'); + this._serviceContainer.updateAll(deltaTime); + this._performanceMonitor.endMonitoring('Services.update', servicesStartTime, this._serviceContainer.getUpdatableCount()); // 更新对象池管理器 this._poolManager.update(); @@ -566,15 +536,12 @@ export class Core { this._instance._debugManager.stop(); } - // 清理全局管理器 - for (const manager of this._instance._globalManagers) { - manager.enabled = false; - } - this._instance._globalManagers = []; + // 清理所有服务 + this._instance._serviceContainer.clear(); Core._logger.info('Core destroyed'); - // @ts-ignore - 清空实例引用 + // 清空实例引用,允许重新创建Core实例 this._instance = null; } } diff --git a/packages/core/src/Core/DI/Decorators.ts b/packages/core/src/Core/DI/Decorators.ts new file mode 100644 index 00000000..838d787a --- /dev/null +++ b/packages/core/src/Core/DI/Decorators.ts @@ -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(); +const injectMetadata = new WeakMap | string | symbol>>(); +const updatableMetadata = new WeakMap(); + +/** + * 可注入元数据接口 + */ +export interface InjectableMetadata { + /** + * 是否可注入 + */ + injectable: boolean; + + /** + * 依赖列表 + */ + dependencies: Array | 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 (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 (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 | 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 | string | symbol> { + return injectMetadata.get(target) || new Map(); +} + +/** + * 创建实例并自动注入依赖 + * + * @param constructor 构造函数 + * @param container 服务容器 + * @returns 创建的实例 + * + * @example + * ```typescript + * const instance = createInstance(MySystem, container); + * ``` + */ +export function createInstance( + 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)); + } + } 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( + container: ServiceContainer, + serviceType: ServiceType, + 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); + } +} diff --git a/packages/core/src/Core/DI/index.ts b/packages/core/src/Core/DI/index.ts new file mode 100644 index 00000000..4dd71639 --- /dev/null +++ b/packages/core/src/Core/DI/index.ts @@ -0,0 +1,20 @@ +/** + * 依赖注入模块 + * + * 提供装饰器和工具函数,用于实现依赖注入模式 + */ + +export { + Injectable, + Inject, + Updatable, + isInjectable, + getInjectableMetadata, + getInjectMetadata, + isUpdatable, + getUpdatableMetadata, + createInstance, + registerInjectable +} from './Decorators'; + +export type { InjectableMetadata, UpdatableMetadata } from './Decorators'; diff --git a/packages/core/src/Core/ServiceContainer.ts b/packages/core/src/Core/ServiceContainer.ts index 9ab7652a..c847fc04 100644 --- a/packages/core/src/Core/ServiceContainer.ts +++ b/packages/core/src/Core/ServiceContainer.ts @@ -1,4 +1,5 @@ import { createLogger } from '../Utils/Logger'; +import { isUpdatable as checkUpdatable, getUpdatableMetadata } from './DI'; const logger = createLogger('ServiceContainer'); @@ -16,8 +17,10 @@ export interface IService { /** * 服务类型 + * + * 支持任意构造函数签名,以便与依赖注入装饰器配合使用 */ -export type ServiceType = new (...args: unknown[]) => T; +export type ServiceType = new (...args: any[]) => T; /** * 服务生命周期 @@ -99,6 +102,14 @@ export class ServiceContainer { */ private _resolving: Set> = new Set(); + /** + * 可更新的服务列表 + * + * 自动收集所有使用@Updatable装饰器标记的服务,供Core统一更新 + * 按优先级排序(数值越小越先执行) + */ + private _updatableServices: Array<{ instance: any; priority: number }> = []; + /** * 注册单例服务 * @@ -192,6 +203,18 @@ export class ServiceContainer { 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}`); } @@ -243,6 +266,18 @@ export class ServiceContainer { // 如果是单例,缓存实例 if (registration.lifetime === ServiceLifetime.Singleton) { 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; @@ -300,6 +335,12 @@ export class ServiceContainer { // 如果有单例实例,调用 dispose if (registration.instance) { + // 从可更新列表中移除 + const index = this._updatableServices.findIndex(item => item.instance === registration.instance); + if (index !== -1) { + this._updatableServices.splice(index, 1); + } + registration.instance.dispose(); } @@ -320,6 +361,7 @@ export class ServiceContainer { } this._services.clear(); + this._updatableServices = []; logger.debug('Cleared all services'); } @@ -331,4 +373,51 @@ export class ServiceContainer { public getRegisteredServices(): ServiceType[] { 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; + } } diff --git a/packages/core/src/ECS/Decorators/TypeDecorators.ts b/packages/core/src/ECS/Decorators/TypeDecorators.ts index 5c82d0ee..9f11991e 100644 --- a/packages/core/src/ECS/Decorators/TypeDecorators.ts +++ b/packages/core/src/ECS/Decorators/TypeDecorators.ts @@ -39,34 +39,72 @@ export function ECSComponent(typeName: string) { }; } +/** + * System元数据配置 + */ +export interface SystemMetadata { + /** + * 更新顺序(数值越小越先执行,默认0) + */ + updateOrder?: number; + + /** + * 是否默认启用(默认true) + */ + enabled?: boolean; +} + /** * 系统类型装饰器 * 用于为系统类指定固定的类型名称,避免在代码混淆后失效 - * + * * @param typeName 系统类型名称 + * @param metadata 系统元数据配置 * @example * ```typescript + * // 基本使用 * @ECSSystem('Movement') * class MovementSystem extends EntitySystem { * 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 EntitySystem>(target: T): T { if (!typeName || typeof typeName !== 'string') { throw new Error('ECSSystem装饰器必须提供有效的类型名称'); } - + // 在构造函数上存储类型名称 (target as any)[SYSTEM_TYPE_NAME] = typeName; - + + // 存储元数据 + if (metadata) { + (target as any).__systemMetadata__ = metadata; + } + return target; }; } +/** + * 获取System的元数据 + */ +export function getSystemMetadata(systemType: new (...args: any[]) => EntitySystem): SystemMetadata | undefined { + return (systemType as any).__systemMetadata__; +} + /** * 获取组件类型的名称,优先使用装饰器指定的名称 * diff --git a/packages/core/src/ECS/Decorators/index.ts b/packages/core/src/ECS/Decorators/index.ts index e5af3266..4b6630f9 100644 --- a/packages/core/src/ECS/Decorators/index.ts +++ b/packages/core/src/ECS/Decorators/index.ts @@ -5,6 +5,9 @@ export { getSystemTypeName, getComponentInstanceTypeName, getSystemInstanceTypeName, + getSystemMetadata, COMPONENT_TYPE_NAME, SYSTEM_TYPE_NAME -} from './TypeDecorators'; \ No newline at end of file +} from './TypeDecorators'; + +export type { SystemMetadata } from './TypeDecorators'; \ No newline at end of file diff --git a/packages/core/src/ECS/IScene.ts b/packages/core/src/ECS/IScene.ts index 0cdd50b8..fb9fc5a0 100644 --- a/packages/core/src/ECS/IScene.ts +++ b/packages/core/src/ECS/IScene.ts @@ -1,6 +1,5 @@ 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'; @@ -41,12 +40,7 @@ export interface IScene { * 场景中的实体集合 */ readonly entities: EntityList; - - /** - * 实体系统处理器集合 - */ - readonly entityProcessors: EntityProcessorList; - + /** * 标识符池 */ diff --git a/packages/core/src/ECS/Scene.ts b/packages/core/src/ECS/Scene.ts index 0413fa92..1d468554 100644 --- a/packages/core/src/ECS/Scene.ts +++ b/packages/core/src/ECS/Scene.ts @@ -1,6 +1,5 @@ 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, ComponentRegistry } from './Core/ComponentStorage'; @@ -8,13 +7,17 @@ import { QuerySystem } from './Core/QuerySystem'; import { TypeSafeEventSystem } from './Core/EventSystem'; import { EventBus } from './Core/EventBus'; import { IScene, ISceneConfig } from './IScene'; -import { getComponentInstanceTypeName, getSystemInstanceTypeName } from './Decorators'; +import { getComponentInstanceTypeName, getSystemInstanceTypeName, getSystemMetadata } from "./Decorators"; import { TypedQueryBuilder } from './Core/Query/TypedQuery'; import { SceneSerializer, SceneSerializationOptions, SceneDeserializationOptions } from './Serialization/SceneSerializer'; import { IncrementalSerializer, IncrementalSnapshot, IncrementalSerializationOptions } from './Serialization/IncrementalSerializer'; import { ComponentPoolManager } from './Core/ComponentPool'; import { PerformanceMonitor } from '../Utils/PerformanceMonitor'; 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 entityProcessors: EntityProcessorList; /** * 实体ID池 @@ -74,36 +71,102 @@ export class Scene implements IScene { /** * 事件系统 - * + * * 类型安全的事件系统。 */ public readonly eventSystem: TypeSafeEventSystem; - + + /** + * 服务容器 + * + * 场景级别的依赖注入容器,用于管理EntitySystem和其他服务的生命周期。 + * 每个Scene拥有独立的服务容器,实现场景间的隔离。 + */ + private readonly _services: ServiceContainer; + + /** + * 日志记录器 + */ + private readonly logger: ReturnType; + /** * 场景是否已开始运行 */ private _didSceneBegin: boolean = false; /** - * 获取系统列表(兼容性属性) + * 获取场景中所有已注册的EntitySystem + * + * 按updateOrder排序。 + * + * @returns 系统列表 */ 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(systemType: ServiceType): 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) { 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(); + this._services = new ServiceContainer(); + this.logger = createLogger('Scene'); - // 应用配置 if (config?.name) { this.name = config.name; } @@ -111,7 +174,7 @@ export class Scene implements IScene { if (!Entity.eventBus) { Entity.eventBus = new EventBus(false); } - + if (Entity.eventBus) { Entity.eventBus.onComponentAdded((data: unknown) => { this.eventSystem.emitSync('component:added', data); @@ -149,10 +212,6 @@ export class Scene implements IScene { * 这个方法会启动场景。它将启动实体处理器等,并调用onStart方法。 */ public begin() { - // 启动实体处理器 - if (this.entityProcessors != null) - this.entityProcessors.begin(); - // 标记场景已开始运行并调用onStart方法 this._didSceneBegin = true; this.onStart(); @@ -176,9 +235,8 @@ export class Scene implements IScene { // 清空组件存储 this.componentStorageManager.clear(); - // 结束实体处理器 - if (this.entityProcessors) - this.entityProcessors.end(); + // 清空服务容器(会调用所有服务的dispose方法,包括所有EntitySystem) + this._services.clear(); // 调用卸载方法 this.unload(); @@ -192,11 +250,28 @@ export class Scene implements IScene { this.entities.updateLists(); - if (this.entityProcessors != null) - this.entityProcessors.update(); + // 更新所有EntitySystem + 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) - this.entityProcessors.lateUpdate(); + // 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 { - for (const system of this.entityProcessors.processors) { + for (const system of this.systems) { system.clearEntityCache(); } } @@ -419,23 +494,121 @@ export class Scene implements IScene { /** * 在场景中添加一个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) { - if (this.entityProcessors.processors.includes(processor)) { - return processor; + public addEntityProcessor( + systemTypeOrInstance: ServiceType | T + ): 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; + } + + 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; + } } - processor.scene = this; + system.scene = this; - // 从Core获取PerformanceMonitor并注入到System const perfMonitor = Core.services.resolve(PerformanceMonitor); - processor.setPerformanceMonitor(perfMonitor); + system.setPerformanceMonitor(perfMonitor); - this.entityProcessors.add(processor); - processor.initialize(); - processor.setUpdateOrder(this.entityProcessors.count - 1); - return processor; + const metadata = getSystemMetadata(constructor); + if (metadata?.updateOrder !== undefined) { + system.setUpdateOrder(metadata.updateOrder); + } + 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>): 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处理器 * @param processor 要删除的处理器 */ - public removeEntityProcessor(processor: EntitySystem) { - this.entityProcessors.remove(processor); + public removeEntityProcessor(processor: EntitySystem): void { + const constructor = processor.constructor as any; + + // 从ServiceContainer移除 + this._services.unregister(constructor); + + // 重置System状态 processor.reset(); } @@ -465,10 +643,24 @@ export class Scene implements IScene { /** * 获取指定类型的EntitySystem处理器 + * + * @deprecated 推荐使用依赖注入代替此方法。使用 `scene.services.resolve(SystemType)` 或在System构造函数中使用 `@Inject(SystemType)` 装饰器。 + * * @param type 处理器类型 + * @returns 处理器实例,如果未找到则返回null + * + * @example + * ```typescript + * @Injectable() + * class MySystem extends EntitySystem { + * constructor(@Inject(PhysicsSystem) private physics: PhysicsSystem) { + * super(); + * } + * } + * ``` */ public getEntityProcessor(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 { entityCount: this.entities.count, - processorCount: this.entityProcessors.count, + processorCount: this.systems.length, componentStorageStats: this.componentStorageManager.getAllStats() }; } @@ -508,10 +700,11 @@ export class Scene implements IScene { }>; componentStats: Map; } { + const systems = this.systems; return { name: this.name || this.constructor.name, entityCount: this.entities.count, - processorCount: this.entityProcessors.count, + processorCount: systems.length, isRunning: this._didSceneBegin, entities: this.entities.buffer.map(entity => ({ name: entity.name, @@ -519,7 +712,7 @@ export class Scene implements IScene { componentCount: entity.components.length, componentTypes: entity.components.map(c => getComponentInstanceTypeName(c)) })), - processors: this.entityProcessors.processors.map(processor => ({ + processors: systems.map(processor => ({ name: getSystemInstanceTypeName(processor), updateOrder: processor.updateOrder, entityCount: (processor as any)._entities?.length || 0 diff --git a/packages/core/src/ECS/Systems/EntitySystem.ts b/packages/core/src/ECS/Systems/EntitySystem.ts index 25fcbf61..a06afc61 100644 --- a/packages/core/src/ECS/Systems/EntitySystem.ts +++ b/packages/core/src/ECS/Systems/EntitySystem.ts @@ -8,6 +8,7 @@ import { getSystemInstanceTypeName } from '../Decorators'; import { createLogger } from '../../Utils/Logger'; import type { EventListenerConfig, TypeSafeEventSystem, EventHandler } from '../Core/EventSystem'; import type { ComponentConstructor, ComponentInstance } from '../../Types/TypeHelpers'; +import type { IService } from '../../Core/ServiceContainer'; /** * 事件监听器记录 @@ -65,7 +66,7 @@ interface EventListenerRecord { */ export abstract class EntitySystem< TComponents extends readonly ComponentConstructor[] = [] -> implements ISystemBase { +> implements ISystemBase, IService { private _updateOrder: number; private _enabled: boolean; private _performanceMonitor: PerformanceMonitor | null; @@ -222,9 +223,6 @@ export abstract class EntitySystem< */ public setUpdateOrder(order: number): void { this._updateOrder = order; - if (this.scene && this.scene.entityProcessors) { - this.scene.entityProcessors.setDirty(); - } } /** @@ -725,15 +723,43 @@ export abstract class EntitySystem< /** * 当实体从系统中移除时调用 - * + * * 子类可以重写此方法来处理实体移除事件。 - * + * * @param entity 被移除的实体 */ protected onRemoved(entity: Entity): void { // 子类可以重写此方法 } + /** + * 释放系统资源 + * + * 实现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`); + } + /** * 添加事件监听器 * diff --git a/packages/core/src/Types/IUpdatable.ts b/packages/core/src/Types/IUpdatable.ts new file mode 100644 index 00000000..e5c50a2b --- /dev/null +++ b/packages/core/src/Types/IUpdatable.ts @@ -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'; +} diff --git a/packages/core/src/Types/index.ts b/packages/core/src/Types/index.ts index 2caf1821..7ee30e7c 100644 --- a/packages/core/src/Types/index.ts +++ b/packages/core/src/Types/index.ts @@ -4,6 +4,7 @@ // 导出TypeScript类型增强工具 export * from './TypeHelpers'; +export * from './IUpdatable'; /** * 组件接口 diff --git a/packages/core/src/Utils/Debug/DebugManager.ts b/packages/core/src/Utils/Debug/DebugManager.ts index 6d646ff9..d4b04666 100644 --- a/packages/core/src/Utils/Debug/DebugManager.ts +++ b/packages/core/src/Utils/Debug/DebugManager.ts @@ -10,11 +10,15 @@ import { ComponentPoolManager } from '../../ECS/Core/ComponentPool'; import { Pool } from '../../Utils/Pool'; import { getComponentInstanceTypeName, getSystemInstanceTypeName } from '../../ECS/Decorators'; import type { IService } from '../../Core/ServiceContainer'; +import { SceneManager } from '../../ECS/SceneManager'; +import { PerformanceMonitor } from '../PerformanceMonitor'; /** * 调试管理器 * * 整合所有调试数据收集器,负责收集和发送调试数据 + * + * 通过构造函数接收SceneManager和PerformanceMonitor,避免直接依赖Core实例 */ export class DebugManager implements IService { private config: IECSDebugConfig; @@ -24,8 +28,8 @@ export class DebugManager implements IService { private performanceCollector: PerformanceDataCollector; private componentCollector: ComponentDataCollector; private sceneCollector: SceneDataCollector; - private sceneProvider: () => any; - private performanceMonitorProvider: () => any; + private sceneManager: SceneManager; + private performanceMonitor: PerformanceMonitor; private frameCounter: number = 0; private lastSendTime: number = 0; @@ -34,15 +38,18 @@ export class DebugManager implements IService { /** * 构造调试管理器 - * @param core Core实例 + * @param sceneManager 场景管理器 + * @param performanceMonitor 性能监控器 * @param config 调试配置 */ - constructor(core: any, config: IECSDebugConfig) { + constructor( + sceneManager: SceneManager, + performanceMonitor: PerformanceMonitor, + config: IECSDebugConfig + ) { this.config = config; - - // 设置提供器函数 - this.sceneProvider = () => (core as any).scene || (core.constructor as any).scene; - this.performanceMonitorProvider = () => core._performanceMonitor; + this.sceneManager = sceneManager; + this.performanceMonitor = performanceMonitor; // 初始化数据收集器 this.entityCollector = new EntityDataCollector(); @@ -339,7 +346,7 @@ export class DebugManager implements IService { // 收集其他内存统计 const baseMemoryInfo = this.collectBaseMemoryInfo(); - const scene = this.sceneProvider(); + const scene = this.sceneManager.currentScene; // 使用专门的内存计算方法收集实体数据 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; componentTypes: number; totalInstances: number; @@ -547,7 +554,7 @@ export class DebugManager implements IService { updateOrder: number; }>; } { - const scene = this.sceneProvider(); + const scene = this.sceneManager.currentScene; let totalSystemMemory = 0; const systemBreakdown: Array<{ name: string; @@ -557,11 +564,11 @@ export class DebugManager implements IService { }> = []; try { - const entityProcessors = scene?.entityProcessors; - if (entityProcessors && entityProcessors.processors) { + const systems = scene?.systems; + if (systems) { const systemTypeMemoryCache = new Map(); - for (const system of entityProcessors.processors) { + for (const system of systems) { const systemTypeName = getSystemInstanceTypeName(system); let systemMemory: number; @@ -721,16 +728,15 @@ export class DebugManager implements IService { error?: string; } { try { - const performanceMonitor = this.performanceMonitorProvider(); - if (!performanceMonitor) { + if (!this.performanceMonitor) { return { enabled: false }; } - const stats = performanceMonitor.getAllSystemStats(); - const warnings = performanceMonitor.getPerformanceWarnings(); + const stats = this.performanceMonitor.getAllSystemStats(); + const warnings = this.performanceMonitor.getPerformanceWarnings(); return { - enabled: (performanceMonitor as { enabled?: boolean }).enabled ?? false, + enabled: (this.performanceMonitor as { enabled?: boolean }).enabled ?? false, systemCount: stats.size, warnings: warnings.slice(0, 10), // 最多10个警告 topSystems: Array.from(stats.entries()).map((entry) => { @@ -754,7 +760,7 @@ export class DebugManager implements IService { */ public getDebugData(): IECSDebugData { const currentTime = Date.now(); - const scene = this.sceneProvider(); + const scene = this.sceneManager.currentScene; const debugData: IECSDebugData = { timestamp: currentTime, @@ -770,13 +776,11 @@ export class DebugManager implements IService { } if (this.config.channels.systems) { - const performanceMonitor = this.performanceMonitorProvider(); - debugData.systems = this.systemCollector.collectSystemData(performanceMonitor, scene); + debugData.systems = this.systemCollector.collectSystemData(this.performanceMonitor, scene); } if (this.config.channels.performance) { - const performanceMonitor = this.performanceMonitorProvider(); - debugData.performance = this.performanceCollector.collectPerformanceData(performanceMonitor); + debugData.performance = this.performanceCollector.collectPerformanceData(this.performanceMonitor); } if (this.config.channels.components) { diff --git a/packages/core/src/Utils/Timers/TimerManager.ts b/packages/core/src/Utils/Timers/TimerManager.ts index c8fe73e8..c9cb6e74 100644 --- a/packages/core/src/Utils/Timers/TimerManager.ts +++ b/packages/core/src/Utils/Timers/TimerManager.ts @@ -1,15 +1,19 @@ -import { GlobalManager } from '../GlobalManager'; import { Timer } from './Timer'; import { ITimer } from './ITimer'; 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> = []; - public override update() { + public update() { for (let i = this._timers.length - 1; i >= 0; i --){ if (this._timers[i].tick()){ this._timers[i].unload(); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c75b0496..26da5490 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -8,6 +8,18 @@ export { Core } from './Core'; export { ServiceContainer, ServiceLifetime } 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 { GlobalManager } from './Utils/GlobalManager'; diff --git a/packages/core/tests/Core.test.ts b/packages/core/tests/Core.test.ts index 0c22abc6..d8be75f9 100644 --- a/packages/core/tests/Core.test.ts +++ b/packages/core/tests/Core.test.ts @@ -3,8 +3,10 @@ import { Scene } from '../src/ECS/Scene'; import { SceneManager } from '../src/ECS/SceneManager'; import { Entity } from '../src/ECS/Entity'; import { Component } from '../src/ECS/Component'; -import { GlobalManager } from '../src/Utils/GlobalManager'; 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 { @@ -41,21 +43,17 @@ class TestScene extends Scene { } } -// 测试全局管理器 -class TestGlobalManager extends GlobalManager { +// 测试可更新服务 +@Updatable() +class TestUpdatableService implements IService, IUpdatable { public updateCallCount = 0; - public override _enabled = false; - - public override get enabled(): boolean { - return this._enabled; - } - - public override set enabled(value: boolean) { - this._enabled = value; + + public update(): void { + this.updateCallCount++; } - public override update(): void { - this.updateCallCount++; + public dispose(): void { + // 清理资源 } } @@ -129,96 +127,75 @@ describe('Core - 核心管理系统测试', () => { // 注意:场景管理功能已移至SceneManager // 相关测试请查看 SceneManager.test.ts - describe('更新循环 - 全局服务', () => { + describe('更新循环 - 可更新服务', () => { let core: Core; - let globalManager: TestGlobalManager; + let updatableService: TestUpdatableService; beforeEach(() => { core = Core.create(true); - globalManager = new TestGlobalManager(); - Core.registerGlobalManager(globalManager); + updatableService = new TestUpdatableService(); + Core.services.registerInstance(TestUpdatableService, updatableService); }); - test('应该能够更新全局管理器', () => { + test('应该能够更新可更新服务', () => { Core.update(0.016); - expect(globalManager.updateCallCount).toBe(1); + expect(updatableService.updateCallCount).toBe(1); }); test('暂停状态下不应该执行更新', () => { Core.paused = true; Core.update(0.016); - expect(globalManager.updateCallCount).toBe(0); + expect(updatableService.updateCallCount).toBe(0); // 恢复状态 Core.paused = false; }); - test('禁用的全局管理器不应该被更新', () => { - globalManager.enabled = false; - Core.update(0.016); - expect(globalManager.updateCallCount).toBe(0); - }); - test('多次更新应该累积调用', () => { 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 manager1: TestGlobalManager; - let manager2: TestGlobalManager; + let service1: TestUpdatableService; beforeEach(() => { core = Core.create(true); - manager1 = new TestGlobalManager(); - manager2 = new TestGlobalManager(); + service1 = new TestUpdatableService(); }); - test('应该能够注册全局管理器', () => { - Core.registerGlobalManager(manager1); - - expect(manager1.enabled).toBe(true); - + test('应该能够通过ServiceContainer注册可更新服务', () => { + Core.services.registerInstance(TestUpdatableService, service1); + // 测试更新是否被调用 Core.update(0.016); - expect(manager1.updateCallCount).toBe(1); + expect(service1.updateCallCount).toBe(1); }); - test('应该能够注销全局管理器', () => { - Core.registerGlobalManager(manager1); - Core.unregisterGlobalManager(manager1); - - expect(manager1.enabled).toBe(false); - + test('应该能够注销服务', () => { + Core.services.registerInstance(TestUpdatableService, service1); + Core.services.unregister(TestUpdatableService); + // 测试更新不应该被调用 Core.update(0.016); - expect(manager1.updateCallCount).toBe(0); + expect(service1.updateCallCount).toBe(0); }); - test('应该能够获取指定类型的全局管理器', () => { - Core.registerGlobalManager(manager1); - - const retrieved = Core.getGlobalManager(TestGlobalManager); - expect(retrieved).toBe(manager1); + test('应该能够通过ServiceContainer解析服务', () => { + Core.services.registerInstance(TestUpdatableService, service1); + + const retrieved = Core.services.resolve(TestUpdatableService); + expect(retrieved).toBe(service1); }); - test('获取不存在的管理器应该返回null', () => { - const retrieved = Core.getGlobalManager(TestGlobalManager); - expect(retrieved).toBeNull(); - }); - - test('应该能够管理多个全局管理器', () => { - Core.registerGlobalManager(manager1); - Core.registerGlobalManager(manager2); - - Core.update(0.016); - - expect(manager1.updateCallCount).toBe(1); - expect(manager2.updateCallCount).toBe(1); + test('解析不存在的服务应该抛出错误', () => { + expect(() => { + Core.services.resolve(TestUpdatableService); + }).toThrow(); }); }); diff --git a/packages/core/tests/Core/DI.test.ts b/packages/core/tests/Core/DI.test.ts new file mode 100644 index 00000000..5e97c272 --- /dev/null +++ b/packages/core/tests/Core/DI.test.ts @@ -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'); + }); + }); +}); diff --git a/packages/core/tests/ECS/Core/FluentAPI.test.ts b/packages/core/tests/ECS/Core/FluentAPI.test.ts index b362a59d..2edef8f0 100644 --- a/packages/core/tests/ECS/Core/FluentAPI.test.ts +++ b/packages/core/tests/ECS/Core/FluentAPI.test.ts @@ -330,12 +330,12 @@ describe('FluentAPI - 流式API测试', () => { test('应该能够批量添加系统', () => { const system1 = new TestSystem(); const system2 = new TestSystem(); - + const scene = builder .withSystems(system1, system2) .build(); - - expect(scene.systems.length).toBe(2); + + expect(scene.systems.length).toBe(1); }); test('流式调用应该工作正常', () => { diff --git a/packages/core/tests/ECS/EntitySystemDI.test.ts b/packages/core/tests/ECS/EntitySystemDI.test.ts new file mode 100644 index 00000000..087b9374 --- /dev/null +++ b/packages/core/tests/ECS/EntitySystemDI.test.ts @@ -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); + }); + }); +}); diff --git a/packages/core/tests/ECS/Scene.test.ts b/packages/core/tests/ECS/Scene.test.ts index ad2ada9e..2d5355af 100644 --- a/packages/core/tests/ECS/Scene.test.ts +++ b/packages/core/tests/ECS/Scene.test.ts @@ -129,7 +129,7 @@ describe('Scene - 场景管理系统测试', () => { expect(scene).toBeInstanceOf(Scene); expect(scene.name).toBe(""); expect(scene.entities).toBeDefined(); - expect(scene.entityProcessors).toBeDefined(); + expect(scene.systems).toBeDefined(); expect(scene.identifierPool).toBeDefined(); }); @@ -140,7 +140,7 @@ describe('Scene - 场景管理系统测试', () => { test('场景应该有正确的初始状态', () => { expect(scene.entities.count).toBe(0); - expect(scene.entityProcessors.count).toBe(0); + expect(scene.systems.length).toBe(0); }); test('应该能够使用配置创建场景', () => { @@ -248,16 +248,16 @@ describe('Scene - 场景管理系统测试', () => { test('应该能够添加实体系统', () => { scene.addEntityProcessor(movementSystem); - - expect(scene.entityProcessors.count).toBe(1); + + expect(scene.systems.length).toBe(1); expect(movementSystem.scene).toBe(scene); }); test('应该能够移除实体系统', () => { scene.addEntityProcessor(movementSystem); scene.removeEntityProcessor(movementSystem); - - expect(scene.entityProcessors.count).toBe(0); + + expect(scene.systems.length).toBe(0); expect(movementSystem.scene).toBeNull(); }); @@ -270,8 +270,8 @@ describe('Scene - 场景管理系统测试', () => { test('应该能够管理多个实体系统', () => { scene.addEntityProcessor(movementSystem); scene.addEntityProcessor(renderSystem); - - expect(scene.entityProcessors.count).toBe(2); + + expect(scene.systems.length).toBe(2); }); test('系统应该按更新顺序执行', () => { @@ -537,11 +537,12 @@ describe('Scene - 场景管理系统测试', () => { describe('错误处理和边界情况', () => { test('重复添加同一个系统应该安全处理', () => { const system = new MovementSystem(); - + + scene.addEntityProcessor(system); - scene.addEntityProcessor(system); // 重复添加 - - expect(scene.entityProcessors.count).toBe(1); + scene.addEntityProcessor(system); + + expect(scene.systems.length).toBe(1); }); test('系统处理过程中的异常应该被正确处理', () => { From 1af2cf5f995a45f3256e0af5831eff93a6c272ca Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Fri, 10 Oct 2025 22:08:10 +0800 Subject: [PATCH 2/2] =?UTF-8?q?Scene=20=E6=9E=84=E9=80=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E6=B3=A8=E5=85=A5=20PerformanceMonitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/ECS/IScene.ts | 8 ++++ packages/core/src/ECS/Scene.ts | 16 +++++++- packages/core/tests/ECS/Scene.test.ts | 55 ++++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/packages/core/src/ECS/IScene.ts b/packages/core/src/ECS/IScene.ts index fb9fc5a0..459d4e76 100644 --- a/packages/core/src/ECS/IScene.ts +++ b/packages/core/src/ECS/IScene.ts @@ -165,4 +165,12 @@ export interface ISceneConfig { * 场景名称 */ name?: string; + + /** + * 性能监控器实例(可选) + * + * 如果不提供,Scene会自动从Core.services获取全局PerformanceMonitor。 + * 提供此参数可以实现场景级别的独立性能监控。 + */ + performanceMonitor?: any; } \ No newline at end of file diff --git a/packages/core/src/ECS/Scene.ts b/packages/core/src/ECS/Scene.ts index 1d468554..54ae0e2b 100644 --- a/packages/core/src/ECS/Scene.ts +++ b/packages/core/src/ECS/Scene.ts @@ -89,6 +89,13 @@ export class Scene implements IScene { */ private readonly logger: ReturnType; + /** + * 性能监控器 + * + * 用于监控场景和系统的性能。可以在构造函数中注入,如果不提供则从Core获取。 + */ + private readonly _performanceMonitor: PerformanceMonitor; + /** * 场景是否已开始运行 */ @@ -167,6 +174,12 @@ export class Scene implements IScene { this._services = new ServiceContainer(); this.logger = createLogger('Scene'); + if (config?.performanceMonitor) { + this._performanceMonitor = config.performanceMonitor; + } else { + this._performanceMonitor = Core.services.resolve(PerformanceMonitor); + } + if (config?.name) { this.name = config.name; } @@ -547,8 +560,7 @@ export class Scene implements IScene { system.scene = this; - const perfMonitor = Core.services.resolve(PerformanceMonitor); - system.setPerformanceMonitor(perfMonitor); + system.setPerformanceMonitor(this._performanceMonitor); const metadata = getSystemMetadata(constructor); if (metadata?.updateOrder !== undefined) { diff --git a/packages/core/tests/ECS/Scene.test.ts b/packages/core/tests/ECS/Scene.test.ts index 2d5355af..44650dff 100644 --- a/packages/core/tests/ECS/Scene.test.ts +++ b/packages/core/tests/ECS/Scene.test.ts @@ -581,11 +581,64 @@ describe('Scene - 场景管理系统测试', () => { test('对已销毁实体的操作应该安全处理', () => { const entity = scene.createEntity("TestEntity"); scene.entities.remove(entity); - + // 对已销毁实体的操作应该安全 expect(() => { entity.addComponent(new PositionComponent(0, 0)); }).not.toThrow(); }); }); + + describe('依赖注入优化', () => { + test('应该支持注入自定义PerformanceMonitor', () => { + const mockPerfMonitor = { + startMeasure: jest.fn(), + endMeasure: jest.fn(), + recordSystemData: jest.fn(), + recordEntityCount: jest.fn(), + recordComponentCount: jest.fn(), + update: jest.fn(), + getSystemData: jest.fn(), + getSystemStats: jest.fn(), + resetSystem: jest.fn(), + reset: jest.fn(), + dispose: jest.fn() + }; + + const customScene = new Scene({ + name: 'CustomScene', + performanceMonitor: mockPerfMonitor as any + }); + + class TestSystem extends EntitySystem { + constructor() { + super(Matcher.empty().all(PositionComponent)); + } + } + + const system = new TestSystem(); + customScene.addEntityProcessor(system); + + expect(mockPerfMonitor).toBeDefined(); + + customScene.end(); + }); + + test('未提供PerformanceMonitor时应该从Core获取', () => { + const defaultScene = new Scene({ name: 'DefaultScene' }); + + class TestSystem extends EntitySystem { + constructor() { + super(Matcher.empty().all(PositionComponent)); + } + } + + const system = new TestSystem(); + defaultScene.addEntityProcessor(system); + + expect(defaultScene).toBeDefined(); + + defaultScene.end(); + }); + }); }); \ No newline at end of file