From 9445c735c33fecc6a1c222a4e5e449836bd4a877 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Fri, 10 Oct 2025 10:16:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E6=B1=A0=E5=86=85=E5=AD=98?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/ECS/Core/ComponentPool.ts | 189 ++++++++++++++++++-- packages/core/src/ECS/Entity.ts | 51 +++++- packages/core/src/ECS/Scene.ts | 32 +++- 3 files changed, 245 insertions(+), 27 deletions(-) diff --git a/packages/core/src/ECS/Core/ComponentPool.ts b/packages/core/src/ECS/Core/ComponentPool.ts index 098fa812..130d2796 100644 --- a/packages/core/src/ECS/Core/ComponentPool.ts +++ b/packages/core/src/ECS/Core/ComponentPool.ts @@ -8,24 +8,41 @@ export class ComponentPool { private createFn: () => T; private resetFn?: (component: T) => void; private maxSize: number; + private minSize: number; + private growthFactor: number; + + private stats = { + totalCreated: 0, + totalAcquired: 0, + totalReleased: 0 + }; constructor( createFn: () => T, resetFn?: (component: T) => void, - maxSize: number = 1000 + maxSize: number = 1000, + minSize: number = 10, + growthFactor: number = 1.5 ) { this.createFn = createFn; this.resetFn = resetFn; this.maxSize = maxSize; + this.minSize = Math.max(1, minSize); + this.growthFactor = Math.max(1.1, growthFactor); } /** * 获取一个组件实例 */ acquire(): T { + this.stats.totalAcquired++; + if (this.pool.length > 0) { return this.pool.pop()!; } + + this.stats.totalCreated++; + return this.createFn(); } @@ -33,20 +50,41 @@ export class ComponentPool { * 释放一个组件实例回池中 */ release(component: T): void { - if (this.pool.length < this.maxSize) { - if (this.resetFn) { - this.resetFn(component); - } - this.pool.push(component); + this.stats.totalReleased++; + + if (this.pool.length >= this.maxSize) { + return; } + + if (this.resetFn) { + this.resetFn(component); + } + + this.pool.push(component); } /** * 预填充对象池 */ prewarm(count: number): void { - for (let i = 0; i < count && this.pool.length < this.maxSize; i++) { - this.pool.push(this.createFn()); + const targetCount = Math.min(count, this.maxSize); + + for (let i = this.pool.length; i < targetCount; i++) { + const component = this.createFn(); + if (this.resetFn) { + this.resetFn(component); + } + this.pool.push(component); + this.stats.totalCreated++; + } + } + + /** + * 自动收缩池大小 + */ + shrink(): void { + while (this.pool.length > this.minSize) { + this.pool.pop(); } } @@ -70,6 +108,35 @@ export class ComponentPool { getMaxSize(): number { return this.maxSize; } + + /** + * 获取统计信息 + */ + getStats() { + const hitRate = this.stats.totalAcquired === 0 + ? 0 + : (this.stats.totalAcquired - this.stats.totalCreated) / this.stats.totalAcquired; + + return { + totalCreated: this.stats.totalCreated, + totalAcquired: this.stats.totalAcquired, + totalReleased: this.stats.totalReleased, + hitRate: hitRate, + currentSize: this.pool.length, + maxSize: this.maxSize, + minSize: this.minSize, + utilizationRate: this.pool.length / this.maxSize + }; + } +} + +/** + * 组件使用追踪 + */ +interface ComponentUsageTracker { + createCount: number; + releaseCount: number; + lastAccessTime: number; } /** @@ -78,6 +145,10 @@ export class ComponentPool { export class ComponentPoolManager { private static instance: ComponentPoolManager; private pools = new Map>(); + private usageTracker = new Map(); + + private autoCleanupInterval = 60000; + private lastCleanupTime = 0; private constructor() {} @@ -95,9 +166,16 @@ export class ComponentPoolManager { componentName: string, createFn: () => T, resetFn?: (component: T) => void, - maxSize?: number + maxSize?: number, + minSize?: number ): void { - this.pools.set(componentName, new ComponentPool(createFn, resetFn, maxSize)); + this.pools.set(componentName, new ComponentPool(createFn, resetFn, maxSize, minSize)); + + this.usageTracker.set(componentName, { + createCount: 0, + releaseCount: 0, + lastAccessTime: Date.now() + }); } /** @@ -105,6 +183,9 @@ export class ComponentPoolManager { */ acquireComponent(componentName: string): T | null { const pool = this.pools.get(componentName); + + this.trackUsage(componentName, 'create'); + return pool ? pool.acquire() : null; } @@ -113,11 +194,71 @@ export class ComponentPoolManager { */ releaseComponent(componentName: string, component: T): void { const pool = this.pools.get(componentName); + + this.trackUsage(componentName, 'release'); + if (pool) { pool.release(component); } } + /** + * 追踪使用情况 + */ + private trackUsage(componentName: string, action: 'create' | 'release'): void { + let tracker = this.usageTracker.get(componentName); + + if (!tracker) { + tracker = { + createCount: 0, + releaseCount: 0, + lastAccessTime: Date.now() + }; + this.usageTracker.set(componentName, tracker); + } + + if (action === 'create') { + tracker.createCount++; + } else { + tracker.releaseCount++; + } + + tracker.lastAccessTime = Date.now(); + } + + /** + * 自动清理(定期调用) + */ + public update(): void { + const now = Date.now(); + + if (now - this.lastCleanupTime < this.autoCleanupInterval) { + return; + } + + for (const [name, tracker] of this.usageTracker.entries()) { + const inactive = now - tracker.lastAccessTime > 120000; + + if (inactive) { + const pool = this.pools.get(name); + if (pool) { + pool.shrink(); + } + } + } + + this.lastCleanupTime = now; + } + + /** + * 获取热点组件列表 + */ + public getHotComponents(threshold: number = 100): string[] { + return Array.from(this.usageTracker.entries()) + .filter(([_, tracker]) => tracker.createCount > threshold) + .map(([name]) => name); + } + /** * 预热所有池 */ @@ -137,10 +278,28 @@ export class ComponentPoolManager { } /** - * 重置管理器,移除所有注册的池 + * 重置管理器 */ reset(): void { this.pools.clear(); + this.usageTracker.clear(); + } + + /** + * 获取全局统计信息 + */ + getGlobalStats() { + const stats: any[] = []; + + for (const [name, pool] of this.pools.entries()) { + stats.push({ + componentName: name, + poolStats: pool.getStats(), + usage: this.usageTracker.get(name) + }); + } + + return stats; } /** @@ -158,7 +317,7 @@ export class ComponentPoolManager { } /** - * 获取池利用率信息(用于调试) + * 获取池利用率信息 */ getPoolUtilization(): Map { const utilization = new Map(); @@ -167,7 +326,7 @@ export class ComponentPoolManager { const maxSize = pool.getMaxSize(); const used = maxSize - available; const utilRate = maxSize > 0 ? (used / maxSize * 100) : 0; - + utilization.set(name, { used: used, total: maxSize, @@ -183,11 +342,11 @@ export class ComponentPoolManager { getComponentUtilization(componentName: string): number { const pool = this.pools.get(componentName); if (!pool) return 0; - + const available = pool.getAvailableCount(); const maxSize = pool.getMaxSize(); const used = maxSize - available; - + return maxSize > 0 ? (used / maxSize * 100) : 0; } } \ No newline at end of file diff --git a/packages/core/src/ECS/Entity.ts b/packages/core/src/ECS/Entity.ts index 250338c4..0360b2d9 100644 --- a/packages/core/src/ECS/Entity.ts +++ b/packages/core/src/ECS/Entity.ts @@ -874,8 +874,8 @@ export class Entity { /** * 销毁实体 - * - * 移除所有组件、子实体并标记为已销毁。 + * + * 移除所有组件、子实体并标记为已销毁 */ public destroy(): void { if (this._isDestroyed) { @@ -883,29 +883,66 @@ export class Entity { } this._isDestroyed = true; - + const childrenToDestroy = [...this._children]; for (const child of childrenToDestroy) { child.destroy(); } - + if (this._parent) { this._parent.removeChild(this); } - + this.removeAllComponents(); - + if (this.scene) { if (this.scene.querySystem) { this.scene.querySystem.removeEntity(this); } - + if (this.scene.entities) { this.scene.entities.remove(this); } } } + /** + * 批量销毁所有子实体 + */ + public destroyAllChildren(): void { + if (this._children.length === 0) return; + + const scene = this.scene; + const toDestroy: Entity[] = []; + + const collectChildren = (entity: Entity) => { + for (const child of entity._children) { + toDestroy.push(child); + collectChildren(child); + } + }; + collectChildren(this); + + for (const entity of toDestroy) { + entity._isDestroyed = true; + } + + for (const entity of toDestroy) { + entity.removeAllComponents(); + } + + if (scene) { + for (const entity of toDestroy) { + scene.entities.remove(entity); + scene.querySystem.removeEntity(entity); + } + + scene.clearSystemEntityCaches(); + } + + this._children.length = 0; + } + /** * 比较实体 * diff --git a/packages/core/src/ECS/Scene.ts b/packages/core/src/ECS/Scene.ts index 2f2a8efc..457f0a49 100644 --- a/packages/core/src/ECS/Scene.ts +++ b/packages/core/src/ECS/Scene.ts @@ -12,6 +12,7 @@ import { getComponentInstanceTypeName, getSystemInstanceTypeName } from './Decor import { TypedQueryBuilder } from './Core/Query/TypedQuery'; import { SceneSerializer, SceneSerializationOptions, SceneDeserializationOptions } from './Serialization/SceneSerializer'; import { IncrementalSerializer, IncrementalSnapshot, IncrementalSerializationOptions } from './Serialization/IncrementalSerializer'; +import { ComponentPoolManager } from './Core/ComponentPool'; /** * 游戏场景默认实现类 @@ -185,14 +186,13 @@ export class Scene implements IScene { * 更新场景 */ public update() { - // 更新实体列表(处理延迟操作) + ComponentPoolManager.getInstance().update(); + this.entities.updateLists(); - // 更新实体处理器 if (this.entityProcessors != null) this.entityProcessors.update(); - // 更新实体处理器后处理 if (this.entityProcessors != null) this.entityProcessors.lateUpdate(); } @@ -273,13 +273,35 @@ export class Scene implements IScene { } + /** + * 批量销毁实体 + */ + public destroyEntities(entities: Entity[]): void { + if (entities.length === 0) return; + + for (const entity of entities) { + entity._isDestroyed = true; + } + + for (const entity of entities) { + entity.removeAllComponents(); + } + + for (const entity of entities) { + this.entities.remove(entity); + this.querySystem.removeEntity(entity); + } + + this.querySystem.clearCache(); + this.clearSystemEntityCaches(); + } + /** * 从场景中删除所有实体 */ public destroyAllEntities() { this.entities.removeAllEntities(); - - // 清理查询系统中的实体引用和缓存 + this.querySystem.setEntities([]); }