依赖注入引入DI容器
This commit is contained in:
@@ -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<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 onTime - 定时器触发时的回调函数
|
||||
* @returns 创建的定时器实例
|
||||
* @throws 如果Core实例未创建或onTime回调未提供
|
||||
*
|
||||
* @example
|
||||
* ```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> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 { isUpdatable as checkUpdatable, getUpdatableMetadata } from './DI';
|
||||
|
||||
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();
|
||||
|
||||
/**
|
||||
* 可更新的服务列表
|
||||
*
|
||||
* 自动收集所有使用@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<IService>[] {
|
||||
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,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 <T extends new (...args: any[]) => 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__;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件类型的名称,优先使用装饰器指定的名称
|
||||
*
|
||||
|
||||
@@ -5,6 +5,9 @@ export {
|
||||
getSystemTypeName,
|
||||
getComponentInstanceTypeName,
|
||||
getSystemInstanceTypeName,
|
||||
getSystemMetadata,
|
||||
COMPONENT_TYPE_NAME,
|
||||
SYSTEM_TYPE_NAME
|
||||
} from './TypeDecorators';
|
||||
} from './TypeDecorators';
|
||||
|
||||
export type { SystemMetadata } from './TypeDecorators';
|
||||
@@ -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;
|
||||
|
||||
|
||||
/**
|
||||
* 标识符池
|
||||
*/
|
||||
|
||||
@@ -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<typeof createLogger>;
|
||||
|
||||
/**
|
||||
* 场景是否已开始运行
|
||||
*/
|
||||
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<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) {
|
||||
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<T extends EntitySystem>(
|
||||
systemTypeOrInstance: ServiceType<T> | 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<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处理器
|
||||
* @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<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 {
|
||||
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<string, any>;
|
||||
} {
|
||||
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
|
||||
|
||||
@@ -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`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加事件监听器
|
||||
*
|
||||
|
||||
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类型增强工具
|
||||
export * from './TypeHelpers';
|
||||
export * from './IUpdatable';
|
||||
|
||||
/**
|
||||
* 组件接口
|
||||
|
||||
@@ -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<string, number>();
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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<Timer<unknown>> = [];
|
||||
|
||||
public override update() {
|
||||
public update() {
|
||||
for (let i = this._timers.length - 1; i >= 0; i --){
|
||||
if (this._timers[i].tick()){
|
||||
this._timers[i].unload();
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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('流式调用应该工作正常', () => {
|
||||
|
||||
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.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('系统处理过程中的异常应该被正确处理', () => {
|
||||
|
||||
Reference in New Issue
Block a user