diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension index e222e9e9..66c3acbe 160000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension @@ -1 +1 @@ -Subproject commit e222e9e9d86e3704ff5c2c9aff929f8dd38d5384 +Subproject commit 66c3acbe9e458b3841377dc8abe02f91d4226b94 diff --git a/src/Utils/Debug/ComponentDataCollector.ts b/src/Utils/Debug/ComponentDataCollector.ts index cf508fac..1a4e6396 100644 --- a/src/Utils/Debug/ComponentDataCollector.ts +++ b/src/Utils/Debug/ComponentDataCollector.ts @@ -5,8 +5,10 @@ import { Core } from '../../Core'; * 组件数据收集器 */ export class ComponentDataCollector { + private static componentSizeCache = new Map(); + /** - * 收集组件数据 + * 收集组件数据(轻量版,不计算实际内存大小) */ public collectComponentData(): IComponentDebugData { const scene = Core.scene; @@ -69,7 +71,8 @@ export class ComponentDataCollector { componentStats: Array.from(componentStats.entries()).map(([typeName, stats]) => { const poolSize = poolSizes.get(typeName) || 0; const poolUtilization = poolUtilizations.get(typeName) || 0; - const memoryPerInstance = this.calculateComponentMemorySize(typeName); + // 使用预估的基础内存大小,避免每帧计算 + const memoryPerInstance = this.getEstimatedComponentSize(typeName); return { typeName, @@ -85,14 +88,93 @@ export class ComponentDataCollector { } /** - * 计算组件实际内存大小 + * 获取组件类型的估算内存大小(基于预设值,不进行实际计算) */ - private calculateComponentMemorySize(typeName: string): number { + private getEstimatedComponentSize(typeName: string): number { + if (ComponentDataCollector.componentSizeCache.has(typeName)) { + return ComponentDataCollector.componentSizeCache.get(typeName)!; + } + const scene = Core.scene; - if (!scene) return 32; + if (!scene) return 64; const entityList = (scene as any).entities; - if (!entityList?.buffer) return 32; + if (!entityList?.buffer) return 64; + + let calculatedSize = 64; + + try { + for (const entity of entityList.buffer) { + if (entity.components) { + const component = entity.components.find((c: any) => c.constructor.name === typeName); + if (component) { + calculatedSize = this.calculateQuickObjectSize(component); + break; + } + } + } + } catch (error) { + calculatedSize = 64; + } + + ComponentDataCollector.componentSizeCache.set(typeName, calculatedSize); + return calculatedSize; + } + + private calculateQuickObjectSize(obj: any): number { + if (!obj || typeof obj !== 'object') return 8; + + let size = 32; + const visited = new WeakSet(); + + const calculate = (item: any, depth: number = 0): number => { + if (!item || typeof item !== 'object' || visited.has(item) || depth > 3) { + return 0; + } + visited.add(item); + + let itemSize = 0; + + try { + const keys = Object.keys(item); + for (let i = 0; i < Math.min(keys.length, 20); i++) { + const key = keys[i]; + if (key === 'entity' || key === '_entity' || key === 'constructor') continue; + + const value = item[key]; + itemSize += key.length * 2; + + if (typeof value === 'string') { + itemSize += Math.min(value.length * 2, 200); + } else if (typeof value === 'number') { + itemSize += 8; + } else if (typeof value === 'boolean') { + itemSize += 4; + } else if (typeof value === 'object' && value !== null) { + itemSize += calculate(value, depth + 1); + } + } + } catch (error) { + return 32; + } + + return itemSize; + }; + + size += calculate(obj); + return Math.max(size, 32); + } + + /** + * 为内存快照功能提供的详细内存计算 + * 只在用户主动请求内存快照时调用 + */ + public calculateDetailedComponentMemory(typeName: string): number { + const scene = Core.scene; + if (!scene) return this.getEstimatedComponentSize(typeName); + + const entityList = (scene as any).entities; + if (!entityList?.buffer) return this.getEstimatedComponentSize(typeName); try { // 找到第一个包含此组件的实体,分析组件大小 @@ -105,18 +187,18 @@ export class ComponentDataCollector { } } } catch (error) { - // 忽略错误,使用默认值 + // 忽略错误,使用估算值 } - // 如果无法计算,返回基础大小 - return 32; // 基础对象开销 + return this.getEstimatedComponentSize(typeName); } /** - * 估算对象内存大小 + * 估算对象内存大小(仅用于内存快照) + * 优化版本:减少递归深度,提高性能 */ private estimateObjectSize(obj: any, visited = new WeakSet(), depth = 0): number { - if (obj === null || obj === undefined || depth > 50) return 0; + if (obj === null || obj === undefined || depth > 10) return 0; if (visited.has(obj)) return 0; let size = 0; @@ -124,55 +206,55 @@ export class ComponentDataCollector { switch (type) { case 'boolean': - size = 1; + size = 4; break; case 'number': size = 8; break; case 'string': - const stringSize = 24 + (obj.length * 2); - size = Math.ceil(stringSize / 8) * 8; + size = 24 + Math.min(obj.length * 2, 1000); break; case 'object': visited.add(obj); if (Array.isArray(obj)) { size = 40 + (obj.length * 8); - for (let i = 0; i < obj.length; i++) { + const maxElements = Math.min(obj.length, 50); + for (let i = 0; i < maxElements; i++) { size += this.estimateObjectSize(obj[i], visited, depth + 1); } } else { size = 32; - const allKeys = [ - ...Object.getOwnPropertyNames(obj), - ...Object.getOwnPropertySymbols(obj) - ]; - for (const key of allKeys) { - try { - if (typeof key === 'string' && ( - key === 'constructor' || + try { + const ownKeys = Object.getOwnPropertyNames(obj); + const maxProps = Math.min(ownKeys.length, 30); + + for (let i = 0; i < maxProps; i++) { + const key = ownKeys[i]; + + if (key === 'constructor' || key === '__proto__' || - key === 'entity' || key === '_entity' - )) { + key === 'entity' || + key === '_entity' || + key.startsWith('_cc_') || + key.startsWith('__')) { continue; } - const descriptor = Object.getOwnPropertyDescriptor(obj, key); - if (!descriptor) continue; - - if (typeof key === 'string') { + try { size += 16 + (key.length * 2); - } else { - size += 24; + + const value = obj[key]; + if (value !== undefined && value !== null) { + size += this.estimateObjectSize(value, visited, depth + 1); + } + } catch (error) { + continue; } - - if (descriptor.value !== undefined) { - size += this.estimateObjectSize(descriptor.value, visited, depth + 1); - } - } catch (error) { - continue; } + } catch (error) { + size = 128; } } break; @@ -182,4 +264,8 @@ export class ComponentDataCollector { return Math.ceil(size / 8) * 8; } + + public static clearCache(): void { + ComponentDataCollector.componentSizeCache.clear(); + } } \ No newline at end of file diff --git a/src/Utils/Debug/DebugManager.ts b/src/Utils/Debug/DebugManager.ts index 6c9cd87e..0eb8cd71 100644 --- a/src/Utils/Debug/DebugManager.ts +++ b/src/Utils/Debug/DebugManager.ts @@ -410,35 +410,59 @@ export class DebugManager { /** - * 收集组件内存统计 + * 收集组件内存统计(仅用于内存快照) */ private collectComponentMemoryStats(entityList: any): any { const componentStats = new Map(); let totalComponentMemory = 0; + // 首先统计组件类型和数量 + const componentTypeCounts = new Map(); for (const entity of entityList.buffer) { if (!entity || entity.destroyed || !entity.components) continue; for (const component of entity.components) { const typeName = component.constructor.name; - const componentMemory = this.entityCollector.calculateObjectSize(component, ['entity']); - totalComponentMemory += componentMemory; - - if (!componentStats.has(typeName)) { - componentStats.set(typeName, { count: 0, totalMemory: 0, instances: [] }); - } - - const stats = componentStats.get(typeName)!; - stats.count++; - stats.totalMemory += componentMemory; - stats.instances.push({ - entityId: entity.id, - entityName: entity.name || `Entity_${entity.id}`, - memory: componentMemory - }); + componentTypeCounts.set(typeName, (componentTypeCounts.get(typeName) || 0) + 1); } } + // 为每种组件类型计算详细内存(只计算一次,然后乘以数量) + for (const [typeName, count] of componentTypeCounts.entries()) { + const detailedMemoryPerInstance = this.componentCollector.calculateDetailedComponentMemory(typeName); + const totalMemoryForType = detailedMemoryPerInstance * count; + totalComponentMemory += totalMemoryForType; + + // 收集该类型组件的实例信息(用于显示最大的几个实例) + const instances: any[] = []; + let instanceCount = 0; + + for (const entity of entityList.buffer) { + if (!entity || entity.destroyed || !entity.components) continue; + + for (const component of entity.components) { + if (component.constructor.name === typeName) { + instances.push({ + entityId: entity.id, + entityName: entity.name || `Entity_${entity.id}`, + memory: detailedMemoryPerInstance // 使用统一的详细计算结果 + }); + instanceCount++; + + // 限制收集的实例数量,避免过多数据 + if (instanceCount >= 100) break; + } + } + if (instanceCount >= 100) break; + } + + componentStats.set(typeName, { + count: count, + totalMemory: totalMemoryForType, + instances: instances.slice(0, 10) // 只保留前10个实例用于显示 + }); + } + const componentBreakdown = Array.from(componentStats.entries()).map(([typeName, stats]) => ({ typeName, instanceCount: stats.count, @@ -456,9 +480,6 @@ export class DebugManager { }; } - /** - * 收集系统内存统计 - */ private collectSystemMemoryStats(): any { const scene = Core.scene; let totalSystemMemory = 0; @@ -467,12 +488,23 @@ export class DebugManager { try { const entityProcessors = (scene as any).entityProcessors; if (entityProcessors && entityProcessors.processors) { + const systemTypeMemoryCache = new Map(); + for (const system of entityProcessors.processors) { - const systemMemory = this.entityCollector.calculateObjectSize(system); + const systemTypeName = system.constructor.name; + + let systemMemory: number; + if (systemTypeMemoryCache.has(systemTypeName)) { + systemMemory = systemTypeMemoryCache.get(systemTypeName)!; + } else { + systemMemory = this.calculateQuickSystemSize(system); + systemTypeMemoryCache.set(systemTypeName, systemMemory); + } + totalSystemMemory += systemMemory; systemBreakdown.push({ - name: system.constructor.name, + name: systemTypeName, memory: systemMemory, enabled: system.enabled !== false, updateOrder: system.updateOrder || 0 @@ -490,6 +522,39 @@ export class DebugManager { }; } + private calculateQuickSystemSize(system: any): number { + if (!system || typeof system !== 'object') return 64; + + let size = 128; + + try { + const keys = Object.keys(system); + for (let i = 0; i < Math.min(keys.length, 15); i++) { + const key = keys[i]; + if (key === 'entities' || key === 'scene' || key === 'constructor') continue; + + const value = system[key]; + size += key.length * 2; + + if (typeof value === 'string') { + size += Math.min(value.length * 2, 100); + } else if (typeof value === 'number') { + size += 8; + } else if (typeof value === 'boolean') { + size += 4; + } else if (Array.isArray(value)) { + size += 40 + Math.min(value.length * 8, 200); + } else if (typeof value === 'object' && value !== null) { + size += 64; + } + } + } catch (error) { + return 128; + } + + return Math.max(size, 64); + } + /** * 收集对象池内存统计 */ diff --git a/src/Utils/Debug/EntityDataCollector.ts b/src/Utils/Debug/EntityDataCollector.ts index 9adb44f1..7e38f86e 100644 --- a/src/Utils/Debug/EntityDataCollector.ts +++ b/src/Utils/Debug/EntityDataCollector.ts @@ -103,8 +103,13 @@ export class EntityDataCollector { const componentDetails = this.extractComponentDetails(entity.components); + const sceneInfo = this.getSceneInfo(scene); + return { ...baseDebugInfo, + scene: sceneInfo.name, + sceneName: sceneInfo.name, + sceneType: sceneInfo.type, parentName: entity.parent?.name || null, components: componentDetails || [], componentCount: entity.components?.length || 0, @@ -113,6 +118,7 @@ export class EntityDataCollector { } catch (error) { return { error: `获取实体详情失败: ${error instanceof Error ? error.message : String(error)}`, + scene: '获取失败', components: [], componentCount: 0, componentTypes: [] @@ -120,6 +126,32 @@ export class EntityDataCollector { } } + private getSceneInfo(scene: any): { name: string; type: string } { + let sceneName = '当前场景'; + let sceneType = 'Scene'; + + try { + if (scene.name && typeof scene.name === 'string' && scene.name.trim()) { + sceneName = scene.name.trim(); + } else if (scene.constructor && scene.constructor.name) { + sceneName = scene.constructor.name; + sceneType = scene.constructor.name; + } else if (scene._name && typeof scene._name === 'string' && scene._name.trim()) { + sceneName = scene._name.trim(); + } else { + const sceneClassName = Object.getPrototypeOf(scene)?.constructor?.name; + if (sceneClassName && sceneClassName !== 'Object') { + sceneName = sceneClassName; + sceneType = sceneClassName; + } + } + } catch (error) { + sceneName = '场景名获取失败'; + } + + return { name: sceneName, type: sceneType }; + } + public collectEntityDataWithMemory(): IEntityDebugData { const scene = Core.scene; @@ -463,49 +495,60 @@ export class EntityDataCollector { public calculateObjectSize(obj: any, excludeKeys: string[] = []): number { if (!obj || typeof obj !== 'object') return 0; - try { - let size = 0; const visited = new WeakSet(); - const maxDepth = 3; - - const calculate = (item: any, depth: number = 0): number => { - if (!item || typeof item !== 'object' || visited.has(item) || depth >= maxDepth) { - return 0; - } + const maxDepth = 2; + + const calculate = (item: any, depth: number = 0): number => { + if (!item || typeof item !== 'object' || depth >= maxDepth) { + return 0; + } + + if (visited.has(item)) return 0; visited.add(item); - let itemSize = 0; + let itemSize = 32; try { - const keys = Object.keys(item); - for (let i = 0; i < Math.min(keys.length, 50); i++) { - const key = keys[i]; - if (excludeKeys.includes(key)) continue; + const keys = Object.keys(item); + const maxKeys = Math.min(keys.length, 20); + + for (let i = 0; i < maxKeys; i++) { + const key = keys[i]; + if (excludeKeys.includes(key) || + key === 'constructor' || + key === '__proto__' || + key.startsWith('_cc_') || + key.startsWith('__')) { + continue; + } const value = item[key]; - itemSize += key.length * 2; + itemSize += key.length * 2; if (typeof value === 'string') { - itemSize += Math.min(value.length * 2, 1000); + itemSize += Math.min(value.length * 2, 200); } else if (typeof value === 'number') { itemSize += 8; } else if (typeof value === 'boolean') { itemSize += 4; + } else if (Array.isArray(value)) { + itemSize += 40 + Math.min(value.length * 8, 160); } else if (typeof value === 'object' && value !== null) { - itemSize += calculate(value, depth + 1); - } + itemSize += calculate(value, depth + 1); } - } catch (error) { - return 0; } - - return isNaN(itemSize) ? 0 : itemSize; - }; - - size = calculate(obj); - return isNaN(size) || size < 0 ? 0 : size; + } catch (error) { + return 64; + } + + return itemSize; + }; + + try { + const size = calculate(obj); + return Math.max(size, 32); } catch (error) { - return 0; + return 64; } } @@ -632,6 +675,9 @@ export class EntityDataCollector { * 构建实体基础信息 */ private buildFallbackEntityInfo(entity: Entity): any { + const scene = Core.scene; + const sceneInfo = this.getSceneInfo(scene); + return { name: entity.name || `Entity_${entity.id}`, id: entity.id, @@ -639,6 +685,9 @@ export class EntityDataCollector { active: entity.active !== false, activeInHierarchy: entity.activeInHierarchy !== false, destroyed: entity.isDestroyed || false, + scene: sceneInfo.name, + sceneName: sceneInfo.name, + sceneType: sceneInfo.type, componentCount: entity.components.length, componentTypes: entity.components.map((component: Component) => component.constructor.name), componentMask: entity.componentMask?.toString() || '0', @@ -673,13 +722,33 @@ export class EntityDataCollector { } } + // 提取实际的组件属性 + const properties: Record = {}; + + try { + const propertyKeys = Object.keys(component); + propertyKeys.forEach(propertyKey => { + if (!propertyKey.startsWith('_') && propertyKey !== 'entity' && propertyKey !== 'constructor') { + const propertyValue = (component as any)[propertyKey]; + if (propertyValue !== undefined && propertyValue !== null) { + properties[propertyKey] = this.formatPropertyValue(propertyValue); + } + } + }); + + // 如果没有找到任何属性,添加一些调试信息 + if (Object.keys(properties).length === 0) { + properties._info = '该组件没有公开属性'; + properties._componentId = component.constructor.name; + } + } catch (error) { + properties._error = '属性提取失败'; + properties._componentId = component.constructor.name; + } + return { typeName: typeName, - properties: { - _componentId: component.constructor.name, - _propertyCount: Object.keys(component).filter(key => !key.startsWith('_') && key !== 'entity').length, - _lazyLoad: true - } + properties: properties }; }); } diff --git a/thirdparty/BehaviourTree-ai b/thirdparty/BehaviourTree-ai index 7df0745b..f3e91b9f 160000 --- a/thirdparty/BehaviourTree-ai +++ b/thirdparty/BehaviourTree-ai @@ -1 +1 @@ -Subproject commit 7df0745b80d52b6b4b3e8befdca7f6193f80f1c2 +Subproject commit f3e91b9f34eaa41d38a5a284be01597e0376c3b3