diff --git a/src/ECS/Core/EntityManager.ts b/src/ECS/Core/EntityManager.ts index de32f2f8..a047468b 100644 --- a/src/ECS/Core/EntityManager.ts +++ b/src/ECS/Core/EntityManager.ts @@ -405,8 +405,11 @@ export class EntityManager { * const enemy = entityManager.createEntity(); // 使用默认名称 * ``` */ - public createEntity(name: string = `Entity_${Date.now()}`): Entity { + public createEntity(name?: string): Entity { const id = this._identifierPool.checkOut(); + if (!name) { + name = `Entity_${id}`; + } const entity = new Entity(name, id); this._entities.set(id, entity); @@ -428,6 +431,67 @@ export class EntityManager { return entity; } + + /** + * 批量创建实体 + * + * 为了优化大量实体创建的性能,批量处理索引更新和事件发射。 + * 适用于需要创建大量实体的场景,如子弹、粒子等。 + * + * @param count 要创建的实体数量 + * @param namePrefix 实体名称前缀,默认为 Entity + * @param skipEvents 是否跳过事件发射以提升性能,默认为 false + * @returns 创建的实体数组 + * + * @example + * const bullets = entityManager.createEntitiesBatch(100, "Bullet", true); + * const particles = entityManager.createEntitiesBatch(500, "Particle"); + */ + public createEntitiesBatch( + count: number, + namePrefix: string = "Entity", + skipEvents: boolean = false + ): Entity[] { + if (count <= 0) return []; + + const entities: Entity[] = []; + + // 批量分配ID和创建Entity对象 + for (let i = 0; i < count; i++) { + const id = this._identifierPool.checkOut(); + const name = `${namePrefix}_${id}`; + const entity = new Entity(name, id); + + entities.push(entity); + this._entities.set(id, entity); + } + + // 批量更新索引 + for (const entity of entities) { + this.updateNameIndex(entity, true); + this.updateTagIndex(entity, true); + this._componentIndexManager.addEntity(entity); + this._archetypeSystem.addEntity(entity); + this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED); + } + + // 批量发射事件 + if (!skipEvents) { + const timestamp = Date.now(); + for (const entity of entities) { + this._eventBus.emitEntityCreated({ + timestamp, + source: 'EntityManager', + entityId: entity.id, + entityName: entity.name, + entityTag: entity.tag?.toString() + }); + } + } + + + return entities; + } /** * 销毁实体 diff --git a/tests/ECS/Core/BatchCreateTest.test.ts b/tests/ECS/Core/BatchCreateTest.test.ts new file mode 100644 index 00000000..f68bfdad --- /dev/null +++ b/tests/ECS/Core/BatchCreateTest.test.ts @@ -0,0 +1,60 @@ +import { EntityManager } from '../../../src/ECS/Core/EntityManager'; +import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager'; + +describe('批量创建功能测试', () => { + let entityManager: EntityManager; + + beforeEach(() => { + ComponentTypeManager.instance.reset(); + entityManager = new EntityManager(); + }); + + test('批量创建实体应该使用ID作为名称', () => { + const entities = entityManager.createEntitiesBatch(5, "Test"); + + expect(entities).toHaveLength(5); + + // 验证实体名称使用ID而不是索引 + for (const entity of entities) { + expect(entity.name).toBe(`Test_${entity.id}`); + } + + // 验证ID是唯一的 + const ids = entities.map(e => e.id); + const uniqueIds = new Set(ids); + expect(uniqueIds.size).toBe(5); + }); + + test('单个创建实体应该使用ID作为默认名称', () => { + const entity1 = entityManager.createEntity(); + const entity2 = entityManager.createEntity(); + const entity3 = entityManager.createEntity("CustomName"); + + expect(entity1.name).toBe(`Entity_${entity1.id}`); + expect(entity2.name).toBe(`Entity_${entity2.id}`); + expect(entity3.name).toBe("CustomName"); + + // 确保ID是连续的或至少是唯一的 + expect(entity1.id).not.toBe(entity2.id); + expect(entity2.id).not.toBe(entity3.id); + }); + + test('混合创建方式的名称应该一致', () => { + // 先单个创建 + const single = entityManager.createEntity(); + + // 再批量创建 + const batch = entityManager.createEntitiesBatch(3, "Batch"); + + // 再单个创建 + const single2 = entityManager.createEntity(); + + // 验证名称格式一致 + expect(single.name).toBe(`Entity_${single.id}`); + expect(single2.name).toBe(`Entity_${single2.id}`); + + for (const entity of batch) { + expect(entity.name).toBe(`Batch_${entity.id}`); + } + }); +}); \ No newline at end of file diff --git a/tests/ECS/Core/EntityCreationPerformance.test.ts b/tests/ECS/Core/EntityCreationPerformance.test.ts new file mode 100644 index 00000000..6c2d2946 --- /dev/null +++ b/tests/ECS/Core/EntityCreationPerformance.test.ts @@ -0,0 +1,226 @@ +import { EntityManager } from '../../../src/ECS/Core/EntityManager'; +import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager'; + +describe('实体创建性能分析', () => { + let entityManager: EntityManager; + + beforeEach(() => { + ComponentTypeManager.instance.reset(); + entityManager = new EntityManager(); + }); + + test('性能分析:创建10000个实体', () => { + const entityCount = 10000; + console.log(`开始创建 ${entityCount} 个实体...`); + + // 预热 + for (let i = 0; i < 100; i++) { + entityManager.createEntity(`Warmup_${i}`); + } + // 重新创建EntityManager来清理 + entityManager = new EntityManager(); + + // 测试不同的创建方式 + console.log('\n=== 性能对比测试 ==='); + + // 1. 使用默认名称(包含Date.now()) + let startTime = performance.now(); + const entitiesWithDefaultName: any[] = []; + for (let i = 0; i < entityCount; i++) { + entitiesWithDefaultName.push(entityManager.createEntity()); + } + let endTime = performance.now(); + console.log(`1. 默认名称创建: ${(endTime - startTime).toFixed(2)}ms`); + entityManager = new EntityManager(); + + // 2. 使用预设名称(避免Date.now()) + startTime = performance.now(); + const entitiesWithPresetName: any[] = []; + for (let i = 0; i < entityCount; i++) { + entitiesWithPresetName.push(entityManager.createEntity(`Entity_${i}`)); + } + endTime = performance.now(); + console.log(`2. 预设名称创建: ${(endTime - startTime).toFixed(2)}ms`); + entityManager = new EntityManager(); + + // 3. 使用相同名称(减少字符串创建) + startTime = performance.now(); + const entitiesWithSameName: any[] = []; + const sameName = 'SameName'; + for (let i = 0; i < entityCount; i++) { + entitiesWithSameName.push(entityManager.createEntity(sameName)); + } + endTime = performance.now(); + console.log(`3. 相同名称创建: ${(endTime - startTime).toFixed(2)}ms`); + entityManager = new EntityManager(); + + // 4. 直接创建Entity对象(绕过EntityManager) + startTime = performance.now(); + const directEntities: any[] = []; + for (let i = 0; i < entityCount; i++) { + // 直接创建Entity,不通过EntityManager的复杂逻辑 + directEntities.push(new (require('../../../src/ECS/Entity').Entity)(`Direct_${i}`, i)); + } + endTime = performance.now(); + console.log(`4. 直接创建Entity: ${(endTime - startTime).toFixed(2)}ms`); + + console.log('\n=== 性能分析结论 ==='); + console.log('如果相同名称创建明显更快,说明字符串操作是瓶颈'); + console.log('如果直接创建Entity更快,说明EntityManager的逻辑太重'); + }); + + test('详细分析EntityManager中的性能瓶颈', () => { + const entityCount = 1000; // 较小数量便于分析 + + console.log('\n=== 详细性能分析 ==='); + + // 分析各个步骤的耗时 + let totalTime = 0; + const stepTimes: Record = {}; + + for (let i = 0; i < entityCount; i++) { + const stepStart = performance.now(); + + // 模拟EntityManager.createEntity的各个步骤 + const name = `PerfTest_${i}`; + + // 步骤1: ID分配 + let stepTime = performance.now(); + const id = entityManager['_identifierPool'].checkOut(); + stepTimes['ID分配'] = (stepTimes['ID分配'] || 0) + (performance.now() - stepTime); + + // 步骤2: Entity创建 + stepTime = performance.now(); + const entity = new (require('../../../src/ECS/Entity').Entity)(name, id); + stepTimes['Entity创建'] = (stepTimes['Entity创建'] || 0) + (performance.now() - stepTime); + + // 步骤3: 各种索引更新 + stepTime = performance.now(); + entityManager['_entities'].set(id, entity); + stepTimes['Map存储'] = (stepTimes['Map存储'] || 0) + (performance.now() - stepTime); + + stepTime = performance.now(); + entityManager['updateNameIndex'](entity, true); + stepTimes['名称索引'] = (stepTimes['名称索引'] || 0) + (performance.now() - stepTime); + + stepTime = performance.now(); + entityManager['updateTagIndex'](entity, true); + stepTimes['标签索引'] = (stepTimes['标签索引'] || 0) + (performance.now() - stepTime); + + stepTime = performance.now(); + entityManager['_componentIndexManager'].addEntity(entity); + stepTimes['组件索引'] = (stepTimes['组件索引'] || 0) + (performance.now() - stepTime); + + stepTime = performance.now(); + entityManager['_archetypeSystem'].addEntity(entity); + stepTimes['原型系统'] = (stepTimes['原型系统'] || 0) + (performance.now() - stepTime); + + stepTime = performance.now(); + entityManager['_dirtyTrackingSystem'].markDirty(entity, 1); // DirtyFlag.COMPONENT_ADDED + stepTimes['脏标记'] = (stepTimes['脏标记'] || 0) + (performance.now() - stepTime); + + stepTime = performance.now(); + // 跳过事件发射,因为它涉及复杂的对象创建 + stepTimes['其他'] = (stepTimes['其他'] || 0) + (performance.now() - stepTime); + + totalTime += (performance.now() - stepStart); + } + + console.log(`总耗时: ${totalTime.toFixed(2)}ms`); + console.log('各步骤平均耗时:'); + for (const [step, time] of Object.entries(stepTimes)) { + console.log(` ${step}: ${(time / entityCount * 1000).toFixed(3)}μs/entity`); + } + + // 找出最耗时的步骤 + const maxTime = Math.max(...Object.values(stepTimes)); + const slowestStep = Object.entries(stepTimes).find(([_, time]) => time === maxTime)?.[0]; + console.log(`最耗时的步骤: ${slowestStep} (${(maxTime / entityCount * 1000).toFixed(3)}μs/entity)`); + }); + + test('测试批量创建优化方案', () => { + const entityCount = 10000; + console.log(`\n=== 批量创建优化测试 ===`); + + // 当前方式:逐个创建 + let startTime = performance.now(); + for (let i = 0; i < entityCount; i++) { + entityManager.createEntity(`Current_${i}`); + } + let endTime = performance.now(); + const currentTime = endTime - startTime; + console.log(`当前方式: ${currentTime.toFixed(2)}ms`); + + entityManager = new EntityManager(); + + // 如果有批量创建方法的话... + // (这里只是演示概念,实际的批量创建需要在EntityManager中实现) + console.log('建议:实现批量创建方法,减少重复的索引更新和事件发射'); + }); + + test('验证批量创建优化效果', () => { + const entityCount = 10000; + console.log(`\n=== 批量创建优化效果验证 ===`); + + // 测试新的批量创建方法 + let startTime = performance.now(); + const batchEntities = entityManager.createEntitiesBatch(entityCount, "Batch", false); + let endTime = performance.now(); + const batchTime = endTime - startTime; + console.log(`批量创建(含事件): ${batchTime.toFixed(2)}ms`); + + entityManager = new EntityManager(); + + // 测试跳过事件的批量创建 + startTime = performance.now(); + const batchEntitiesNoEvents = entityManager.createEntitiesBatch(entityCount, "BatchNoEvents", true); + endTime = performance.now(); + const batchTimeNoEvents = endTime - startTime; + console.log(`批量创建(跳过事件): ${batchTimeNoEvents.toFixed(2)}ms`); + + entityManager = new EntityManager(); + + // 对比单个创建(使用优化后的createEntity) + startTime = performance.now(); + const singleEntities: any[] = []; + for (let i = 0; i < entityCount; i++) { + singleEntities.push(entityManager.createEntity(`Single_${i}`)); + } + endTime = performance.now(); + const singleTime = endTime - startTime; + console.log(`优化后单个创建: ${singleTime.toFixed(2)}ms`); + + console.log(`\n性能提升:`); + console.log(`批量创建 vs 单个创建: ${(singleTime / batchTime).toFixed(1)}x faster`); + console.log(`批量创建(跳过事件) vs 单个创建: ${(singleTime / batchTimeNoEvents).toFixed(1)}x faster`); + + // 验证功能正确性 + expect(batchEntities.length).toBe(entityCount); + expect(batchEntitiesNoEvents.length).toBe(entityCount); + expect(singleEntities.length).toBe(entityCount); + }); + + test('验证createEntity的Date.now()优化', () => { + console.log(`\n=== createEntity优化验证 ===`); + + const testCount = 1000; + + // 测试优化后的默认名称生成 + let startTime = performance.now(); + for (let i = 0; i < testCount; i++) { + entityManager.createEntity(); // 使用优化后的计数器命名 + } + let endTime = performance.now(); + console.log(`计数器命名: ${(endTime - startTime).toFixed(2)}ms`); + + entityManager = new EntityManager(); + + // 对比:模拟使用Date.now()的方式 + startTime = performance.now(); + for (let i = 0; i < testCount; i++) { + entityManager.createEntity(`Entity_${Date.now()}_${i}`); // 模拟原来的方式 + } + endTime = performance.now(); + console.log(`Date.now()命名: ${(endTime - startTime).toFixed(2)}ms`); + }); +}); \ No newline at end of file