使用BitMask64Data.segments扩展ComponentRegistry
This commit is contained in:
@@ -20,7 +20,6 @@ export class ComponentRegistry {
|
|||||||
private static componentNameToId = new Map<string, number>();
|
private static componentNameToId = new Map<string, number>();
|
||||||
private static maskCache = new Map<string, BitMask64Data>();
|
private static maskCache = new Map<string, BitMask64Data>();
|
||||||
private static nextBitIndex = 0;
|
private static nextBitIndex = 0;
|
||||||
private static maxComponents = 64; // 支持最多64种组件类型
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册组件类型并分配位掩码
|
* 注册组件类型并分配位掩码
|
||||||
@@ -35,10 +34,6 @@ export class ComponentRegistry {
|
|||||||
return existingIndex;
|
return existingIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.nextBitIndex >= this.maxComponents) {
|
|
||||||
throw new Error(`Maximum number of component types (${this.maxComponents}) exceeded`);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
@@ -59,7 +54,10 @@ export class ComponentRegistry {
|
|||||||
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`);
|
||||||
}
|
}
|
||||||
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;
|
return (this.bitIndexToType.get(bitIndex) as ComponentType) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前已注册的组件类型数量
|
||||||
|
* @returns 已注册数量
|
||||||
|
*/
|
||||||
|
public static getRegisteredCount(): number {
|
||||||
|
return this.nextBitIndex;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过名称获取组件类型
|
* 通过名称获取组件类型
|
||||||
* @param componentName 组件名称
|
* @param componentName 组件名称
|
||||||
@@ -138,10 +144,6 @@ export class ComponentRegistry {
|
|||||||
return this.componentNameToId.get(componentName)!;
|
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++;
|
const bitIndex = this.nextBitIndex++;
|
||||||
this.componentNameToId.set(componentName, bitIndex);
|
this.componentNameToId.set(componentName, bitIndex);
|
||||||
return bitIndex;
|
return bitIndex;
|
||||||
|
|||||||
@@ -188,10 +188,10 @@ export class Entity {
|
|||||||
const components: Component[] = [];
|
const components: Component[] = [];
|
||||||
const mask = this._componentMask;
|
const mask = this._componentMask;
|
||||||
|
|
||||||
// 遍历位掩码中设置的位
|
const maxBitIndex = ComponentRegistry.getRegisteredCount();
|
||||||
for (let bitIndex = 0; bitIndex < 64; bitIndex++) {
|
|
||||||
const bitMask = BitMask64Utils.create(bitIndex);
|
for (let bitIndex = 0; bitIndex < maxBitIndex; bitIndex++) {
|
||||||
if (BitMask64Utils.hasAny(mask, bitMask)) {
|
if (BitMask64Utils.getBitExtended(mask, bitIndex)) {
|
||||||
const componentType = ComponentRegistry.getTypeByBitIndex(bitIndex);
|
const componentType = ComponentRegistry.getTypeByBitIndex(bitIndex);
|
||||||
if (componentType) {
|
if (componentType) {
|
||||||
let component: Component | null = null;
|
let component: Component | null = null;
|
||||||
@@ -504,7 +504,7 @@ export class Entity {
|
|||||||
this._localComponents.delete(componentType);
|
this._localComponents.delete(componentType);
|
||||||
|
|
||||||
// 更新位掩码
|
// 更新位掩码
|
||||||
BitMask64Utils.clearBit(this._componentMask, bitIndex);
|
BitMask64Utils.clearBitExtended(this._componentMask, bitIndex);
|
||||||
|
|
||||||
// 使缓存失效
|
// 使缓存失效
|
||||||
this._componentCache = null;
|
this._componentCache = null;
|
||||||
|
|||||||
@@ -61,7 +61,32 @@ export class BitMask64Utils {
|
|||||||
* @returns 如果掩码包含bits中的任意位则返回true
|
* @returns 如果掩码包含bits中的任意位则返回true
|
||||||
*/
|
*/
|
||||||
public static hasAny(mask: BitMask64Data, bits: BitMask64Data): boolean {
|
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 {
|
public static orInPlace(target: BitMask64Data, other: BitMask64Data): void {
|
||||||
target.lo |= other.lo;
|
target.lo |= other.lo;
|
||||||
target.hi |= other.hi;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
283
packages/core/tests/ECS/Core/ComponentRegistry.extended.test.ts
Normal file
283
packages/core/tests/ECS/Core/ComponentRegistry.extended.test.ts
Normal file
@@ -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<number, any>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -94,18 +94,6 @@ describe('ComponentRegistry - 组件注册表测试', () => {
|
|||||||
expect(ComponentRegistry.isRegistered(TestComponent)).toBe(true);
|
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('位掩码功能', () => {
|
describe('位掩码功能', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user