refactor(core): 提取 IComponentRegistry 接口

将组件注册表抽象为接口,支持场景级组件注册:
- 新增 IComponentRegistry 接口定义
- Scene 持有独立的 componentRegistry 实例
- 支持从 GlobalComponentRegistry 克隆
- 各系统支持传入自定义注册表
This commit is contained in:
yhh
2025-12-16 11:11:29 +08:00
parent c8dc9869a3
commit 844a770335
14 changed files with 484 additions and 222 deletions

View File

@@ -1,5 +1,5 @@
import { Entity } from '../Entity'; import { Entity } from '../Entity';
import { ComponentType, ComponentRegistry } from './ComponentStorage'; import { ComponentType, GlobalComponentRegistry } from './ComponentStorage';
import { BitMask64Data, BitMask64Utils } from '../Utils'; import { BitMask64Data, BitMask64Utils } from '../Utils';
import { BitMaskHashMap } from '../Utils/BitMaskHashMap'; import { BitMaskHashMap } from '../Utils/BitMaskHashMap';
@@ -271,7 +271,7 @@ export class ArchetypeSystem {
private generateArchetypeId(componentTypes: ComponentType[]): ArchetypeId { private generateArchetypeId(componentTypes: ComponentType[]): ArchetypeId {
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO); const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
for (const type of componentTypes) { for (const type of componentTypes) {
const bitMask = ComponentRegistry.getBitMask(type); const bitMask = GlobalComponentRegistry.getBitMask(type);
BitMask64Utils.orInPlace(mask, bitMask); BitMask64Utils.orInPlace(mask, bitMask);
} }
return mask; return mask;

View File

@@ -1,6 +1,6 @@
import { Entity } from '../Entity'; import { Entity } from '../Entity';
import { Component } from '../Component'; import { Component } from '../Component';
import { ComponentType, ComponentRegistry } from './ComponentStorage'; import { ComponentType, GlobalComponentRegistry } from './ComponentStorage';
import { IScene } from '../IScene'; import { IScene } from '../IScene';
import { createLogger } from '../../Utils/Logger'; import { createLogger } from '../../Utils/Logger';
@@ -198,10 +198,10 @@ export class CommandBuffer {
private getTypeId(componentOrType: Component | ComponentType): number { private getTypeId(componentOrType: Component | ComponentType): number {
if (typeof componentOrType === 'function') { if (typeof componentOrType === 'function') {
// ComponentType // ComponentType
return ComponentRegistry.getBitIndex(componentOrType); return GlobalComponentRegistry.getBitIndex(componentOrType);
} else { } else {
// Component instance // 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) { if (ops.removes && ops.removes.size > 0) {
for (const typeId of ops.removes) { for (const typeId of ops.removes) {
try { try {
const componentType = ComponentRegistry.getTypeByBitIndex(typeId); const componentType = GlobalComponentRegistry.getTypeByBitIndex(typeId);
if (componentType) { if (componentType) {
entity.removeComponentByType(componentType); entity.removeComponentByType(componentType);
commandCount++; commandCount++;

View File

@@ -3,10 +3,13 @@ import { BitMask64Utils, BitMask64Data } from '../Utils/BigIntCompatibility';
import { SoAStorage, SupportedTypedArray } from './SoAStorage'; import { SoAStorage, SupportedTypedArray } from './SoAStorage';
import { createLogger } from '../../Utils/Logger'; import { createLogger } from '../../Utils/Logger';
import { getComponentTypeName, ComponentType } from '../Decorators'; 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 }; export type { ComponentType };
@@ -333,15 +336,18 @@ export class ComponentStorageManager {
/** /**
* 获取实体的组件位掩码 * 获取实体的组件位掩码
* @param entityId 实体ID * Get component bitmask for entity
* @returns 组件位掩码 *
* @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); const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
for (const [componentType, storage] of this.storages.entries()) { for (const [componentType, storage] of this.storages.entries()) {
if (storage.hasComponent(entityId)) { if (storage.hasComponent(entityId)) {
const componentMask = ComponentRegistry.getBitMask(componentType as ComponentType); const componentMask = registry.getBitMask(componentType as ComponentType);
BitMask64Utils.orInPlace(mask, componentMask); BitMask64Utils.orInPlace(mask, componentMask);
} }
} }

View File

@@ -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 { Component } from '../../Component';
import { BitMask64Utils, BitMask64Data } from '../../Utils/BigIntCompatibility'; import { BitMask64Utils, BitMask64Data } from '../../Utils/BigIntCompatibility';
import { createLogger } from '../../../Utils/Logger'; import { createLogger } from '../../../Utils/Logger';
@@ -6,48 +16,43 @@ import {
getComponentTypeName, getComponentTypeName,
hasECSComponentDecorator hasECSComponentDecorator
} from './ComponentTypeUtils'; } from './ComponentTypeUtils';
import type { IComponentRegistry } from './IComponentRegistry';
const logger = createLogger('ComponentRegistry');
/** /**
* 组件注册表 * Component Registry.
* 管理组件类型的位掩码分配 * 组件注册表。
*/
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;
/**
* 热更新模式标志,默认禁用
* 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
* *
* @param componentType 组件类型 * Instance-based registry for component type management.
* @returns 分配的位索引 * Each Scene should have its own registry.
* 基于实例的组件类型管理注册表。
* 每个 Scene 应有自己的注册表。
*/ */
public static register<T extends Component>(componentType: ComponentType<T>): number { 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>();
/**
* Register component type and allocate bitmask.
* 注册组件类型并分配位掩码。
*
* @param componentType - Component constructor | 组件构造函数
* @returns Allocated bit index | 分配的位索引
*/
public register<T extends Component>(componentType: ComponentType<T>): number {
const typeName = getComponentTypeName(componentType); const typeName = getComponentTypeName(componentType);
// 检查是否使用了 @ECSComponent 装饰器
// Check if @ECSComponent decorator is used // Check if @ECSComponent decorator is used
if (!hasECSComponentDecorator(componentType) && !this.warnedComponents.has(componentType)) { // 检查是否使用了 @ECSComponent 装饰器
this.warnedComponents.add(componentType); if (!hasECSComponentDecorator(componentType) && !this._warnedComponents.has(componentType)) {
this._warnedComponents.add(componentType);
console.warn( console.warn(
`[ComponentRegistry] Component "${typeName}" is missing @ECSComponent decorator. ` + `[ComponentRegistry] Component "${typeName}" is missing @ECSComponent decorator. ` +
`This may cause issues with serialization and code minification. ` + `This may cause issues with serialization and code minification. ` +
@@ -55,51 +60,43 @@ export class ComponentRegistry {
); );
} }
if (this.componentTypes.has(componentType)) { if (this._componentTypes.has(componentType)) {
const existingIndex = this.componentTypes.get(componentType)!; return this._componentTypes.get(componentType)!;
return existingIndex;
} }
// 检查是否有同名但不同类的组件已注册(热更新场景) // Hot reload: check if same-named component exists
// Check if a component with the same name but different class is registered (hot reload scenario) // 热更新:检查是否有同名组件
if (this.hotReloadEnabled && this.componentNameToType.has(typeName)) { if (this._hotReloadEnabled && this._componentNameToType.has(typeName)) {
const existingType = this.componentNameToType.get(typeName); const existingType = this._componentNameToType.get(typeName);
if (existingType !== componentType) { if (existingType !== componentType) {
// 热更新:替换旧的类为新的类,复用相同的 bitIndex // Reuse old bitIndex, replace class mapping
// Hot reload: replace old class with new class, reuse the same bitIndex // 复用旧的 bitIndex,替换类映射
const existingIndex = this.componentTypes.get(existingType!)!; 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);
// 移除旧类的映射 logger.debug(`Hot reload: replaced component "${typeName}"`);
// 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}"`);
return existingIndex; return existingIndex;
} }
} }
const bitIndex = this.nextBitIndex++; const bitIndex = this._nextBitIndex++;
this.componentTypes.set(componentType, bitIndex); this._componentTypes.set(componentType, bitIndex);
this.bitIndexToType.set(bitIndex, componentType); this._bitIndexToType.set(bitIndex, componentType);
this.componentNameToType.set(typeName, componentType); this._componentNameToType.set(typeName, componentType);
this.componentNameToId.set(typeName, bitIndex); this._componentNameToId.set(typeName, bitIndex);
return bitIndex; return bitIndex;
} }
/** /**
* 获取组件类型的位掩码 * Get component type's bitmask.
* @param componentType 组件类型 * 获取组件类型的位掩码。
* @returns 位掩码
*/ */
public static getBitMask<T extends Component>(componentType: ComponentType<T>): BitMask64Data { public getBitMask<T extends Component>(componentType: ComponentType<T>): BitMask64Data {
const bitIndex = this.componentTypes.get(componentType); const bitIndex = this._componentTypes.get(componentType);
if (bitIndex === undefined) { if (bitIndex === undefined) {
const typeName = getComponentTypeName(componentType); const typeName = getComponentTypeName(componentType);
throw new Error(`Component type ${typeName} is not registered`); throw new Error(`Component type ${typeName} is not registered`);
@@ -108,12 +105,11 @@ export class ComponentRegistry {
} }
/** /**
* 获取组件类型的位索引 * Get component type's bit index.
* @param componentType 组件类型 * 获取组件类型的位索引。
* @returns 位索引
*/ */
public static getBitIndex<T extends Component>(componentType: ComponentType<T>): number { public getBitIndex<T extends Component>(componentType: ComponentType<T>): number {
const bitIndex = this.componentTypes.get(componentType); const bitIndex = this._componentTypes.get(componentType);
if (bitIndex === undefined) { if (bitIndex === undefined) {
const typeName = getComponentTypeName(componentType); const typeName = getComponentTypeName(componentType);
throw new Error(`Component type ${typeName} is not registered`); throw new Error(`Component type ${typeName} is not registered`);
@@ -122,90 +118,84 @@ export class ComponentRegistry {
} }
/** /**
* 检查组件类型是否已注册 * Check if component type is registered.
* @param componentType 组件类型 * 检查组件类型是否已注册。
* @returns 是否已注册
*/ */
public static isRegistered<T extends Component>(componentType: ComponentType<T>): boolean { public isRegistered<T extends Component>(componentType: ComponentType<T>): boolean {
return this.componentTypes.has(componentType); return this._componentTypes.has(componentType);
} }
/** /**
* 通过位索引获取组件类型 * Get component type by bit index.
* @param bitIndex 位索引 * 通过位索引获取组件类型。
* @returns 组件类型构造函数或null
*/ */
public static getTypeByBitIndex(bitIndex: number): ComponentType | null { public getTypeByBitIndex(bitIndex: number): ComponentType | null {
return (this.bitIndexToType.get(bitIndex) as ComponentType) || null; return (this._bitIndexToType.get(bitIndex) as ComponentType) || null;
} }
/** /**
* 获取当前已注册的组件类型数量 * Get registered component count.
* @returns 已注册数量 * 获取已注册的组件数量
*/ */
public static getRegisteredCount(): number { public getRegisteredCount(): number {
return this.nextBitIndex; return this._nextBitIndex;
} }
/** /**
* 通过名称获取组件类型 * Get component type by name.
* @param componentName 组件名称 * 通过名称获取组件类型。
* @returns 组件类型构造函数
*/ */
public static getComponentType(componentName: string): Function | null { public getComponentType(componentName: string): Function | null {
return this.componentNameToType.get(componentName) || null; return this._componentNameToType.get(componentName) || null;
} }
/** /**
* 获取所有已注册的组件类型 * Get all registered component types.
* @returns 组件类型映射 * 获取所有已注册的组件类型
*/ */
public static getAllRegisteredTypes(): Map<Function, number> { public getAllRegisteredTypes(): Map<Function, number> {
return new Map(this.componentTypes); return new Map(this._componentTypes);
} }
/** /**
* 获取所有组件名称到类型的映射 * Get all component names.
* @returns 名称到类型的映射 * 获取所有组件名称。
*/ */
public static getAllComponentNames(): Map<string, Function> { public getAllComponentNames(): Map<string, Function> {
return new Map(this.componentNameToType); return new Map(this._componentNameToType);
} }
/** /**
* 通过名称获取组件类型ID * Get component type ID by name.
* @param componentName 组件名称 * 通过名称获取组件类型 ID。
* @returns 组件类型ID
*/ */
public static getComponentId(componentName: string): number | undefined { public getComponentId(componentName: string): number | undefined {
return this.componentNameToId.get(componentName); return this._componentNameToId.get(componentName);
} }
/** /**
* 注册组件类型(通过名称) * Register component type by name.
* @param componentName 组件名称 * 通过名称注册组件类型。
* @returns 分配的组件ID
*/ */
public static registerComponentByName(componentName: string): number { public registerComponentByName(componentName: string): number {
if (this.componentNameToId.has(componentName)) { if (this._componentNameToId.has(componentName)) {
return this.componentNameToId.get(componentName)!; return this._componentNameToId.get(componentName)!;
} }
const bitIndex = this.nextBitIndex++; const bitIndex = this._nextBitIndex++;
this.componentNameToId.set(componentName, bitIndex); this._componentNameToId.set(componentName, bitIndex);
return bitIndex; return bitIndex;
} }
/** /**
* 创建单个组件的掩码 * Create single component mask.
* @param componentName 组件名称 * 创建单个组件的掩码。
* @returns 组件掩码
*/ */
public static createSingleComponentMask(componentName: string): BitMask64Data { public createSingleComponentMask(componentName: string): BitMask64Data {
const cacheKey = `single:${componentName}`; const cacheKey = `single:${componentName}`;
if (this.maskCache.has(cacheKey)) { if (this._maskCache.has(cacheKey)) {
return this.maskCache.get(cacheKey)!; return this._maskCache.get(cacheKey)!;
} }
const componentId = this.getComponentId(componentName); const componentId = this.getComponentId(componentName);
@@ -214,21 +204,20 @@ export class ComponentRegistry {
} }
const mask = BitMask64Utils.create(componentId); const mask = BitMask64Utils.create(componentId);
this.maskCache.set(cacheKey, mask); this._maskCache.set(cacheKey, mask);
return mask; return mask;
} }
/** /**
* 创建多个组件的掩码 * Create component mask for multiple components.
* @param componentNames 组件名称数组 * 创建多个组件的掩码。
* @returns 组合掩码
*/ */
public static createComponentMask(componentNames: string[]): BitMask64Data { public createComponentMask(componentNames: string[]): BitMask64Data {
const sortedNames = [...componentNames].sort(); const sortedNames = [...componentNames].sort();
const cacheKey = `multi:${sortedNames.join(',')}`; const cacheKey = `multi:${sortedNames.join(',')}`;
if (this.maskCache.has(cacheKey)) { if (this._maskCache.has(cacheKey)) {
return this.maskCache.get(cacheKey)!; return this._maskCache.get(cacheKey)!;
} }
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO); 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; return mask;
} }
/** /**
* 清除掩码缓存 * Clear mask cache.
* 清除掩码缓存。
*/ */
public static clearMaskCache(): void { public clearMaskCache(): void {
this.maskCache.clear(); this._maskCache.clear();
} }
/** /**
* 启用热更新模式 * Enable hot reload mode.
* Enable hot reload mode * 启用热更新模式。
* 在编辑器环境中调用以支持脚本热更新
* Call in editor environment to support script hot reload
*/ */
public static enableHotReload(): void { public enableHotReload(): void {
this.hotReloadEnabled = true; this._hotReloadEnabled = true;
} }
/** /**
* 禁用热更新模式 * Disable hot reload mode.
* Disable hot reload mode * 禁用热更新模式。
*/ */
public static disableHotReload(): void { public disableHotReload(): void {
this.hotReloadEnabled = false; this._hotReloadEnabled = false;
} }
/** /**
* 检查热更新模式是否启用 * Check if hot reload mode is enabled.
* Check if hot reload mode is enabled * 检查热更新模式是否启用。
*/ */
public static isHotReloadEnabled(): boolean { public isHotReloadEnabled(): boolean {
return this.hotReloadEnabled; return this._hotReloadEnabled;
} }
/** /**
* 注销组件类型 * Unregister component type.
* Unregister component type * 注销组件类型。
*
* 用于插件卸载时清理组件。
* 注意:这不会释放 bitIndex以避免索引冲突。
*
* Used for cleanup during plugin unload.
* Note: This does not release bitIndex to avoid index conflicts.
*
* @param componentName 组件名称 | Component name
*/ */
public static unregister(componentName: string): void { public unregister(componentName: string): void {
const componentType = this.componentNameToType.get(componentName); const componentType = this._componentNameToType.get(componentName);
if (!componentType) { if (!componentType) {
return; return;
} }
const bitIndex = this.componentTypes.get(componentType); const bitIndex = this._componentTypes.get(componentType);
// 移除类型映射
// Remove type mappings // Remove type mappings
this.componentTypes.delete(componentType); // 移除类型映射
this._componentTypes.delete(componentType);
if (bitIndex !== undefined) { if (bitIndex !== undefined) {
this.bitIndexToType.delete(bitIndex); this._bitIndexToType.delete(bitIndex);
} }
this.componentNameToType.delete(componentName); this._componentNameToType.delete(componentName);
this.componentNameToId.delete(componentName); this._componentNameToId.delete(componentName);
// 清除相关的掩码缓存
// Clear related mask cache // Clear related mask cache
// 清除相关的掩码缓存
this.clearMaskCache(); this.clearMaskCache();
this._logger.debug(`Component unregistered: ${componentName}`); logger.debug(`Component unregistered: ${componentName}`);
} }
/** /**
* 获取所有已注册的组件信息 * Get all registered component info.
* Get all registered component info * 获取所有已注册的组件信息。
*
* @returns 组件信息数组 | Array of 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 }> = []; const result: Array<{ name: string; type: Function; bitIndex: number }> = [];
for (const [name, type] of this.componentNameToType) { for (const [name, type] of this._componentNameToType) {
const bitIndex = this.componentTypes.get(type); const bitIndex = this._componentTypes.get(type);
if (bitIndex !== undefined) { if (bitIndex !== undefined) {
result.push({ name, type, bitIndex }); result.push({ name, type, bitIndex });
} }
@@ -333,17 +311,48 @@ export class ComponentRegistry {
} }
/** /**
* 重置注册表(用于测试) * Reset registry.
* Reset registry (for testing) * 重置注册表。
*/ */
public static reset(): void { public reset(): void {
this.componentTypes.clear(); this._componentTypes.clear();
this.bitIndexToType.clear(); this._bitIndexToType.clear();
this.componentNameToType.clear(); this._componentNameToType.clear();
this.componentNameToId.clear(); this._componentNameToId.clear();
this.maskCache.clear(); this._maskCache.clear();
this.warnedComponents.clear(); this._warnedComponents.clear();
this.nextBitIndex = 0; this._nextBitIndex = 0;
this.hotReloadEnabled = false; 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();

View File

@@ -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;
}

View File

@@ -1,6 +1,6 @@
import { Entity } from '../Entity'; import { Entity } from '../Entity';
import { Component } from '../Component'; import { Component } from '../Component';
import { ComponentRegistry, ComponentType } from './ComponentStorage'; import { GlobalComponentRegistry, ComponentType } from './ComponentStorage';
import { BitMask64Utils, BitMask64Data } from '../Utils/BigIntCompatibility'; import { BitMask64Utils, BitMask64Data } from '../Utils/BigIntCompatibility';
import { createLogger } from '../../Utils/Logger'; import { createLogger } from '../../Utils/Logger';
import { getComponentTypeName } from '../Decorators'; import { getComponentTypeName } from '../Decorators';
@@ -932,7 +932,7 @@ export class QuerySystem {
// 使用ComponentRegistry确保bitIndex一致 // 使用ComponentRegistry确保bitIndex一致
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO); const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
for (const type of componentTypes) { for (const type of componentTypes) {
const bitMask = ComponentRegistry.getBitMask(type); const bitMask = GlobalComponentRegistry.getBitMask(type);
BitMask64Utils.orInPlace(mask, bitMask); BitMask64Utils.orInPlace(mask, bitMask);
} }
@@ -1341,7 +1341,7 @@ export class QueryBuilder {
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO); const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
for (const type of componentTypes) { for (const type of componentTypes) {
try { try {
const bitMask = ComponentRegistry.getBitMask(type); const bitMask = GlobalComponentRegistry.getBitMask(type);
BitMask64Utils.orInPlace(mask, bitMask); BitMask64Utils.orInPlace(mask, bitMask);
} catch (error) { } catch (error) {
this._logger.warn(`组件类型 ${getComponentTypeName(type)} 未注册,跳过`); this._logger.warn(`组件类型 ${getComponentTypeName(type)} 未注册,跳过`);

View File

@@ -1,3 +1,4 @@
export { ComponentPool, ComponentPoolManager } from '../ComponentPool'; 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'; export { EnableSoA, Float64, Float32, Int32, SerializeMap, SoAStorage } from '../SoAStorage';

View File

@@ -1,6 +1,6 @@
import 'reflect-metadata'; 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 * Vector property options
*/ */
interface VectorPropertyOptions extends PropertyOptionsBase { 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: 'asset'; assetType?: PropertyAssetType; extensions?: string[] }
| { type: 'vector2' } | { type: 'vector2' }
| { type: 'vector3' } | { type: 'vector3' }
| { type: 'vector4' }
| { type: 'color'; alpha?: boolean } | { type: 'color'; alpha?: boolean }
| { type: 'enum'; options: EnumOption[] }; | { type: 'enum'; options: EnumOption[] };

View File

@@ -10,7 +10,7 @@
import type { Component } from '../Component'; import type { Component } from '../Component';
import type { EntitySystem } from '../Systems'; import type { EntitySystem } from '../Systems';
import { ComponentRegistry } from '../Core/ComponentStorage/ComponentRegistry'; import { GlobalComponentRegistry } from '../Core/ComponentStorage/ComponentRegistry';
import { import {
COMPONENT_TYPE_NAME, COMPONENT_TYPE_NAME,
COMPONENT_DEPENDENCIES, COMPONENT_DEPENDENCIES,
@@ -88,9 +88,9 @@ export function ECSComponent(typeName: string, options?: ComponentOptions) {
(target as any)[COMPONENT_EDITOR_OPTIONS] = options.editor; (target as any)[COMPONENT_EDITOR_OPTIONS] = options.editor;
} }
// 自动注册到 ComponentRegistry使组件可以通过名称查找 // 自动注册到全局 ComponentRegistry使组件可以通过名称查找
// Auto-register to ComponentRegistry, enabling lookup by name // Auto-register to GlobalComponentRegistry, enabling lookup by name
ComponentRegistry.register(target); GlobalComponentRegistry.register(target);
return target; return target;
}; };

View File

@@ -1,5 +1,5 @@
import { Component } from './Component'; import { Component } from './Component';
import { ComponentRegistry, ComponentType } from './Core/ComponentStorage'; import { ComponentType, GlobalComponentRegistry } from './Core/ComponentStorage';
import { EEntityLifecyclePolicy } from './Core/EntityLifecyclePolicy'; import { EEntityLifecyclePolicy } from './Core/EntityLifecyclePolicy';
import { BitMask64Utils, BitMask64Data } from './Utils/BigIntCompatibility'; import { BitMask64Utils, BitMask64Data } from './Utils/BigIntCompatibility';
import { createLogger } from '../Utils/Logger'; import { createLogger } from '../Utils/Logger';
@@ -293,11 +293,12 @@ export class Entity {
} }
const mask = this._componentMask; const mask = this._componentMask;
const maxBitIndex = ComponentRegistry.getRegisteredCount(); const registry = this.scene.componentRegistry;
const maxBitIndex = registry.getRegisteredCount();
for (let bitIndex = 0; bitIndex < maxBitIndex; bitIndex++) { for (let bitIndex = 0; bitIndex < maxBitIndex; bitIndex++) {
if (BitMask64Utils.getBit(mask, bitIndex)) { if (BitMask64Utils.getBit(mask, bitIndex)) {
const componentType = ComponentRegistry.getTypeByBitIndex(bitIndex); const componentType = registry.getTypeByBitIndex(bitIndex);
if (componentType) { if (componentType) {
const component = this.scene.componentStorageManager.getComponent(this.id, componentType); const component = this.scene.componentStorageManager.getComponent(this.id, componentType);
@@ -428,7 +429,8 @@ export class Entity {
// 更新位掩码(组件已通过 @ECSComponent 装饰器自动注册) // 更新位掩码(组件已通过 @ECSComponent 装饰器自动注册)
// Update bitmask (component already registered via @ECSComponent decorator) // 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); BitMask64Utils.orInPlace(this._componentMask, componentMask);
// 使缓存失效 // 使缓存失效
@@ -565,11 +567,12 @@ export class Entity {
* ``` * ```
*/ */
public hasComponent<T extends Component>(type: ComponentType<T>): boolean { 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; return false;
} }
const mask = ComponentRegistry.getBitMask(type); const mask = registry.getBitMask(type);
return BitMask64Utils.hasAny(this._componentMask, mask); return BitMask64Utils.hasAny(this._componentMask, mask);
} }
@@ -641,12 +644,13 @@ export class Entity {
*/ */
public removeComponent(component: Component): void { public removeComponent(component: Component): void {
const componentType = component.constructor as ComponentType; const componentType = component.constructor as ComponentType;
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
if (!ComponentRegistry.isRegistered(componentType)) { if (!registry.isRegistered(componentType)) {
return; return;
} }
const bitIndex = ComponentRegistry.getBitIndex(componentType); const bitIndex = registry.getBitIndex(componentType);
// 更新位掩码 // 更新位掩码
BitMask64Utils.clearBit(this._componentMask, bitIndex); BitMask64Utils.clearBit(this._componentMask, bitIndex);

View File

@@ -3,6 +3,7 @@ import { EntityList } from './Utils/EntityList';
import { IdentifierPool } from './Utils/IdentifierPool'; import { IdentifierPool } from './Utils/IdentifierPool';
import { EntitySystem } from './Systems/EntitySystem'; import { EntitySystem } from './Systems/EntitySystem';
import { ComponentStorageManager, ComponentType } from './Core/ComponentStorage'; import { ComponentStorageManager, ComponentType } from './Core/ComponentStorage';
import type { IComponentRegistry } from './Core/ComponentStorage';
import { QuerySystem } from './Core/QuerySystem'; import { QuerySystem } from './Core/QuerySystem';
import { TypeSafeEventSystem } from './Core/EventSystem'; import { TypeSafeEventSystem } from './Core/EventSystem';
import { EpochManager } from './Core/EpochManager'; import { EpochManager } from './Core/EpochManager';
@@ -57,6 +58,17 @@ export interface IScene {
*/ */
readonly componentStorageManager: ComponentStorageManager; 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 { export interface ISceneConfig {
/** /**
* 场景名称 * 场景名称
* Scene name
*/ */
name?: string; name?: string;
/**
* 是否从全局注册表继承组件类型
* Whether to inherit component types from global registry
*
* @default true
*/
inheritGlobalRegistry?: boolean;
} }

View File

@@ -2,7 +2,13 @@ import { Entity } from './Entity';
import { EntityList } from './Utils/EntityList'; import { EntityList } from './Utils/EntityList';
import { IdentifierPool } from './Utils/IdentifierPool'; import { IdentifierPool } from './Utils/IdentifierPool';
import { EntitySystem } from './Systems/EntitySystem'; 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 { QuerySystem } from './Core/QuerySystem';
import { TypeSafeEventSystem } from './Core/EventSystem'; import { TypeSafeEventSystem } from './Core/EventSystem';
import { ReferenceTracker } from './Core/ReferenceTracker'; import { ReferenceTracker } from './Core/ReferenceTracker';
@@ -75,6 +81,15 @@ export class Scene implements IScene {
*/ */
public readonly componentStorageManager: ComponentStorageManager; 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) { constructor(config?: ISceneConfig) {
this.entities = new EntityList(this); this.entities = new EntityList(this);
this.identifierPool = new IdentifierPool(); this.identifierPool = new IdentifierPool();
this.componentStorageManager = new ComponentStorageManager(); 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.querySystem = new QuerySystem();
this.eventSystem = new TypeSafeEventSystem(); this.eventSystem = new TypeSafeEventSystem();
this.referenceTracker = new ReferenceTracker(); this.referenceTracker = new ReferenceTracker();
@@ -671,8 +698,8 @@ export class Scene implements IScene {
const notifiedSystems = new Set<EntitySystem>(); const notifiedSystems = new Set<EntitySystem>();
// 如果提供了组件类型,使用索引优化 | If component type provided, use index optimization // 如果提供了组件类型,使用索引优化 | If component type provided, use index optimization
if (changedComponentType && ComponentRegistry.isRegistered(changedComponentType)) { if (changedComponentType && this.componentRegistry.isRegistered(changedComponentType)) {
const componentId = ComponentRegistry.getBitIndex(changedComponentType); const componentId = this.componentRegistry.getBitIndex(changedComponentType);
const interestedSystems = this._componentIdToSystems.get(componentId); const interestedSystems = this._componentIdToSystems.get(componentId);
if (interestedSystems) { if (interestedSystems) {
@@ -760,7 +787,7 @@ export class Scene implements IScene {
* @param system 系统 | System * @param system 系统 | System
*/ */
private addSystemToComponentIndex(componentType: ComponentType, system: EntitySystem): void { private addSystemToComponentIndex(componentType: ComponentType, system: EntitySystem): void {
const componentId = ComponentRegistry.getBitIndex(componentType); const componentId = this.componentRegistry.getBitIndex(componentType);
let systems = this._componentIdToSystems.get(componentId); let systems = this._componentIdToSystems.get(componentId);
if (!systems) { if (!systems) {
@@ -1506,7 +1533,7 @@ export class Scene implements IScene {
? IncrementalSerializer.deserializeIncremental(incremental as string | Uint8Array) ? IncrementalSerializer.deserializeIncremental(incremental as string | Uint8Array)
: (incremental as IncrementalSnapshot); : (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); IncrementalSerializer.applyIncremental(this, snapshot, registry);
} }

View File

@@ -6,7 +6,7 @@
import type { IScene } from '../IScene'; import type { IScene } from '../IScene';
import { Entity } from '../Entity'; import { Entity } from '../Entity';
import { ComponentType, ComponentRegistry } from '../Core/ComponentStorage'; import { ComponentType, GlobalComponentRegistry } from '../Core/ComponentStorage';
import { EntitySerializer, SerializedEntity } from './EntitySerializer'; import { EntitySerializer, SerializedEntity } from './EntitySerializer';
import { getComponentTypeName } from '../Decorators'; import { getComponentTypeName } from '../Decorators';
import { getSerializationMetadata } from './SerializationDecorators'; import { getSerializationMetadata } from './SerializationDecorators';
@@ -565,7 +565,7 @@ export class SceneSerializer {
* 从所有已注册的组件类型构建注册表 * 从所有已注册的组件类型构建注册表
*/ */
private static getGlobalComponentRegistry(): Map<string, ComponentType> { private static getGlobalComponentRegistry(): Map<string, ComponentType> {
return ComponentRegistry.getAllComponentNames() as Map<string, ComponentType>; return GlobalComponentRegistry.getAllComponentNames() as Map<string, ComponentType>;
} }
/** /**

View File

@@ -1,5 +1,5 @@
import { Entity } from '../Entity'; import { Entity } from '../Entity';
import { ComponentType, ComponentRegistry } from '../Core/ComponentStorage'; import { ComponentType, GlobalComponentRegistry } from '../Core/ComponentStorage';
import { BitMask64Utils, BitMask64Data } from './BigIntCompatibility'; import { BitMask64Utils, BitMask64Data } from './BigIntCompatibility';
import { SparseSet } from './SparseSet'; import { SparseSet } from './SparseSet';
import { Pool } from '../../Utils/Pool/Pool'; import { Pool } from '../../Utils/Pool/Pool';
@@ -86,7 +86,7 @@ export class ComponentSparseSet {
entityComponents.add(componentType); entityComponents.add(componentType);
// 获取组件位掩码并合并 // 获取组件位掩码并合并
const bitMask = ComponentRegistry.getBitMask(componentType); const bitMask = GlobalComponentRegistry.getBitMask(componentType);
BitMask64Utils.orInPlace(componentMask, bitMask); BitMask64Utils.orInPlace(componentMask, bitMask);
} }
@@ -166,10 +166,10 @@ export class ComponentSparseSet {
// 构建目标位掩码 // 构建目标位掩码
const targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO); const targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
for (const componentType of componentTypes) { for (const componentType of componentTypes) {
if (!ComponentRegistry.isRegistered(componentType)) { if (!GlobalComponentRegistry.isRegistered(componentType)) {
return new Set<Entity>(); // 未注册的组件类型,结果为空 return new Set<Entity>(); // 未注册的组件类型,结果为空
} }
const bitMask = ComponentRegistry.getBitMask(componentType); const bitMask = GlobalComponentRegistry.getBitMask(componentType);
BitMask64Utils.orInPlace(targetMask, bitMask); BitMask64Utils.orInPlace(targetMask, bitMask);
} }
@@ -206,8 +206,8 @@ export class ComponentSparseSet {
// 构建目标位掩码 // 构建目标位掩码
const targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO); const targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
for (const componentType of componentTypes) { for (const componentType of componentTypes) {
if (ComponentRegistry.isRegistered(componentType)) { if (GlobalComponentRegistry.isRegistered(componentType)) {
const bitMask = ComponentRegistry.getBitMask(componentType); const bitMask = GlobalComponentRegistry.getBitMask(componentType);
BitMask64Utils.orInPlace(targetMask, bitMask); BitMask64Utils.orInPlace(targetMask, bitMask);
} }
} }
@@ -242,12 +242,12 @@ export class ComponentSparseSet {
return false; return false;
} }
if (!ComponentRegistry.isRegistered(componentType)) { if (!GlobalComponentRegistry.isRegistered(componentType)) {
return false; return false;
} }
const entityMask = this._componentMasks[entityIndex]!; const entityMask = this._componentMasks[entityIndex]!;
const componentMask = ComponentRegistry.getBitMask(componentType); const componentMask = GlobalComponentRegistry.getBitMask(componentType);
return BitMask64Utils.hasAny(entityMask, componentMask); return BitMask64Utils.hasAny(entityMask, componentMask);
} }