diff --git a/packages/core/src/ECS/Core/ArchetypeSystem.ts b/packages/core/src/ECS/Core/ArchetypeSystem.ts index a2d5fdc4..f951385a 100644 --- a/packages/core/src/ECS/Core/ArchetypeSystem.ts +++ b/packages/core/src/ECS/Core/ArchetypeSystem.ts @@ -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; diff --git a/packages/core/src/ECS/Core/CommandBuffer.ts b/packages/core/src/ECS/Core/CommandBuffer.ts index 35a3543f..a100a6c3 100644 --- a/packages/core/src/ECS/Core/CommandBuffer.ts +++ b/packages/core/src/ECS/Core/CommandBuffer.ts @@ -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++; diff --git a/packages/core/src/ECS/Core/ComponentStorage.ts b/packages/core/src/ECS/Core/ComponentStorage.ts index 01c3d8b4..45d9f5ab 100644 --- a/packages/core/src/ECS/Core/ComponentStorage.ts +++ b/packages/core/src/ECS/Core/ComponentStorage.ts @@ -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); } } diff --git a/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts b/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts index faa88ab6..f71e8f5c 100644 --- a/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts +++ b/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts @@ -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(); - private static bitIndexToType = new Map(); - private static componentNameToType = new Map(); - private static componentNameToId = new Map(); - private static maskCache = new Map(); - private static nextBitIndex = 0; +export class ComponentRegistry implements IComponentRegistry { + private _componentTypes = new Map(); + private _bitIndexToType = new Map(); + private _componentNameToType = new Map(); + private _componentNameToId = new Map(); + private _maskCache = new Map(); + private _nextBitIndex = 0; + private _hotReloadEnabled = false; + private _warnedComponents = new Set(); /** - * 热更新模式标志,默认禁用 - * 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(); - - /** - * 注册组件类型并分配位掩码 - * 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(componentType: ComponentType): number { + public register(componentType: ComponentType): 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(componentType: ComponentType): BitMask64Data { - const bitIndex = this.componentTypes.get(componentType); + public getBitMask(componentType: ComponentType): 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(componentType: ComponentType): number { - const bitIndex = this.componentTypes.get(componentType); + public getBitIndex(componentType: ComponentType): 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(componentType: ComponentType): boolean { - return this.componentTypes.has(componentType); + public isRegistered(componentType: ComponentType): 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 { - return new Map(this.componentTypes); + public getAllRegisteredTypes(): Map { + return new Map(this._componentTypes); } /** - * 获取所有组件名称到类型的映射 - * @returns 名称到类型的映射 + * Get all component names. + * 获取所有组件名称。 */ - public static getAllComponentNames(): Map { - return new Map(this.componentNameToType); + public getAllComponentNames(): Map { + 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(); diff --git a/packages/core/src/ECS/Core/ComponentStorage/IComponentRegistry.ts b/packages/core/src/ECS/Core/ComponentStorage/IComponentRegistry.ts new file mode 100644 index 00000000..87e90761 --- /dev/null +++ b/packages/core/src/ECS/Core/ComponentStorage/IComponentRegistry.ts @@ -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(componentType: ComponentType): number; + + /** + * Get component type's bitmask. + * 获取组件类型的位掩码。 + * + * @param componentType - Component constructor | 组件构造函数 + * @returns Bitmask | 位掩码 + */ + getBitMask(componentType: ComponentType): BitMask64Data; + + /** + * Get component type's bit index. + * 获取组件类型的位索引。 + * + * @param componentType - Component constructor | 组件构造函数 + * @returns Bit index | 位索引 + */ + getBitIndex(componentType: ComponentType): number; + + /** + * Check if component type is registered. + * 检查组件类型是否已注册。 + * + * @param componentType - Component constructor | 组件构造函数 + * @returns Whether registered | 是否已注册 + */ + isRegistered(componentType: ComponentType): 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; + + /** + * Get all component names. + * 获取所有组件名称。 + * + * @returns Map of name to component type | 名称到组件类型的映射 + */ + getAllComponentNames(): Map; + + /** + * 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; +} diff --git a/packages/core/src/ECS/Core/QuerySystem.ts b/packages/core/src/ECS/Core/QuerySystem.ts index bddbc79a..b8ba3050 100644 --- a/packages/core/src/ECS/Core/QuerySystem.ts +++ b/packages/core/src/ECS/Core/QuerySystem.ts @@ -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)} 未注册,跳过`); diff --git a/packages/core/src/ECS/Core/Storage/index.ts b/packages/core/src/ECS/Core/Storage/index.ts index 6dc45ec0..b1757fa7 100644 --- a/packages/core/src/ECS/Core/Storage/index.ts +++ b/packages/core/src/ECS/Core/Storage/index.ts @@ -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'; diff --git a/packages/core/src/ECS/Decorators/PropertyDecorator.ts b/packages/core/src/ECS/Decorators/PropertyDecorator.ts index 8c694211..1ca60948 100644 --- a/packages/core/src/ECS/Decorators/PropertyDecorator.ts +++ b/packages/core/src/ECS/Decorators/PropertyDecorator.ts @@ -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[] }; diff --git a/packages/core/src/ECS/Decorators/TypeDecorators.ts b/packages/core/src/ECS/Decorators/TypeDecorators.ts index 899a7abb..20c35deb 100644 --- a/packages/core/src/ECS/Decorators/TypeDecorators.ts +++ b/packages/core/src/ECS/Decorators/TypeDecorators.ts @@ -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; }; diff --git a/packages/core/src/ECS/Entity.ts b/packages/core/src/ECS/Entity.ts index 330ca772..74b4ae91 100644 --- a/packages/core/src/ECS/Entity.ts +++ b/packages/core/src/ECS/Entity.ts @@ -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(type: ComponentType): 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); diff --git a/packages/core/src/ECS/IScene.ts b/packages/core/src/ECS/IScene.ts index 566838e9..b5d5427a 100644 --- a/packages/core/src/ECS/IScene.ts +++ b/packages/core/src/ECS/IScene.ts @@ -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 { /** * 场景配置接口 + * Scene configuration interface */ export interface ISceneConfig { /** * 场景名称 + * Scene name */ name?: string; + + /** + * 是否从全局注册表继承组件类型 + * Whether to inherit component types from global registry + * + * @default true + */ + inheritGlobalRegistry?: boolean; } diff --git a/packages/core/src/ECS/Scene.ts b/packages/core/src/ECS/Scene.ts index 8de11b87..4ed97e72 100644 --- a/packages/core/src/ECS/Scene.ts +++ b/packages/core/src/ECS/Scene.ts @@ -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(); // 如果提供了组件类型,使用索引优化 | 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); + const registry = componentRegistry || (this.componentRegistry.getAllComponentNames() as Map); IncrementalSerializer.applyIncremental(this, snapshot, registry); } diff --git a/packages/core/src/ECS/Serialization/SceneSerializer.ts b/packages/core/src/ECS/Serialization/SceneSerializer.ts index 6be0cc28..b4ee866c 100644 --- a/packages/core/src/ECS/Serialization/SceneSerializer.ts +++ b/packages/core/src/ECS/Serialization/SceneSerializer.ts @@ -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 { - return ComponentRegistry.getAllComponentNames() as Map; + return GlobalComponentRegistry.getAllComponentNames() as Map; } /** diff --git a/packages/core/src/ECS/Utils/ComponentSparseSet.ts b/packages/core/src/ECS/Utils/ComponentSparseSet.ts index af5a2b6a..60f08912 100644 --- a/packages/core/src/ECS/Utils/ComponentSparseSet.ts +++ b/packages/core/src/ECS/Utils/ComponentSparseSet.ts @@ -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(); // 未注册的组件类型,结果为空 } - 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); }