整合组件类型至统一的componentregistry中

This commit is contained in:
YHH
2025-08-07 09:43:34 +08:00
parent 9a08ae74b6
commit 7a000318a6
13 changed files with 136 additions and 1652 deletions

View File

@@ -71,7 +71,7 @@
ComponentRegistry • IdentifierPool ComponentRegistry • IdentifierPool
</text> </text>
<text x="700" y="105" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white"> <text x="700" y="105" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
BitMaskOptimizer • ConfigManager EventBus • ConfigManager
</text> </text>
</g> </g>

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -1,431 +0,0 @@
# 位掩码使用指南
本文档详细解释ECS框架中位掩码的概念、原理和使用方法。
## 目录
1. [什么是位掩码](#什么是位掩码)
2. [位掩码的优势](#位掩码的优势)
3. [基础使用方法](#基础使用方法)
4. [高级位掩码操作](#高级位掩码操作)
5. [实际应用场景](#实际应用场景)
6. [性能优化技巧](#性能优化技巧)
## 什么是位掩码
### 基本概念
位掩码BitMask是一种使用二进制位来表示状态或属性的技术。在ECS框架中每个组件类型对应一个二进制位实体的组件组合可以用一个数字来表示。
### 简单例子
假设我们有以下组件:
- PositionComponent → 位置 0 (二进制: 001)
- VelocityComponent → 位置 1 (二进制: 010)
- HealthComponent → 位置 2 (二进制: 100)
那么一个同时拥有Position和Health组件的实体其位掩码就是
```
001 (Position) + 100 (Health) = 101 (二进制) = 5 (十进制)
```
### 可视化理解
```typescript
// 组件类型对应的位位置
PositionComponent 0 2^0 = 1 二进制: 001
VelocityComponent 1 2^1 = 2 二进制: 010
HealthComponent 2 2^2 = 4 二进制: 100
RenderComponent 3 2^3 = 8 二进制: 1000
// 实体的组件组合示例
实体A: Position + Velocity 001 + 010 = 011 () = 3 ()
实体B: Position + Health 001 + 100 = 101 () = 5 ()
实体C: Position + Velocity + Health 001 + 010 + 100 = 111 () = 7 ()
```
## 位掩码的优势
### 1. 极快的查询速度
```typescript
// 传统方式:需要遍历组件列表
function hasComponents(entity, componentTypes) {
for (const type of componentTypes) {
if (!entity.hasComponent(type)) {
return false;
}
}
return true;
}
// 位掩码方式:一次位运算即可
function hasComponentsMask(entityMask, requiredMask) {
return (entityMask & requiredMask) === requiredMask;
}
```
### 2. 内存效率
```typescript
// 一个bigint可以表示64个组件的组合状态
// 相比存储组件列表,内存使用量大大减少
const entity = scene.createEntity("Player");
entity.addComponent(new PositionComponent());
entity.addComponent(new HealthComponent());
// 获取位掩码(只是一个数字)
const mask = entity.componentMask; // bigint类型
console.log(`位掩码: ${mask}`); // 输出: 5 (二进制: 101)
```
### 3. 批量操作优化
```typescript
// 可以快速筛选大量实体
const entities = scene.getAllEntities();
const requiredMask = BigInt(0b101); // Position + Health
const filteredEntities = entities.filter(entity =>
(entity.componentMask & requiredMask) === requiredMask
);
```
## 基础使用方法
### 获取实体的位掩码
```typescript
import { Scene, Entity, Component } from '@esengine/ecs-framework';
// 创建组件
class PositionComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
}
}
class HealthComponent extends Component {
constructor(public maxHealth: number = 100) {
super();
}
}
// 创建实体并添加组件
const scene = new Scene();
const entity = scene.createEntity("Player");
console.log(`初始位掩码: ${entity.componentMask}`); // 0
entity.addComponent(new PositionComponent(100, 200));
console.log(`添加Position后: ${entity.componentMask}`); // 可能是 1
entity.addComponent(new HealthComponent(100));
console.log(`添加Health后: ${entity.componentMask}`); // 可能是 5
// 查看二进制表示
console.log(`二进制表示: ${entity.componentMask.toString(2)}`);
```
### 手动检查位掩码
```typescript
// 检查实体是否拥有特定组件组合
function checkEntityComponents(entity: Entity) {
const mask = entity.componentMask;
// 将位掩码转换为二进制字符串查看
const binaryString = mask.toString(2).padStart(8, '0');
console.log(`实体组件状态: ${binaryString}`);
// 分析每一位
console.log(`位0 (Position): ${(mask & 1n) !== 0n ? '有' : '无'}`);
console.log(`位1 (Velocity): ${(mask & 2n) !== 0n ? '有' : '无'}`);
console.log(`位2 (Health): ${(mask & 4n) !== 0n ? '有' : '无'}`);
console.log(`位3 (Render): ${(mask & 8n) !== 0n ? '有' : '无'}`);
}
```
## 高级位掩码操作
### 使用BitMaskOptimizer
框架提供了BitMaskOptimizer类来简化位掩码操作
```typescript
import { BitMaskOptimizer } from '@esengine/ecs-framework';
// 获取优化器实例
const optimizer = BitMaskOptimizer.getInstance();
// 注册组件类型(建议在游戏初始化时进行)
optimizer.registerComponentType('PositionComponent');
optimizer.registerComponentType('VelocityComponent');
optimizer.registerComponentType('HealthComponent');
optimizer.registerComponentType('RenderComponent');
// 创建单个组件的掩码
const positionMask = optimizer.createSingleComponentMask('PositionComponent');
console.log(`Position掩码: ${positionMask} (二进制: ${positionMask.toString(2)})`);
// 创建组合掩码
const movementMask = optimizer.createCombinedMask(['PositionComponent', 'VelocityComponent']);
console.log(`Movement掩码: ${movementMask} (二进制: ${movementMask.toString(2)})`);
// 检查实体是否匹配掩码
const entity = scene.createEntity("TestEntity");
entity.addComponent(new PositionComponent());
entity.addComponent(new VelocityComponent());
const hasMovementComponents = optimizer.maskContainsAllComponents(
entity.componentMask,
['PositionComponent', 'VelocityComponent']
);
console.log(`实体拥有移动组件: ${hasMovementComponents}`);
```
### 位掩码分析工具
```typescript
// 分析位掩码的实用函数
class MaskAnalyzer {
private optimizer = BitMaskOptimizer.getInstance();
// 分析实体的组件组合
analyzeEntity(entity: Entity): void {
const mask = entity.componentMask;
const componentNames = this.optimizer.maskToComponentNames(mask);
const componentCount = this.optimizer.getComponentCount(mask);
console.log(`实体 ${entity.name} 分析:`);
console.log(`- 位掩码: ${mask} (二进制: ${mask.toString(2)})`);
console.log(`- 组件数量: ${componentCount}`);
console.log(`- 组件列表: ${componentNames.join(', ')}`);
}
// 比较两个实体的组件差异
compareEntities(entityA: Entity, entityB: Entity): void {
const maskA = entityA.componentMask;
const maskB = entityB.componentMask;
const commonMask = maskA & maskB;
const onlyInA = maskA & ~maskB;
const onlyInB = maskB & ~maskA;
console.log(`实体比较:`);
console.log(`- 共同组件: ${this.optimizer.maskToComponentNames(commonMask).join(', ')}`);
console.log(`- 仅在A中: ${this.optimizer.maskToComponentNames(onlyInA).join(', ')}`);
console.log(`- 仅在B中: ${this.optimizer.maskToComponentNames(onlyInB).join(', ')}`);
}
// 查找具有特定组件组合的实体
findEntitiesWithMask(entities: Entity[], requiredComponents: string[]): Entity[] {
const requiredMask = this.optimizer.createCombinedMask(requiredComponents);
return entities.filter(entity =>
(entity.componentMask & requiredMask) === requiredMask
);
}
}
// 使用示例
const analyzer = new MaskAnalyzer();
analyzer.analyzeEntity(entity);
```
## 实际应用场景
### 1. 高性能实体查询
```typescript
class GameSystem {
private optimizer = BitMaskOptimizer.getInstance();
private movementMask: bigint;
private combatMask: bigint;
constructor() {
// 预计算常用掩码
this.movementMask = this.optimizer.createCombinedMask([
'PositionComponent', 'VelocityComponent'
]);
this.combatMask = this.optimizer.createCombinedMask([
'PositionComponent', 'HealthComponent', 'WeaponComponent'
]);
}
// 快速查找移动实体
findMovingEntities(entities: Entity[]): Entity[] {
return entities.filter(entity =>
(entity.componentMask & this.movementMask) === this.movementMask
);
}
// 快速查找战斗单位
findCombatUnits(entities: Entity[]): Entity[] {
return entities.filter(entity =>
(entity.componentMask & this.combatMask) === this.combatMask
);
}
}
```
### 2. 实体分类和管理
```typescript
class EntityClassifier {
private optimizer = BitMaskOptimizer.getInstance();
// 定义实体类型掩码
private readonly ENTITY_TYPES = {
PLAYER: this.optimizer.createCombinedMask([
'PositionComponent', 'HealthComponent', 'InputComponent'
]),
ENEMY: this.optimizer.createCombinedMask([
'PositionComponent', 'HealthComponent', 'AIComponent'
]),
PROJECTILE: this.optimizer.createCombinedMask([
'PositionComponent', 'VelocityComponent', 'DamageComponent'
]),
PICKUP: this.optimizer.createCombinedMask([
'PositionComponent', 'PickupComponent'
])
};
// 根据组件组合判断实体类型
classifyEntity(entity: Entity): string {
const mask = entity.componentMask;
for (const [type, typeMask] of Object.entries(this.ENTITY_TYPES)) {
if ((mask & typeMask) === typeMask) {
return type;
}
}
return 'UNKNOWN';
}
// 批量分类实体
classifyEntities(entities: Entity[]): Map<string, Entity[]> {
const classified = new Map<string, Entity[]>();
for (const entity of entities) {
const type = this.classifyEntity(entity);
if (!classified.has(type)) {
classified.set(type, []);
}
classified.get(type)!.push(entity);
}
return classified;
}
}
```
## 性能优化技巧
### 1. 预计算常用掩码
```typescript
class MaskCache {
private optimizer = BitMaskOptimizer.getInstance();
// 预计算游戏中常用的组件组合
public readonly COMMON_MASKS = {
RENDERABLE: this.optimizer.createCombinedMask([
'PositionComponent', 'RenderComponent'
]),
MOVABLE: this.optimizer.createCombinedMask([
'PositionComponent', 'VelocityComponent'
]),
LIVING: this.optimizer.createCombinedMask([
'HealthComponent'
]),
INTERACTIVE: this.optimizer.createCombinedMask([
'PositionComponent', 'ColliderComponent'
])
};
constructor() {
// 预计算常用组合以提升性能
this.optimizer.precomputeCommonMasks([
['PositionComponent', 'RenderComponent'],
['PositionComponent', 'VelocityComponent'],
['PositionComponent', 'HealthComponent', 'WeaponComponent']
]);
}
}
```
### 2. 位掩码调试工具
```typescript
// 位掩码调试工具
class MaskDebugger {
private optimizer = BitMaskOptimizer.getInstance();
// 可视化位掩码
visualizeMask(mask: bigint, maxBits: number = 16): string {
const binary = mask.toString(2).padStart(maxBits, '0');
const components = this.optimizer.maskToComponentNames(mask);
let visualization = `位掩码: ${mask} (二进制: ${binary})\n`;
visualization += `组件: ${components.join(', ')}\n`;
visualization += `可视化: `;
for (let i = maxBits - 1; i >= 0; i--) {
const bit = (mask & (1n << BigInt(i))) !== 0n ? '1' : '0';
visualization += bit;
if (i % 4 === 0 && i > 0) visualization += ' ';
}
return visualization;
}
}
```
## 最佳实践
### 1. 组件注册
```typescript
// 在游戏初始化时注册所有组件类型
function initializeComponentTypes() {
const optimizer = BitMaskOptimizer.getInstance();
// 按使用频率注册(常用的组件分配较小的位位置)
optimizer.registerComponentType('PositionComponent'); // 位置0
optimizer.registerComponentType('VelocityComponent'); // 位置1
optimizer.registerComponentType('HealthComponent'); // 位置2
optimizer.registerComponentType('RenderComponent'); // 位置3
// ... 其他组件
}
```
### 2. 掩码缓存管理
```typescript
// 定期清理掩码缓存以避免内存泄漏
setInterval(() => {
const optimizer = BitMaskOptimizer.getInstance();
const stats = optimizer.getCacheStats();
// 如果缓存过大,清理一部分
if (stats.size > 1000) {
optimizer.clearCache();
console.log('位掩码缓存已清理');
}
}, 60000); // 每分钟检查一次
```
## 总结
位掩码是ECS框架中的核心优化技术它提供了
1. **极快的查询速度** - 位运算比遍历快数百倍
2. **内存效率** - 用一个数字表示复杂的组件组合
3. **批量操作优化** - 可以快速处理大量实体
4. **灵活的查询构建** - 支持复杂的组件组合查询
通过理解和正确使用位掩码,可以显著提升游戏的性能表现。记住要在游戏初始化时注册组件类型,预计算常用掩码,并合理管理缓存。

View File

@@ -1,214 +0,0 @@
import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
/**
* 位掩码优化器,用于预计算和缓存常用的组件掩码
*
* 使用BigInt兼容层确保在所有平台上的正常运行。
*/
export class BitMaskOptimizer {
private static instance: BitMaskOptimizer;
private maskCache = new Map<string, IBigIntLike>();
private componentTypeMap = new Map<string, number>();
private nextComponentId = 0;
private constructor() {}
static getInstance(): BitMaskOptimizer {
if (!BitMaskOptimizer.instance) {
BitMaskOptimizer.instance = new BitMaskOptimizer();
}
return BitMaskOptimizer.instance;
}
/**
* 注册组件类型
*/
registerComponentType(componentName: string): number {
if (!this.componentTypeMap.has(componentName)) {
this.componentTypeMap.set(componentName, this.nextComponentId++);
}
return this.componentTypeMap.get(componentName)!;
}
/**
* 获取组件类型ID
*/
getComponentTypeId(componentName: string): number | undefined {
return this.componentTypeMap.get(componentName);
}
/**
* 创建单个组件的掩码
* @param componentName 组件名称
* @returns 组件掩码
*/
createSingleComponentMask(componentName: string): IBigIntLike {
const cacheKey = `single:${componentName}`;
if (this.maskCache.has(cacheKey)) {
return this.maskCache.get(cacheKey)!;
}
const componentId = this.getComponentTypeId(componentName);
if (componentId === undefined) {
throw new Error(`Component type not registered: ${componentName}`);
}
const mask = BigIntFactory.one().shiftLeft(componentId);
this.maskCache.set(cacheKey, mask);
return mask;
}
/**
* 创建多个组件的组合掩码
* @param componentNames 组件名称数组
* @returns 组合掩码
*/
createCombinedMask(componentNames: string[]): IBigIntLike {
const sortedNames = [...componentNames].sort();
const cacheKey = `combined:${sortedNames.join(',')}`;
if (this.maskCache.has(cacheKey)) {
return this.maskCache.get(cacheKey)!;
}
let mask = BigIntFactory.zero();
for (const componentName of componentNames) {
const componentId = this.getComponentTypeId(componentName);
if (componentId === undefined) {
throw new Error(`Component type not registered: ${componentName}`);
}
const componentMask = BigIntFactory.one().shiftLeft(componentId);
mask = mask.or(componentMask);
}
this.maskCache.set(cacheKey, mask);
return mask;
}
/**
* 检查掩码是否包含指定组件
* @param mask 要检查的掩码
* @param componentName 组件名称
* @returns 是否包含指定组件
*/
maskContainsComponent(mask: IBigIntLike, componentName: string): boolean {
const componentMask = this.createSingleComponentMask(componentName);
return !mask.and(componentMask).isZero();
}
/**
* 检查掩码是否包含所有指定组件
* @param mask 要检查的掩码
* @param componentNames 组件名称数组
* @returns 是否包含所有指定组件
*/
maskContainsAllComponents(mask: IBigIntLike, componentNames: string[]): boolean {
const requiredMask = this.createCombinedMask(componentNames);
const intersection = mask.and(requiredMask);
return intersection.equals(requiredMask);
}
/**
* 检查掩码是否包含任一指定组件
* @param mask 要检查的掩码
* @param componentNames 组件名称数组
* @returns 是否包含任一指定组件
*/
maskContainsAnyComponent(mask: IBigIntLike, componentNames: string[]): boolean {
const anyMask = this.createCombinedMask(componentNames);
return !mask.and(anyMask).isZero();
}
/**
* 添加组件到掩码
* @param mask 原始掩码
* @param componentName 要添加的组件名称
* @returns 新的掩码
*/
addComponentToMask(mask: IBigIntLike, componentName: string): IBigIntLike {
const componentMask = this.createSingleComponentMask(componentName);
return mask.or(componentMask);
}
/**
* 从掩码中移除组件
* @param mask 原始掩码
* @param componentName 要移除的组件名称
* @returns 新的掩码
*/
removeComponentFromMask(mask: IBigIntLike, componentName: string): IBigIntLike {
const componentMask = this.createSingleComponentMask(componentName);
const notComponentMask = componentMask.not();
return mask.and(notComponentMask);
}
/**
* 预计算常用掩码组合
*/
precomputeCommonMasks(commonCombinations: string[][]): void {
for (const combination of commonCombinations) {
this.createCombinedMask(combination);
}
}
/**
* 获取掩码缓存统计信息
*/
getCacheStats(): { size: number; componentTypes: number } {
return {
size: this.maskCache.size,
componentTypes: this.componentTypeMap.size
};
}
/**
* 清空缓存
*/
clearCache(): void {
this.maskCache.clear();
}
/**
* 重置优化器
*/
reset(): void {
this.maskCache.clear();
this.componentTypeMap.clear();
this.nextComponentId = 0;
}
/**
* 将掩码转换为组件名称数组
*/
maskToComponentNames(mask: IBigIntLike): string[] {
const componentNames: string[] = [];
for (const [componentName, componentId] of this.componentTypeMap) {
const componentMask = BigIntFactory.one().shiftLeft(componentId);
if (!mask.and(componentMask).isZero()) {
componentNames.push(componentName);
}
}
return componentNames;
}
/**
* 获取掩码中组件的数量
*/
getComponentCount(mask: IBigIntLike): number {
let count = 0;
let tempMask = mask.clone();
const one = BigIntFactory.one();
while (!tempMask.isZero()) {
if (!tempMask.and(one).isZero()) {
count++;
}
tempMask = tempMask.shiftRight(1);
}
return count;
}
}

View File

@@ -16,6 +16,9 @@ export type ComponentType<T extends Component = Component> = new (...args: unkno
*/ */
export class ComponentRegistry { export class ComponentRegistry {
private static componentTypes = new Map<Function, number>(); private static componentTypes = new Map<Function, number>();
private static componentNameToType = new Map<string, Function>();
private static componentNameToId = new Map<string, number>();
private static maskCache = new Map<string, IBigIntLike>();
private static nextBitIndex = 0; private static nextBitIndex = 0;
private static maxComponents = 64; // 支持最多64种组件类型 private static maxComponents = 64; // 支持最多64种组件类型
@@ -35,6 +38,8 @@ export class ComponentRegistry {
const bitIndex = this.nextBitIndex++; const bitIndex = this.nextBitIndex++;
this.componentTypes.set(componentType, bitIndex); this.componentTypes.set(componentType, bitIndex);
this.componentNameToType.set(componentType.name, componentType);
this.componentNameToId.set(componentType.name, bitIndex);
return bitIndex; return bitIndex;
} }
@@ -73,6 +78,15 @@ export class ComponentRegistry {
return this.componentTypes.has(componentType); return this.componentTypes.has(componentType);
} }
/**
* 通过名称获取组件类型
* @param componentName 组件名称
* @returns 组件类型构造函数
*/
public static getComponentType(componentName: string): Function | null {
return this.componentNameToType.get(componentName) || null;
}
/** /**
* 获取所有已注册的组件类型 * 获取所有已注册的组件类型
* @returns 组件类型映射 * @returns 组件类型映射
@@ -81,11 +95,104 @@ export class ComponentRegistry {
return new Map(this.componentTypes); return new Map(this.componentTypes);
} }
/**
* 获取所有组件名称到类型的映射
* @returns 名称到类型的映射
*/
public static getAllComponentNames(): Map<string, Function> {
return new Map(this.componentNameToType);
}
/**
* 通过名称获取组件类型ID
* @param componentName 组件名称
* @returns 组件类型ID
*/
public static getComponentId(componentName: string): number | undefined {
return this.componentNameToId.get(componentName);
}
/**
* 注册组件类型(通过名称)
* @param componentName 组件名称
* @returns 分配的组件ID
*/
public static registerComponentByName(componentName: string): number {
if (this.componentNameToId.has(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++;
this.componentNameToId.set(componentName, bitIndex);
return bitIndex;
}
/**
* 创建单个组件的掩码
* @param componentName 组件名称
* @returns 组件掩码
*/
public static createSingleComponentMask(componentName: string): IBigIntLike {
const cacheKey = `single:${componentName}`;
if (this.maskCache.has(cacheKey)) {
return this.maskCache.get(cacheKey)!;
}
const componentId = this.getComponentId(componentName);
if (componentId === undefined) {
throw new Error(`Component type ${componentName} is not registered`);
}
const mask = BigIntFactory.one().shiftLeft(componentId);
this.maskCache.set(cacheKey, mask);
return mask;
}
/**
* 创建多个组件的掩码
* @param componentNames 组件名称数组
* @returns 组合掩码
*/
public static createComponentMask(componentNames: string[]): IBigIntLike {
const sortedNames = [...componentNames].sort();
const cacheKey = `multi:${sortedNames.join(',')}`;
if (this.maskCache.has(cacheKey)) {
return this.maskCache.get(cacheKey)!;
}
let mask = BigIntFactory.zero();
for (const name of componentNames) {
const componentId = this.getComponentId(name);
if (componentId !== undefined) {
mask = mask.or(BigIntFactory.one().shiftLeft(componentId));
}
}
this.maskCache.set(cacheKey, mask);
return mask;
}
/**
* 清除掩码缓存
*/
public static clearMaskCache(): void {
this.maskCache.clear();
}
/** /**
* 重置注册表(用于测试) * 重置注册表(用于测试)
*/ */
public static reset(): void { public static reset(): void {
this.componentTypes.clear(); this.componentTypes.clear();
this.componentNameToType.clear();
this.componentNameToId.clear();
this.maskCache.clear();
this.nextBitIndex = 0; this.nextBitIndex = 0;
} }
} }

View File

@@ -1,256 +0,0 @@
import { Entity } from '../Entity';
/**
* 索引更新操作类型
*/
export enum IndexUpdateType {
ADD_ENTITY = 'add_entity',
REMOVE_ENTITY = 'remove_entity',
UPDATE_ENTITY = 'update_entity'
}
/**
* 索引更新操作
*/
export interface IndexUpdateOperation {
type: IndexUpdateType;
entity: Entity;
oldMask?: bigint;
newMask?: bigint;
}
/**
* 延迟索引更新器,用于批量更新查询索引以提高性能
*/
export class IndexUpdateBatcher {
private pendingOperations: IndexUpdateOperation[] = [];
private isProcessing = false;
private batchSize = 1000;
private flushTimeout: NodeJS.Timeout | null = null;
private flushDelay = 16; // 16ms约60fps
/**
* 添加索引更新操作
*/
addOperation(operation: IndexUpdateOperation): void {
this.pendingOperations.push(operation);
// 如果达到批量大小,立即处理
if (this.pendingOperations.length >= this.batchSize) {
this.flush();
} else {
// 否则延迟处理
this.scheduleFlush();
}
}
/**
* 批量添加实体
*/
addEntities(entities: Entity[]): void {
for (const entity of entities) {
this.pendingOperations.push({
type: IndexUpdateType.ADD_ENTITY,
entity
});
}
if (this.pendingOperations.length >= this.batchSize) {
this.flush();
} else {
this.scheduleFlush();
}
}
/**
* 批量移除实体
*/
removeEntities(entities: Entity[]): void {
for (const entity of entities) {
this.pendingOperations.push({
type: IndexUpdateType.REMOVE_ENTITY,
entity
});
}
if (this.pendingOperations.length >= this.batchSize) {
this.flush();
} else {
this.scheduleFlush();
}
}
/**
* 批量更新实体
*/
updateEntities(updates: Array<{ entity: Entity; oldMask: bigint; newMask: bigint }>): void {
for (const update of updates) {
this.pendingOperations.push({
type: IndexUpdateType.UPDATE_ENTITY,
entity: update.entity,
oldMask: update.oldMask,
newMask: update.newMask
});
}
if (this.pendingOperations.length >= this.batchSize) {
this.flush();
} else {
this.scheduleFlush();
}
}
/**
* 安排延迟刷新
*/
private scheduleFlush(): void {
if (this.flushTimeout) {
return;
}
this.flushTimeout = setTimeout(() => {
this.flush();
}, this.flushDelay);
}
/**
* 立即处理所有待处理的操作
*/
flush(): void {
if (this.isProcessing || this.pendingOperations.length === 0) {
return;
}
this.isProcessing = true;
if (this.flushTimeout) {
clearTimeout(this.flushTimeout);
this.flushTimeout = null;
}
try {
this.processBatch();
} finally {
this.isProcessing = false;
}
}
/**
* 处理批量操作
*/
private processBatch(): void {
const operations = this.pendingOperations;
this.pendingOperations = [];
// 按操作类型分组以优化处理
const addOperations: Entity[] = [];
const removeOperations: Entity[] = [];
const updateOperations: Array<{ entity: Entity; oldMask: bigint; newMask: bigint }> = [];
for (const operation of operations) {
switch (operation.type) {
case IndexUpdateType.ADD_ENTITY:
addOperations.push(operation.entity);
break;
case IndexUpdateType.REMOVE_ENTITY:
removeOperations.push(operation.entity);
break;
case IndexUpdateType.UPDATE_ENTITY:
if (operation.oldMask !== undefined && operation.newMask !== undefined) {
updateOperations.push({
entity: operation.entity,
oldMask: operation.oldMask,
newMask: operation.newMask
});
}
break;
}
}
// 批量处理每种类型的操作
if (addOperations.length > 0) {
this.processBatchAdd(addOperations);
}
if (removeOperations.length > 0) {
this.processBatchRemove(removeOperations);
}
if (updateOperations.length > 0) {
this.processBatchUpdate(updateOperations);
}
}
/**
* 批量处理添加操作
*/
private processBatchAdd(entities: Entity[]): void {
// 这里应该调用QuerySystem的批量添加方法
// 由于需要访问QuerySystem这个方法应该由外部注入处理函数
if (this.onBatchAdd) {
this.onBatchAdd(entities);
}
}
/**
* 批量处理移除操作
*/
private processBatchRemove(entities: Entity[]): void {
if (this.onBatchRemove) {
this.onBatchRemove(entities);
}
}
/**
* 批量处理更新操作
*/
private processBatchUpdate(updates: Array<{ entity: Entity; oldMask: bigint; newMask: bigint }>): void {
if (this.onBatchUpdate) {
this.onBatchUpdate(updates);
}
}
/**
* 设置批量大小
*/
setBatchSize(size: number): void {
this.batchSize = Math.max(1, size);
}
/**
* 设置刷新延迟
*/
setFlushDelay(delay: number): void {
this.flushDelay = Math.max(0, delay);
}
/**
* 获取待处理操作数量
*/
getPendingCount(): number {
return this.pendingOperations.length;
}
/**
* 清空所有待处理操作
*/
clear(): void {
this.pendingOperations.length = 0;
if (this.flushTimeout) {
clearTimeout(this.flushTimeout);
this.flushTimeout = null;
}
}
/**
* 检查是否有待处理操作
*/
hasPendingOperations(): boolean {
return this.pendingOperations.length > 0;
}
// 回调函数,由外部设置
public onBatchAdd?: (entities: Entity[]) => void;
public onBatchRemove?: (entities: Entity[]) => void;
public onBatchUpdate?: (updates: Array<{ entity: Entity; oldMask: bigint; newMask: bigint }>) => void;
}

View File

@@ -18,5 +18,4 @@ export {
DirtyListener DirtyListener
} from '../DirtyTrackingSystem'; } from '../DirtyTrackingSystem';
export { IndexUpdateBatcher } from '../IndexUpdateBatcher';
export { BitMaskOptimizer } from '../BitMaskOptimizer';

View File

@@ -4,8 +4,6 @@ import { ComponentRegistry, ComponentType } from './ComponentStorage';
import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility'; import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
import { ComponentPoolManager } from './ComponentPool'; import { ComponentPoolManager } from './ComponentPool';
import { BitMaskOptimizer } from './BitMaskOptimizer';
import { IndexUpdateBatcher } from './IndexUpdateBatcher';
import { ComponentIndexManager, IndexType } from './ComponentIndex'; import { ComponentIndexManager, IndexType } from './ComponentIndex';
import { ArchetypeSystem, Archetype, ArchetypeQueryResult } from './ArchetypeSystem'; import { ArchetypeSystem, Archetype, ArchetypeQueryResult } from './ArchetypeSystem';
import { DirtyTrackingSystem, DirtyFlag } from './DirtyTrackingSystem'; import { DirtyTrackingSystem, DirtyFlag } from './DirtyTrackingSystem';
@@ -98,8 +96,6 @@ export class QuerySystem {
// 优化组件 // 优化组件
private componentPoolManager: ComponentPoolManager; private componentPoolManager: ComponentPoolManager;
private bitMaskOptimizer: BitMaskOptimizer;
private indexUpdateBatcher: IndexUpdateBatcher;
// 新增性能优化系统 // 新增性能优化系统
private componentIndexManager: ComponentIndexManager; private componentIndexManager: ComponentIndexManager;
@@ -126,33 +122,10 @@ export class QuerySystem {
// 初始化优化组件 // 初始化优化组件
this.componentPoolManager = ComponentPoolManager.getInstance(); this.componentPoolManager = ComponentPoolManager.getInstance();
this.bitMaskOptimizer = BitMaskOptimizer.getInstance();
this.indexUpdateBatcher = new IndexUpdateBatcher();
// 初始化新的性能优化系统 // 初始化新的性能优化系统
this.componentIndexManager = new ComponentIndexManager(IndexType.HASH); this.componentIndexManager = new ComponentIndexManager(IndexType.HASH);
this.archetypeSystem = new ArchetypeSystem(); this.archetypeSystem = new ArchetypeSystem();
this.dirtyTrackingSystem = new DirtyTrackingSystem(); this.dirtyTrackingSystem = new DirtyTrackingSystem();
// 设置索引更新批处理器的回调
this.indexUpdateBatcher.onBatchAdd = (entities) => {
for (const entity of entities) {
this.addEntityToIndexes(entity);
}
};
this.indexUpdateBatcher.onBatchRemove = (entities) => {
for (const entity of entities) {
this.removeEntityFromIndexes(entity);
}
};
this.indexUpdateBatcher.onBatchUpdate = (updates) => {
for (const update of updates) {
this.removeEntityFromIndexes(update.entity);
this.addEntityToIndexes(update.entity);
}
};
} }

View File

@@ -13,18 +13,8 @@ import {
isProtoSerializable, isProtoSerializable,
getProtoName getProtoName
} from './ProtobufDecorators'; } from './ProtobufDecorators';
import { SerializedData } from './SerializationTypes';
/**
* 序列化数据接口
*/
export interface SerializedData {
/** 组件类型名称 */
componentType: string;
/** 序列化后的数据 */
data: Uint8Array;
/** 数据大小(字节) */
size: number;
}
/** /**
* Protobuf序列化器 * Protobuf序列化器
@@ -143,6 +133,7 @@ export class ProtobufSerializer {
const buffer = MessageType.encode(message).finish(); const buffer = MessageType.encode(message).finish();
return { return {
type: 'protobuf',
componentType: componentType, componentType: componentType,
data: buffer, data: buffer,
size: buffer.length size: buffer.length
@@ -245,6 +236,7 @@ export class ProtobufSerializer {
const buffer = MessageType.encode(message).finish(); const buffer = MessageType.encode(message).finish();
results.push({ results.push({
type: 'protobuf',
componentType: component.constructor.name, componentType: component.constructor.name,
data: buffer, data: buffer,
size: buffer.length size: buffer.length

View File

@@ -0,0 +1,17 @@
/**
* 序列化类型定义
*/
/**
* 序列化数据接口
*/
export interface SerializedData {
/** 序列化类型 */
type: 'protobuf';
/** 组件类型名称 */
componentType: string;
/** 序列化后的数据 */
data: Uint8Array;
/** 数据大小(字节) */
size: number;
}

View File

@@ -1,371 +0,0 @@
/**
* 静态Protobuf序列化器
*
* 使用预生成的protobuf静态模块进行序列化
*/
import { Component } from '../../ECS/Component';
import {
ProtobufRegistry,
ProtoComponentDefinition,
isProtoSerializable,
getProtoName
} from './ProtobufDecorators';
/**
* 序列化数据接口
*/
export interface SerializedData {
/** 序列化类型 */
type: 'protobuf' | 'json';
/** 组件类型名称 */
componentType: string;
/** 序列化后的数据 */
data: Uint8Array | any;
/** 数据大小(字节) */
size: number;
}
/**
* 静态Protobuf序列化器
*
* 使用CLI预生成的protobuf静态模块
*/
export class StaticProtobufSerializer {
private registry: ProtobufRegistry;
private static instance: StaticProtobufSerializer;
/** 预生成的protobuf根对象 */
private protobufRoot: any = null;
private isInitialized: boolean = false;
private constructor() {
this.registry = ProtobufRegistry.getInstance();
this.initializeStaticProtobuf();
}
public static getInstance(): StaticProtobufSerializer {
if (!StaticProtobufSerializer.instance) {
StaticProtobufSerializer.instance = new StaticProtobufSerializer();
}
return StaticProtobufSerializer.instance;
}
/**
* 初始化静态protobuf模块
*/
private async initializeStaticProtobuf(): Promise<void> {
try {
// 尝试加载预生成的protobuf模块
const ecsProto = await this.loadGeneratedProtobuf();
if (ecsProto && ecsProto.ecs) {
this.protobufRoot = ecsProto.ecs;
this.isInitialized = true;
console.log('[StaticProtobufSerializer] 预生成的Protobuf模块已加载');
} else {
console.warn('[StaticProtobufSerializer] 未找到预生成的protobuf模块将使用JSON序列化');
console.log('💡 请运行: npm run proto:build');
}
} catch (error) {
console.warn('[StaticProtobufSerializer] 初始化失败将使用JSON序列化:', error.message);
}
}
/**
* 加载预生成的protobuf模块
*/
private async loadGeneratedProtobuf(): Promise<any> {
const possiblePaths = [
// 项目中的生成路径
'./generated/ecs-components',
'../generated/ecs-components',
'../../generated/ecs-components',
// 相对于当前文件的路径
'../../../generated/ecs-components'
];
for (const path of possiblePaths) {
try {
const module = await import(path);
return module;
} catch (error) {
// 继续尝试下一个路径
continue;
}
}
// 如果所有路径都失败尝试require方式
for (const path of possiblePaths) {
try {
const module = require(path);
return module;
} catch (error) {
continue;
}
}
return null;
}
/**
* 序列化组件
*/
public serialize(component: Component): SerializedData {
const componentType = component.constructor.name;
// 检查是否支持protobuf序列化
if (!isProtoSerializable(component) || !this.isInitialized) {
return this.fallbackToJSON(component);
}
try {
const protoName = getProtoName(component);
if (!protoName) {
return this.fallbackToJSON(component);
}
const definition = this.registry.getComponentDefinition(protoName);
if (!definition) {
console.warn(`[StaticProtobufSerializer] 未找到组件定义: ${protoName}`);
return this.fallbackToJSON(component);
}
// 获取对应的protobuf消息类型
const MessageType = this.protobufRoot[protoName];
if (!MessageType) {
console.warn(`[StaticProtobufSerializer] 未找到protobuf消息类型: ${protoName}`);
return this.fallbackToJSON(component);
}
// 构建protobuf数据对象
const protoData = this.buildProtoData(component, definition);
// 验证数据
const error = MessageType.verify(protoData);
if (error) {
console.warn(`[StaticProtobufSerializer] 数据验证失败: ${error}`);
return this.fallbackToJSON(component);
}
// 创建消息并编码
const message = MessageType.create(protoData);
const buffer = MessageType.encode(message).finish();
return {
type: 'protobuf',
componentType: componentType,
data: buffer,
size: buffer.length
};
} catch (error) {
console.warn(`[StaticProtobufSerializer] 序列化失败回退到JSON: ${componentType}`, error);
return this.fallbackToJSON(component);
}
}
/**
* 反序列化组件
*/
public deserialize(component: Component, serializedData: SerializedData): void {
if (serializedData.type === 'json') {
this.deserializeFromJSON(component, serializedData.data);
return;
}
if (!this.isInitialized) {
console.warn('[StaticProtobufSerializer] Protobuf未初始化无法反序列化');
return;
}
try {
const protoName = getProtoName(component);
if (!protoName) {
this.deserializeFromJSON(component, serializedData.data);
return;
}
const MessageType = this.protobufRoot[protoName];
if (!MessageType) {
console.warn(`[StaticProtobufSerializer] 反序列化时未找到消息类型: ${protoName}`);
return;
}
// 解码消息
const message = MessageType.decode(serializedData.data as Uint8Array);
const data = MessageType.toObject(message);
// 应用数据到组件
this.applyDataToComponent(component, data);
} catch (error) {
console.warn(`[StaticProtobufSerializer] 反序列化失败: ${component.constructor.name}`, error);
}
}
/**
* 检查组件是否支持protobuf序列化
*/
public canSerialize(component: Component): boolean {
return this.isInitialized && isProtoSerializable(component);
}
/**
* 获取序列化统计信息
*/
public getStats(): {
registeredComponents: number;
protobufAvailable: boolean;
initialized: boolean;
} {
return {
registeredComponents: this.registry.getAllComponents().size,
protobufAvailable: !!this.protobufRoot,
initialized: this.isInitialized
};
}
/**
* 手动设置protobuf根对象用于测试
*/
public setProtobufRoot(root: any): void {
this.protobufRoot = root;
this.isInitialized = !!root;
}
/**
* 构建protobuf数据对象
*/
private buildProtoData(component: Component, definition: ProtoComponentDefinition): any {
const data: any = {};
for (const [propertyName, fieldDef] of definition.fields) {
const value = (component as any)[propertyName];
if (value !== undefined && value !== null) {
data[fieldDef.name] = this.convertValueToProtoType(value, fieldDef.type);
}
}
return data;
}
/**
* 转换值到protobuf类型
*/
private convertValueToProtoType(value: any, type: string): any {
switch (type) {
case 'int32':
case 'uint32':
case 'sint32':
case 'fixed32':
case 'sfixed32':
return parseInt(value) || 0;
case 'float':
case 'double':
return parseFloat(value) || 0;
case 'bool':
return Boolean(value);
case 'string':
return String(value);
default:
return value;
}
}
/**
* 应用数据到组件
*/
private applyDataToComponent(component: Component, data: any): void {
const protoName = getProtoName(component);
if (!protoName) return;
const definition = this.registry.getComponentDefinition(protoName);
if (!definition) return;
for (const [propertyName, fieldDef] of definition.fields) {
const value = data[fieldDef.name];
if (value !== undefined) {
(component as any)[propertyName] = value;
}
}
}
/**
* 回退到JSON序列化
*/
private fallbackToJSON(component: Component): SerializedData {
const data = this.defaultJSONSerialize(component);
const jsonString = JSON.stringify(data);
return {
type: 'json',
componentType: component.constructor.name,
data: data,
size: new Blob([jsonString]).size
};
}
/**
* 默认JSON序列化
*/
private defaultJSONSerialize(component: Component): any {
const data: any = {};
for (const key in component) {
if (component.hasOwnProperty(key) &&
typeof (component as any)[key] !== 'function' &&
key !== 'id' &&
key !== 'entity' &&
key !== '_enabled' &&
key !== '_updateOrder') {
const value = (component as any)[key];
if (this.isSerializableValue(value)) {
data[key] = value;
}
}
}
return data;
}
/**
* JSON反序列化
*/
private deserializeFromJSON(component: Component, data: any): void {
for (const key in data) {
if (component.hasOwnProperty(key) &&
typeof (component as any)[key] !== 'function' &&
key !== 'id' &&
key !== 'entity' &&
key !== '_enabled' &&
key !== '_updateOrder') {
(component as any)[key] = data[key];
}
}
}
/**
* 检查值是否可序列化
*/
private isSerializableValue(value: any): boolean {
if (value === null || value === undefined) return true;
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return true;
if (Array.isArray(value)) return value.every(v => this.isSerializableValue(v));
if (typeof value === 'object') {
try {
JSON.stringify(value);
return true;
} catch {
return false;
}
}
return false;
}
}

View File

@@ -2,6 +2,6 @@
* 序列化模块导出 * 序列化模块导出
*/ */
export * from './SerializationTypes';
export * from './ProtobufDecorators'; export * from './ProtobufDecorators';
export * from './ProtobufSerializer'; export * from './ProtobufSerializer';
export * from './StaticProtobufSerializer';

View File

@@ -1,8 +1,10 @@
import { Entity } from '../../ECS/Entity'; import { Entity } from '../../ECS/Entity';
import { Component } from '../../ECS/Component'; import { Component } from '../../ECS/Component';
import { ISnapshotable, SceneSnapshot, EntitySnapshot, ComponentSnapshot, SnapshotConfig } from './ISnapshotable'; import { ISnapshotable, SceneSnapshot, EntitySnapshot, ComponentSnapshot, SnapshotConfig } from './ISnapshotable';
import { ProtobufSerializer, SerializedData } from '../Serialization/ProtobufSerializer'; import { ProtobufSerializer } from '../Serialization/ProtobufSerializer';
import { SerializedData } from '../Serialization/SerializationTypes';
import { isProtoSerializable } from '../Serialization/ProtobufDecorators'; import { isProtoSerializable } from '../Serialization/ProtobufDecorators';
import { ComponentRegistry } from '../../ECS/Core/ComponentStorage';
/** /**
* 快照管理器 * 快照管理器
@@ -41,6 +43,7 @@ export class SnapshotManager {
this.protobufSerializer = ProtobufSerializer.getInstance(); this.protobufSerializer = ProtobufSerializer.getInstance();
} }
/** /**
* 创建场景快照 * 创建场景快照
* *
@@ -183,9 +186,7 @@ export class SnapshotManager {
* 获取组件类型 * 获取组件类型
*/ */
private getComponentType(typeName: string): any { private getComponentType(typeName: string): any {
// 这里需要与组件注册系统集成 return ComponentRegistry.getComponentType(typeName);
// 暂时返回null实际实现需要组件类型管理器
return null;
} }
/** /**

View File

@@ -1,333 +0,0 @@
import { BitMaskOptimizer } from '../../../src/ECS/Core/BitMaskOptimizer';
import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility';
describe('BitMaskOptimizer - 位掩码优化器测试', () => {
let optimizer: BitMaskOptimizer;
beforeEach(() => {
optimizer = BitMaskOptimizer.getInstance();
optimizer.reset(); // 确保每个测试开始时都是干净的状态
});
describe('单例模式测试', () => {
it('应该返回同一个实例', () => {
const instance1 = BitMaskOptimizer.getInstance();
const instance2 = BitMaskOptimizer.getInstance();
expect(instance1).toBe(instance2);
});
});
describe('组件类型注册', () => {
it('应该能够注册组件类型', () => {
const id = optimizer.registerComponentType('Transform');
expect(id).toBe(0);
});
it('重复注册相同组件应该返回相同ID', () => {
const id1 = optimizer.registerComponentType('Transform');
const id2 = optimizer.registerComponentType('Transform');
expect(id1).toBe(id2);
});
it('应该能够注册多个不同组件类型', () => {
const id1 = optimizer.registerComponentType('Transform');
const id2 = optimizer.registerComponentType('Velocity');
const id3 = optimizer.registerComponentType('Health');
expect(id1).toBe(0);
expect(id2).toBe(1);
expect(id3).toBe(2);
});
it('应该能够获取已注册组件的类型ID', () => {
optimizer.registerComponentType('Transform');
const id = optimizer.getComponentTypeId('Transform');
expect(id).toBe(0);
});
it('获取未注册组件的类型ID应该返回undefined', () => {
const id = optimizer.getComponentTypeId('UnknownComponent');
expect(id).toBeUndefined();
});
});
describe('单组件掩码创建', () => {
beforeEach(() => {
optimizer.registerComponentType('Transform');
optimizer.registerComponentType('Velocity');
});
it('应该能够创建单个组件的掩码', () => {
const mask = optimizer.createSingleComponentMask('Transform');
expect(mask.toString()).toBe('1');
});
it('不同组件应该有不同的掩码', () => {
const transformMask = optimizer.createSingleComponentMask('Transform');
const velocityMask = optimizer.createSingleComponentMask('Velocity');
expect(transformMask.toString()).toBe('1');
expect(velocityMask.toString()).toBe('2');
});
it('创建未注册组件的掩码应该抛出错误', () => {
expect(() => {
optimizer.createSingleComponentMask('UnknownComponent');
}).toThrow('Component type not registered: UnknownComponent');
});
it('相同组件的掩码应该使用缓存', () => {
const mask1 = optimizer.createSingleComponentMask('Transform');
const mask2 = optimizer.createSingleComponentMask('Transform');
expect(mask1).toBe(mask2);
});
});
describe('组合掩码创建', () => {
beforeEach(() => {
optimizer.registerComponentType('Transform');
optimizer.registerComponentType('Velocity');
optimizer.registerComponentType('Health');
});
it('应该能够创建多个组件的组合掩码', () => {
const mask = optimizer.createCombinedMask(['Transform', 'Velocity']);
expect(mask.toString()).toBe('3'); // 1 | 2 = 3
});
it('组件顺序不应该影响掩码结果', () => {
const mask1 = optimizer.createCombinedMask(['Transform', 'Velocity']);
const mask2 = optimizer.createCombinedMask(['Velocity', 'Transform']);
expect(mask1).toBe(mask2);
});
it('三个组件的组合掩码应该正确', () => {
const mask = optimizer.createCombinedMask(['Transform', 'Velocity', 'Health']);
expect(mask.toString()).toBe('7'); // 1 | 2 | 4 = 7
});
it('空数组应该返回0掩码', () => {
const mask = optimizer.createCombinedMask([]);
expect(mask.isZero()).toBe(true);
});
it('包含未注册组件应该抛出错误', () => {
expect(() => {
optimizer.createCombinedMask(['Transform', 'UnknownComponent']);
}).toThrow('Component type not registered: UnknownComponent');
});
});
describe('掩码检查功能', () => {
beforeEach(() => {
optimizer.registerComponentType('Transform');
optimizer.registerComponentType('Velocity');
optimizer.registerComponentType('Health');
});
it('应该能够检查掩码是否包含指定组件', () => {
const mask = optimizer.createCombinedMask(['Transform', 'Velocity']);
expect(optimizer.maskContainsComponent(mask, 'Transform')).toBe(true);
expect(optimizer.maskContainsComponent(mask, 'Velocity')).toBe(true);
expect(optimizer.maskContainsComponent(mask, 'Health')).toBe(false);
});
it('应该能够检查掩码是否包含所有指定组件', () => {
const mask = optimizer.createCombinedMask(['Transform', 'Velocity', 'Health']);
expect(optimizer.maskContainsAllComponents(mask, ['Transform'])).toBe(true);
expect(optimizer.maskContainsAllComponents(mask, ['Transform', 'Velocity'])).toBe(true);
expect(optimizer.maskContainsAllComponents(mask, ['Transform', 'Velocity', 'Health'])).toBe(true);
});
it('不完全包含时maskContainsAllComponents应该返回false', () => {
const mask = optimizer.createCombinedMask(['Transform']);
expect(optimizer.maskContainsAllComponents(mask, ['Transform', 'Velocity'])).toBe(false);
});
it('应该能够检查掩码是否包含任一指定组件', () => {
const mask = optimizer.createCombinedMask(['Transform']);
expect(optimizer.maskContainsAnyComponent(mask, ['Transform', 'Velocity'])).toBe(true);
expect(optimizer.maskContainsAnyComponent(mask, ['Velocity', 'Health'])).toBe(false);
});
});
describe('掩码操作功能', () => {
beforeEach(() => {
optimizer.registerComponentType('Transform');
optimizer.registerComponentType('Velocity');
optimizer.registerComponentType('Health');
});
it('应该能够向掩码添加组件', () => {
let mask = optimizer.createSingleComponentMask('Transform');
mask = optimizer.addComponentToMask(mask, 'Velocity');
expect(optimizer.maskContainsComponent(mask, 'Transform')).toBe(true);
expect(optimizer.maskContainsComponent(mask, 'Velocity')).toBe(true);
});
it('应该能够从掩码移除组件', () => {
let mask = optimizer.createCombinedMask(['Transform', 'Velocity']);
mask = optimizer.removeComponentFromMask(mask, 'Velocity');
expect(optimizer.maskContainsComponent(mask, 'Transform')).toBe(true);
expect(optimizer.maskContainsComponent(mask, 'Velocity')).toBe(false);
});
it('移除不存在的组件不应该影响掩码', () => {
const originalMask = optimizer.createSingleComponentMask('Transform');
const newMask = optimizer.removeComponentFromMask(originalMask, 'Velocity');
expect(newMask.equals(originalMask)).toBe(true);
});
});
describe('工具功能', () => {
beforeEach(() => {
optimizer.registerComponentType('Transform');
optimizer.registerComponentType('Velocity');
optimizer.registerComponentType('Health');
});
it('应该能够将掩码转换为组件名称数组', () => {
const mask = optimizer.createCombinedMask(['Transform', 'Health']);
const componentNames = optimizer.maskToComponentNames(mask);
expect(componentNames).toContain('Transform');
expect(componentNames).toContain('Health');
expect(componentNames).not.toContain('Velocity');
expect(componentNames.length).toBe(2);
});
it('空掩码应该返回空数组', () => {
const componentNames = optimizer.maskToComponentNames(BigIntFactory.zero());
expect(componentNames).toEqual([]);
});
it('应该能够获取掩码中组件的数量', () => {
const mask1 = optimizer.createSingleComponentMask('Transform');
const mask2 = optimizer.createCombinedMask(['Transform', 'Velocity']);
const mask3 = optimizer.createCombinedMask(['Transform', 'Velocity', 'Health']);
expect(optimizer.getComponentCount(mask1)).toBe(1);
expect(optimizer.getComponentCount(mask2)).toBe(2);
expect(optimizer.getComponentCount(mask3)).toBe(3);
});
it('空掩码的组件数量应该为0', () => {
expect(optimizer.getComponentCount(BigIntFactory.zero())).toBe(0);
});
});
describe('缓存和性能优化', () => {
beforeEach(() => {
optimizer.registerComponentType('Transform');
optimizer.registerComponentType('Velocity');
optimizer.registerComponentType('Health');
});
it('应该能够预计算常用掩码组合', () => {
const commonCombinations = [
['Transform', 'Velocity'],
['Transform', 'Health'],
['Velocity', 'Health']
];
optimizer.precomputeCommonMasks(commonCombinations);
// 验证掩码已被缓存
const stats = optimizer.getCacheStats();
expect(stats.size).toBeGreaterThan(0);
});
it('应该能够获取缓存统计信息', () => {
optimizer.createSingleComponentMask('Transform');
optimizer.createCombinedMask(['Transform', 'Velocity']);
const stats = optimizer.getCacheStats();
expect(stats.size).toBeGreaterThan(0);
expect(stats.componentTypes).toBe(3);
});
it('应该能够清空缓存', () => {
optimizer.createSingleComponentMask('Transform');
optimizer.clearCache();
const stats = optimizer.getCacheStats();
expect(stats.size).toBe(0);
expect(stats.componentTypes).toBe(3); // 组件类型不会被清除
});
it('应该能够重置优化器', () => {
optimizer.registerComponentType('NewComponent');
optimizer.createSingleComponentMask('Transform');
optimizer.reset();
const stats = optimizer.getCacheStats();
expect(stats.size).toBe(0);
expect(stats.componentTypes).toBe(0);
});
});
describe('边界情况和错误处理', () => {
it('处理大量组件类型注册', () => {
for (let i = 0; i < 100; i++) {
const id = optimizer.registerComponentType(`Component${i}`);
expect(id).toBe(i);
}
});
it('处理大掩码值', () => {
// 注册64个组件类型
for (let i = 0; i < 64; i++) {
optimizer.registerComponentType(`Component${i}`);
}
const mask = optimizer.createSingleComponentMask('Component63');
expect(mask.toString()).toBe(BigIntFactory.one().shiftLeft(63).toString());
});
it('空组件名称数组的组合掩码', () => {
const mask = optimizer.createCombinedMask([]);
expect(mask.isZero()).toBe(true);
expect(optimizer.getComponentCount(mask)).toBe(0);
});
});
describe('性能测试', () => {
beforeEach(() => {
// 注册一些组件类型
for (let i = 0; i < 10; i++) {
optimizer.registerComponentType(`Component${i}`);
}
});
it('大量掩码创建应该高效', () => {
const startTime = performance.now();
for (let i = 0; i < 1000; i++) {
optimizer.createSingleComponentMask('Component0');
}
const endTime = performance.now();
expect(endTime - startTime).toBeLessThan(100); // 应该在100ms内完成
});
it('大量掩码检查应该高效', () => {
const mask = optimizer.createCombinedMask(['Component0', 'Component1', 'Component2']);
const startTime = performance.now();
for (let i = 0; i < 10000; i++) {
optimizer.maskContainsComponent(mask, 'Component1');
}
const endTime = performance.now();
expect(endTime - startTime).toBeLessThan(100); // 应该在100ms内完成
});
});
});