feat: 纹理路径稳定 ID 与架构改进 (#305)
* feat(asset-system): 实现路径稳定 ID 生成器 使用 FNV-1a hash 算法为纹理生成稳定的运行时 ID: - 新增 _pathIdCache 静态缓存,跨 Play/Stop 循环保持稳定 - 新增 getStableIdForPath() 方法,相同路径永远返回相同 ID - 修改 loadTextureForComponent/loadTextureByGuid 使用稳定 ID - clearTextureMappings() 不再清除 _pathIdCache 这解决了 Play/Stop 后纹理 ID 失效的根本问题。 * fix(runtime-core): 移除 Play/Stop 循环中的 clearTextureMappings 调用 使用路径稳定 ID 后,不再需要在快照保存/恢复时清除纹理缓存: - saveSceneSnapshot() 移除 clearTextureMappings() 调用 - restoreSceneSnapshot() 移除 clearTextureMappings() 调用 - 组件保存的 textureId 在 Play/Stop 后仍然有效 * fix(editor-core): 修复场景切换时的资源泄漏 在 openScene() 加载新场景前先卸载旧场景资源: - 调用 sceneResourceManager.unloadSceneResources() 释放旧资源 - 使用引用计数机制,仅卸载不再被引用的资源 - 路径稳定 ID 缓存不受影响,保持 ID 稳定性 * fix(runtime-core): 修复 PluginManager 组件注册类型错误 将 ComponentRegistry 类改为 GlobalComponentRegistry 实例: - registerComponents() 期望 IComponentRegistry 接口实例 - GlobalComponentRegistry 是 ComponentRegistry 的全局实例 * refactor(core): 提取 IComponentRegistry 接口 将组件注册表抽象为接口,支持场景级组件注册: - 新增 IComponentRegistry 接口定义 - Scene 持有独立的 componentRegistry 实例 - 支持从 GlobalComponentRegistry 克隆 - 各系统支持传入自定义注册表 * refactor(engine-core): 改进插件服务注册机制 - 更新 IComponentRegistry 类型引用 - 优化 PluginServiceRegistry 服务管理 * refactor(modules): 适配新的组件注册接口 更新各模块 RuntimeModule 使用 IComponentRegistry 接口: - audio, behavior-tree, camera - sprite, tilemap, world-streaming * fix(physics-rapier2d): 修复物理插件组件注册 - PhysicsEditorPlugin 添加 runtimeModule 引用 - 适配 IComponentRegistry 接口 - 修复物理组件在场景加载时未注册的问题 * feat(editor-core): 添加 UserCodeService 就绪信号机制 - 新增 waitForReady()/signalReady() API - 支持等待用户脚本编译完成 - 解决场景加载时组件未注册的时序问题 * fix(editor-app): 在编译完成后调用 signalReady() 确保用户脚本编译完成后发出就绪信号: - 编译成功后调用 userCodeService.signalReady() - 编译失败也要发出信号,避免阻塞场景加载 * feat(editor-core): 改进编辑器核心服务 - EntityStoreService 添加调试日志 - AssetRegistryService 优化资产注册 - PluginManager 改进插件管理 - IFileAPI 添加 getFileMtime 接口 * feat(engine): 改进 Rust 纹理管理器 - 支持任意 ID 的纹理加载(非递增) - 添加纹理状态追踪 API - 优化纹理缓存清理机制 - 更新 TypeScript 绑定 * feat(ui): 添加场景切换和文本闪烁组件 新增组件: - SceneLoadTriggerComponent: 场景切换触发器 - TextBlinkComponent: 文本闪烁效果 新增系统: - SceneLoadTriggerSystem: 处理场景切换逻辑 - TextBlinkSystem: 处理文本闪烁动画 其他改进: - UIRuntimeModule 适配新组件注册接口 - UI 渲染系统优化 * feat(editor-app): 添加外部文件修改检测 - 新增 ExternalModificationDialog 组件 - TauriFileAPI 支持 getFileMtime - 场景文件被外部修改时提示用户 * feat(editor-app): 添加渲染调试面板 - 新增 RenderDebugService 和调试面板 UI - App/ContentBrowser 添加调试日志 - TitleBar/Viewport 优化 - DialogManager 改进 * refactor(editor-app): 编辑器服务和组件优化 - EngineService 改进引擎集成 - EditorEngineSync 同步优化 - AssetFileInspector 改进 - VectorFieldEditors 优化 - InstantiatePrefabCommand 改进 * feat(i18n): 更新国际化翻译 - 添加新功能相关翻译 - 更新中文、英文、西班牙文 * feat(tauri): 添加文件修改时间查询命令 - 新增 get_file_mtime 命令 - 支持检测文件外部修改 * refactor(particle): 粒子系统改进 - 适配新的组件注册接口 - ParticleSystem 优化 - 添加单元测试 * refactor(platform): 平台适配层优化 - BrowserRuntime 改进 - 新增 RuntimeSceneManager 服务 - 导出优化 * refactor(asset-system-editor): 资产元数据改进 - AssetMetaFile 优化 - 导出调整 * fix(asset-system): 移除未使用的 TextureLoader 导入 * fix(tests): 更新测试以使用 GlobalComponentRegistry 实例 修复多个测试文件以适配 ComponentRegistry 从静态类变为实例类的变更: - ComponentStorage.test.ts: 使用 GlobalComponentRegistry.reset() - EntitySerializer.test.ts: 使用 GlobalComponentRegistry 实例 - IncrementalSerialization.test.ts: 使用 GlobalComponentRegistry 实例 - SceneSerializer.test.ts: 使用 GlobalComponentRegistry 实例 - ComponentRegistry.extended.test.ts: 使用 GlobalComponentRegistry,同时注册到 scene.componentRegistry - SystemTypes.test.ts: 在 Scene 创建前注册组件 - QuerySystem.test.ts: mockScene 添加 componentRegistry
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { ComponentType, ComponentRegistry } from './ComponentStorage';
|
||||
import { ComponentType, GlobalComponentRegistry } from './ComponentStorage';
|
||||
import { BitMask64Data, BitMask64Utils } from '../Utils';
|
||||
import { BitMaskHashMap } from '../Utils/BitMaskHashMap';
|
||||
|
||||
@@ -271,7 +271,7 @@ export class ArchetypeSystem {
|
||||
private generateArchetypeId(componentTypes: ComponentType[]): ArchetypeId {
|
||||
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
for (const type of componentTypes) {
|
||||
const bitMask = ComponentRegistry.getBitMask(type);
|
||||
const bitMask = GlobalComponentRegistry.getBitMask(type);
|
||||
BitMask64Utils.orInPlace(mask, bitMask);
|
||||
}
|
||||
return mask;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType, ComponentRegistry } from './ComponentStorage';
|
||||
import { ComponentType, GlobalComponentRegistry } from './ComponentStorage';
|
||||
import { IScene } from '../IScene';
|
||||
import { createLogger } from '../../Utils/Logger';
|
||||
|
||||
@@ -198,10 +198,10 @@ export class CommandBuffer {
|
||||
private getTypeId(componentOrType: Component | ComponentType): number {
|
||||
if (typeof componentOrType === 'function') {
|
||||
// ComponentType
|
||||
return ComponentRegistry.getBitIndex(componentOrType);
|
||||
return GlobalComponentRegistry.getBitIndex(componentOrType);
|
||||
} else {
|
||||
// Component instance
|
||||
return ComponentRegistry.getBitIndex(componentOrType.constructor as ComponentType);
|
||||
return GlobalComponentRegistry.getBitIndex(componentOrType.constructor as ComponentType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +413,7 @@ export class CommandBuffer {
|
||||
if (ops.removes && ops.removes.size > 0) {
|
||||
for (const typeId of ops.removes) {
|
||||
try {
|
||||
const componentType = ComponentRegistry.getTypeByBitIndex(typeId);
|
||||
const componentType = GlobalComponentRegistry.getTypeByBitIndex(typeId);
|
||||
if (componentType) {
|
||||
entity.removeComponentByType(componentType);
|
||||
commandCount++;
|
||||
|
||||
@@ -3,10 +3,13 @@ import { BitMask64Utils, BitMask64Data } from '../Utils/BigIntCompatibility';
|
||||
import { SoAStorage, SupportedTypedArray } from './SoAStorage';
|
||||
import { createLogger } from '../../Utils/Logger';
|
||||
import { getComponentTypeName, ComponentType } from '../Decorators';
|
||||
import { ComponentRegistry } from './ComponentStorage/ComponentRegistry';
|
||||
import { ComponentRegistry, GlobalComponentRegistry } from './ComponentStorage/ComponentRegistry';
|
||||
import type { IComponentRegistry } from './ComponentStorage/IComponentRegistry';
|
||||
|
||||
// 导出核心类型
|
||||
export { ComponentRegistry };
|
||||
// Export core types
|
||||
export { ComponentRegistry, GlobalComponentRegistry };
|
||||
export type { IComponentRegistry };
|
||||
export type { ComponentType };
|
||||
|
||||
|
||||
@@ -333,15 +336,18 @@ export class ComponentStorageManager {
|
||||
|
||||
/**
|
||||
* 获取实体的组件位掩码
|
||||
* @param entityId 实体ID
|
||||
* @returns 组件位掩码
|
||||
* Get component bitmask for entity
|
||||
*
|
||||
* @param entityId 实体ID | Entity ID
|
||||
* @param registry 组件注册表(可选,默认使用全局注册表)| Component registry (optional, defaults to global)
|
||||
* @returns 组件位掩码 | Component bitmask
|
||||
*/
|
||||
public getComponentMask(entityId: number): BitMask64Data {
|
||||
public getComponentMask(entityId: number, registry: IComponentRegistry = GlobalComponentRegistry): BitMask64Data {
|
||||
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
|
||||
for (const [componentType, storage] of this.storages.entries()) {
|
||||
if (storage.hasComponent(entityId)) {
|
||||
const componentMask = ComponentRegistry.getBitMask(componentType as ComponentType);
|
||||
const componentMask = registry.getBitMask(componentType as ComponentType);
|
||||
BitMask64Utils.orInPlace(mask, componentMask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/**
|
||||
* Component Registry Implementation.
|
||||
* 组件注册表实现。
|
||||
*
|
||||
* Manages component type bitmask allocation.
|
||||
* Each Scene has its own registry instance for isolation.
|
||||
* 管理组件类型的位掩码分配。
|
||||
* 每个 Scene 都有自己的注册表实例以实现隔离。
|
||||
*/
|
||||
|
||||
import { Component } from '../../Component';
|
||||
import { BitMask64Utils, BitMask64Data } from '../../Utils/BigIntCompatibility';
|
||||
import { createLogger } from '../../../Utils/Logger';
|
||||
@@ -6,48 +16,43 @@ import {
|
||||
getComponentTypeName,
|
||||
hasECSComponentDecorator
|
||||
} from './ComponentTypeUtils';
|
||||
import type { IComponentRegistry } from './IComponentRegistry';
|
||||
|
||||
const logger = createLogger('ComponentRegistry');
|
||||
|
||||
/**
|
||||
* 组件注册表
|
||||
* 管理组件类型的位掩码分配
|
||||
* Component Registry.
|
||||
* 组件注册表。
|
||||
*
|
||||
* Instance-based registry for component type management.
|
||||
* Each Scene should have its own registry.
|
||||
* 基于实例的组件类型管理注册表。
|
||||
* 每个 Scene 应有自己的注册表。
|
||||
*/
|
||||
export class ComponentRegistry {
|
||||
protected static readonly _logger = createLogger('ComponentStorage');
|
||||
private static componentTypes = new Map<Function, number>();
|
||||
private static bitIndexToType = new Map<number, Function>();
|
||||
private static componentNameToType = new Map<string, Function>();
|
||||
private static componentNameToId = new Map<string, number>();
|
||||
private static maskCache = new Map<string, BitMask64Data>();
|
||||
private static nextBitIndex = 0;
|
||||
export class ComponentRegistry implements IComponentRegistry {
|
||||
private _componentTypes = new Map<Function, number>();
|
||||
private _bitIndexToType = new Map<number, Function>();
|
||||
private _componentNameToType = new Map<string, Function>();
|
||||
private _componentNameToId = new Map<string, number>();
|
||||
private _maskCache = new Map<string, BitMask64Data>();
|
||||
private _nextBitIndex = 0;
|
||||
private _hotReloadEnabled = false;
|
||||
private _warnedComponents = new Set<Function>();
|
||||
|
||||
/**
|
||||
* 热更新模式标志,默认禁用
|
||||
* Hot reload mode flag, disabled by default
|
||||
* 编辑器环境应启用此选项以支持脚本热更新
|
||||
* Editor environment should enable this to support script hot reload
|
||||
*/
|
||||
private static hotReloadEnabled = false;
|
||||
|
||||
/**
|
||||
* 已警告过的组件类型集合,避免重复警告
|
||||
* Set of warned component types to avoid duplicate warnings
|
||||
*/
|
||||
private static warnedComponents = new Set<Function>();
|
||||
|
||||
/**
|
||||
* 注册组件类型并分配位掩码
|
||||
* Register component type and allocate bitmask
|
||||
* Register component type and allocate bitmask.
|
||||
* 注册组件类型并分配位掩码。
|
||||
*
|
||||
* @param componentType 组件类型
|
||||
* @returns 分配的位索引
|
||||
* @param componentType - Component constructor | 组件构造函数
|
||||
* @returns Allocated bit index | 分配的位索引
|
||||
*/
|
||||
public static register<T extends Component>(componentType: ComponentType<T>): number {
|
||||
public register<T extends Component>(componentType: ComponentType<T>): number {
|
||||
const typeName = getComponentTypeName(componentType);
|
||||
|
||||
// 检查是否使用了 @ECSComponent 装饰器
|
||||
// Check if @ECSComponent decorator is used
|
||||
if (!hasECSComponentDecorator(componentType) && !this.warnedComponents.has(componentType)) {
|
||||
this.warnedComponents.add(componentType);
|
||||
// 检查是否使用了 @ECSComponent 装饰器
|
||||
if (!hasECSComponentDecorator(componentType) && !this._warnedComponents.has(componentType)) {
|
||||
this._warnedComponents.add(componentType);
|
||||
console.warn(
|
||||
`[ComponentRegistry] Component "${typeName}" is missing @ECSComponent decorator. ` +
|
||||
`This may cause issues with serialization and code minification. ` +
|
||||
@@ -55,51 +60,43 @@ export class ComponentRegistry {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.componentTypes.has(componentType)) {
|
||||
const existingIndex = this.componentTypes.get(componentType)!;
|
||||
return existingIndex;
|
||||
if (this._componentTypes.has(componentType)) {
|
||||
return this._componentTypes.get(componentType)!;
|
||||
}
|
||||
|
||||
// 检查是否有同名但不同类的组件已注册(热更新场景)
|
||||
// Check if a component with the same name but different class is registered (hot reload scenario)
|
||||
if (this.hotReloadEnabled && this.componentNameToType.has(typeName)) {
|
||||
const existingType = this.componentNameToType.get(typeName);
|
||||
// Hot reload: check if same-named component exists
|
||||
// 热更新:检查是否有同名组件
|
||||
if (this._hotReloadEnabled && this._componentNameToType.has(typeName)) {
|
||||
const existingType = this._componentNameToType.get(typeName);
|
||||
if (existingType !== componentType) {
|
||||
// 热更新:替换旧的类为新的类,复用相同的 bitIndex
|
||||
// Hot reload: replace old class with new class, reuse the same bitIndex
|
||||
const existingIndex = this.componentTypes.get(existingType!)!;
|
||||
// Reuse old bitIndex, replace class mapping
|
||||
// 复用旧的 bitIndex,替换类映射
|
||||
const existingIndex = this._componentTypes.get(existingType!)!;
|
||||
this._componentTypes.delete(existingType!);
|
||||
this._componentTypes.set(componentType, existingIndex);
|
||||
this._bitIndexToType.set(existingIndex, componentType);
|
||||
this._componentNameToType.set(typeName, componentType);
|
||||
|
||||
// 移除旧类的映射
|
||||
// Remove old class mapping
|
||||
this.componentTypes.delete(existingType!);
|
||||
|
||||
// 用新类更新映射
|
||||
// Update mappings with new class
|
||||
this.componentTypes.set(componentType, existingIndex);
|
||||
this.bitIndexToType.set(existingIndex, componentType);
|
||||
this.componentNameToType.set(typeName, componentType);
|
||||
|
||||
console.log(`[ComponentRegistry] Hot reload: replaced component "${typeName}"`);
|
||||
logger.debug(`Hot reload: replaced component "${typeName}"`);
|
||||
return existingIndex;
|
||||
}
|
||||
}
|
||||
|
||||
const bitIndex = this.nextBitIndex++;
|
||||
this.componentTypes.set(componentType, bitIndex);
|
||||
this.bitIndexToType.set(bitIndex, componentType);
|
||||
this.componentNameToType.set(typeName, componentType);
|
||||
this.componentNameToId.set(typeName, bitIndex);
|
||||
const bitIndex = this._nextBitIndex++;
|
||||
this._componentTypes.set(componentType, bitIndex);
|
||||
this._bitIndexToType.set(bitIndex, componentType);
|
||||
this._componentNameToType.set(typeName, componentType);
|
||||
this._componentNameToId.set(typeName, bitIndex);
|
||||
|
||||
return bitIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件类型的位掩码
|
||||
* @param componentType 组件类型
|
||||
* @returns 位掩码
|
||||
* Get component type's bitmask.
|
||||
* 获取组件类型的位掩码。
|
||||
*/
|
||||
public static getBitMask<T extends Component>(componentType: ComponentType<T>): BitMask64Data {
|
||||
const bitIndex = this.componentTypes.get(componentType);
|
||||
public getBitMask<T extends Component>(componentType: ComponentType<T>): BitMask64Data {
|
||||
const bitIndex = this._componentTypes.get(componentType);
|
||||
if (bitIndex === undefined) {
|
||||
const typeName = getComponentTypeName(componentType);
|
||||
throw new Error(`Component type ${typeName} is not registered`);
|
||||
@@ -108,12 +105,11 @@ export class ComponentRegistry {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件类型的位索引
|
||||
* @param componentType 组件类型
|
||||
* @returns 位索引
|
||||
* Get component type's bit index.
|
||||
* 获取组件类型的位索引。
|
||||
*/
|
||||
public static getBitIndex<T extends Component>(componentType: ComponentType<T>): number {
|
||||
const bitIndex = this.componentTypes.get(componentType);
|
||||
public getBitIndex<T extends Component>(componentType: ComponentType<T>): number {
|
||||
const bitIndex = this._componentTypes.get(componentType);
|
||||
if (bitIndex === undefined) {
|
||||
const typeName = getComponentTypeName(componentType);
|
||||
throw new Error(`Component type ${typeName} is not registered`);
|
||||
@@ -122,90 +118,84 @@ export class ComponentRegistry {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查组件类型是否已注册
|
||||
* @param componentType 组件类型
|
||||
* @returns 是否已注册
|
||||
* Check if component type is registered.
|
||||
* 检查组件类型是否已注册。
|
||||
*/
|
||||
public static isRegistered<T extends Component>(componentType: ComponentType<T>): boolean {
|
||||
return this.componentTypes.has(componentType);
|
||||
public isRegistered<T extends Component>(componentType: ComponentType<T>): boolean {
|
||||
return this._componentTypes.has(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过位索引获取组件类型
|
||||
* @param bitIndex 位索引
|
||||
* @returns 组件类型构造函数或null
|
||||
* Get component type by bit index.
|
||||
* 通过位索引获取组件类型。
|
||||
*/
|
||||
public static getTypeByBitIndex(bitIndex: number): ComponentType | null {
|
||||
return (this.bitIndexToType.get(bitIndex) as ComponentType) || null;
|
||||
public getTypeByBitIndex(bitIndex: number): ComponentType | null {
|
||||
return (this._bitIndexToType.get(bitIndex) as ComponentType) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前已注册的组件类型数量
|
||||
* @returns 已注册数量
|
||||
* Get registered component count.
|
||||
* 获取已注册的组件数量。
|
||||
*/
|
||||
public static getRegisteredCount(): number {
|
||||
return this.nextBitIndex;
|
||||
public getRegisteredCount(): number {
|
||||
return this._nextBitIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过名称获取组件类型
|
||||
* @param componentName 组件名称
|
||||
* @returns 组件类型构造函数
|
||||
* Get component type by name.
|
||||
* 通过名称获取组件类型。
|
||||
*/
|
||||
public static getComponentType(componentName: string): Function | null {
|
||||
return this.componentNameToType.get(componentName) || null;
|
||||
public getComponentType(componentName: string): Function | null {
|
||||
return this._componentNameToType.get(componentName) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已注册的组件类型
|
||||
* @returns 组件类型映射
|
||||
* Get all registered component types.
|
||||
* 获取所有已注册的组件类型。
|
||||
*/
|
||||
public static getAllRegisteredTypes(): Map<Function, number> {
|
||||
return new Map(this.componentTypes);
|
||||
public getAllRegisteredTypes(): Map<Function, number> {
|
||||
return new Map(this._componentTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有组件名称到类型的映射
|
||||
* @returns 名称到类型的映射
|
||||
* Get all component names.
|
||||
* 获取所有组件名称。
|
||||
*/
|
||||
public static getAllComponentNames(): Map<string, Function> {
|
||||
return new Map(this.componentNameToType);
|
||||
public getAllComponentNames(): Map<string, Function> {
|
||||
return new Map(this._componentNameToType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过名称获取组件类型ID
|
||||
* @param componentName 组件名称
|
||||
* @returns 组件类型ID
|
||||
* Get component type ID by name.
|
||||
* 通过名称获取组件类型 ID。
|
||||
*/
|
||||
public static getComponentId(componentName: string): number | undefined {
|
||||
return this.componentNameToId.get(componentName);
|
||||
public getComponentId(componentName: string): number | undefined {
|
||||
return this._componentNameToId.get(componentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册组件类型(通过名称)
|
||||
* @param componentName 组件名称
|
||||
* @returns 分配的组件ID
|
||||
* Register component type by name.
|
||||
* 通过名称注册组件类型。
|
||||
*/
|
||||
public static registerComponentByName(componentName: string): number {
|
||||
if (this.componentNameToId.has(componentName)) {
|
||||
return this.componentNameToId.get(componentName)!;
|
||||
public registerComponentByName(componentName: string): number {
|
||||
if (this._componentNameToId.has(componentName)) {
|
||||
return this._componentNameToId.get(componentName)!;
|
||||
}
|
||||
|
||||
const bitIndex = this.nextBitIndex++;
|
||||
this.componentNameToId.set(componentName, bitIndex);
|
||||
const bitIndex = this._nextBitIndex++;
|
||||
this._componentNameToId.set(componentName, bitIndex);
|
||||
return bitIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单个组件的掩码
|
||||
* @param componentName 组件名称
|
||||
* @returns 组件掩码
|
||||
* Create single component mask.
|
||||
* 创建单个组件的掩码。
|
||||
*/
|
||||
public static createSingleComponentMask(componentName: string): BitMask64Data {
|
||||
public createSingleComponentMask(componentName: string): BitMask64Data {
|
||||
const cacheKey = `single:${componentName}`;
|
||||
|
||||
if (this.maskCache.has(cacheKey)) {
|
||||
return this.maskCache.get(cacheKey)!;
|
||||
if (this._maskCache.has(cacheKey)) {
|
||||
return this._maskCache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
const componentId = this.getComponentId(componentName);
|
||||
@@ -214,21 +204,20 @@ export class ComponentRegistry {
|
||||
}
|
||||
|
||||
const mask = BitMask64Utils.create(componentId);
|
||||
this.maskCache.set(cacheKey, mask);
|
||||
this._maskCache.set(cacheKey, mask);
|
||||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建多个组件的掩码
|
||||
* @param componentNames 组件名称数组
|
||||
* @returns 组合掩码
|
||||
* Create component mask for multiple components.
|
||||
* 创建多个组件的掩码。
|
||||
*/
|
||||
public static createComponentMask(componentNames: string[]): BitMask64Data {
|
||||
public createComponentMask(componentNames: string[]): BitMask64Data {
|
||||
const sortedNames = [...componentNames].sort();
|
||||
const cacheKey = `multi:${sortedNames.join(',')}`;
|
||||
|
||||
if (this.maskCache.has(cacheKey)) {
|
||||
return this.maskCache.get(cacheKey)!;
|
||||
if (this._maskCache.has(cacheKey)) {
|
||||
return this._maskCache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
@@ -240,90 +229,79 @@ export class ComponentRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
this.maskCache.set(cacheKey, mask);
|
||||
this._maskCache.set(cacheKey, mask);
|
||||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除掩码缓存
|
||||
* Clear mask cache.
|
||||
* 清除掩码缓存。
|
||||
*/
|
||||
public static clearMaskCache(): void {
|
||||
this.maskCache.clear();
|
||||
public clearMaskCache(): void {
|
||||
this._maskCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用热更新模式
|
||||
* Enable hot reload mode
|
||||
* 在编辑器环境中调用以支持脚本热更新
|
||||
* Call in editor environment to support script hot reload
|
||||
* Enable hot reload mode.
|
||||
* 启用热更新模式。
|
||||
*/
|
||||
public static enableHotReload(): void {
|
||||
this.hotReloadEnabled = true;
|
||||
public enableHotReload(): void {
|
||||
this._hotReloadEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用热更新模式
|
||||
* Disable hot reload mode
|
||||
* Disable hot reload mode.
|
||||
* 禁用热更新模式。
|
||||
*/
|
||||
public static disableHotReload(): void {
|
||||
this.hotReloadEnabled = false;
|
||||
public disableHotReload(): void {
|
||||
this._hotReloadEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查热更新模式是否启用
|
||||
* Check if hot reload mode is enabled
|
||||
* Check if hot reload mode is enabled.
|
||||
* 检查热更新模式是否启用。
|
||||
*/
|
||||
public static isHotReloadEnabled(): boolean {
|
||||
return this.hotReloadEnabled;
|
||||
public isHotReloadEnabled(): boolean {
|
||||
return this._hotReloadEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销组件类型
|
||||
* Unregister component type
|
||||
*
|
||||
* 用于插件卸载时清理组件。
|
||||
* 注意:这不会释放 bitIndex,以避免索引冲突。
|
||||
*
|
||||
* Used for cleanup during plugin unload.
|
||||
* Note: This does not release bitIndex to avoid index conflicts.
|
||||
*
|
||||
* @param componentName 组件名称 | Component name
|
||||
* Unregister component type.
|
||||
* 注销组件类型。
|
||||
*/
|
||||
public static unregister(componentName: string): void {
|
||||
const componentType = this.componentNameToType.get(componentName);
|
||||
public unregister(componentName: string): void {
|
||||
const componentType = this._componentNameToType.get(componentName);
|
||||
if (!componentType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bitIndex = this.componentTypes.get(componentType);
|
||||
const bitIndex = this._componentTypes.get(componentType);
|
||||
|
||||
// 移除类型映射
|
||||
// Remove type mappings
|
||||
this.componentTypes.delete(componentType);
|
||||
// 移除类型映射
|
||||
this._componentTypes.delete(componentType);
|
||||
if (bitIndex !== undefined) {
|
||||
this.bitIndexToType.delete(bitIndex);
|
||||
this._bitIndexToType.delete(bitIndex);
|
||||
}
|
||||
this.componentNameToType.delete(componentName);
|
||||
this.componentNameToId.delete(componentName);
|
||||
this._componentNameToType.delete(componentName);
|
||||
this._componentNameToId.delete(componentName);
|
||||
|
||||
// 清除相关的掩码缓存
|
||||
// Clear related mask cache
|
||||
// 清除相关的掩码缓存
|
||||
this.clearMaskCache();
|
||||
|
||||
this._logger.debug(`Component unregistered: ${componentName}`);
|
||||
logger.debug(`Component unregistered: ${componentName}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已注册的组件信息
|
||||
* Get all registered component info
|
||||
*
|
||||
* @returns 组件信息数组 | Array of component info
|
||||
* Get all registered component info.
|
||||
* 获取所有已注册的组件信息。
|
||||
*/
|
||||
public static getRegisteredComponents(): Array<{ name: string; type: Function; bitIndex: number }> {
|
||||
public getRegisteredComponents(): Array<{ name: string; type: Function; bitIndex: number }> {
|
||||
const result: Array<{ name: string; type: Function; bitIndex: number }> = [];
|
||||
|
||||
for (const [name, type] of this.componentNameToType) {
|
||||
const bitIndex = this.componentTypes.get(type);
|
||||
for (const [name, type] of this._componentNameToType) {
|
||||
const bitIndex = this._componentTypes.get(type);
|
||||
if (bitIndex !== undefined) {
|
||||
result.push({ name, type, bitIndex });
|
||||
}
|
||||
@@ -333,17 +311,48 @@ export class ComponentRegistry {
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置注册表(用于测试)
|
||||
* Reset registry (for testing)
|
||||
* Reset registry.
|
||||
* 重置注册表。
|
||||
*/
|
||||
public static reset(): void {
|
||||
this.componentTypes.clear();
|
||||
this.bitIndexToType.clear();
|
||||
this.componentNameToType.clear();
|
||||
this.componentNameToId.clear();
|
||||
this.maskCache.clear();
|
||||
this.warnedComponents.clear();
|
||||
this.nextBitIndex = 0;
|
||||
this.hotReloadEnabled = false;
|
||||
public reset(): void {
|
||||
this._componentTypes.clear();
|
||||
this._bitIndexToType.clear();
|
||||
this._componentNameToType.clear();
|
||||
this._componentNameToId.clear();
|
||||
this._maskCache.clear();
|
||||
this._warnedComponents.clear();
|
||||
this._nextBitIndex = 0;
|
||||
this._hotReloadEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone component types from another registry.
|
||||
* 从另一个注册表克隆组件类型。
|
||||
*
|
||||
* Used to inherit framework components when creating a new Scene.
|
||||
* 用于在创建新 Scene 时继承框架组件。
|
||||
*/
|
||||
public cloneFrom(source: IComponentRegistry): void {
|
||||
const types = source.getAllRegisteredTypes();
|
||||
for (const [type, index] of types) {
|
||||
this._componentTypes.set(type, index);
|
||||
this._bitIndexToType.set(index, type);
|
||||
const typeName = getComponentTypeName(type as ComponentType);
|
||||
this._componentNameToType.set(typeName, type);
|
||||
this._componentNameToId.set(typeName, index);
|
||||
}
|
||||
this._nextBitIndex = source.getRegisteredCount();
|
||||
this._hotReloadEnabled = source.isHotReloadEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Global Component Registry.
|
||||
* 全局组件注册表。
|
||||
*
|
||||
* Used by framework components and decorators.
|
||||
* Scene instances clone from this registry on creation.
|
||||
* 用于框架组件和装饰器。
|
||||
* Scene 实例在创建时从此注册表克隆。
|
||||
*/
|
||||
export const GlobalComponentRegistry = new ComponentRegistry();
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* Component Registry Interface.
|
||||
* 组件注册表接口。
|
||||
*
|
||||
* Defines the contract for component type registration and lookup.
|
||||
* Each Scene has its own ComponentRegistry instance for isolation.
|
||||
* 定义组件类型注册和查找的契约。
|
||||
* 每个 Scene 都有自己的 ComponentRegistry 实例以实现隔离。
|
||||
*/
|
||||
|
||||
import type { Component } from '../../Component';
|
||||
import type { BitMask64Data } from '../../Utils/BigIntCompatibility';
|
||||
import type { ComponentType } from './ComponentTypeUtils';
|
||||
|
||||
/**
|
||||
* Component Registry Interface.
|
||||
* 组件注册表接口。
|
||||
*/
|
||||
export interface IComponentRegistry {
|
||||
/**
|
||||
* Register component type and allocate bitmask.
|
||||
* 注册组件类型并分配位掩码。
|
||||
*
|
||||
* @param componentType - Component constructor | 组件构造函数
|
||||
* @returns Allocated bit index | 分配的位索引
|
||||
*/
|
||||
register<T extends Component>(componentType: ComponentType<T>): number;
|
||||
|
||||
/**
|
||||
* Get component type's bitmask.
|
||||
* 获取组件类型的位掩码。
|
||||
*
|
||||
* @param componentType - Component constructor | 组件构造函数
|
||||
* @returns Bitmask | 位掩码
|
||||
*/
|
||||
getBitMask<T extends Component>(componentType: ComponentType<T>): BitMask64Data;
|
||||
|
||||
/**
|
||||
* Get component type's bit index.
|
||||
* 获取组件类型的位索引。
|
||||
*
|
||||
* @param componentType - Component constructor | 组件构造函数
|
||||
* @returns Bit index | 位索引
|
||||
*/
|
||||
getBitIndex<T extends Component>(componentType: ComponentType<T>): number;
|
||||
|
||||
/**
|
||||
* Check if component type is registered.
|
||||
* 检查组件类型是否已注册。
|
||||
*
|
||||
* @param componentType - Component constructor | 组件构造函数
|
||||
* @returns Whether registered | 是否已注册
|
||||
*/
|
||||
isRegistered<T extends Component>(componentType: ComponentType<T>): boolean;
|
||||
|
||||
/**
|
||||
* Get component type by bit index.
|
||||
* 通过位索引获取组件类型。
|
||||
*
|
||||
* @param bitIndex - Bit index | 位索引
|
||||
* @returns Component constructor or null | 组件构造函数或 null
|
||||
*/
|
||||
getTypeByBitIndex(bitIndex: number): ComponentType | null;
|
||||
|
||||
/**
|
||||
* Get component type by name.
|
||||
* 通过名称获取组件类型。
|
||||
*
|
||||
* @param componentName - Component name | 组件名称
|
||||
* @returns Component constructor or null | 组件构造函数或 null
|
||||
*/
|
||||
getComponentType(componentName: string): Function | null;
|
||||
|
||||
/**
|
||||
* Get component type ID by name.
|
||||
* 通过名称获取组件类型 ID。
|
||||
*
|
||||
* @param componentName - Component name | 组件名称
|
||||
* @returns Component type ID or undefined | 组件类型 ID 或 undefined
|
||||
*/
|
||||
getComponentId(componentName: string): number | undefined;
|
||||
|
||||
/**
|
||||
* Get all registered component types.
|
||||
* 获取所有已注册的组件类型。
|
||||
*
|
||||
* @returns Map of component type to bit index | 组件类型到位索引的映射
|
||||
*/
|
||||
getAllRegisteredTypes(): Map<Function, number>;
|
||||
|
||||
/**
|
||||
* Get all component names.
|
||||
* 获取所有组件名称。
|
||||
*
|
||||
* @returns Map of name to component type | 名称到组件类型的映射
|
||||
*/
|
||||
getAllComponentNames(): Map<string, Function>;
|
||||
|
||||
/**
|
||||
* Get registered component count.
|
||||
* 获取已注册的组件数量。
|
||||
*
|
||||
* @returns Count | 数量
|
||||
*/
|
||||
getRegisteredCount(): number;
|
||||
|
||||
/**
|
||||
* Register component type by name.
|
||||
* 通过名称注册组件类型。
|
||||
*
|
||||
* @param componentName - Component name | 组件名称
|
||||
* @returns Allocated component ID | 分配的组件 ID
|
||||
*/
|
||||
registerComponentByName(componentName: string): number;
|
||||
|
||||
/**
|
||||
* Create single component mask.
|
||||
* 创建单个组件的掩码。
|
||||
*
|
||||
* @param componentName - Component name | 组件名称
|
||||
* @returns Component mask | 组件掩码
|
||||
*/
|
||||
createSingleComponentMask(componentName: string): BitMask64Data;
|
||||
|
||||
/**
|
||||
* Create component mask for multiple components.
|
||||
* 创建多个组件的掩码。
|
||||
*
|
||||
* @param componentNames - Component names | 组件名称数组
|
||||
* @returns Combined mask | 组合掩码
|
||||
*/
|
||||
createComponentMask(componentNames: string[]): BitMask64Data;
|
||||
|
||||
/**
|
||||
* Unregister component type.
|
||||
* 注销组件类型。
|
||||
*
|
||||
* @param componentName - Component name | 组件名称
|
||||
*/
|
||||
unregister(componentName: string): void;
|
||||
|
||||
/**
|
||||
* Enable hot reload mode.
|
||||
* 启用热更新模式。
|
||||
*/
|
||||
enableHotReload(): void;
|
||||
|
||||
/**
|
||||
* Disable hot reload mode.
|
||||
* 禁用热更新模式。
|
||||
*/
|
||||
disableHotReload(): void;
|
||||
|
||||
/**
|
||||
* Check if hot reload mode is enabled.
|
||||
* 检查热更新模式是否启用。
|
||||
*
|
||||
* @returns Whether enabled | 是否启用
|
||||
*/
|
||||
isHotReloadEnabled(): boolean;
|
||||
|
||||
/**
|
||||
* Clear mask cache.
|
||||
* 清除掩码缓存。
|
||||
*/
|
||||
clearMaskCache(): void;
|
||||
|
||||
/**
|
||||
* Reset registry.
|
||||
* 重置注册表。
|
||||
*/
|
||||
reset(): void;
|
||||
|
||||
/**
|
||||
* Get all registered component info.
|
||||
* 获取所有已注册的组件信息。
|
||||
*
|
||||
* @returns Array of component info | 组件信息数组
|
||||
*/
|
||||
getRegisteredComponents(): Array<{ name: string; type: Function; bitIndex: number }>;
|
||||
|
||||
/**
|
||||
* Clone component types from another registry.
|
||||
* 从另一个注册表克隆组件类型。
|
||||
*
|
||||
* Used to inherit framework components when creating a new Scene.
|
||||
* 用于在创建新 Scene 时继承框架组件。
|
||||
*
|
||||
* @param source - Source registry | 源注册表
|
||||
*/
|
||||
cloneFrom(source: IComponentRegistry): void;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { ComponentRegistry, ComponentType } from './ComponentStorage';
|
||||
import { GlobalComponentRegistry, ComponentType } from './ComponentStorage';
|
||||
import { BitMask64Utils, BitMask64Data } from '../Utils/BigIntCompatibility';
|
||||
import { createLogger } from '../../Utils/Logger';
|
||||
import { getComponentTypeName } from '../Decorators';
|
||||
@@ -932,7 +932,7 @@ export class QuerySystem {
|
||||
// 使用ComponentRegistry确保bitIndex一致
|
||||
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
for (const type of componentTypes) {
|
||||
const bitMask = ComponentRegistry.getBitMask(type);
|
||||
const bitMask = GlobalComponentRegistry.getBitMask(type);
|
||||
BitMask64Utils.orInPlace(mask, bitMask);
|
||||
}
|
||||
|
||||
@@ -1341,7 +1341,7 @@ export class QueryBuilder {
|
||||
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
for (const type of componentTypes) {
|
||||
try {
|
||||
const bitMask = ComponentRegistry.getBitMask(type);
|
||||
const bitMask = GlobalComponentRegistry.getBitMask(type);
|
||||
BitMask64Utils.orInPlace(mask, bitMask);
|
||||
} catch (error) {
|
||||
this._logger.warn(`组件类型 ${getComponentTypeName(type)} 未注册,跳过`);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export { ComponentPool, ComponentPoolManager } from '../ComponentPool';
|
||||
export { ComponentStorage, ComponentRegistry } from '../ComponentStorage';
|
||||
export { ComponentStorage, ComponentRegistry, GlobalComponentRegistry } from '../ComponentStorage';
|
||||
export type { IComponentRegistry } from '../ComponentStorage';
|
||||
export { EnableSoA, Float64, Float32, Int32, SerializeMap, SoAStorage } from '../SoAStorage';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
export type PropertyType = 'number' | 'integer' | 'string' | 'boolean' | 'color' | 'vector2' | 'vector3' | 'enum' | 'asset' | 'array' | 'animationClips' | 'collisionLayer' | 'collisionMask';
|
||||
export type PropertyType = 'number' | 'integer' | 'string' | 'boolean' | 'color' | 'vector2' | 'vector3' | 'vector4' | 'enum' | 'asset' | 'array' | 'animationClips' | 'collisionLayer' | 'collisionMask';
|
||||
|
||||
/**
|
||||
* 属性资源类型
|
||||
@@ -102,7 +102,7 @@ interface ColorPropertyOptions extends PropertyOptionsBase {
|
||||
* Vector property options
|
||||
*/
|
||||
interface VectorPropertyOptions extends PropertyOptionsBase {
|
||||
type: 'vector2' | 'vector3';
|
||||
type: 'vector2' | 'vector3' | 'vector4';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,6 +139,7 @@ export type ArrayItemType =
|
||||
| { type: 'asset'; assetType?: PropertyAssetType; extensions?: string[] }
|
||||
| { type: 'vector2' }
|
||||
| { type: 'vector3' }
|
||||
| { type: 'vector4' }
|
||||
| { type: 'color'; alpha?: boolean }
|
||||
| { type: 'enum'; options: EnumOption[] };
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
import type { Component } from '../Component';
|
||||
import type { EntitySystem } from '../Systems';
|
||||
import { ComponentRegistry } from '../Core/ComponentStorage/ComponentRegistry';
|
||||
import { GlobalComponentRegistry } from '../Core/ComponentStorage/ComponentRegistry';
|
||||
import {
|
||||
COMPONENT_TYPE_NAME,
|
||||
COMPONENT_DEPENDENCIES,
|
||||
@@ -88,9 +88,9 @@ export function ECSComponent(typeName: string, options?: ComponentOptions) {
|
||||
(target as any)[COMPONENT_EDITOR_OPTIONS] = options.editor;
|
||||
}
|
||||
|
||||
// 自动注册到 ComponentRegistry,使组件可以通过名称查找
|
||||
// Auto-register to ComponentRegistry, enabling lookup by name
|
||||
ComponentRegistry.register(target);
|
||||
// 自动注册到全局 ComponentRegistry,使组件可以通过名称查找
|
||||
// Auto-register to GlobalComponentRegistry, enabling lookup by name
|
||||
GlobalComponentRegistry.register(target);
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component } from './Component';
|
||||
import { ComponentRegistry, ComponentType } from './Core/ComponentStorage';
|
||||
import { ComponentType, GlobalComponentRegistry } from './Core/ComponentStorage';
|
||||
import { EEntityLifecyclePolicy } from './Core/EntityLifecyclePolicy';
|
||||
import { BitMask64Utils, BitMask64Data } from './Utils/BigIntCompatibility';
|
||||
import { createLogger } from '../Utils/Logger';
|
||||
@@ -293,11 +293,12 @@ export class Entity {
|
||||
}
|
||||
|
||||
const mask = this._componentMask;
|
||||
const maxBitIndex = ComponentRegistry.getRegisteredCount();
|
||||
const registry = this.scene.componentRegistry;
|
||||
const maxBitIndex = registry.getRegisteredCount();
|
||||
|
||||
for (let bitIndex = 0; bitIndex < maxBitIndex; bitIndex++) {
|
||||
if (BitMask64Utils.getBit(mask, bitIndex)) {
|
||||
const componentType = ComponentRegistry.getTypeByBitIndex(bitIndex);
|
||||
const componentType = registry.getTypeByBitIndex(bitIndex);
|
||||
if (componentType) {
|
||||
const component = this.scene.componentStorageManager.getComponent(this.id, componentType);
|
||||
|
||||
@@ -428,7 +429,8 @@ export class Entity {
|
||||
|
||||
// 更新位掩码(组件已通过 @ECSComponent 装饰器自动注册)
|
||||
// Update bitmask (component already registered via @ECSComponent decorator)
|
||||
const componentMask = ComponentRegistry.getBitMask(componentType);
|
||||
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
|
||||
const componentMask = registry.getBitMask(componentType);
|
||||
BitMask64Utils.orInPlace(this._componentMask, componentMask);
|
||||
|
||||
// 使缓存失效
|
||||
@@ -565,11 +567,12 @@ export class Entity {
|
||||
* ```
|
||||
*/
|
||||
public hasComponent<T extends Component>(type: ComponentType<T>): boolean {
|
||||
if (!ComponentRegistry.isRegistered(type)) {
|
||||
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
|
||||
if (!registry.isRegistered(type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const mask = ComponentRegistry.getBitMask(type);
|
||||
const mask = registry.getBitMask(type);
|
||||
return BitMask64Utils.hasAny(this._componentMask, mask);
|
||||
}
|
||||
|
||||
@@ -641,12 +644,13 @@ export class Entity {
|
||||
*/
|
||||
public removeComponent(component: Component): void {
|
||||
const componentType = component.constructor as ComponentType;
|
||||
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
|
||||
|
||||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||||
if (!registry.isRegistered(componentType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bitIndex = ComponentRegistry.getBitIndex(componentType);
|
||||
const bitIndex = registry.getBitIndex(componentType);
|
||||
|
||||
// 更新位掩码
|
||||
BitMask64Utils.clearBit(this._componentMask, bitIndex);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { EntityList } from './Utils/EntityList';
|
||||
import { IdentifierPool } from './Utils/IdentifierPool';
|
||||
import { EntitySystem } from './Systems/EntitySystem';
|
||||
import { ComponentStorageManager, ComponentType } from './Core/ComponentStorage';
|
||||
import type { IComponentRegistry } from './Core/ComponentStorage';
|
||||
import { QuerySystem } from './Core/QuerySystem';
|
||||
import { TypeSafeEventSystem } from './Core/EventSystem';
|
||||
import { EpochManager } from './Core/EpochManager';
|
||||
@@ -57,6 +58,17 @@ export interface IScene {
|
||||
*/
|
||||
readonly componentStorageManager: ComponentStorageManager;
|
||||
|
||||
/**
|
||||
* 组件注册表
|
||||
* Component Registry
|
||||
*
|
||||
* Each scene has its own registry for component type isolation.
|
||||
* Clones from GlobalComponentRegistry on creation.
|
||||
* 每个场景有自己的组件类型注册表以实现隔离。
|
||||
* 创建时从 GlobalComponentRegistry 克隆。
|
||||
*/
|
||||
readonly componentRegistry: IComponentRegistry;
|
||||
|
||||
/**
|
||||
* 查询系统
|
||||
*/
|
||||
@@ -359,10 +371,20 @@ export interface ISceneFactory<T extends IScene> {
|
||||
|
||||
/**
|
||||
* 场景配置接口
|
||||
* Scene configuration interface
|
||||
*/
|
||||
export interface ISceneConfig {
|
||||
/**
|
||||
* 场景名称
|
||||
* Scene name
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* 是否从全局注册表继承组件类型
|
||||
* Whether to inherit component types from global registry
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
inheritGlobalRegistry?: boolean;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,13 @@ import { Entity } from './Entity';
|
||||
import { EntityList } from './Utils/EntityList';
|
||||
import { IdentifierPool } from './Utils/IdentifierPool';
|
||||
import { EntitySystem } from './Systems/EntitySystem';
|
||||
import { ComponentStorageManager, ComponentRegistry, ComponentType } from './Core/ComponentStorage';
|
||||
import {
|
||||
ComponentStorageManager,
|
||||
ComponentRegistry,
|
||||
GlobalComponentRegistry,
|
||||
ComponentType
|
||||
} from './Core/ComponentStorage';
|
||||
import type { IComponentRegistry } from './Core/ComponentStorage';
|
||||
import { QuerySystem } from './Core/QuerySystem';
|
||||
import { TypeSafeEventSystem } from './Core/EventSystem';
|
||||
import { ReferenceTracker } from './Core/ReferenceTracker';
|
||||
@@ -75,6 +81,15 @@ export class Scene implements IScene {
|
||||
*/
|
||||
public readonly componentStorageManager: ComponentStorageManager;
|
||||
|
||||
/**
|
||||
* 组件注册表
|
||||
* Component Registry
|
||||
*
|
||||
* Each scene has its own registry for component type isolation.
|
||||
* 每个场景有自己的组件类型注册表以实现隔离。
|
||||
*/
|
||||
public readonly componentRegistry: IComponentRegistry;
|
||||
|
||||
/**
|
||||
* 查询系统
|
||||
*
|
||||
@@ -364,11 +379,23 @@ export class Scene implements IScene {
|
||||
|
||||
/**
|
||||
* 创建场景实例
|
||||
* Create scene instance
|
||||
*/
|
||||
constructor(config?: ISceneConfig) {
|
||||
this.entities = new EntityList(this);
|
||||
this.identifierPool = new IdentifierPool();
|
||||
this.componentStorageManager = new ComponentStorageManager();
|
||||
|
||||
// 创建场景级别的组件注册表
|
||||
// Create scene-level component registry
|
||||
this.componentRegistry = new ComponentRegistry();
|
||||
|
||||
// 从全局注册表继承框架组件(默认启用)
|
||||
// Inherit framework components from global registry (enabled by default)
|
||||
if (config?.inheritGlobalRegistry !== false) {
|
||||
this.componentRegistry.cloneFrom(GlobalComponentRegistry);
|
||||
}
|
||||
|
||||
this.querySystem = new QuerySystem();
|
||||
this.eventSystem = new TypeSafeEventSystem();
|
||||
this.referenceTracker = new ReferenceTracker();
|
||||
@@ -671,8 +698,8 @@ export class Scene implements IScene {
|
||||
const notifiedSystems = new Set<EntitySystem>();
|
||||
|
||||
// 如果提供了组件类型,使用索引优化 | If component type provided, use index optimization
|
||||
if (changedComponentType && ComponentRegistry.isRegistered(changedComponentType)) {
|
||||
const componentId = ComponentRegistry.getBitIndex(changedComponentType);
|
||||
if (changedComponentType && this.componentRegistry.isRegistered(changedComponentType)) {
|
||||
const componentId = this.componentRegistry.getBitIndex(changedComponentType);
|
||||
const interestedSystems = this._componentIdToSystems.get(componentId);
|
||||
|
||||
if (interestedSystems) {
|
||||
@@ -760,7 +787,7 @@ export class Scene implements IScene {
|
||||
* @param system 系统 | System
|
||||
*/
|
||||
private addSystemToComponentIndex(componentType: ComponentType, system: EntitySystem): void {
|
||||
const componentId = ComponentRegistry.getBitIndex(componentType);
|
||||
const componentId = this.componentRegistry.getBitIndex(componentType);
|
||||
let systems = this._componentIdToSystems.get(componentId);
|
||||
|
||||
if (!systems) {
|
||||
@@ -1506,7 +1533,7 @@ export class Scene implements IScene {
|
||||
? IncrementalSerializer.deserializeIncremental(incremental as string | Uint8Array)
|
||||
: (incremental as IncrementalSnapshot);
|
||||
|
||||
const registry = componentRegistry || (ComponentRegistry.getAllComponentNames() as Map<string, ComponentType>);
|
||||
const registry = componentRegistry || (this.componentRegistry.getAllComponentNames() as Map<string, ComponentType>);
|
||||
|
||||
IncrementalSerializer.applyIncremental(this, snapshot, registry);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type { IScene } from '../IScene';
|
||||
import { Entity } from '../Entity';
|
||||
import { ComponentType, ComponentRegistry } from '../Core/ComponentStorage';
|
||||
import { ComponentType, GlobalComponentRegistry } from '../Core/ComponentStorage';
|
||||
import { EntitySerializer, SerializedEntity } from './EntitySerializer';
|
||||
import { getComponentTypeName } from '../Decorators';
|
||||
import { getSerializationMetadata } from './SerializationDecorators';
|
||||
@@ -565,7 +565,7 @@ export class SceneSerializer {
|
||||
* 从所有已注册的组件类型构建注册表
|
||||
*/
|
||||
private static getGlobalComponentRegistry(): Map<string, ComponentType> {
|
||||
return ComponentRegistry.getAllComponentNames() as Map<string, ComponentType>;
|
||||
return GlobalComponentRegistry.getAllComponentNames() as Map<string, ComponentType>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity } from '../Entity';
|
||||
import { ComponentType, ComponentRegistry } from '../Core/ComponentStorage';
|
||||
import { ComponentType, GlobalComponentRegistry } from '../Core/ComponentStorage';
|
||||
import { BitMask64Utils, BitMask64Data } from './BigIntCompatibility';
|
||||
import { SparseSet } from './SparseSet';
|
||||
import { Pool } from '../../Utils/Pool/Pool';
|
||||
@@ -86,7 +86,7 @@ export class ComponentSparseSet {
|
||||
entityComponents.add(componentType);
|
||||
|
||||
// 获取组件位掩码并合并
|
||||
const bitMask = ComponentRegistry.getBitMask(componentType);
|
||||
const bitMask = GlobalComponentRegistry.getBitMask(componentType);
|
||||
BitMask64Utils.orInPlace(componentMask, bitMask);
|
||||
}
|
||||
|
||||
@@ -166,10 +166,10 @@ export class ComponentSparseSet {
|
||||
// 构建目标位掩码
|
||||
const targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
for (const componentType of componentTypes) {
|
||||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||||
if (!GlobalComponentRegistry.isRegistered(componentType)) {
|
||||
return new Set<Entity>(); // 未注册的组件类型,结果为空
|
||||
}
|
||||
const bitMask = ComponentRegistry.getBitMask(componentType);
|
||||
const bitMask = GlobalComponentRegistry.getBitMask(componentType);
|
||||
BitMask64Utils.orInPlace(targetMask, bitMask);
|
||||
}
|
||||
|
||||
@@ -206,8 +206,8 @@ export class ComponentSparseSet {
|
||||
// 构建目标位掩码
|
||||
const targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
for (const componentType of componentTypes) {
|
||||
if (ComponentRegistry.isRegistered(componentType)) {
|
||||
const bitMask = ComponentRegistry.getBitMask(componentType);
|
||||
if (GlobalComponentRegistry.isRegistered(componentType)) {
|
||||
const bitMask = GlobalComponentRegistry.getBitMask(componentType);
|
||||
BitMask64Utils.orInPlace(targetMask, bitMask);
|
||||
}
|
||||
}
|
||||
@@ -242,12 +242,12 @@ export class ComponentSparseSet {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ComponentRegistry.isRegistered(componentType)) {
|
||||
if (!GlobalComponentRegistry.isRegistered(componentType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const entityMask = this._componentMasks[entityIndex]!;
|
||||
const componentMask = ComponentRegistry.getBitMask(componentType);
|
||||
const componentMask = GlobalComponentRegistry.getBitMask(componentType);
|
||||
|
||||
return BitMask64Utils.hasAny(entityMask, componentMask);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { Component } from '../../../src/ECS/Component';
|
||||
import { ComponentRegistry } from '../../../src/ECS/Core/ComponentStorage/ComponentRegistry';
|
||||
import { GlobalComponentRegistry } from '../../../src/ECS/Core/ComponentStorage/ComponentRegistry';
|
||||
import { Entity } from '../../../src/ECS/Entity';
|
||||
import { Scene } from '../../../src/ECS/Scene';
|
||||
|
||||
describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
// 组件类缓存
|
||||
// 组件类缓存 | Component class cache
|
||||
const componentClassCache = new Map<number, any>();
|
||||
|
||||
beforeEach(() => {
|
||||
ComponentRegistry.reset();
|
||||
GlobalComponentRegistry.reset();
|
||||
componentClassCache.clear();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
ComponentRegistry.reset();
|
||||
GlobalComponentRegistry.reset();
|
||||
componentClassCache.clear();
|
||||
});
|
||||
|
||||
@@ -39,11 +39,11 @@ describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
// 注册 100 个组件类型
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const ComponentClass = createTestComponent(i);
|
||||
const bitIndex = ComponentRegistry.register(ComponentClass);
|
||||
const bitIndex = GlobalComponentRegistry.register(ComponentClass);
|
||||
componentTypes.push(ComponentClass);
|
||||
|
||||
expect(bitIndex).toBe(i);
|
||||
expect(ComponentRegistry.isRegistered(ComponentClass)).toBe(true);
|
||||
expect(GlobalComponentRegistry.isRegistered(ComponentClass)).toBe(true);
|
||||
}
|
||||
|
||||
expect(componentTypes.length).toBe(100);
|
||||
@@ -53,14 +53,14 @@ describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
// 注册 80 个组件
|
||||
for (let i = 0; i < 80; i++) {
|
||||
const ComponentClass = createTestComponent(i);
|
||||
ComponentRegistry.register(ComponentClass);
|
||||
GlobalComponentRegistry.register(ComponentClass);
|
||||
}
|
||||
|
||||
// 验证第 70 个组件的位掩码
|
||||
const Component70 = createTestComponent(70);
|
||||
ComponentRegistry.register(Component70);
|
||||
GlobalComponentRegistry.register(Component70);
|
||||
|
||||
const bitMask = ComponentRegistry.getBitMask(Component70);
|
||||
const bitMask = GlobalComponentRegistry.getBitMask(Component70);
|
||||
expect(bitMask).toBeDefined();
|
||||
expect(bitMask.segments).toBeDefined(); // 应该有扩展段
|
||||
expect(bitMask.segments!.length).toBeGreaterThan(0);
|
||||
@@ -70,11 +70,11 @@ describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
// 注册 1500 个组件验证无限制
|
||||
for (let i = 0; i < 1500; i++) {
|
||||
const ComponentClass = createTestComponent(i);
|
||||
const bitIndex = ComponentRegistry.register(ComponentClass);
|
||||
const bitIndex = GlobalComponentRegistry.register(ComponentClass);
|
||||
expect(bitIndex).toBe(i);
|
||||
}
|
||||
|
||||
expect(ComponentRegistry.getRegisteredCount()).toBe(1500);
|
||||
expect(GlobalComponentRegistry.getRegisteredCount()).toBe(1500);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -92,10 +92,13 @@ describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
const componentTypes: any[] = [];
|
||||
const components: any[] = [];
|
||||
|
||||
// 添加 80 个组件
|
||||
// 添加 80 个组件 | Add 80 components
|
||||
// 需要同时注册到 GlobalComponentRegistry(ArchetypeSystem 使用)和 Scene registry
|
||||
// Need to register to both GlobalComponentRegistry (used by ArchetypeSystem) and Scene registry
|
||||
for (let i = 0; i < 80; i++) {
|
||||
const ComponentClass = createTestComponent(i);
|
||||
ComponentRegistry.register(ComponentClass);
|
||||
GlobalComponentRegistry.register(ComponentClass);
|
||||
scene.componentRegistry.register(ComponentClass);
|
||||
componentTypes.push(ComponentClass);
|
||||
|
||||
const component = new ComponentClass();
|
||||
@@ -115,32 +118,35 @@ describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
});
|
||||
|
||||
it('应该能够正确检查超过 64 个组件的存在性', () => {
|
||||
// 添加组件 0-79
|
||||
// 添加组件 0-79 | Add components 0-79
|
||||
for (let i = 0; i < 80; i++) {
|
||||
const ComponentClass = createTestComponent(i);
|
||||
ComponentRegistry.register(ComponentClass);
|
||||
GlobalComponentRegistry.register(ComponentClass);
|
||||
scene.componentRegistry.register(ComponentClass);
|
||||
entity.addComponent(new ComponentClass());
|
||||
}
|
||||
|
||||
// 验证 hasComponent 对所有组件都工作
|
||||
// 验证 hasComponent 对所有组件都工作 | Verify hasComponent works for all
|
||||
for (let i = 0; i < 80; i++) {
|
||||
const ComponentClass = createTestComponent(i);
|
||||
expect(entity.hasComponent(ComponentClass)).toBe(true);
|
||||
}
|
||||
|
||||
// 验证不存在的组件
|
||||
// 验证不存在的组件 | Verify non-existent component
|
||||
const NonExistentComponent = createTestComponent(999);
|
||||
ComponentRegistry.register(NonExistentComponent);
|
||||
GlobalComponentRegistry.register(NonExistentComponent);
|
||||
scene.componentRegistry.register(NonExistentComponent);
|
||||
expect(entity.hasComponent(NonExistentComponent)).toBe(false);
|
||||
});
|
||||
|
||||
it('应该能够移除超过 64 索引的组件', () => {
|
||||
const componentTypes: any[] = [];
|
||||
|
||||
// 添加 80 个组件
|
||||
// 添加 80 个组件 | Add 80 components
|
||||
for (let i = 0; i < 80; i++) {
|
||||
const ComponentClass = createTestComponent(i);
|
||||
ComponentRegistry.register(ComponentClass);
|
||||
GlobalComponentRegistry.register(ComponentClass);
|
||||
scene.componentRegistry.register(ComponentClass);
|
||||
componentTypes.push(ComponentClass);
|
||||
entity.addComponent(new ComponentClass());
|
||||
}
|
||||
@@ -162,10 +168,11 @@ describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
});
|
||||
|
||||
it('应该能够正确遍历超过 64 个组件', () => {
|
||||
// 添加 80 个组件
|
||||
// 添加 80 个组件 | Add 80 components
|
||||
for (let i = 0; i < 80; i++) {
|
||||
const ComponentClass = createTestComponent(i);
|
||||
ComponentRegistry.register(ComponentClass);
|
||||
GlobalComponentRegistry.register(ComponentClass);
|
||||
scene.componentRegistry.register(ComponentClass);
|
||||
entity.addComponent(new ComponentClass());
|
||||
}
|
||||
|
||||
@@ -182,29 +189,29 @@ describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
|
||||
describe('热更新模式', () => {
|
||||
it('默认应该禁用热更新模式', () => {
|
||||
expect(ComponentRegistry.isHotReloadEnabled()).toBe(false);
|
||||
expect(GlobalComponentRegistry.isHotReloadEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('应该能够启用和禁用热更新模式', () => {
|
||||
expect(ComponentRegistry.isHotReloadEnabled()).toBe(false);
|
||||
expect(GlobalComponentRegistry.isHotReloadEnabled()).toBe(false);
|
||||
|
||||
ComponentRegistry.enableHotReload();
|
||||
expect(ComponentRegistry.isHotReloadEnabled()).toBe(true);
|
||||
GlobalComponentRegistry.enableHotReload();
|
||||
expect(GlobalComponentRegistry.isHotReloadEnabled()).toBe(true);
|
||||
|
||||
ComponentRegistry.disableHotReload();
|
||||
expect(ComponentRegistry.isHotReloadEnabled()).toBe(false);
|
||||
GlobalComponentRegistry.disableHotReload();
|
||||
expect(GlobalComponentRegistry.isHotReloadEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('reset 应该重置热更新模式为禁用', () => {
|
||||
ComponentRegistry.enableHotReload();
|
||||
expect(ComponentRegistry.isHotReloadEnabled()).toBe(true);
|
||||
GlobalComponentRegistry.enableHotReload();
|
||||
expect(GlobalComponentRegistry.isHotReloadEnabled()).toBe(true);
|
||||
|
||||
ComponentRegistry.reset();
|
||||
expect(ComponentRegistry.isHotReloadEnabled()).toBe(false);
|
||||
GlobalComponentRegistry.reset();
|
||||
expect(GlobalComponentRegistry.isHotReloadEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('启用热更新时应该替换同名组件类', () => {
|
||||
ComponentRegistry.enableHotReload();
|
||||
GlobalComponentRegistry.enableHotReload();
|
||||
|
||||
// 模拟热更新场景:两个不同的类但有相同的 constructor.name
|
||||
// Simulate hot reload: two different classes with same constructor.name
|
||||
@@ -229,20 +236,20 @@ describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
expect(TestComponentV1.name).toBe(TestComponentV2.name);
|
||||
expect(TestComponentV1).not.toBe(TestComponentV2);
|
||||
|
||||
const index1 = ComponentRegistry.register(TestComponentV1);
|
||||
const index2 = ComponentRegistry.register(TestComponentV2);
|
||||
const index1 = GlobalComponentRegistry.register(TestComponentV1);
|
||||
const index2 = GlobalComponentRegistry.register(TestComponentV2);
|
||||
|
||||
// 应该复用相同的 bitIndex
|
||||
expect(index1).toBe(index2);
|
||||
|
||||
// 新类应该替换旧类
|
||||
expect(ComponentRegistry.isRegistered(TestComponentV2)).toBe(true);
|
||||
expect(ComponentRegistry.isRegistered(TestComponentV1)).toBe(false);
|
||||
expect(GlobalComponentRegistry.isRegistered(TestComponentV2)).toBe(true);
|
||||
expect(GlobalComponentRegistry.isRegistered(TestComponentV1)).toBe(false);
|
||||
});
|
||||
|
||||
it('禁用热更新时不应该替换同名组件类', () => {
|
||||
// 确保热更新被禁用
|
||||
ComponentRegistry.disableHotReload();
|
||||
GlobalComponentRegistry.disableHotReload();
|
||||
|
||||
// 创建两个同名组件
|
||||
// Create two classes with same constructor.name
|
||||
@@ -265,15 +272,15 @@ describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
expect(TestCompA.name).toBe(TestCompB.name);
|
||||
expect(TestCompA).not.toBe(TestCompB);
|
||||
|
||||
const index1 = ComponentRegistry.register(TestCompA);
|
||||
const index2 = ComponentRegistry.register(TestCompB);
|
||||
const index1 = GlobalComponentRegistry.register(TestCompA);
|
||||
const index2 = GlobalComponentRegistry.register(TestCompB);
|
||||
|
||||
// 应该分配不同的 bitIndex(因为热更新被禁用)
|
||||
expect(index2).toBe(index1 + 1);
|
||||
|
||||
// 两个类都应该被注册
|
||||
expect(ComponentRegistry.isRegistered(TestCompA)).toBe(true);
|
||||
expect(ComponentRegistry.isRegistered(TestCompB)).toBe(true);
|
||||
expect(GlobalComponentRegistry.isRegistered(TestCompA)).toBe(true);
|
||||
expect(GlobalComponentRegistry.isRegistered(TestCompB)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -282,14 +289,15 @@ describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
const scene = new Scene();
|
||||
const entity = scene.createEntity('TestEntity');
|
||||
|
||||
// 注册 65 个组件(跨越 64 位边界)
|
||||
// 注册 65 个组件(跨越 64 位边界)| Register 65 components (crossing 64-bit boundary)
|
||||
for (let i = 0; i < 65; i++) {
|
||||
const ComponentClass = createTestComponent(i);
|
||||
ComponentRegistry.register(ComponentClass);
|
||||
GlobalComponentRegistry.register(ComponentClass);
|
||||
scene.componentRegistry.register(ComponentClass);
|
||||
entity.addComponent(new ComponentClass());
|
||||
}
|
||||
|
||||
// 验证第 63, 64, 65 个组件
|
||||
// 验证第 63, 64, 65 个组件 | Verify components 63, 64
|
||||
const Component63 = createTestComponent(63);
|
||||
const Component64 = createTestComponent(64);
|
||||
|
||||
@@ -301,25 +309,27 @@ describe('ComponentRegistry Extended - 64+ 组件支持', () => {
|
||||
const scene = new Scene();
|
||||
const entity = scene.createEntity('TestEntity');
|
||||
|
||||
// 添加 80 个组件
|
||||
// 添加 80 个组件 | Add 80 components
|
||||
for (let i = 0; i < 80; i++) {
|
||||
const ComponentClass = createTestComponent(i);
|
||||
ComponentRegistry.register(ComponentClass);
|
||||
GlobalComponentRegistry.register(ComponentClass);
|
||||
scene.componentRegistry.register(ComponentClass);
|
||||
entity.addComponent(new ComponentClass());
|
||||
}
|
||||
|
||||
// 强制重建缓存(通过访问 components)
|
||||
// 强制重建缓存(通过访问 components)| Force cache rebuild
|
||||
const components1 = entity.components;
|
||||
expect(components1.length).toBe(80);
|
||||
|
||||
// 添加更多组件
|
||||
// 添加更多组件 | Add more components
|
||||
for (let i = 80; i < 90; i++) {
|
||||
const ComponentClass = createTestComponent(i);
|
||||
ComponentRegistry.register(ComponentClass);
|
||||
GlobalComponentRegistry.register(ComponentClass);
|
||||
scene.componentRegistry.register(ComponentClass);
|
||||
entity.addComponent(new ComponentClass());
|
||||
}
|
||||
|
||||
// 重新获取组件数组(应该重建缓存)
|
||||
// 重新获取组件数组(应该重建缓存)| Re-get component array (should rebuild cache)
|
||||
const components2 = entity.components;
|
||||
expect(components2.length).toBe(90);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { ComponentRegistry, ComponentStorage, ComponentStorageManager } from '../../../src/ECS/Core/ComponentStorage';
|
||||
import { ComponentRegistry, GlobalComponentRegistry, ComponentStorage, ComponentStorageManager } from '../../../src/ECS/Core/ComponentStorage';
|
||||
import { Component } from '../../../src/ECS/Component';
|
||||
import { BitMask64Utils } from '../../../src/ECS/Utils/BigIntCompatibility';
|
||||
|
||||
// 为测试创建独立的注册表实例 | Create isolated registry instance for tests
|
||||
let testRegistry: ComponentRegistry;
|
||||
|
||||
// 测试组件类(默认使用原始存储)
|
||||
class TestComponent extends Component {
|
||||
public value: number;
|
||||
@@ -51,89 +54,88 @@ class HealthComponent extends Component {
|
||||
|
||||
describe('ComponentRegistry - 组件注册表测试', () => {
|
||||
beforeEach(() => {
|
||||
// 重置注册表状态
|
||||
(ComponentRegistry as any).componentTypes = new Map<Function, number>();
|
||||
(ComponentRegistry as any).nextBitIndex = 0;
|
||||
// 每个测试创建新的注册表实例 | Create new registry instance for each test
|
||||
testRegistry = new ComponentRegistry();
|
||||
});
|
||||
|
||||
describe('组件注册功能', () => {
|
||||
test('应该能够注册组件类型', () => {
|
||||
const bitIndex = ComponentRegistry.register(TestComponent);
|
||||
|
||||
const bitIndex = testRegistry.register(TestComponent);
|
||||
|
||||
expect(bitIndex).toBe(0);
|
||||
expect(ComponentRegistry.isRegistered(TestComponent)).toBe(true);
|
||||
expect(testRegistry.isRegistered(TestComponent)).toBe(true);
|
||||
});
|
||||
|
||||
test('重复注册相同组件应该返回相同的位索引', () => {
|
||||
const bitIndex1 = ComponentRegistry.register(TestComponent);
|
||||
const bitIndex2 = ComponentRegistry.register(TestComponent);
|
||||
|
||||
const bitIndex1 = testRegistry.register(TestComponent);
|
||||
const bitIndex2 = testRegistry.register(TestComponent);
|
||||
|
||||
expect(bitIndex1).toBe(bitIndex2);
|
||||
expect(bitIndex1).toBe(0);
|
||||
});
|
||||
|
||||
test('应该能够注册多个组件类型', () => {
|
||||
const bitIndex1 = ComponentRegistry.register(TestComponent);
|
||||
const bitIndex2 = ComponentRegistry.register(PositionComponent);
|
||||
const bitIndex3 = ComponentRegistry.register(VelocityComponent);
|
||||
|
||||
const bitIndex1 = testRegistry.register(TestComponent);
|
||||
const bitIndex2 = testRegistry.register(PositionComponent);
|
||||
const bitIndex3 = testRegistry.register(VelocityComponent);
|
||||
|
||||
expect(bitIndex1).toBe(0);
|
||||
expect(bitIndex2).toBe(1);
|
||||
expect(bitIndex3).toBe(2);
|
||||
});
|
||||
|
||||
test('应该能够检查组件是否已注册', () => {
|
||||
expect(ComponentRegistry.isRegistered(TestComponent)).toBe(false);
|
||||
|
||||
ComponentRegistry.register(TestComponent);
|
||||
expect(ComponentRegistry.isRegistered(TestComponent)).toBe(true);
|
||||
expect(testRegistry.isRegistered(TestComponent)).toBe(false);
|
||||
|
||||
testRegistry.register(TestComponent);
|
||||
expect(testRegistry.isRegistered(TestComponent)).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('位掩码功能', () => {
|
||||
test('应该能够获取组件的位掩码', () => {
|
||||
ComponentRegistry.register(TestComponent);
|
||||
ComponentRegistry.register(PositionComponent);
|
||||
|
||||
const mask1 = ComponentRegistry.getBitMask(TestComponent);
|
||||
const mask2 = ComponentRegistry.getBitMask(PositionComponent);
|
||||
|
||||
testRegistry.register(TestComponent);
|
||||
testRegistry.register(PositionComponent);
|
||||
|
||||
const mask1 = testRegistry.getBitMask(TestComponent);
|
||||
const mask2 = testRegistry.getBitMask(PositionComponent);
|
||||
|
||||
expect(BitMask64Utils.getBit(mask1,0)).toBe(true); // 2^0
|
||||
expect(BitMask64Utils.getBit(mask2,1)).toBe(true); // 2^1
|
||||
});
|
||||
|
||||
test('应该能够获取组件的位索引', () => {
|
||||
ComponentRegistry.register(TestComponent);
|
||||
ComponentRegistry.register(PositionComponent);
|
||||
|
||||
const index1 = ComponentRegistry.getBitIndex(TestComponent);
|
||||
const index2 = ComponentRegistry.getBitIndex(PositionComponent);
|
||||
|
||||
testRegistry.register(TestComponent);
|
||||
testRegistry.register(PositionComponent);
|
||||
|
||||
const index1 = testRegistry.getBitIndex(TestComponent);
|
||||
const index2 = testRegistry.getBitIndex(PositionComponent);
|
||||
|
||||
expect(index1).toBe(0);
|
||||
expect(index2).toBe(1);
|
||||
});
|
||||
|
||||
test('获取未注册组件的位掩码应该抛出错误', () => {
|
||||
expect(() => {
|
||||
ComponentRegistry.getBitMask(TestComponent);
|
||||
testRegistry.getBitMask(TestComponent);
|
||||
}).toThrow('Component type TestComponent is not registered');
|
||||
});
|
||||
|
||||
test('获取未注册组件的位索引应该抛出错误', () => {
|
||||
expect(() => {
|
||||
ComponentRegistry.getBitIndex(TestComponent);
|
||||
testRegistry.getBitIndex(TestComponent);
|
||||
}).toThrow('Component type TestComponent is not registered');
|
||||
});
|
||||
});
|
||||
|
||||
describe('注册表管理', () => {
|
||||
test('应该能够获取所有已注册的组件类型', () => {
|
||||
ComponentRegistry.register(TestComponent);
|
||||
ComponentRegistry.register(PositionComponent);
|
||||
|
||||
const allTypes = ComponentRegistry.getAllRegisteredTypes();
|
||||
|
||||
testRegistry.register(TestComponent);
|
||||
testRegistry.register(PositionComponent);
|
||||
|
||||
const allTypes = testRegistry.getAllRegisteredTypes();
|
||||
|
||||
expect(allTypes.size).toBe(2);
|
||||
expect(allTypes.has(TestComponent)).toBe(true);
|
||||
expect(allTypes.has(PositionComponent)).toBe(true);
|
||||
@@ -142,12 +144,12 @@ describe('ComponentRegistry - 组件注册表测试', () => {
|
||||
});
|
||||
|
||||
test('返回的注册表副本不应该影响原始数据', () => {
|
||||
ComponentRegistry.register(TestComponent);
|
||||
|
||||
const allTypes = ComponentRegistry.getAllRegisteredTypes();
|
||||
testRegistry.register(TestComponent);
|
||||
|
||||
const allTypes = testRegistry.getAllRegisteredTypes();
|
||||
allTypes.set(PositionComponent, 999);
|
||||
|
||||
expect(ComponentRegistry.isRegistered(PositionComponent)).toBe(false);
|
||||
|
||||
expect(testRegistry.isRegistered(PositionComponent)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -156,10 +158,9 @@ describe('ComponentStorage - 组件存储器测试', () => {
|
||||
let storage: ComponentStorage<TestComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
// 重置注册表
|
||||
(ComponentRegistry as any).componentTypes = new Map<Function, number>();
|
||||
(ComponentRegistry as any).nextBitIndex = 0;
|
||||
|
||||
// 每个测试创建新的注册表实例 | Create new registry instance for each test
|
||||
testRegistry = new ComponentRegistry();
|
||||
|
||||
storage = new ComponentStorage(TestComponent);
|
||||
});
|
||||
|
||||
@@ -358,10 +359,9 @@ describe('ComponentStorageManager - 组件存储管理器测试', () => {
|
||||
let manager: ComponentStorageManager;
|
||||
|
||||
beforeEach(() => {
|
||||
// 重置注册表
|
||||
(ComponentRegistry as any).componentTypes = new Map<Function, number>();
|
||||
(ComponentRegistry as any).nextBitIndex = 0;
|
||||
|
||||
// 重置全局注册表 | Reset global registry
|
||||
GlobalComponentRegistry.reset();
|
||||
|
||||
manager = new ComponentStorageManager();
|
||||
});
|
||||
|
||||
@@ -455,10 +455,10 @@ describe('ComponentStorageManager - 组件存储管理器测试', () => {
|
||||
|
||||
describe('位掩码功能', () => {
|
||||
test('应该能够获取实体的组件位掩码', () => {
|
||||
// 确保组件已注册
|
||||
ComponentRegistry.register(TestComponent);
|
||||
ComponentRegistry.register(PositionComponent);
|
||||
ComponentRegistry.register(VelocityComponent);
|
||||
// 确保组件已注册 | Ensure components are registered
|
||||
GlobalComponentRegistry.register(TestComponent);
|
||||
GlobalComponentRegistry.register(PositionComponent);
|
||||
GlobalComponentRegistry.register(VelocityComponent);
|
||||
|
||||
manager.addComponent(1, new TestComponent(100));
|
||||
manager.addComponent(1, new PositionComponent(10, 20));
|
||||
@@ -475,8 +475,8 @@ describe('ComponentStorageManager - 组件存储管理器测试', () => {
|
||||
});
|
||||
|
||||
test('添加和移除组件应该更新掩码', () => {
|
||||
ComponentRegistry.register(TestComponent);
|
||||
ComponentRegistry.register(PositionComponent);
|
||||
GlobalComponentRegistry.register(TestComponent);
|
||||
GlobalComponentRegistry.register(PositionComponent);
|
||||
|
||||
manager.addComponent(1, new TestComponent(100));
|
||||
let mask = manager.getComponentMask(1);
|
||||
|
||||
@@ -894,10 +894,12 @@ describe('QuerySystem - 查询系统测试', () => {
|
||||
const independentQuerySystem = new QuerySystem();
|
||||
const testEntity = scene.createEntity('ArchetypeTestEntity');
|
||||
|
||||
// 模拟Scene环境(保留componentStorageManager)
|
||||
// 模拟Scene环境(保留componentStorageManager和componentRegistry)
|
||||
// Mock Scene environment (keep componentStorageManager and componentRegistry)
|
||||
const mockScene = {
|
||||
querySystem: independentQuerySystem,
|
||||
componentStorageManager: scene.componentStorageManager,
|
||||
componentRegistry: scene.componentRegistry,
|
||||
clearSystemEntityCaches: jest.fn()
|
||||
};
|
||||
testEntity.scene = mockScene as any;
|
||||
@@ -938,10 +940,12 @@ describe('QuerySystem - 查询系统测试', () => {
|
||||
const independentQuerySystem = new QuerySystem();
|
||||
const testEntity = scene.createEntity('RemoveAllTestEntity');
|
||||
|
||||
// 模拟Scene环境(保留componentStorageManager)
|
||||
// 模拟Scene环境(保留componentStorageManager和componentRegistry)
|
||||
// Mock Scene environment (keep componentStorageManager and componentRegistry)
|
||||
const mockScene = {
|
||||
querySystem: independentQuerySystem,
|
||||
componentStorageManager: scene.componentStorageManager,
|
||||
componentRegistry: scene.componentRegistry,
|
||||
clearSystemEntityCaches: jest.fn()
|
||||
};
|
||||
testEntity.scene = mockScene as any;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Component } from '../../../src/ECS/Component';
|
||||
import { HierarchySystem } from '../../../src/ECS/Systems/HierarchySystem';
|
||||
import { HierarchyComponent } from '../../../src/ECS/Components/HierarchyComponent';
|
||||
import { ECSComponent } from '../../../src/ECS/Decorators';
|
||||
import { ComponentRegistry, ComponentType } from '../../../src/ECS/Core/ComponentStorage';
|
||||
import { GlobalComponentRegistry, ComponentType } from '../../../src/ECS/Core/ComponentStorage';
|
||||
import { Serializable, Serialize } from '../../../src/ECS/Serialization';
|
||||
|
||||
@ECSComponent('EntitySerTest_Position')
|
||||
@@ -40,16 +40,18 @@ describe('EntitySerializer', () => {
|
||||
let componentRegistry: Map<string, ComponentType>;
|
||||
|
||||
beforeEach(() => {
|
||||
ComponentRegistry.reset();
|
||||
ComponentRegistry.register(PositionComponent);
|
||||
ComponentRegistry.register(VelocityComponent);
|
||||
ComponentRegistry.register(HierarchyComponent);
|
||||
// 重置全局注册表 | Reset global registry
|
||||
GlobalComponentRegistry.reset();
|
||||
|
||||
GlobalComponentRegistry.register(PositionComponent);
|
||||
GlobalComponentRegistry.register(VelocityComponent);
|
||||
GlobalComponentRegistry.register(HierarchyComponent);
|
||||
|
||||
scene = new Scene({ name: 'EntitySerializerTestScene' });
|
||||
hierarchySystem = new HierarchySystem();
|
||||
scene.addSystem(hierarchySystem);
|
||||
|
||||
componentRegistry = ComponentRegistry.getAllComponentNames() as Map<string, ComponentType>;
|
||||
componentRegistry = GlobalComponentRegistry.getAllComponentNames() as Map<string, ComponentType>;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
ChangeOperation
|
||||
} from '../../../src/ECS/Serialization';
|
||||
import { ECSComponent } from '../../../src/ECS/Decorators';
|
||||
import { ComponentRegistry } from '../../../src/ECS/Core/ComponentStorage';
|
||||
import { GlobalComponentRegistry } from '../../../src/ECS/Core/ComponentStorage';
|
||||
|
||||
// 测试组件定义
|
||||
@ECSComponent('IncTest_Position')
|
||||
@@ -56,12 +56,14 @@ describe('Incremental Serialization System', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
IncrementalSerializer.resetVersion();
|
||||
ComponentRegistry.reset();
|
||||
|
||||
// 重新注册测试组件
|
||||
ComponentRegistry.register(PositionComponent);
|
||||
ComponentRegistry.register(VelocityComponent);
|
||||
ComponentRegistry.register(HealthComponent);
|
||||
// 重置全局注册表 | Reset global registry
|
||||
GlobalComponentRegistry.reset();
|
||||
|
||||
// 重新注册测试组件 | Re-register test components
|
||||
GlobalComponentRegistry.register(PositionComponent);
|
||||
GlobalComponentRegistry.register(VelocityComponent);
|
||||
GlobalComponentRegistry.register(HealthComponent);
|
||||
|
||||
scene = new Scene({ name: 'IncrementalTestScene' });
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Component } from '../../../src/ECS/Component';
|
||||
import { HierarchySystem } from '../../../src/ECS/Systems/HierarchySystem';
|
||||
import { HierarchyComponent } from '../../../src/ECS/Components/HierarchyComponent';
|
||||
import { ECSComponent } from '../../../src/ECS/Decorators';
|
||||
import { ComponentRegistry, ComponentType } from '../../../src/ECS/Core/ComponentStorage';
|
||||
import { GlobalComponentRegistry, ComponentType } from '../../../src/ECS/Core/ComponentStorage';
|
||||
import { Serializable, Serialize } from '../../../src/ECS/Serialization';
|
||||
|
||||
@ECSComponent('SceneSerTest_Position')
|
||||
@@ -40,7 +40,7 @@ describe('SceneSerializer', () => {
|
||||
beforeEach(() => {
|
||||
scene = new Scene({ name: 'SceneSerializerTestScene' });
|
||||
|
||||
componentRegistry = ComponentRegistry.getAllComponentNames() as Map<string, ComponentType>;
|
||||
componentRegistry = GlobalComponentRegistry.getAllComponentNames() as Map<string, ComponentType>;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { IntervalSystem } from '../../../src/ECS/Systems/IntervalSystem';
|
||||
import { ProcessingSystem } from '../../../src/ECS/Systems/ProcessingSystem';
|
||||
import { Entity } from '../../../src/ECS/Entity';
|
||||
import { Component } from '../../../src/ECS/Component';
|
||||
import { ComponentRegistry } from '../../../src/ECS/Core/ComponentStorage';
|
||||
import { GlobalComponentRegistry } from '../../../src/ECS/Core/ComponentStorage';
|
||||
import { Time } from '../../../src/Utils/Time';
|
||||
import { Matcher } from '../../../src/ECS/Utils/Matcher';
|
||||
import { Core } from '../../../src/Core';
|
||||
@@ -85,13 +85,15 @@ describe('System Types - 系统类型测试', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
Core.create();
|
||||
// 注册测试组件类型 | Register test component types
|
||||
// 必须在创建 Scene 之前注册,因为 Scene 会克隆 GlobalComponentRegistry
|
||||
// Must register before Scene creation, as Scene clones GlobalComponentRegistry
|
||||
GlobalComponentRegistry.register(TestComponent);
|
||||
GlobalComponentRegistry.register(AnotherComponent);
|
||||
scene = new Scene();
|
||||
entity = scene.createEntity('TestEntity');
|
||||
// 重置时间系统
|
||||
Time.update(0.016);
|
||||
// 注册测试组件类型
|
||||
ComponentRegistry.register(TestComponent);
|
||||
ComponentRegistry.register(AnotherComponent);
|
||||
});
|
||||
|
||||
describe('PassiveSystem - 被动系统', () => {
|
||||
|
||||
Reference in New Issue
Block a user