优化组件递归调用导致的性能问题/新增实体所在的场景显示
This commit is contained in:
Submodule extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension updated: e222e9e9d8...66c3acbe9e
@@ -5,8 +5,10 @@ import { Core } from '../../Core';
|
|||||||
* 组件数据收集器
|
* 组件数据收集器
|
||||||
*/
|
*/
|
||||||
export class ComponentDataCollector {
|
export class ComponentDataCollector {
|
||||||
|
private static componentSizeCache = new Map<string, number>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 收集组件数据
|
* 收集组件数据(轻量版,不计算实际内存大小)
|
||||||
*/
|
*/
|
||||||
public collectComponentData(): IComponentDebugData {
|
public collectComponentData(): IComponentDebugData {
|
||||||
const scene = Core.scene;
|
const scene = Core.scene;
|
||||||
@@ -69,7 +71,8 @@ export class ComponentDataCollector {
|
|||||||
componentStats: Array.from(componentStats.entries()).map(([typeName, stats]) => {
|
componentStats: Array.from(componentStats.entries()).map(([typeName, stats]) => {
|
||||||
const poolSize = poolSizes.get(typeName) || 0;
|
const poolSize = poolSizes.get(typeName) || 0;
|
||||||
const poolUtilization = poolUtilizations.get(typeName) || 0;
|
const poolUtilization = poolUtilizations.get(typeName) || 0;
|
||||||
const memoryPerInstance = this.calculateComponentMemorySize(typeName);
|
// 使用预估的基础内存大小,避免每帧计算
|
||||||
|
const memoryPerInstance = this.getEstimatedComponentSize(typeName);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
typeName,
|
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;
|
const scene = Core.scene;
|
||||||
if (!scene) return 32;
|
if (!scene) return 64;
|
||||||
|
|
||||||
const entityList = (scene as any).entities;
|
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 {
|
try {
|
||||||
// 找到第一个包含此组件的实体,分析组件大小
|
// 找到第一个包含此组件的实体,分析组件大小
|
||||||
@@ -105,18 +187,18 @@ export class ComponentDataCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 忽略错误,使用默认值
|
// 忽略错误,使用估算值
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果无法计算,返回基础大小
|
return this.getEstimatedComponentSize(typeName);
|
||||||
return 32; // 基础对象开销
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 估算对象内存大小
|
* 估算对象内存大小(仅用于内存快照)
|
||||||
|
* 优化版本:减少递归深度,提高性能
|
||||||
*/
|
*/
|
||||||
private estimateObjectSize(obj: any, visited = new WeakSet(), depth = 0): number {
|
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;
|
if (visited.has(obj)) return 0;
|
||||||
|
|
||||||
let size = 0;
|
let size = 0;
|
||||||
@@ -124,55 +206,55 @@ export class ComponentDataCollector {
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
size = 1;
|
size = 4;
|
||||||
break;
|
break;
|
||||||
case 'number':
|
case 'number':
|
||||||
size = 8;
|
size = 8;
|
||||||
break;
|
break;
|
||||||
case 'string':
|
case 'string':
|
||||||
const stringSize = 24 + (obj.length * 2);
|
size = 24 + Math.min(obj.length * 2, 1000);
|
||||||
size = Math.ceil(stringSize / 8) * 8;
|
|
||||||
break;
|
break;
|
||||||
case 'object':
|
case 'object':
|
||||||
visited.add(obj);
|
visited.add(obj);
|
||||||
|
|
||||||
if (Array.isArray(obj)) {
|
if (Array.isArray(obj)) {
|
||||||
size = 40 + (obj.length * 8);
|
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);
|
size += this.estimateObjectSize(obj[i], visited, depth + 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
size = 32;
|
size = 32;
|
||||||
const allKeys = [
|
|
||||||
...Object.getOwnPropertyNames(obj),
|
|
||||||
...Object.getOwnPropertySymbols(obj)
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const key of allKeys) {
|
try {
|
||||||
try {
|
const ownKeys = Object.getOwnPropertyNames(obj);
|
||||||
if (typeof key === 'string' && (
|
const maxProps = Math.min(ownKeys.length, 30);
|
||||||
key === 'constructor' ||
|
|
||||||
|
for (let i = 0; i < maxProps; i++) {
|
||||||
|
const key = ownKeys[i];
|
||||||
|
|
||||||
|
if (key === 'constructor' ||
|
||||||
key === '__proto__' ||
|
key === '__proto__' ||
|
||||||
key === 'entity' || key === '_entity'
|
key === 'entity' ||
|
||||||
)) {
|
key === '_entity' ||
|
||||||
|
key.startsWith('_cc_') ||
|
||||||
|
key.startsWith('__')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(obj, key);
|
try {
|
||||||
if (!descriptor) continue;
|
|
||||||
|
|
||||||
if (typeof key === 'string') {
|
|
||||||
size += 16 + (key.length * 2);
|
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;
|
break;
|
||||||
@@ -182,4 +264,8 @@ export class ComponentDataCollector {
|
|||||||
|
|
||||||
return Math.ceil(size / 8) * 8;
|
return Math.ceil(size / 8) * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static clearCache(): void {
|
||||||
|
ComponentDataCollector.componentSizeCache.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -410,35 +410,59 @@ export class DebugManager {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 收集组件内存统计
|
* 收集组件内存统计(仅用于内存快照)
|
||||||
*/
|
*/
|
||||||
private collectComponentMemoryStats(entityList: any): any {
|
private collectComponentMemoryStats(entityList: any): any {
|
||||||
const componentStats = new Map<string, { count: number; totalMemory: number; instances: any[] }>();
|
const componentStats = new Map<string, { count: number; totalMemory: number; instances: any[] }>();
|
||||||
let totalComponentMemory = 0;
|
let totalComponentMemory = 0;
|
||||||
|
|
||||||
|
// 首先统计组件类型和数量
|
||||||
|
const componentTypeCounts = new Map<string, number>();
|
||||||
for (const entity of entityList.buffer) {
|
for (const entity of entityList.buffer) {
|
||||||
if (!entity || entity.destroyed || !entity.components) continue;
|
if (!entity || entity.destroyed || !entity.components) continue;
|
||||||
|
|
||||||
for (const component of entity.components) {
|
for (const component of entity.components) {
|
||||||
const typeName = component.constructor.name;
|
const typeName = component.constructor.name;
|
||||||
const componentMemory = this.entityCollector.calculateObjectSize(component, ['entity']);
|
componentTypeCounts.set(typeName, (componentTypeCounts.get(typeName) || 0) + 1);
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 为每种组件类型计算详细内存(只计算一次,然后乘以数量)
|
||||||
|
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]) => ({
|
const componentBreakdown = Array.from(componentStats.entries()).map(([typeName, stats]) => ({
|
||||||
typeName,
|
typeName,
|
||||||
instanceCount: stats.count,
|
instanceCount: stats.count,
|
||||||
@@ -456,9 +480,6 @@ export class DebugManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 收集系统内存统计
|
|
||||||
*/
|
|
||||||
private collectSystemMemoryStats(): any {
|
private collectSystemMemoryStats(): any {
|
||||||
const scene = Core.scene;
|
const scene = Core.scene;
|
||||||
let totalSystemMemory = 0;
|
let totalSystemMemory = 0;
|
||||||
@@ -467,12 +488,23 @@ export class DebugManager {
|
|||||||
try {
|
try {
|
||||||
const entityProcessors = (scene as any).entityProcessors;
|
const entityProcessors = (scene as any).entityProcessors;
|
||||||
if (entityProcessors && entityProcessors.processors) {
|
if (entityProcessors && entityProcessors.processors) {
|
||||||
|
const systemTypeMemoryCache = new Map<string, number>();
|
||||||
|
|
||||||
for (const system of entityProcessors.processors) {
|
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;
|
totalSystemMemory += systemMemory;
|
||||||
|
|
||||||
systemBreakdown.push({
|
systemBreakdown.push({
|
||||||
name: system.constructor.name,
|
name: systemTypeName,
|
||||||
memory: systemMemory,
|
memory: systemMemory,
|
||||||
enabled: system.enabled !== false,
|
enabled: system.enabled !== false,
|
||||||
updateOrder: system.updateOrder || 0
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 收集对象池内存统计
|
* 收集对象池内存统计
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -103,8 +103,13 @@ export class EntityDataCollector {
|
|||||||
|
|
||||||
const componentDetails = this.extractComponentDetails(entity.components);
|
const componentDetails = this.extractComponentDetails(entity.components);
|
||||||
|
|
||||||
|
const sceneInfo = this.getSceneInfo(scene);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...baseDebugInfo,
|
...baseDebugInfo,
|
||||||
|
scene: sceneInfo.name,
|
||||||
|
sceneName: sceneInfo.name,
|
||||||
|
sceneType: sceneInfo.type,
|
||||||
parentName: entity.parent?.name || null,
|
parentName: entity.parent?.name || null,
|
||||||
components: componentDetails || [],
|
components: componentDetails || [],
|
||||||
componentCount: entity.components?.length || 0,
|
componentCount: entity.components?.length || 0,
|
||||||
@@ -113,6 +118,7 @@ export class EntityDataCollector {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
error: `获取实体详情失败: ${error instanceof Error ? error.message : String(error)}`,
|
error: `获取实体详情失败: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
scene: '获取失败',
|
||||||
components: [],
|
components: [],
|
||||||
componentCount: 0,
|
componentCount: 0,
|
||||||
componentTypes: []
|
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 {
|
public collectEntityDataWithMemory(): IEntityDebugData {
|
||||||
const scene = Core.scene;
|
const scene = Core.scene;
|
||||||
@@ -463,49 +495,60 @@ export class EntityDataCollector {
|
|||||||
public calculateObjectSize(obj: any, excludeKeys: string[] = []): number {
|
public calculateObjectSize(obj: any, excludeKeys: string[] = []): number {
|
||||||
if (!obj || typeof obj !== 'object') return 0;
|
if (!obj || typeof obj !== 'object') return 0;
|
||||||
|
|
||||||
try {
|
|
||||||
let size = 0;
|
|
||||||
const visited = new WeakSet();
|
const visited = new WeakSet();
|
||||||
const maxDepth = 3;
|
const maxDepth = 2;
|
||||||
|
|
||||||
const calculate = (item: any, depth: number = 0): number => {
|
const calculate = (item: any, depth: number = 0): number => {
|
||||||
if (!item || typeof item !== 'object' || visited.has(item) || depth >= maxDepth) {
|
if (!item || typeof item !== 'object' || depth >= maxDepth) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (visited.has(item)) return 0;
|
||||||
visited.add(item);
|
visited.add(item);
|
||||||
|
|
||||||
let itemSize = 0;
|
let itemSize = 32;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const keys = Object.keys(item);
|
const keys = Object.keys(item);
|
||||||
for (let i = 0; i < Math.min(keys.length, 50); i++) {
|
const maxKeys = Math.min(keys.length, 20);
|
||||||
const key = keys[i];
|
|
||||||
if (excludeKeys.includes(key)) continue;
|
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];
|
const value = item[key];
|
||||||
itemSize += key.length * 2;
|
itemSize += key.length * 2;
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
itemSize += Math.min(value.length * 2, 1000);
|
itemSize += Math.min(value.length * 2, 200);
|
||||||
} else if (typeof value === 'number') {
|
} else if (typeof value === 'number') {
|
||||||
itemSize += 8;
|
itemSize += 8;
|
||||||
} else if (typeof value === 'boolean') {
|
} else if (typeof value === 'boolean') {
|
||||||
itemSize += 4;
|
itemSize += 4;
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
itemSize += 40 + Math.min(value.length * 8, 160);
|
||||||
} else if (typeof value === 'object' && value !== null) {
|
} else if (typeof value === 'object' && value !== null) {
|
||||||
itemSize += calculate(value, depth + 1);
|
itemSize += calculate(value, depth + 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
return isNaN(itemSize) ? 0 : itemSize;
|
return 64;
|
||||||
};
|
}
|
||||||
|
|
||||||
size = calculate(obj);
|
return itemSize;
|
||||||
return isNaN(size) || size < 0 ? 0 : size;
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const size = calculate(obj);
|
||||||
|
return Math.max(size, 32);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return 0;
|
return 64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,6 +675,9 @@ export class EntityDataCollector {
|
|||||||
* 构建实体基础信息
|
* 构建实体基础信息
|
||||||
*/
|
*/
|
||||||
private buildFallbackEntityInfo(entity: Entity): any {
|
private buildFallbackEntityInfo(entity: Entity): any {
|
||||||
|
const scene = Core.scene;
|
||||||
|
const sceneInfo = this.getSceneInfo(scene);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: entity.name || `Entity_${entity.id}`,
|
name: entity.name || `Entity_${entity.id}`,
|
||||||
id: entity.id,
|
id: entity.id,
|
||||||
@@ -639,6 +685,9 @@ export class EntityDataCollector {
|
|||||||
active: entity.active !== false,
|
active: entity.active !== false,
|
||||||
activeInHierarchy: entity.activeInHierarchy !== false,
|
activeInHierarchy: entity.activeInHierarchy !== false,
|
||||||
destroyed: entity.isDestroyed || false,
|
destroyed: entity.isDestroyed || false,
|
||||||
|
scene: sceneInfo.name,
|
||||||
|
sceneName: sceneInfo.name,
|
||||||
|
sceneType: sceneInfo.type,
|
||||||
componentCount: entity.components.length,
|
componentCount: entity.components.length,
|
||||||
componentTypes: entity.components.map((component: Component) => component.constructor.name),
|
componentTypes: entity.components.map((component: Component) => component.constructor.name),
|
||||||
componentMask: entity.componentMask?.toString() || '0',
|
componentMask: entity.componentMask?.toString() || '0',
|
||||||
@@ -673,13 +722,33 @@ export class EntityDataCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 提取实际的组件属性
|
||||||
|
const properties: Record<string, any> = {};
|
||||||
|
|
||||||
|
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 {
|
return {
|
||||||
typeName: typeName,
|
typeName: typeName,
|
||||||
properties: {
|
properties: properties
|
||||||
_componentId: component.constructor.name,
|
|
||||||
_propertyCount: Object.keys(component).filter(key => !key.startsWith('_') && key !== 'entity').length,
|
|
||||||
_lazyLoad: true
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
2
thirdparty/BehaviourTree-ai
vendored
2
thirdparty/BehaviourTree-ai
vendored
Submodule thirdparty/BehaviourTree-ai updated: 7df0745b80...f3e91b9f34
Reference in New Issue
Block a user