2025-06-09 14:51:26 +08:00
|
|
|
|
import { Entity } from './Entity';
|
|
|
|
|
|
import { EntityList } from './Utils/EntityList';
|
|
|
|
|
|
import { IdentifierPool } from './Utils/IdentifierPool';
|
|
|
|
|
|
import { EntitySystem } from './Systems/EntitySystem';
|
2025-11-01 17:18:12 +08:00
|
|
|
|
import { ComponentStorageManager, ComponentRegistry, ComponentType } from './Core/ComponentStorage';
|
2025-06-09 14:51:26 +08:00
|
|
|
|
import { QuerySystem } from './Core/QuerySystem';
|
2025-08-06 09:39:08 +08:00
|
|
|
|
import { TypeSafeEventSystem } from './Core/EventSystem';
|
2025-10-10 23:38:48 +08:00
|
|
|
|
import { ReferenceTracker } from './Core/ReferenceTracker';
|
2025-08-12 09:39:07 +08:00
|
|
|
|
import { IScene, ISceneConfig } from './IScene';
|
2025-11-01 10:23:46 +08:00
|
|
|
|
import { getComponentInstanceTypeName, getSystemInstanceTypeName, getSystemMetadata } from './Decorators';
|
2025-10-08 13:13:23 +08:00
|
|
|
|
import { TypedQueryBuilder } from './Core/Query/TypedQuery';
|
2025-11-01 10:23:46 +08:00
|
|
|
|
import {
|
|
|
|
|
|
SceneSerializer,
|
|
|
|
|
|
SceneSerializationOptions,
|
|
|
|
|
|
SceneDeserializationOptions
|
|
|
|
|
|
} from './Serialization/SceneSerializer';
|
|
|
|
|
|
import {
|
|
|
|
|
|
IncrementalSerializer,
|
|
|
|
|
|
IncrementalSnapshot,
|
|
|
|
|
|
IncrementalSerializationOptions
|
|
|
|
|
|
} from './Serialization/IncrementalSerializer';
|
2025-10-10 10:16:44 +08:00
|
|
|
|
import { ComponentPoolManager } from './Core/ComponentPool';
|
2025-10-10 18:13:28 +08:00
|
|
|
|
import { PerformanceMonitor } from '../Utils/PerformanceMonitor';
|
2025-11-01 17:18:12 +08:00
|
|
|
|
import { ServiceContainer, type ServiceType, type IService } from '../Core/ServiceContainer';
|
2025-10-12 23:39:32 +08:00
|
|
|
|
import { createInstance, isInjectable, injectProperties } from '../Core/DI';
|
2025-10-10 21:52:43 +08:00
|
|
|
|
import { createLogger } from '../Utils/Logger';
|
2025-06-09 14:51:26 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2025-08-12 09:39:07 +08:00
|
|
|
|
* 游戏场景默认实现类
|
2025-10-17 22:13:32 +08:00
|
|
|
|
*
|
2025-08-12 09:39:07 +08:00
|
|
|
|
* 实现IScene接口,提供场景的基础功能。
|
|
|
|
|
|
* 推荐使用组合而非继承的方式来构建自定义场景。
|
2025-06-09 14:51:26 +08:00
|
|
|
|
*/
|
2025-08-12 09:39:07 +08:00
|
|
|
|
export class Scene implements IScene {
|
2025-06-09 14:51:26 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 场景名称
|
2025-10-08 18:34:15 +08:00
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* 用于标识和调试的友好名称。
|
|
|
|
|
|
*/
|
2025-11-01 10:23:46 +08:00
|
|
|
|
public name: string = '';
|
2025-06-09 14:51:26 +08:00
|
|
|
|
|
2025-10-08 18:34:15 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 场景自定义数据
|
|
|
|
|
|
*
|
|
|
|
|
|
* 用于存储场景级别的配置和状态数据。
|
|
|
|
|
|
*/
|
2025-11-01 17:18:12 +08:00
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2025-10-08 18:34:15 +08:00
|
|
|
|
public readonly sceneData: Map<string, any> = new Map();
|
|
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 场景中的实体集合
|
2025-10-08 18:34:15 +08:00
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* 管理场景内所有实体的生命周期。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public readonly entities: EntityList;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 实体ID池
|
2025-11-01 10:23:46 +08:00
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* 用于分配和回收实体的唯一标识符。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public readonly identifierPool: IdentifierPool;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 组件存储管理器
|
2025-11-01 10:23:46 +08:00
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* 高性能的组件存储和查询系统。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public readonly componentStorageManager: ComponentStorageManager;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查询系统
|
2025-11-01 10:23:46 +08:00
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* 基于位掩码的高性能实体查询系统。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public readonly querySystem: QuerySystem;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 事件系统
|
2025-10-10 21:52:43 +08:00
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* 类型安全的事件系统。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public readonly eventSystem: TypeSafeEventSystem;
|
2025-10-10 21:52:43 +08:00
|
|
|
|
|
2025-10-10 23:38:48 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 引用追踪器
|
|
|
|
|
|
*
|
|
|
|
|
|
* 追踪Component中对Entity的引用,当Entity销毁时自动清理引用。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public readonly referenceTracker: ReferenceTracker;
|
|
|
|
|
|
|
2025-10-10 21:52:43 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 服务容器
|
|
|
|
|
|
*
|
|
|
|
|
|
* 场景级别的依赖注入容器,用于管理EntitySystem和其他服务的生命周期。
|
|
|
|
|
|
* 每个Scene拥有独立的服务容器,实现场景间的隔离。
|
|
|
|
|
|
*/
|
|
|
|
|
|
private readonly _services: ServiceContainer;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 日志记录器
|
|
|
|
|
|
*/
|
|
|
|
|
|
private readonly logger: ReturnType<typeof createLogger>;
|
|
|
|
|
|
|
2025-10-10 22:08:10 +08:00
|
|
|
|
/**
|
2025-10-17 22:13:32 +08:00
|
|
|
|
* 性能监控器缓存
|
2025-10-10 22:08:10 +08:00
|
|
|
|
*
|
2025-10-17 22:13:32 +08:00
|
|
|
|
* 用于监控场景和系统的性能。从 ServiceContainer 获取。
|
2025-10-10 22:08:10 +08:00
|
|
|
|
*/
|
2025-10-17 22:13:32 +08:00
|
|
|
|
private _performanceMonitor: PerformanceMonitor | null = null;
|
2025-10-10 22:08:10 +08:00
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 场景是否已开始运行
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _didSceneBegin: boolean = false;
|
|
|
|
|
|
|
2025-10-30 23:27:37 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 系统列表缓存
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _cachedSystems: EntitySystem[] | null = null;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 系统顺序脏标记
|
|
|
|
|
|
*
|
|
|
|
|
|
* 当系统增删或 updateOrder 改变时标记为 true,下次访问 systems 时重新构建缓存
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _systemsOrderDirty: boolean = true;
|
|
|
|
|
|
|
2025-11-01 17:18:12 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 系统错误计数器
|
|
|
|
|
|
*
|
|
|
|
|
|
* 跟踪每个系统的错误次数,用于自动禁用频繁出错的系统
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _systemErrorCount: Map<EntitySystem, number> = new Map();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 最大允许错误次数
|
|
|
|
|
|
*
|
|
|
|
|
|
* 系统错误次数超过此阈值后将被自动禁用
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _maxErrorCount: number = 10;
|
|
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
/**
|
2025-10-10 21:52:43 +08:00
|
|
|
|
* 获取场景中所有已注册的EntitySystem
|
|
|
|
|
|
*
|
2025-10-30 23:27:37 +08:00
|
|
|
|
* 按updateOrder排序。使用缓存机制,仅在系统变化时重新排序。
|
2025-10-10 21:52:43 +08:00
|
|
|
|
*
|
|
|
|
|
|
* @returns 系统列表
|
2025-06-09 14:51:26 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public get systems(): EntitySystem[] {
|
2025-10-30 23:27:37 +08:00
|
|
|
|
if (!this._systemsOrderDirty && this._cachedSystems) {
|
|
|
|
|
|
return this._cachedSystems;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-01 17:18:12 +08:00
|
|
|
|
this._cachedSystems = this._rebuildSystemsCache();
|
|
|
|
|
|
this._systemsOrderDirty = false;
|
2025-10-10 21:52:43 +08:00
|
|
|
|
|
2025-11-01 17:18:12 +08:00
|
|
|
|
return this._cachedSystems;
|
|
|
|
|
|
}
|
2025-10-10 21:52:43 +08:00
|
|
|
|
|
2025-11-01 17:18:12 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 重新构建系统缓存
|
|
|
|
|
|
*
|
|
|
|
|
|
* 从服务容器中提取所有EntitySystem并排序
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _rebuildSystemsCache(): EntitySystem[] {
|
|
|
|
|
|
const allServices = this._services.getAll();
|
|
|
|
|
|
const systems = this._filterEntitySystems(allServices);
|
|
|
|
|
|
return this._sortSystemsByUpdateOrder(systems);
|
|
|
|
|
|
}
|
2025-10-10 21:52:43 +08:00
|
|
|
|
|
2025-11-01 17:18:12 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 从服务列表中过滤出EntitySystem实例
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _filterEntitySystems(services: IService[]): EntitySystem[] {
|
|
|
|
|
|
return services.filter((service): service is EntitySystem => service instanceof EntitySystem);
|
|
|
|
|
|
}
|
2025-10-30 23:27:37 +08:00
|
|
|
|
|
2025-11-01 17:18:12 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 按updateOrder排序系统
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _sortSystemsByUpdateOrder(systems: EntitySystem[]): EntitySystem[] {
|
|
|
|
|
|
return systems.sort((a, b) => a.updateOrder - b.updateOrder);
|
2025-06-09 14:51:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-10 21:52:43 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 通过类型获取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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-30 23:27:37 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 标记系统顺序为脏
|
|
|
|
|
|
*
|
|
|
|
|
|
* 当系统列表或顺序发生变化时调用,使缓存失效
|
|
|
|
|
|
*/
|
|
|
|
|
|
public markSystemsOrderDirty(): void {
|
|
|
|
|
|
this._systemsOrderDirty = true;
|
|
|
|
|
|
}
|
2025-10-10 21:52:43 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取场景的服务容器
|
|
|
|
|
|
*
|
|
|
|
|
|
* 用于注册和解析场景级别的服务(如EntitySystem)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
|
|
|
|
|
* // 注册服务
|
|
|
|
|
|
* scene.services.registerSingleton(PhysicsSystem);
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 解析服务
|
|
|
|
|
|
* const physics = scene.services.resolve(PhysicsSystem);
|
|
|
|
|
|
* ```
|
|
|
|
|
|
*/
|
|
|
|
|
|
public get services(): ServiceContainer {
|
|
|
|
|
|
return this._services;
|
|
|
|
|
|
}
|
2025-08-12 09:39:07 +08:00
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 创建场景实例
|
|
|
|
|
|
*/
|
2025-08-12 09:39:07 +08:00
|
|
|
|
constructor(config?: ISceneConfig) {
|
2025-06-09 14:51:26 +08:00
|
|
|
|
this.entities = new EntityList(this);
|
|
|
|
|
|
this.identifierPool = new IdentifierPool();
|
|
|
|
|
|
this.componentStorageManager = new ComponentStorageManager();
|
|
|
|
|
|
this.querySystem = new QuerySystem();
|
|
|
|
|
|
this.eventSystem = new TypeSafeEventSystem();
|
2025-10-10 23:38:48 +08:00
|
|
|
|
this.referenceTracker = new ReferenceTracker();
|
2025-10-10 21:52:43 +08:00
|
|
|
|
this._services = new ServiceContainer();
|
|
|
|
|
|
this.logger = createLogger('Scene');
|
2025-06-09 14:51:26 +08:00
|
|
|
|
|
2025-08-12 09:39:07 +08:00
|
|
|
|
if (config?.name) {
|
|
|
|
|
|
this.name = config.name;
|
|
|
|
|
|
}
|
2025-06-09 14:51:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-17 22:13:32 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取性能监控器
|
|
|
|
|
|
*
|
|
|
|
|
|
* 从 ServiceContainer 获取,如果未注册则创建默认实例(向后兼容)
|
|
|
|
|
|
*/
|
2025-11-09 11:32:04 +08:00
|
|
|
|
public get performanceMonitor(): PerformanceMonitor {
|
2025-10-17 22:13:32 +08:00
|
|
|
|
if (!this._performanceMonitor) {
|
2025-11-01 10:23:46 +08:00
|
|
|
|
this._performanceMonitor = this._services.tryResolve(PerformanceMonitor) ?? new PerformanceMonitor();
|
2025-10-17 22:13:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
return this._performanceMonitor;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 初始化场景
|
2025-11-01 10:23:46 +08:00
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* 在场景创建时调用,子类可以重写此方法来设置初始实体和组件。
|
|
|
|
|
|
*/
|
2025-11-01 10:23:46 +08:00
|
|
|
|
public initialize(): void {}
|
2025-06-09 14:51:26 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 场景开始运行时的回调
|
2025-11-01 10:23:46 +08:00
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* 在场景开始运行时调用,可以在此方法中执行场景启动逻辑。
|
|
|
|
|
|
*/
|
2025-11-01 10:23:46 +08:00
|
|
|
|
public onStart(): void {}
|
2025-06-09 14:51:26 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 场景卸载时的回调
|
2025-11-01 10:23:46 +08:00
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* 在场景被销毁时调用,可以在此方法中执行清理工作。
|
|
|
|
|
|
*/
|
2025-11-01 10:23:46 +08:00
|
|
|
|
public unload(): void {}
|
2025-06-09 14:51:26 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 开始场景,启动实体处理器等
|
|
|
|
|
|
*
|
|
|
|
|
|
* 这个方法会启动场景。它将启动实体处理器等,并调用onStart方法。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public begin() {
|
|
|
|
|
|
// 标记场景已开始运行并调用onStart方法
|
|
|
|
|
|
this._didSceneBegin = true;
|
|
|
|
|
|
this.onStart();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 结束场景,清除实体、实体处理器等
|
|
|
|
|
|
*
|
|
|
|
|
|
* 这个方法会结束场景。它将移除所有实体,结束实体处理器等,并调用unload方法。
|
2025-11-14 12:10:59 +08:00
|
|
|
|
*
|
|
|
|
|
|
* 执行顺序:
|
|
|
|
|
|
* 1. 调用 unload() - 用户可以在此访问实体和系统进行清理
|
|
|
|
|
|
* 2. 清理所有实体
|
|
|
|
|
|
* 3. 清空服务容器,触发所有系统的 onDestroy()
|
|
|
|
|
|
*
|
|
|
|
|
|
* 注意:
|
|
|
|
|
|
* - onRemoved 回调不会在 Scene.end() 时触发,因为这是批量销毁场景
|
|
|
|
|
|
* - 用户清理:在 Scene.unload() 中处理(可访问实体和系统)
|
|
|
|
|
|
* - 系统清理:在 System.onDestroy() 中处理(实体已被清理)
|
2025-06-09 14:51:26 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public end() {
|
|
|
|
|
|
// 标记场景已结束运行
|
|
|
|
|
|
this._didSceneBegin = false;
|
|
|
|
|
|
|
2025-11-14 12:10:59 +08:00
|
|
|
|
// 先调用用户的卸载方法,此时用户可以访问实体和系统进行清理
|
|
|
|
|
|
this.unload();
|
|
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
// 移除所有实体
|
|
|
|
|
|
this.entities.removeAllEntities();
|
|
|
|
|
|
|
2025-08-06 09:39:08 +08:00
|
|
|
|
// 清理查询系统中的实体引用和缓存
|
|
|
|
|
|
this.querySystem.setEntities([]);
|
|
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
// 清空组件存储
|
|
|
|
|
|
this.componentStorageManager.clear();
|
|
|
|
|
|
|
2025-10-10 21:52:43 +08:00
|
|
|
|
// 清空服务容器(会调用所有服务的dispose方法,包括所有EntitySystem)
|
2025-11-14 12:10:59 +08:00
|
|
|
|
// 系统的 onDestroy 回调会在这里被触发
|
2025-10-10 21:52:43 +08:00
|
|
|
|
this._services.clear();
|
2025-06-09 14:51:26 +08:00
|
|
|
|
|
2025-10-30 23:27:37 +08:00
|
|
|
|
// 清空系统缓存
|
|
|
|
|
|
this._cachedSystems = null;
|
|
|
|
|
|
this._systemsOrderDirty = true;
|
2025-06-09 14:51:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-26 09:38:51 +08:00
|
|
|
|
* 更新场景
|
2025-06-09 14:51:26 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public update() {
|
2025-10-10 10:16:44 +08:00
|
|
|
|
ComponentPoolManager.getInstance().update();
|
|
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
this.entities.updateLists();
|
|
|
|
|
|
|
2025-10-10 21:52:43 +08:00
|
|
|
|
const systems = this.systems;
|
2025-11-01 17:18:12 +08:00
|
|
|
|
|
2025-10-10 21:52:43 +08:00
|
|
|
|
for (const system of systems) {
|
|
|
|
|
|
if (system.enabled) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
system.update();
|
|
|
|
|
|
} catch (error) {
|
2025-11-01 17:18:12 +08:00
|
|
|
|
this._handleSystemError(system, 'update', error);
|
2025-10-10 21:52:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-09 14:51:26 +08:00
|
|
|
|
|
2025-10-10 21:52:43 +08:00
|
|
|
|
for (const system of systems) {
|
|
|
|
|
|
if (system.enabled) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
system.lateUpdate();
|
|
|
|
|
|
} catch (error) {
|
2025-11-01 17:18:12 +08:00
|
|
|
|
this._handleSystemError(system, 'lateUpdate', error);
|
2025-10-10 21:52:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-09 14:51:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-01 17:18:12 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 处理系统执行错误
|
|
|
|
|
|
*
|
|
|
|
|
|
* 记录错误信息并跟踪错误次数。当系统错误次数超过阈值时自动禁用该系统。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param system 出错的系统
|
|
|
|
|
|
* @param phase 错误发生的阶段(update 或 lateUpdate)
|
|
|
|
|
|
* @param error 错误对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _handleSystemError(system: EntitySystem, phase: 'update' | 'lateUpdate', error: unknown): void {
|
|
|
|
|
|
const errorCount = (this._systemErrorCount.get(system) || 0) + 1;
|
|
|
|
|
|
this._systemErrorCount.set(system, errorCount);
|
|
|
|
|
|
|
|
|
|
|
|
this.logger.error(
|
|
|
|
|
|
`Error in system ${system.constructor.name}.${phase}() [${errorCount}/${this._maxErrorCount}]:`,
|
|
|
|
|
|
error
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (errorCount >= this._maxErrorCount) {
|
|
|
|
|
|
system.enabled = false;
|
|
|
|
|
|
this.logger.error(
|
|
|
|
|
|
`System ${system.constructor.name} has been disabled due to excessive errors (${errorCount} errors)`
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 将实体添加到此场景,并返回它
|
|
|
|
|
|
* @param name 实体名称
|
|
|
|
|
|
*/
|
|
|
|
|
|
public createEntity(name: string) {
|
2025-11-01 10:23:46 +08:00
|
|
|
|
const entity = new Entity(name, this.identifierPool.checkOut());
|
|
|
|
|
|
|
2025-07-28 17:14:10 +08:00
|
|
|
|
this.eventSystem.emitSync('entity:created', { entityName: name, entity, scene: this });
|
2025-11-01 10:23:46 +08:00
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
return this.addEntity(entity);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-26 10:50:31 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 清除所有EntitySystem的实体缓存
|
|
|
|
|
|
* 当实体或组件发生变化时调用
|
|
|
|
|
|
*/
|
|
|
|
|
|
public clearSystemEntityCaches(): void {
|
2025-10-10 21:52:43 +08:00
|
|
|
|
for (const system of this.systems) {
|
2025-09-26 10:50:31 +08:00
|
|
|
|
system.clearEntityCache();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 在场景的实体列表中添加一个实体
|
|
|
|
|
|
* @param entity 要添加的实体
|
|
|
|
|
|
* @param deferCacheClear 是否延迟缓存清理(用于批量操作)
|
|
|
|
|
|
*/
|
|
|
|
|
|
public addEntity(entity: Entity, deferCacheClear: boolean = false) {
|
|
|
|
|
|
this.entities.add(entity);
|
|
|
|
|
|
entity.scene = this;
|
2025-09-26 10:50:31 +08:00
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
// 将实体添加到查询系统(可延迟缓存清理)
|
|
|
|
|
|
this.querySystem.addEntity(entity, deferCacheClear);
|
2025-09-26 10:50:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 清除系统缓存以确保系统能及时发现新实体
|
|
|
|
|
|
if (!deferCacheClear) {
|
|
|
|
|
|
this.clearSystemEntityCaches();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
// 触发实体添加事件
|
|
|
|
|
|
this.eventSystem.emitSync('entity:added', { entity, scene: this });
|
2025-09-26 10:50:31 +08:00
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
return entity;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量创建实体(高性能版本)
|
|
|
|
|
|
* @param count 要创建的实体数量
|
|
|
|
|
|
* @param namePrefix 实体名称前缀
|
|
|
|
|
|
* @returns 创建的实体列表
|
|
|
|
|
|
*/
|
2025-11-01 10:23:46 +08:00
|
|
|
|
public createEntities(count: number, namePrefix: string = 'Entity'): Entity[] {
|
2025-06-09 14:51:26 +08:00
|
|
|
|
const entities: Entity[] = [];
|
2025-11-01 10:23:46 +08:00
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
// 批量创建实体对象,不立即添加到系统
|
|
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
|
|
|
|
const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut());
|
|
|
|
|
|
entity.scene = this;
|
|
|
|
|
|
entities.push(entity);
|
|
|
|
|
|
}
|
2025-11-01 10:23:46 +08:00
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
// 批量添加到实体列表
|
|
|
|
|
|
for (const entity of entities) {
|
|
|
|
|
|
this.entities.add(entity);
|
|
|
|
|
|
}
|
2025-11-01 10:23:46 +08:00
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
// 批量添加到查询系统(无重复检查,性能最优)
|
|
|
|
|
|
this.querySystem.addEntitiesUnchecked(entities);
|
2025-11-01 10:23:46 +08:00
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
// 批量触发事件(可选,减少事件开销)
|
|
|
|
|
|
this.eventSystem.emitSync('entities:batch_added', { entities, scene: this, count });
|
2025-11-01 10:23:46 +08:00
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
return entities;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-10 10:16:44 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 批量销毁实体
|
|
|
|
|
|
*/
|
|
|
|
|
|
public destroyEntities(entities: Entity[]): void {
|
|
|
|
|
|
if (entities.length === 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
for (const entity of entities) {
|
2025-11-01 10:23:46 +08:00
|
|
|
|
entity.setDestroyedState(true);
|
2025-10-10 10:16:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const entity of entities) {
|
|
|
|
|
|
entity.removeAllComponents();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const entity of entities) {
|
|
|
|
|
|
this.entities.remove(entity);
|
|
|
|
|
|
this.querySystem.removeEntity(entity);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.querySystem.clearCache();
|
|
|
|
|
|
this.clearSystemEntityCaches();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 从场景中删除所有实体
|
|
|
|
|
|
*/
|
|
|
|
|
|
public destroyAllEntities() {
|
2025-07-28 17:14:10 +08:00
|
|
|
|
this.entities.removeAllEntities();
|
2025-10-10 10:16:44 +08:00
|
|
|
|
|
2025-08-06 09:39:08 +08:00
|
|
|
|
this.querySystem.setEntities([]);
|
2025-06-09 14:51:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 搜索并返回第一个具有名称的实体
|
|
|
|
|
|
* @param name 实体名称
|
|
|
|
|
|
*/
|
|
|
|
|
|
public findEntity(name: string): Entity | null {
|
|
|
|
|
|
return this.entities.findEntity(name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据ID查找实体
|
|
|
|
|
|
* @param id 实体ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
public findEntityById(id: number): Entity | null {
|
|
|
|
|
|
return this.entities.findEntityById(id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据标签查找实体
|
|
|
|
|
|
* @param tag 实体标签
|
|
|
|
|
|
*/
|
|
|
|
|
|
public findEntitiesByTag(tag: number): Entity[] {
|
|
|
|
|
|
const result: Entity[] = [];
|
|
|
|
|
|
for (const entity of this.entities.buffer) {
|
|
|
|
|
|
if (entity.tag === tag) {
|
|
|
|
|
|
result.push(entity);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据名称查找实体(别名方法)
|
2025-11-01 10:23:46 +08:00
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* @param name 实体名称
|
2025-11-01 10:23:46 +08:00
|
|
|
|
* @deprecated 请使用 findEntity() 代替此方法
|
2025-06-09 14:51:26 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public getEntityByName(name: string): Entity | null {
|
|
|
|
|
|
return this.findEntity(name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据标签查找实体(别名方法)
|
2025-11-01 10:23:46 +08:00
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* @param tag 实体标签
|
2025-11-01 10:23:46 +08:00
|
|
|
|
* @deprecated 请使用 findEntitiesByTag() 代替此方法
|
2025-06-09 14:51:26 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public getEntitiesByTag(tag: number): Entity[] {
|
|
|
|
|
|
return this.findEntitiesByTag(tag);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 13:13:23 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 查询拥有所有指定组件的实体
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param componentTypes - 组件类型数组
|
|
|
|
|
|
* @returns 查询结果
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
|
|
|
|
|
* const result = scene.queryAll(Position, Velocity);
|
|
|
|
|
|
* for (const entity of result.entities) {
|
|
|
|
|
|
* const pos = entity.getComponent(Position);
|
|
|
|
|
|
* const vel = entity.getComponent(Velocity);
|
|
|
|
|
|
* }
|
|
|
|
|
|
* ```
|
|
|
|
|
|
*/
|
2025-11-01 17:18:12 +08:00
|
|
|
|
public queryAll(...componentTypes: ComponentType[]): { entities: readonly Entity[] } {
|
2025-10-08 13:13:23 +08:00
|
|
|
|
return this.querySystem.queryAll(...componentTypes);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查询拥有任意一个指定组件的实体
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param componentTypes - 组件类型数组
|
|
|
|
|
|
* @returns 查询结果
|
|
|
|
|
|
*/
|
2025-11-01 17:18:12 +08:00
|
|
|
|
public queryAny(...componentTypes: ComponentType[]): { entities: readonly Entity[] } {
|
2025-10-08 13:13:23 +08:00
|
|
|
|
return this.querySystem.queryAny(...componentTypes);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查询不包含指定组件的实体
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param componentTypes - 组件类型数组
|
|
|
|
|
|
* @returns 查询结果
|
|
|
|
|
|
*/
|
2025-11-01 17:18:12 +08:00
|
|
|
|
public queryNone(...componentTypes: ComponentType[]): { entities: readonly Entity[] } {
|
2025-10-08 13:13:23 +08:00
|
|
|
|
return this.querySystem.queryNone(...componentTypes);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建类型安全的查询构建器
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns 查询构建器,支持链式调用
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
|
|
|
|
|
* // 使用查询构建器
|
|
|
|
|
|
* const matcher = scene.query()
|
|
|
|
|
|
* .withAll(Position, Velocity)
|
|
|
|
|
|
* .withNone(Disabled)
|
|
|
|
|
|
* .buildMatcher();
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 在System中使用
|
|
|
|
|
|
* class MovementSystem extends EntitySystem {
|
|
|
|
|
|
* constructor() {
|
|
|
|
|
|
* super(matcher);
|
|
|
|
|
|
* }
|
|
|
|
|
|
* }
|
|
|
|
|
|
* ```
|
|
|
|
|
|
*/
|
|
|
|
|
|
public query(): TypedQueryBuilder {
|
|
|
|
|
|
return new TypedQueryBuilder();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-09 14:51:26 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 在场景中添加一个EntitySystem处理器
|
2025-10-10 21:52:43 +08:00
|
|
|
|
*
|
|
|
|
|
|
* 支持两种使用方式:
|
|
|
|
|
|
* 1. 传入类型(推荐):自动使用DI创建实例,支持@Injectable和@Inject装饰器
|
|
|
|
|
|
* 2. 传入实例:直接使用提供的实例
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param systemTypeOrInstance 系统类型或系统实例
|
|
|
|
|
|
* @returns 添加的处理器实例
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
|
|
|
|
|
* // 方式1:传入类型,自动DI(推荐)
|
|
|
|
|
|
* @Injectable()
|
|
|
|
|
|
* class PhysicsSystem extends EntitySystem {
|
|
|
|
|
|
* constructor(@Inject(CollisionSystem) private collision: CollisionSystem) {
|
2025-10-14 13:30:48 +08:00
|
|
|
|
* super(Matcher.empty().all(Transform));
|
2025-10-10 21:52:43 +08:00
|
|
|
|
* }
|
|
|
|
|
|
* }
|
|
|
|
|
|
* scene.addEntityProcessor(PhysicsSystem);
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 方式2:传入实例
|
|
|
|
|
|
* const system = new MySystem();
|
|
|
|
|
|
* scene.addEntityProcessor(system);
|
|
|
|
|
|
* ```
|
2025-06-09 14:51:26 +08:00
|
|
|
|
*/
|
2025-11-01 10:23:46 +08:00
|
|
|
|
public addEntityProcessor<T extends EntitySystem>(systemTypeOrInstance: ServiceType<T> | T): T {
|
2025-10-10 21:52:43 +08:00
|
|
|
|
let system: T;
|
2025-11-01 17:18:12 +08:00
|
|
|
|
let constructor: ServiceType<T>;
|
2025-10-10 21:52:43 +08:00
|
|
|
|
|
|
|
|
|
|
if (typeof systemTypeOrInstance === 'function') {
|
|
|
|
|
|
constructor = systemTypeOrInstance;
|
|
|
|
|
|
|
|
|
|
|
|
if (this._services.isRegistered(constructor)) {
|
2025-10-14 11:48:04 +08:00
|
|
|
|
const existingSystem = this._services.resolve(constructor) as T;
|
|
|
|
|
|
this.logger.debug(`System ${constructor.name} already registered, returning existing instance`);
|
|
|
|
|
|
return existingSystem;
|
2025-10-10 21:52:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isInjectable(constructor)) {
|
|
|
|
|
|
system = createInstance(constructor, this._services) as T;
|
|
|
|
|
|
} else {
|
2025-11-01 17:18:12 +08:00
|
|
|
|
system = new constructor() as T;
|
2025-10-10 21:52:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
system = systemTypeOrInstance;
|
2025-11-01 17:18:12 +08:00
|
|
|
|
constructor = system.constructor as ServiceType<T>;
|
2025-10-10 21:52:43 +08:00
|
|
|
|
|
|
|
|
|
|
if (this._services.isRegistered(constructor)) {
|
2025-10-14 11:48:04 +08:00
|
|
|
|
const existingSystem = this._services.resolve(constructor);
|
|
|
|
|
|
if (existingSystem === system) {
|
|
|
|
|
|
this.logger.debug(`System ${constructor.name} instance already registered, returning it`);
|
|
|
|
|
|
return system;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.logger.warn(
|
|
|
|
|
|
`Attempting to register a different instance of ${constructor.name}, ` +
|
2025-11-01 10:23:46 +08:00
|
|
|
|
'but type is already registered. Returning existing instance.'
|
2025-10-14 11:48:04 +08:00
|
|
|
|
);
|
|
|
|
|
|
return existingSystem as T;
|
|
|
|
|
|
}
|
2025-10-10 21:52:43 +08:00
|
|
|
|
}
|
2025-07-28 17:14:10 +08:00
|
|
|
|
}
|
2025-10-10 18:13:28 +08:00
|
|
|
|
|
2025-10-10 21:52:43 +08:00
|
|
|
|
system.scene = this;
|
2025-10-10 18:13:28 +08:00
|
|
|
|
|
2025-10-17 22:13:32 +08:00
|
|
|
|
system.setPerformanceMonitor(this.performanceMonitor);
|
2025-10-10 21:52:43 +08:00
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
2025-10-30 23:27:37 +08:00
|
|
|
|
// 标记系统列表已变化
|
|
|
|
|
|
this.markSystemsOrderDirty();
|
|
|
|
|
|
|
2025-10-12 23:39:32 +08:00
|
|
|
|
injectProperties(system, this._services);
|
|
|
|
|
|
|
2025-10-10 21:52:43 +08:00
|
|
|
|
system.initialize();
|
|
|
|
|
|
|
2025-10-14 11:48:04 +08:00
|
|
|
|
this.logger.debug(`System ${constructor.name} registered and initialized`);
|
|
|
|
|
|
|
2025-10-10 21:52:43 +08:00
|
|
|
|
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 {
|
2025-10-14 13:30:48 +08:00
|
|
|
|
* constructor() { super(Matcher.empty().all(Collider)); }
|
2025-10-10 21:52:43 +08:00
|
|
|
|
* dispose() {}
|
|
|
|
|
|
* }
|
|
|
|
|
|
*
|
|
|
|
|
|
* @Injectable()
|
|
|
|
|
|
* @ECSSystem('Physics', { updateOrder: 10 })
|
|
|
|
|
|
* class PhysicsSystem extends EntitySystem implements IService {
|
|
|
|
|
|
* constructor(@Inject(CollisionSystem) private collision: CollisionSystem) {
|
2025-10-14 13:30:48 +08:00
|
|
|
|
* super(Matcher.empty().all(Transform, RigidBody));
|
2025-10-10 21:52:43 +08:00
|
|
|
|
* }
|
|
|
|
|
|
* dispose() {}
|
|
|
|
|
|
* }
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 批量注册(自动解析依赖顺序)
|
|
|
|
|
|
* scene.registerSystems([
|
|
|
|
|
|
* CollisionSystem,
|
|
|
|
|
|
* PhysicsSystem, // 自动注入CollisionSystem
|
|
|
|
|
|
* RenderSystem
|
|
|
|
|
|
* ]);
|
|
|
|
|
|
* ```
|
|
|
|
|
|
*/
|
|
|
|
|
|
public registerSystems(systemTypes: Array<ServiceType<EntitySystem>>): EntitySystem[] {
|
|
|
|
|
|
const registeredSystems: EntitySystem[] = [];
|
2025-10-10 18:13:28 +08:00
|
|
|
|
|
2025-10-10 21:52:43 +08:00
|
|
|
|
for (const systemType of systemTypes) {
|
|
|
|
|
|
const system = this.addEntityProcessor(systemType);
|
|
|
|
|
|
registeredSystems.push(system);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return registeredSystems;
|
2025-06-09 14:51:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 添加系统到场景(addEntityProcessor的别名)
|
|
|
|
|
|
* @param system 系统
|
|
|
|
|
|
*/
|
|
|
|
|
|
public addSystem(system: EntitySystem) {
|
|
|
|
|
|
return this.addEntityProcessor(system);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 从场景中删除EntitySystem处理器
|
|
|
|
|
|
* @param processor 要删除的处理器
|
|
|
|
|
|
*/
|
2025-10-10 21:52:43 +08:00
|
|
|
|
public removeEntityProcessor(processor: EntitySystem): void {
|
2025-11-01 17:18:12 +08:00
|
|
|
|
const constructor = processor.constructor as ServiceType<EntitySystem>;
|
2025-10-10 21:52:43 +08:00
|
|
|
|
|
|
|
|
|
|
// 从ServiceContainer移除
|
|
|
|
|
|
this._services.unregister(constructor);
|
|
|
|
|
|
|
2025-10-30 23:27:37 +08:00
|
|
|
|
// 标记系统列表已变化
|
|
|
|
|
|
this.markSystemsOrderDirty();
|
|
|
|
|
|
|
2025-10-10 21:52:43 +08:00
|
|
|
|
// 重置System状态
|
2025-07-30 15:42:19 +08:00
|
|
|
|
processor.reset();
|
2025-09-24 18:14:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 从场景中删除系统(removeEntityProcessor的别名)
|
|
|
|
|
|
* @param system 系统
|
|
|
|
|
|
*/
|
|
|
|
|
|
public removeSystem(system: EntitySystem) {
|
|
|
|
|
|
this.removeEntityProcessor(system);
|
2025-06-09 14:51:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取指定类型的EntitySystem处理器
|
2025-10-10 21:52:43 +08:00
|
|
|
|
*
|
|
|
|
|
|
* @deprecated 推荐使用依赖注入代替此方法。使用 `scene.services.resolve(SystemType)` 或在System构造函数中使用 `@Inject(SystemType)` 装饰器。
|
|
|
|
|
|
*
|
2025-06-09 14:51:26 +08:00
|
|
|
|
* @param type 处理器类型
|
2025-10-10 21:52:43 +08:00
|
|
|
|
* @returns 处理器实例,如果未找到则返回null
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
|
|
|
|
|
* @Injectable()
|
|
|
|
|
|
* class MySystem extends EntitySystem {
|
|
|
|
|
|
* constructor(@Inject(PhysicsSystem) private physics: PhysicsSystem) {
|
|
|
|
|
|
* super();
|
|
|
|
|
|
* }
|
|
|
|
|
|
* }
|
|
|
|
|
|
* ```
|
2025-06-09 14:51:26 +08:00
|
|
|
|
*/
|
2025-07-31 11:56:04 +08:00
|
|
|
|
public getEntityProcessor<T extends EntitySystem>(type: new (...args: unknown[]) => T): T | null {
|
2025-11-01 17:18:12 +08:00
|
|
|
|
return this._services.tryResolve(type as ServiceType<T>) as T | null;
|
2025-06-09 14:51:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取场景统计信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
public getStats(): {
|
|
|
|
|
|
entityCount: number;
|
|
|
|
|
|
processorCount: number;
|
2025-11-01 17:18:12 +08:00
|
|
|
|
componentStorageStats: Map<string, { totalSlots: number; usedSlots: number; freeSlots: number; fragmentation: number }>;
|
|
|
|
|
|
} {
|
2025-06-09 14:51:26 +08:00
|
|
|
|
return {
|
|
|
|
|
|
entityCount: this.entities.count,
|
2025-10-10 21:52:43 +08:00
|
|
|
|
processorCount: this.systems.length,
|
2025-06-09 14:51:26 +08:00
|
|
|
|
componentStorageStats: this.componentStorageManager.getAllStats()
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取场景的调试信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
public getDebugInfo(): {
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
entityCount: number;
|
|
|
|
|
|
processorCount: number;
|
|
|
|
|
|
isRunning: boolean;
|
|
|
|
|
|
entities: Array<{
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
componentCount: number;
|
|
|
|
|
|
componentTypes: string[];
|
|
|
|
|
|
}>;
|
|
|
|
|
|
processors: Array<{
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
updateOrder: number;
|
|
|
|
|
|
entityCount: number;
|
|
|
|
|
|
}>;
|
2025-11-01 17:18:12 +08:00
|
|
|
|
componentStats: Map<string, { totalSlots: number; usedSlots: number; freeSlots: number; fragmentation: number }>;
|
|
|
|
|
|
} {
|
2025-10-10 21:52:43 +08:00
|
|
|
|
const systems = this.systems;
|
2025-06-09 14:51:26 +08:00
|
|
|
|
return {
|
2025-08-14 18:35:03 +08:00
|
|
|
|
name: this.name || this.constructor.name,
|
2025-06-09 14:51:26 +08:00
|
|
|
|
entityCount: this.entities.count,
|
2025-10-10 21:52:43 +08:00
|
|
|
|
processorCount: systems.length,
|
2025-06-09 14:51:26 +08:00
|
|
|
|
isRunning: this._didSceneBegin,
|
2025-11-01 10:23:46 +08:00
|
|
|
|
entities: this.entities.buffer.map((entity) => ({
|
2025-06-09 14:51:26 +08:00
|
|
|
|
name: entity.name,
|
|
|
|
|
|
id: entity.id,
|
|
|
|
|
|
componentCount: entity.components.length,
|
2025-11-01 10:23:46 +08:00
|
|
|
|
componentTypes: entity.components.map((c) => getComponentInstanceTypeName(c))
|
2025-06-09 14:51:26 +08:00
|
|
|
|
})),
|
2025-11-01 10:23:46 +08:00
|
|
|
|
processors: systems.map((processor) => ({
|
2025-08-14 18:35:03 +08:00
|
|
|
|
name: getSystemInstanceTypeName(processor),
|
2025-06-09 14:51:26 +08:00
|
|
|
|
updateOrder: processor.updateOrder,
|
2025-11-01 17:18:12 +08:00
|
|
|
|
entityCount: processor.entities.length
|
2025-06-09 14:51:26 +08:00
|
|
|
|
})),
|
|
|
|
|
|
componentStats: this.componentStorageManager.getAllStats()
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2025-10-08 18:34:15 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 序列化场景
|
|
|
|
|
|
*
|
2025-10-12 18:49:20 +08:00
|
|
|
|
* 将场景及其所有实体、组件序列化为JSON字符串或二进制Uint8Array
|
2025-10-08 18:34:15 +08:00
|
|
|
|
*
|
|
|
|
|
|
* @param options 序列化选项
|
2025-10-12 18:49:20 +08:00
|
|
|
|
* @returns 序列化后的数据(JSON字符串或二进制Uint8Array)
|
2025-10-08 18:34:15 +08:00
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
2025-10-08 20:42:55 +08:00
|
|
|
|
* // JSON格式
|
|
|
|
|
|
* const jsonData = scene.serialize({
|
2025-10-08 18:34:15 +08:00
|
|
|
|
* format: 'json',
|
|
|
|
|
|
* pretty: true
|
|
|
|
|
|
* });
|
2025-10-08 20:42:55 +08:00
|
|
|
|
*
|
|
|
|
|
|
* // 二进制格式(更小、更快)
|
|
|
|
|
|
* const binaryData = scene.serialize({
|
|
|
|
|
|
* format: 'binary'
|
|
|
|
|
|
* });
|
2025-10-08 18:34:15 +08:00
|
|
|
|
* ```
|
|
|
|
|
|
*/
|
2025-10-12 18:49:20 +08:00
|
|
|
|
public serialize(options?: SceneSerializationOptions): string | Uint8Array {
|
2025-10-08 18:34:15 +08:00
|
|
|
|
return SceneSerializer.serialize(this, options);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 反序列化场景
|
|
|
|
|
|
*
|
|
|
|
|
|
* 从序列化数据恢复场景状态
|
|
|
|
|
|
*
|
2025-10-12 18:49:20 +08:00
|
|
|
|
* @param saveData 序列化的数据(JSON字符串或二进制Uint8Array)
|
2025-10-08 18:34:15 +08:00
|
|
|
|
* @param options 反序列化选项
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
2025-10-08 20:42:55 +08:00
|
|
|
|
* // 从JSON恢复(自动从ComponentRegistry获取组件类型)
|
|
|
|
|
|
* scene.deserialize(jsonData, {
|
|
|
|
|
|
* strategy: 'replace'
|
|
|
|
|
|
* });
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 从二进制恢复
|
|
|
|
|
|
* scene.deserialize(binaryData, {
|
|
|
|
|
|
* strategy: 'replace'
|
2025-10-08 18:34:15 +08:00
|
|
|
|
* });
|
|
|
|
|
|
* ```
|
|
|
|
|
|
*/
|
2025-10-12 18:49:20 +08:00
|
|
|
|
public deserialize(saveData: string | Uint8Array, options?: SceneDeserializationOptions): void {
|
2025-10-08 18:34:15 +08:00
|
|
|
|
SceneSerializer.deserialize(this, saveData, options);
|
|
|
|
|
|
}
|
2025-10-09 12:30:04 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 增量序列化 API ====================
|
|
|
|
|
|
|
|
|
|
|
|
/** 增量序列化的基础快照 */
|
2025-11-01 17:18:12 +08:00
|
|
|
|
private _incrementalBaseSnapshot?: unknown;
|
2025-10-09 12:30:04 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建增量序列化的基础快照
|
|
|
|
|
|
*
|
|
|
|
|
|
* 在需要进行增量序列化前,先调用此方法创建基础快照
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param options 序列化选项
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
|
|
|
|
|
* // 创建基础快照
|
|
|
|
|
|
* scene.createIncrementalSnapshot();
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 进行一些修改...
|
|
|
|
|
|
* entity.addComponent(new PositionComponent(100, 200));
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 计算增量变更
|
|
|
|
|
|
* const incremental = scene.serializeIncremental();
|
|
|
|
|
|
* ```
|
|
|
|
|
|
*/
|
|
|
|
|
|
public createIncrementalSnapshot(options?: IncrementalSerializationOptions): void {
|
|
|
|
|
|
this._incrementalBaseSnapshot = IncrementalSerializer.createSnapshot(this, options);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 增量序列化场景
|
|
|
|
|
|
*
|
|
|
|
|
|
* 只序列化相对于基础快照的变更部分
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param options 序列化选项
|
|
|
|
|
|
* @returns 增量快照对象
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
|
|
|
|
|
* // 创建基础快照
|
|
|
|
|
|
* scene.createIncrementalSnapshot();
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 修改场景
|
|
|
|
|
|
* const entity = scene.createEntity('NewEntity');
|
|
|
|
|
|
* entity.addComponent(new PositionComponent(50, 100));
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 获取增量变更
|
|
|
|
|
|
* const incremental = scene.serializeIncremental();
|
|
|
|
|
|
* console.log(`变更数量: ${incremental.entityChanges.length}`);
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 序列化为JSON
|
|
|
|
|
|
* const json = IncrementalSerializer.serializeIncremental(incremental);
|
|
|
|
|
|
* ```
|
|
|
|
|
|
*/
|
|
|
|
|
|
public serializeIncremental(options?: IncrementalSerializationOptions): IncrementalSnapshot {
|
|
|
|
|
|
if (!this._incrementalBaseSnapshot) {
|
|
|
|
|
|
throw new Error('必须先调用 createIncrementalSnapshot() 创建基础快照');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-01 17:18:12 +08:00
|
|
|
|
return IncrementalSerializer.computeIncremental(this, this._incrementalBaseSnapshot as Parameters<typeof IncrementalSerializer.computeIncremental>[1], options);
|
2025-10-09 12:30:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 应用增量变更到场景
|
|
|
|
|
|
*
|
2025-10-12 18:49:20 +08:00
|
|
|
|
* @param incremental 增量快照数据(IncrementalSnapshot对象、JSON字符串或二进制Uint8Array)
|
2025-10-09 12:30:04 +08:00
|
|
|
|
* @param componentRegistry 组件类型注册表(可选,默认使用全局注册表)
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
2025-10-09 17:14:18 +08:00
|
|
|
|
* // 应用增量变更对象
|
2025-10-09 12:30:04 +08:00
|
|
|
|
* scene.applyIncremental(incrementalSnapshot);
|
|
|
|
|
|
*
|
2025-10-09 17:14:18 +08:00
|
|
|
|
* // 从JSON字符串应用
|
|
|
|
|
|
* const jsonData = IncrementalSerializer.serializeIncremental(snapshot, { format: 'json' });
|
|
|
|
|
|
* scene.applyIncremental(jsonData);
|
|
|
|
|
|
*
|
2025-10-12 18:49:20 +08:00
|
|
|
|
* // 从二进制Uint8Array应用
|
2025-10-09 17:14:18 +08:00
|
|
|
|
* const binaryData = IncrementalSerializer.serializeIncremental(snapshot, { format: 'binary' });
|
|
|
|
|
|
* scene.applyIncremental(binaryData);
|
2025-10-09 12:30:04 +08:00
|
|
|
|
* ```
|
|
|
|
|
|
*/
|
|
|
|
|
|
public applyIncremental(
|
2025-10-12 18:49:20 +08:00
|
|
|
|
incremental: IncrementalSnapshot | string | Uint8Array,
|
2025-11-01 17:18:12 +08:00
|
|
|
|
componentRegistry?: Map<string, ComponentType>
|
2025-10-09 12:30:04 +08:00
|
|
|
|
): void {
|
2025-11-01 10:23:46 +08:00
|
|
|
|
const isSerializedData = typeof incremental === 'string' || incremental instanceof Uint8Array;
|
2025-10-09 17:44:15 +08:00
|
|
|
|
|
|
|
|
|
|
const snapshot = isSerializedData
|
2025-10-12 18:49:20 +08:00
|
|
|
|
? IncrementalSerializer.deserializeIncremental(incremental as string | Uint8Array)
|
2025-11-01 10:23:46 +08:00
|
|
|
|
: (incremental as IncrementalSnapshot);
|
2025-10-09 12:30:04 +08:00
|
|
|
|
|
2025-11-01 17:18:12 +08:00
|
|
|
|
const registry = componentRegistry || (ComponentRegistry.getAllComponentNames() as Map<string, ComponentType>);
|
2025-10-09 12:30:04 +08:00
|
|
|
|
|
|
|
|
|
|
IncrementalSerializer.applyIncremental(this, snapshot, registry);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新增量快照基准
|
|
|
|
|
|
*
|
|
|
|
|
|
* 将当前场景状态设为新的增量序列化基准
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param options 序列化选项
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* ```typescript
|
|
|
|
|
|
* // 创建初始快照
|
|
|
|
|
|
* scene.createIncrementalSnapshot();
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 进行一些修改并序列化
|
|
|
|
|
|
* const incremental1 = scene.serializeIncremental();
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 更新基准,之后的增量将基于当前状态
|
|
|
|
|
|
* scene.updateIncrementalSnapshot();
|
|
|
|
|
|
*
|
|
|
|
|
|
* // 继续修改
|
|
|
|
|
|
* const incremental2 = scene.serializeIncremental();
|
|
|
|
|
|
* ```
|
|
|
|
|
|
*/
|
|
|
|
|
|
public updateIncrementalSnapshot(options?: IncrementalSerializationOptions): void {
|
|
|
|
|
|
this.createIncrementalSnapshot(options);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清除增量快照
|
|
|
|
|
|
*
|
|
|
|
|
|
* 释放快照占用的内存
|
|
|
|
|
|
*/
|
|
|
|
|
|
public clearIncrementalSnapshot(): void {
|
|
|
|
|
|
this._incrementalBaseSnapshot = undefined;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查是否有增量快照
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns 如果已创建增量快照返回true
|
|
|
|
|
|
*/
|
|
|
|
|
|
public hasIncrementalSnapshot(): boolean {
|
|
|
|
|
|
return this._incrementalBaseSnapshot !== undefined;
|
|
|
|
|
|
}
|
2025-11-01 10:23:46 +08:00
|
|
|
|
}
|