From 7a000318a60a5a7e57548611822870fd7f6d6554 Mon Sep 17 00:00:00 2001
From: YHH <359807859@qq.com>
Date: Thu, 7 Aug 2025 09:43:34 +0800
Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E5=90=88=E7=BB=84=E4=BB=B6=E7=B1=BB?=
=?UTF-8?q?=E5=9E=8B=E8=87=B3=E7=BB=9F=E4=B8=80=E7=9A=84componentregistry?=
=?UTF-8?q?=E4=B8=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
assets/svg/ecs-architecture.svg | 2 +-
docs/bitmask-guide.md | 431 ------------------
src/ECS/Core/BitMaskOptimizer.ts | 214 ---------
src/ECS/Core/ComponentStorage.ts | 107 +++++
src/ECS/Core/IndexUpdateBatcher.ts | 256 -----------
src/ECS/Core/Performance/index.ts | 3 +-
src/ECS/Core/QuerySystem.ts | 27 --
src/Utils/Serialization/ProtobufSerializer.ts | 14 +-
src/Utils/Serialization/SerializationTypes.ts | 17 +
.../Serialization/StaticProtobufSerializer.ts | 371 ---------------
src/Utils/Serialization/index.ts | 4 +-
src/Utils/Snapshot/SnapshotManager.ts | 9 +-
tests/ECS/Core/BitMaskOptimizer.test.ts | 333 --------------
13 files changed, 136 insertions(+), 1652 deletions(-)
delete mode 100644 docs/bitmask-guide.md
delete mode 100644 src/ECS/Core/BitMaskOptimizer.ts
delete mode 100644 src/ECS/Core/IndexUpdateBatcher.ts
create mode 100644 src/Utils/Serialization/SerializationTypes.ts
delete mode 100644 src/Utils/Serialization/StaticProtobufSerializer.ts
delete mode 100644 tests/ECS/Core/BitMaskOptimizer.test.ts
diff --git a/assets/svg/ecs-architecture.svg b/assets/svg/ecs-architecture.svg
index 014aa430..09c8aef6 100644
--- a/assets/svg/ecs-architecture.svg
+++ b/assets/svg/ecs-architecture.svg
@@ -71,7 +71,7 @@
ComponentRegistry • IdentifierPool
- BitMaskOptimizer • ConfigManager
+ EventBus • ConfigManager
diff --git a/docs/bitmask-guide.md b/docs/bitmask-guide.md
deleted file mode 100644
index eb647986..00000000
--- a/docs/bitmask-guide.md
+++ /dev/null
@@ -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 {
- const classified = new Map();
-
- 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. **灵活的查询构建** - 支持复杂的组件组合查询
-
-通过理解和正确使用位掩码,可以显著提升游戏的性能表现。记住要在游戏初始化时注册组件类型,预计算常用掩码,并合理管理缓存。
\ No newline at end of file
diff --git a/src/ECS/Core/BitMaskOptimizer.ts b/src/ECS/Core/BitMaskOptimizer.ts
deleted file mode 100644
index c7465375..00000000
--- a/src/ECS/Core/BitMaskOptimizer.ts
+++ /dev/null
@@ -1,214 +0,0 @@
-import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
-
-/**
- * 位掩码优化器,用于预计算和缓存常用的组件掩码
- *
- * 使用BigInt兼容层确保在所有平台上的正常运行。
- */
-export class BitMaskOptimizer {
- private static instance: BitMaskOptimizer;
- private maskCache = new Map();
- private componentTypeMap = new Map();
- 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;
- }
-}
\ No newline at end of file
diff --git a/src/ECS/Core/ComponentStorage.ts b/src/ECS/Core/ComponentStorage.ts
index c8157892..68f72926 100644
--- a/src/ECS/Core/ComponentStorage.ts
+++ b/src/ECS/Core/ComponentStorage.ts
@@ -16,6 +16,9 @@ export type ComponentType = new (...args: unkno
*/
export class ComponentRegistry {
private static componentTypes = new Map();
+ private static componentNameToType = new Map();
+ private static componentNameToId = new Map();
+ private static maskCache = new Map();
private static nextBitIndex = 0;
private static maxComponents = 64; // 支持最多64种组件类型
@@ -35,6 +38,8 @@ export class ComponentRegistry {
const bitIndex = this.nextBitIndex++;
this.componentTypes.set(componentType, bitIndex);
+ this.componentNameToType.set(componentType.name, componentType);
+ this.componentNameToId.set(componentType.name, bitIndex);
return bitIndex;
}
@@ -73,6 +78,15 @@ export class ComponentRegistry {
return this.componentTypes.has(componentType);
}
+ /**
+ * 通过名称获取组件类型
+ * @param componentName 组件名称
+ * @returns 组件类型构造函数
+ */
+ public static getComponentType(componentName: string): Function | null {
+ return this.componentNameToType.get(componentName) || null;
+ }
+
/**
* 获取所有已注册的组件类型
* @returns 组件类型映射
@@ -81,11 +95,104 @@ export class ComponentRegistry {
return new Map(this.componentTypes);
}
+ /**
+ * 获取所有组件名称到类型的映射
+ * @returns 名称到类型的映射
+ */
+ public static getAllComponentNames(): Map {
+ 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 {
this.componentTypes.clear();
+ this.componentNameToType.clear();
+ this.componentNameToId.clear();
+ this.maskCache.clear();
this.nextBitIndex = 0;
}
}
diff --git a/src/ECS/Core/IndexUpdateBatcher.ts b/src/ECS/Core/IndexUpdateBatcher.ts
deleted file mode 100644
index 0032405f..00000000
--- a/src/ECS/Core/IndexUpdateBatcher.ts
+++ /dev/null
@@ -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;
-}
\ No newline at end of file
diff --git a/src/ECS/Core/Performance/index.ts b/src/ECS/Core/Performance/index.ts
index fb39c584..8c06a738 100644
--- a/src/ECS/Core/Performance/index.ts
+++ b/src/ECS/Core/Performance/index.ts
@@ -18,5 +18,4 @@ export {
DirtyListener
} from '../DirtyTrackingSystem';
-export { IndexUpdateBatcher } from '../IndexUpdateBatcher';
-export { BitMaskOptimizer } from '../BitMaskOptimizer';
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/ECS/Core/QuerySystem.ts b/src/ECS/Core/QuerySystem.ts
index 09154fb7..acf48b84 100644
--- a/src/ECS/Core/QuerySystem.ts
+++ b/src/ECS/Core/QuerySystem.ts
@@ -4,8 +4,6 @@ import { ComponentRegistry, ComponentType } from './ComponentStorage';
import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
import { ComponentPoolManager } from './ComponentPool';
-import { BitMaskOptimizer } from './BitMaskOptimizer';
-import { IndexUpdateBatcher } from './IndexUpdateBatcher';
import { ComponentIndexManager, IndexType } from './ComponentIndex';
import { ArchetypeSystem, Archetype, ArchetypeQueryResult } from './ArchetypeSystem';
import { DirtyTrackingSystem, DirtyFlag } from './DirtyTrackingSystem';
@@ -98,8 +96,6 @@ export class QuerySystem {
// 优化组件
private componentPoolManager: ComponentPoolManager;
- private bitMaskOptimizer: BitMaskOptimizer;
- private indexUpdateBatcher: IndexUpdateBatcher;
// 新增性能优化系统
private componentIndexManager: ComponentIndexManager;
@@ -126,33 +122,10 @@ export class QuerySystem {
// 初始化优化组件
this.componentPoolManager = ComponentPoolManager.getInstance();
- this.bitMaskOptimizer = BitMaskOptimizer.getInstance();
- this.indexUpdateBatcher = new IndexUpdateBatcher();
-
// 初始化新的性能优化系统
this.componentIndexManager = new ComponentIndexManager(IndexType.HASH);
this.archetypeSystem = new ArchetypeSystem();
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);
- }
- };
}
diff --git a/src/Utils/Serialization/ProtobufSerializer.ts b/src/Utils/Serialization/ProtobufSerializer.ts
index 1712257c..f0a40612 100644
--- a/src/Utils/Serialization/ProtobufSerializer.ts
+++ b/src/Utils/Serialization/ProtobufSerializer.ts
@@ -13,18 +13,8 @@ import {
isProtoSerializable,
getProtoName
} from './ProtobufDecorators';
+import { SerializedData } from './SerializationTypes';
-/**
- * 序列化数据接口
- */
-export interface SerializedData {
- /** 组件类型名称 */
- componentType: string;
- /** 序列化后的数据 */
- data: Uint8Array;
- /** 数据大小(字节) */
- size: number;
-}
/**
* Protobuf序列化器
@@ -143,6 +133,7 @@ export class ProtobufSerializer {
const buffer = MessageType.encode(message).finish();
return {
+ type: 'protobuf',
componentType: componentType,
data: buffer,
size: buffer.length
@@ -245,6 +236,7 @@ export class ProtobufSerializer {
const buffer = MessageType.encode(message).finish();
results.push({
+ type: 'protobuf',
componentType: component.constructor.name,
data: buffer,
size: buffer.length
diff --git a/src/Utils/Serialization/SerializationTypes.ts b/src/Utils/Serialization/SerializationTypes.ts
new file mode 100644
index 00000000..6acf54a9
--- /dev/null
+++ b/src/Utils/Serialization/SerializationTypes.ts
@@ -0,0 +1,17 @@
+/**
+ * 序列化类型定义
+ */
+
+/**
+ * 序列化数据接口
+ */
+export interface SerializedData {
+ /** 序列化类型 */
+ type: 'protobuf';
+ /** 组件类型名称 */
+ componentType: string;
+ /** 序列化后的数据 */
+ data: Uint8Array;
+ /** 数据大小(字节) */
+ size: number;
+}
\ No newline at end of file
diff --git a/src/Utils/Serialization/StaticProtobufSerializer.ts b/src/Utils/Serialization/StaticProtobufSerializer.ts
deleted file mode 100644
index 16c44dff..00000000
--- a/src/Utils/Serialization/StaticProtobufSerializer.ts
+++ /dev/null
@@ -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 {
- 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 {
- 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;
- }
-}
\ No newline at end of file
diff --git a/src/Utils/Serialization/index.ts b/src/Utils/Serialization/index.ts
index ef5a7a3e..b66d983c 100644
--- a/src/Utils/Serialization/index.ts
+++ b/src/Utils/Serialization/index.ts
@@ -2,6 +2,6 @@
* 序列化模块导出
*/
+export * from './SerializationTypes';
export * from './ProtobufDecorators';
-export * from './ProtobufSerializer';
-export * from './StaticProtobufSerializer';
\ No newline at end of file
+export * from './ProtobufSerializer';
\ No newline at end of file
diff --git a/src/Utils/Snapshot/SnapshotManager.ts b/src/Utils/Snapshot/SnapshotManager.ts
index 8b4cf58d..8219fbe0 100644
--- a/src/Utils/Snapshot/SnapshotManager.ts
+++ b/src/Utils/Snapshot/SnapshotManager.ts
@@ -1,8 +1,10 @@
import { Entity } from '../../ECS/Entity';
import { Component } from '../../ECS/Component';
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 { ComponentRegistry } from '../../ECS/Core/ComponentStorage';
/**
* 快照管理器
@@ -41,6 +43,7 @@ export class SnapshotManager {
this.protobufSerializer = ProtobufSerializer.getInstance();
}
+
/**
* 创建场景快照
*
@@ -183,9 +186,7 @@ export class SnapshotManager {
* 获取组件类型
*/
private getComponentType(typeName: string): any {
- // 这里需要与组件注册系统集成
- // 暂时返回null,实际实现需要组件类型管理器
- return null;
+ return ComponentRegistry.getComponentType(typeName);
}
/**
diff --git a/tests/ECS/Core/BitMaskOptimizer.test.ts b/tests/ECS/Core/BitMaskOptimizer.test.ts
deleted file mode 100644
index 08ae141d..00000000
--- a/tests/ECS/Core/BitMaskOptimizer.test.ts
+++ /dev/null
@@ -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内完成
- });
- });
-});
\ No newline at end of file