From 5ea3b72b2b0ec8d56950df959766b3e097e81133 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Tue, 30 Sep 2025 23:58:52 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8BitMask64Data.segments?= =?UTF-8?q?=E6=89=A9=E5=B1=95ComponentRegistry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ComponentStorage/ComponentRegistry.ts | 24 +- packages/core/src/ECS/Entity.ts | 10 +- .../core/src/ECS/Utils/BigIntCompatibility.ts | 45 ++- .../Core/ComponentRegistry.extended.test.ts | 283 ++++++++++++++++++ .../tests/ECS/Core/ComponentStorage.test.ts | 12 - 5 files changed, 345 insertions(+), 29 deletions(-) create mode 100644 packages/core/tests/ECS/Core/ComponentRegistry.extended.test.ts diff --git a/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts b/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts index 7121b898..6c3500bf 100644 --- a/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts +++ b/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts @@ -20,7 +20,6 @@ export class ComponentRegistry { private static componentNameToId = new Map(); private static maskCache = new Map(); private static nextBitIndex = 0; - private static maxComponents = 64; // 支持最多64种组件类型 /** * 注册组件类型并分配位掩码 @@ -29,16 +28,12 @@ export class ComponentRegistry { */ public static register(componentType: ComponentType): number { const typeName = getComponentTypeName(componentType); - + if (this.componentTypes.has(componentType)) { const existingIndex = this.componentTypes.get(componentType)!; return existingIndex; } - if (this.nextBitIndex >= this.maxComponents) { - throw new Error(`Maximum number of component types (${this.maxComponents}) exceeded`); - } - const bitIndex = this.nextBitIndex++; this.componentTypes.set(componentType, bitIndex); this.bitIndexToType.set(bitIndex, componentType); @@ -59,7 +54,10 @@ export class ComponentRegistry { const typeName = getComponentTypeName(componentType); throw new Error(`Component type ${typeName} is not registered`); } - return BitMask64Utils.create(bitIndex); + + const mask: BitMask64Data = { lo: 0, hi: 0 }; + BitMask64Utils.setBitExtended(mask, bitIndex); + return mask; } /** @@ -94,6 +92,14 @@ export class ComponentRegistry { return (this.bitIndexToType.get(bitIndex) as ComponentType) || null; } + /** + * 获取当前已注册的组件类型数量 + * @returns 已注册数量 + */ + public static getRegisteredCount(): number { + return this.nextBitIndex; + } + /** * 通过名称获取组件类型 * @param componentName 组件名称 @@ -138,10 +144,6 @@ export class ComponentRegistry { return this.componentNameToId.get(componentName)!; } - if (this.nextBitIndex >= this.maxComponents) { - throw new Error(`Maximum number of component types (${this.maxComponents}) exceeded`); - } - const bitIndex = this.nextBitIndex++; this.componentNameToId.set(componentName, bitIndex); return bitIndex; diff --git a/packages/core/src/ECS/Entity.ts b/packages/core/src/ECS/Entity.ts index c94aa574..1de94dbc 100644 --- a/packages/core/src/ECS/Entity.ts +++ b/packages/core/src/ECS/Entity.ts @@ -188,10 +188,10 @@ export class Entity { const components: Component[] = []; const mask = this._componentMask; - // 遍历位掩码中设置的位 - for (let bitIndex = 0; bitIndex < 64; bitIndex++) { - const bitMask = BitMask64Utils.create(bitIndex); - if (BitMask64Utils.hasAny(mask, bitMask)) { + const maxBitIndex = ComponentRegistry.getRegisteredCount(); + + for (let bitIndex = 0; bitIndex < maxBitIndex; bitIndex++) { + if (BitMask64Utils.getBitExtended(mask, bitIndex)) { const componentType = ComponentRegistry.getTypeByBitIndex(bitIndex); if (componentType) { let component: Component | null = null; @@ -504,7 +504,7 @@ export class Entity { this._localComponents.delete(componentType); // 更新位掩码 - BitMask64Utils.clearBit(this._componentMask, bitIndex); + BitMask64Utils.clearBitExtended(this._componentMask, bitIndex); // 使缓存失效 this._componentCache = null; diff --git a/packages/core/src/ECS/Utils/BigIntCompatibility.ts b/packages/core/src/ECS/Utils/BigIntCompatibility.ts index 771a4a25..a75eeb07 100644 --- a/packages/core/src/ECS/Utils/BigIntCompatibility.ts +++ b/packages/core/src/ECS/Utils/BigIntCompatibility.ts @@ -61,7 +61,32 @@ export class BitMask64Utils { * @returns 如果掩码包含bits中的任意位则返回true */ public static hasAny(mask: BitMask64Data, bits: BitMask64Data): boolean { - return (mask.lo & bits.lo) !== 0 || (mask.hi & bits.hi) !== 0; + // 检查第一个 64 位段 + if ((mask.lo & bits.lo) !== 0 || (mask.hi & bits.hi) !== 0) { + return true; + } + + // 如果 bits 没有扩展段,检查完成 + if (!bits.segments || bits.segments.length === 0) { + return false; + } + + // 如果 bits 有扩展段但 mask 没有,返回 false + if (!mask.segments || mask.segments.length === 0) { + return false; + } + + // 检查每个扩展段 + const minSegments = Math.min(mask.segments.length, bits.segments.length); + for (let i = 0; i < minSegments; i++) { + const maskSeg = mask.segments[i]; + const bitsSeg = bits.segments[i]; + if ((maskSeg.lo & bitsSeg.lo) !== 0 || (maskSeg.hi & bitsSeg.hi) !== 0) { + return true; + } + } + + return false; } /** @@ -181,6 +206,24 @@ export class BitMask64Utils { public static orInPlace(target: BitMask64Data, other: BitMask64Data): void { target.lo |= other.lo; target.hi |= other.hi; + + // 处理扩展段 + if (other.segments && other.segments.length > 0) { + if (!target.segments) { + target.segments = []; + } + + // 确保 target 有足够的段 + while (target.segments.length < other.segments.length) { + target.segments.push({ lo: 0, hi: 0 }); + } + + // 对每个段执行或操作 + for (let i = 0; i < other.segments.length; i++) { + target.segments[i].lo |= other.segments[i].lo; + target.segments[i].hi |= other.segments[i].hi; + } + } } /** diff --git a/packages/core/tests/ECS/Core/ComponentRegistry.extended.test.ts b/packages/core/tests/ECS/Core/ComponentRegistry.extended.test.ts new file mode 100644 index 00000000..c78c07ab --- /dev/null +++ b/packages/core/tests/ECS/Core/ComponentRegistry.extended.test.ts @@ -0,0 +1,283 @@ +import { Component } from '../../../src/ECS/Component'; +import { ComponentRegistry } from '../../../src/ECS/Core/ComponentStorage/ComponentRegistry'; +import { Entity } from '../../../src/ECS/Entity'; +import { Scene } from '../../../src/ECS/Scene'; + +describe('ComponentRegistry Extended - 64+ 组件支持', () => { + // 组件类缓存 + const componentClassCache = new Map(); + + beforeEach(() => { + ComponentRegistry.reset(); + componentClassCache.clear(); + }); + + afterEach(() => { + ComponentRegistry.reset(); + componentClassCache.clear(); + }); + + // 动态创建或获取缓存的组件类 + function createTestComponent(index: number) { + if (componentClassCache.has(index)) { + return componentClassCache.get(index); + } + + class TestComponent extends Component { + static readonly typeName = `TestComponent${index}`; + public value: number = index; + } + + componentClassCache.set(index, TestComponent); + return TestComponent; + } + + describe('扩展组件注册', () => { + it('应该能够注册超过 64 个组件类型', () => { + const componentTypes: any[] = []; + + // 注册 100 个组件类型 + for (let i = 0; i < 100; i++) { + const ComponentClass = createTestComponent(i); + const bitIndex = ComponentRegistry.register(ComponentClass); + componentTypes.push(ComponentClass); + + expect(bitIndex).toBe(i); + expect(ComponentRegistry.isRegistered(ComponentClass)).toBe(true); + } + + expect(componentTypes.length).toBe(100); + }); + + it('应该能够获取超过 64 索引的组件位掩码', () => { + // 注册 80 个组件 + for (let i = 0; i < 80; i++) { + const ComponentClass = createTestComponent(i); + ComponentRegistry.register(ComponentClass); + } + + // 验证第 70 个组件的位掩码 + const Component70 = createTestComponent(70); + ComponentRegistry.register(Component70); + + const bitMask = ComponentRegistry.getBitMask(Component70); + expect(bitMask).toBeDefined(); + expect(bitMask.segments).toBeDefined(); // 应该有扩展段 + expect(bitMask.segments!.length).toBeGreaterThan(0); + }); + + it('应该支持超过 1000 个组件类型(无限制)', () => { + // 注册 1500 个组件验证无限制 + for (let i = 0; i < 1500; i++) { + const ComponentClass = createTestComponent(i); + const bitIndex = ComponentRegistry.register(ComponentClass); + expect(bitIndex).toBe(i); + } + + expect(ComponentRegistry.getRegisteredCount()).toBe(1500); + }); + + }); + + describe('Entity 扩展组件支持', () => { + let scene: Scene; + let entity: Entity; + + beforeEach(() => { + scene = new Scene(); + entity = scene.createEntity('TestEntity'); + }); + + it('应该能够添加和获取超过 64 个组件', () => { + const componentTypes: any[] = []; + const components: any[] = []; + + // 添加 80 个组件 + for (let i = 0; i < 80; i++) { + const ComponentClass = createTestComponent(i); + ComponentRegistry.register(ComponentClass); + componentTypes.push(ComponentClass); + + const component = new ComponentClass(); + entity.addComponent(component); + components.push(component); + } + + // 验证所有组件都能获取 + for (let i = 0; i < 80; i++) { + const ComponentClass = componentTypes[i]; + const retrieved = entity.getComponent(ComponentClass); + + expect(retrieved).toBeDefined(); + expect(retrieved).toBe(components[i]); + expect((retrieved as any).value).toBe(i); + } + }); + + it('应该能够正确检查超过 64 个组件的存在性', () => { + // 添加组件 0-79 + for (let i = 0; i < 80; i++) { + const ComponentClass = createTestComponent(i); + ComponentRegistry.register(ComponentClass); + entity.addComponent(new ComponentClass()); + } + + // 验证 hasComponent 对所有组件都工作 + for (let i = 0; i < 80; i++) { + const ComponentClass = createTestComponent(i); + expect(entity.hasComponent(ComponentClass)).toBe(true); + } + + // 验证不存在的组件 + const NonExistentComponent = createTestComponent(999); + ComponentRegistry.register(NonExistentComponent); + expect(entity.hasComponent(NonExistentComponent)).toBe(false); + }); + + it('应该能够移除超过 64 索引的组件', () => { + const componentTypes: any[] = []; + + // 添加 80 个组件 + for (let i = 0; i < 80; i++) { + const ComponentClass = createTestComponent(i); + ComponentRegistry.register(ComponentClass); + componentTypes.push(ComponentClass); + entity.addComponent(new ComponentClass()); + } + + // 移除第 70 个组件 + const Component70 = componentTypes[70]; + const component70 = entity.getComponent(Component70); + expect(component70).toBeDefined(); + + entity.removeComponent(component70!); + + // 验证已移除 + expect(entity.hasComponent(Component70)).toBe(false); + expect(entity.getComponent(Component70)).toBeNull(); + + // 验证其他组件仍然存在 + expect(entity.hasComponent(componentTypes[69])).toBe(true); + expect(entity.hasComponent(componentTypes[71])).toBe(true); + }); + + it('应该能够正确遍历超过 64 个组件', () => { + // 添加 80 个组件 + for (let i = 0; i < 80; i++) { + const ComponentClass = createTestComponent(i); + ComponentRegistry.register(ComponentClass); + entity.addComponent(new ComponentClass()); + } + + const components = entity.components; + expect(components.length).toBe(80); + + // 验证组件值 + const values = components.map((c: any) => c.value).sort((a, b) => a - b); + for (let i = 0; i < 80; i++) { + expect(values[i]).toBe(i); + } + }); + }); + + describe('性能测试', () => { + it('大量组件注册应该高效', () => { + const startTime = performance.now(); + + // 注册 200 个组件 + for (let i = 0; i < 200; i++) { + const ComponentClass = createTestComponent(i); + ComponentRegistry.register(ComponentClass); + } + + const endTime = performance.now(); + const duration = endTime - startTime; + + // 应该在 100ms 内完成 + expect(duration).toBeLessThan(100); + }); + + it('大量组件操作应该高效', () => { + const scene = new Scene(); + const entity = scene.createEntity('TestEntity'); + + // 注册 100 个组件 + const componentTypes: any[] = []; + for (let i = 0; i < 100; i++) { + const ComponentClass = createTestComponent(i); + ComponentRegistry.register(ComponentClass); + componentTypes.push(ComponentClass); + } + + const startAdd = performance.now(); + // 添加 100 个组件 + for (let i = 0; i < 100; i++) { + entity.addComponent(new componentTypes[i]()); + } + const endAdd = performance.now(); + + const startGet = performance.now(); + // 获取 100 个组件 + for (let i = 0; i < 100; i++) { + entity.getComponent(componentTypes[i]); + } + const endGet = performance.now(); + + const addDuration = endAdd - startAdd; + const getDuration = endGet - startGet; + + // 添加应该在 50ms 内 + expect(addDuration).toBeLessThan(50); + // 获取应该在 20ms 内 + expect(getDuration).toBeLessThan(20); + }); + }); + + describe('边界情况', () => { + it('应该正确处理第 64 个组件(边界)', () => { + const scene = new Scene(); + const entity = scene.createEntity('TestEntity'); + + // 注册 65 个组件(跨越 64 位边界) + for (let i = 0; i < 65; i++) { + const ComponentClass = createTestComponent(i); + ComponentRegistry.register(ComponentClass); + entity.addComponent(new ComponentClass()); + } + + // 验证第 63, 64, 65 个组件 + const Component63 = createTestComponent(63); + const Component64 = createTestComponent(64); + + expect(entity.hasComponent(Component63)).toBe(true); + expect(entity.hasComponent(Component64)).toBe(true); + }); + + it('应该在组件缓存重建时正确处理扩展位', () => { + const scene = new Scene(); + const entity = scene.createEntity('TestEntity'); + + // 添加 80 个组件 + for (let i = 0; i < 80; i++) { + const ComponentClass = createTestComponent(i); + ComponentRegistry.register(ComponentClass); + entity.addComponent(new ComponentClass()); + } + + // 强制重建缓存(通过访问 components) + const components1 = entity.components; + expect(components1.length).toBe(80); + + // 添加更多组件 + for (let i = 80; i < 90; i++) { + const ComponentClass = createTestComponent(i); + ComponentRegistry.register(ComponentClass); + entity.addComponent(new ComponentClass()); + } + + // 重新获取组件数组(应该重建缓存) + const components2 = entity.components; + expect(components2.length).toBe(90); + }); + }); +}); \ No newline at end of file diff --git a/packages/core/tests/ECS/Core/ComponentStorage.test.ts b/packages/core/tests/ECS/Core/ComponentStorage.test.ts index dbf53ebd..0e18badb 100644 --- a/packages/core/tests/ECS/Core/ComponentStorage.test.ts +++ b/packages/core/tests/ECS/Core/ComponentStorage.test.ts @@ -94,18 +94,6 @@ describe('ComponentRegistry - 组件注册表测试', () => { expect(ComponentRegistry.isRegistered(TestComponent)).toBe(true); }); - test('超过最大组件数量应该抛出错误', () => { - // 设置较小的最大组件数量用于测试 - (ComponentRegistry as any).maxComponents = 3; - - ComponentRegistry.register(TestComponent); - ComponentRegistry.register(PositionComponent); - ComponentRegistry.register(VelocityComponent); - - expect(() => { - ComponentRegistry.register(HealthComponent); - }).toThrow('Maximum number of component types (3) exceeded'); - }); }); describe('位掩码功能', () => {