668 lines
24 KiB
TypeScript
668 lines
24 KiB
TypeScript
|
|
import { IEntityDebugData } from '../../types';
|
|||
|
|
import { Core } from '../../Core';
|
|||
|
|
import { Entity } from '../../ECS/Entity';
|
|||
|
|
import { Component } from '../../ECS/Component';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 实体数据收集器
|
|||
|
|
*/
|
|||
|
|
export class EntityDataCollector {
|
|||
|
|
/**
|
|||
|
|
* 收集实体数据
|
|||
|
|
*/
|
|||
|
|
public collectEntityData(): IEntityDebugData {
|
|||
|
|
const scene = Core.scene;
|
|||
|
|
if (!scene) {
|
|||
|
|
return this.getEmptyEntityDebugData();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const entityList = (scene as any).entities;
|
|||
|
|
if (!entityList) {
|
|||
|
|
return this.getEmptyEntityDebugData();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let stats;
|
|||
|
|
try {
|
|||
|
|
stats = entityList.getStats ? entityList.getStats() : this.calculateFallbackEntityStats(entityList);
|
|||
|
|
} catch (error) {
|
|||
|
|
// console.warn('[ECS Debug] 获取实体统计失败:', error);
|
|||
|
|
return {
|
|||
|
|
totalEntities: 0,
|
|||
|
|
activeEntities: 0,
|
|||
|
|
pendingAdd: 0,
|
|||
|
|
pendingRemove: 0,
|
|||
|
|
entitiesPerArchetype: [],
|
|||
|
|
topEntitiesByComponents: [],
|
|||
|
|
entityHierarchy: [],
|
|||
|
|
entityDetailsMap: {}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 利用ArchetypeSystem的缓存数据
|
|||
|
|
const archetypeData = this.collectArchetypeData(scene);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
totalEntities: stats.totalEntities,
|
|||
|
|
activeEntities: stats.activeEntities,
|
|||
|
|
pendingAdd: stats.pendingAdd || 0,
|
|||
|
|
pendingRemove: stats.pendingRemove || 0,
|
|||
|
|
entitiesPerArchetype: archetypeData.distribution,
|
|||
|
|
topEntitiesByComponents: archetypeData.topEntities,
|
|||
|
|
entityHierarchy: this.buildEntityHierarchyTree(entityList),
|
|||
|
|
entityDetailsMap: this.buildEntityDetailsMap(entityList)
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取空的实体数据
|
|||
|
|
*/
|
|||
|
|
private getEmptyEntityDebugData(): IEntityDebugData {
|
|||
|
|
return {
|
|||
|
|
totalEntities: 0,
|
|||
|
|
activeEntities: 0,
|
|||
|
|
pendingAdd: 0,
|
|||
|
|
pendingRemove: 0,
|
|||
|
|
entitiesPerArchetype: [],
|
|||
|
|
topEntitiesByComponents: [],
|
|||
|
|
entityHierarchy: [],
|
|||
|
|
entityDetailsMap: {}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 计算实体统计信息
|
|||
|
|
*/
|
|||
|
|
private calculateFallbackEntityStats(entityList: any): any {
|
|||
|
|
const allEntities = entityList.buffer || [];
|
|||
|
|
const activeEntities = allEntities.filter((entity: any) =>
|
|||
|
|
entity.enabled && !entity._isDestroyed
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
totalEntities: allEntities.length,
|
|||
|
|
activeEntities: activeEntities.length,
|
|||
|
|
pendingAdd: 0,
|
|||
|
|
pendingRemove: 0,
|
|||
|
|
averageComponentsPerEntity: activeEntities.length > 0 ?
|
|||
|
|
allEntities.reduce((sum: number, e: any) => sum + (e.components?.length || 0), 0) / activeEntities.length : 0
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 收集原型数据
|
|||
|
|
*/
|
|||
|
|
private collectArchetypeData(scene: any): {
|
|||
|
|
distribution: Array<{ signature: string; count: number; memory: number }>;
|
|||
|
|
topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>;
|
|||
|
|
} {
|
|||
|
|
// 利用ArchetypeSystem的缓存数据
|
|||
|
|
if (scene && scene.archetypeSystem && typeof scene.archetypeSystem.getAllArchetypes === 'function') {
|
|||
|
|
return this.extractArchetypeStatistics(scene.archetypeSystem);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 回退到传统方法
|
|||
|
|
const entityContainer = { entities: scene.entities?.buffer || [] };
|
|||
|
|
return {
|
|||
|
|
distribution: this.getArchetypeDistribution(entityContainer),
|
|||
|
|
topEntities: this.getTopEntitiesByComponents(entityContainer)
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 提取原型统计信息
|
|||
|
|
*/
|
|||
|
|
private extractArchetypeStatistics(archetypeSystem: any): {
|
|||
|
|
distribution: Array<{ signature: string; count: number; memory: number }>;
|
|||
|
|
topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }>;
|
|||
|
|
} {
|
|||
|
|
const archetypes = archetypeSystem.getAllArchetypes();
|
|||
|
|
const distribution: Array<{ signature: string; count: number; memory: number }> = [];
|
|||
|
|
const topEntities: Array<{ id: string; name: string; componentCount: number; memory: number }> = [];
|
|||
|
|
|
|||
|
|
archetypes.forEach((archetype: any) => {
|
|||
|
|
const signature = archetype.componentTypes?.map((type: any) => type.name).join(',') || 'Unknown';
|
|||
|
|
const entityCount = archetype.entities?.length || 0;
|
|||
|
|
|
|||
|
|
// 计算实际内存使用量
|
|||
|
|
let actualMemory = 0;
|
|||
|
|
if (archetype.entities && archetype.entities.length > 0) {
|
|||
|
|
const sampleSize = Math.min(5, archetype.entities.length);
|
|||
|
|
let sampleMemory = 0;
|
|||
|
|
|
|||
|
|
for (let i = 0; i < sampleSize; i++) {
|
|||
|
|
sampleMemory += this.estimateEntityMemoryUsage(archetype.entities[i]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
actualMemory = (sampleMemory / sampleSize) * entityCount;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
distribution.push({
|
|||
|
|
signature,
|
|||
|
|
count: entityCount,
|
|||
|
|
memory: actualMemory
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 收集组件数量最多的实体
|
|||
|
|
if (archetype.entities) {
|
|||
|
|
archetype.entities.slice(0, 5).forEach((entity: any) => {
|
|||
|
|
topEntities.push({
|
|||
|
|
id: entity.id.toString(),
|
|||
|
|
name: entity.name || `Entity_${entity.id}`,
|
|||
|
|
componentCount: entity.components?.length || 0,
|
|||
|
|
memory: this.estimateEntityMemoryUsage(entity)
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 按实体数量排序
|
|||
|
|
distribution.sort((a, b) => b.count - a.count);
|
|||
|
|
topEntities.sort((a, b) => b.componentCount - a.componentCount);
|
|||
|
|
|
|||
|
|
return { distribution, topEntities };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 计算实体内存使用量
|
|||
|
|
*/
|
|||
|
|
public estimateEntityMemoryUsage(entity: any): number {
|
|||
|
|
const startTime = performance.now();
|
|||
|
|
|
|||
|
|
let totalSize = 0;
|
|||
|
|
|
|||
|
|
// 计算实体基础大小
|
|||
|
|
totalSize += this.calculateObjectSize(entity, ['components', 'children', 'parent']);
|
|||
|
|
|
|||
|
|
// 计算组件大小
|
|||
|
|
if (entity.components) {
|
|||
|
|
entity.components.forEach((component: any) => {
|
|||
|
|
totalSize += this.calculateObjectSize(component, ['entity']);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 记录计算耗时
|
|||
|
|
const executionTime = performance.now() - startTime;
|
|||
|
|
if (executionTime > 1) {
|
|||
|
|
// console.debug(`[ECS Debug] 实体${entity.id}内存计算耗时: ${executionTime.toFixed(2)}ms`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return totalSize;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 计算对象大小
|
|||
|
|
*/
|
|||
|
|
public calculateObjectSize(obj: any, excludeKeys: string[] = []): number {
|
|||
|
|
if (!obj || typeof obj !== 'object') return 0;
|
|||
|
|
|
|||
|
|
let size = 0;
|
|||
|
|
const visited = new WeakSet();
|
|||
|
|
|
|||
|
|
const calculate = (item: any): number => {
|
|||
|
|
if (!item || typeof item !== 'object' || visited.has(item)) return 0;
|
|||
|
|
visited.add(item);
|
|||
|
|
|
|||
|
|
let itemSize = 0;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
for (const key in item) {
|
|||
|
|
if (excludeKeys.includes(key)) continue;
|
|||
|
|
|
|||
|
|
const value = item[key];
|
|||
|
|
itemSize += key.length * 2; // key size
|
|||
|
|
|
|||
|
|
if (typeof value === 'string') {
|
|||
|
|
itemSize += value.length * 2;
|
|||
|
|
} else if (typeof value === 'number') {
|
|||
|
|
itemSize += 8;
|
|||
|
|
} else if (typeof value === 'boolean') {
|
|||
|
|
itemSize += 4;
|
|||
|
|
} else if (typeof value === 'object' && value !== null) {
|
|||
|
|
itemSize += calculate(value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
// 忽略无法访问的属性
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return itemSize;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return calculate(obj);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 构建实体层次结构树
|
|||
|
|
*/
|
|||
|
|
private buildEntityHierarchyTree(entityList: { buffer?: Entity[] }): Array<{
|
|||
|
|
id: number;
|
|||
|
|
name: string;
|
|||
|
|
active: boolean;
|
|||
|
|
enabled: boolean;
|
|||
|
|
activeInHierarchy: boolean;
|
|||
|
|
componentCount: number;
|
|||
|
|
componentTypes: string[];
|
|||
|
|
parentId: number | null;
|
|||
|
|
children: any[];
|
|||
|
|
depth: number;
|
|||
|
|
tag: number;
|
|||
|
|
updateOrder: number;
|
|||
|
|
}> {
|
|||
|
|
if (!entityList?.buffer) return [];
|
|||
|
|
|
|||
|
|
const rootEntities: any[] = [];
|
|||
|
|
|
|||
|
|
// 直接遍历实体缓冲区,只收集根实体
|
|||
|
|
entityList.buffer.forEach((entity: Entity) => {
|
|||
|
|
if (!entity.parent) {
|
|||
|
|
const hierarchyNode = this.buildEntityHierarchyNode(entity);
|
|||
|
|
rootEntities.push(hierarchyNode);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 按实体名称排序,提供一致的显示顺序
|
|||
|
|
rootEntities.sort((nodeA, nodeB) => {
|
|||
|
|
if (nodeA.name < nodeB.name) return -1;
|
|||
|
|
if (nodeA.name > nodeB.name) return 1;
|
|||
|
|
return nodeA.id - nodeB.id;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return rootEntities;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 构建实体层次结构节点
|
|||
|
|
*/
|
|||
|
|
private buildEntityHierarchyNode(entity: Entity): any {
|
|||
|
|
let node = {
|
|||
|
|
id: entity.id,
|
|||
|
|
name: entity.name || `Entity_${entity.id}`,
|
|||
|
|
active: entity.active !== false,
|
|||
|
|
enabled: entity.enabled !== false,
|
|||
|
|
activeInHierarchy: entity.activeInHierarchy !== false,
|
|||
|
|
componentCount: entity.components.length,
|
|||
|
|
componentTypes: entity.components.map((component: Component) => component.constructor.name),
|
|||
|
|
parentId: entity.parent?.id || null,
|
|||
|
|
children: [] as any[],
|
|||
|
|
depth: entity.getDepth ? entity.getDepth() : 0,
|
|||
|
|
tag: entity.tag || 0,
|
|||
|
|
updateOrder: entity.updateOrder || 0
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 递归构建子实体节点
|
|||
|
|
if (entity.children && entity.children.length > 0) {
|
|||
|
|
node.children = entity.children.map((child: Entity) => this.buildEntityHierarchyNode(child));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 优先使用Entity的getDebugInfo方法
|
|||
|
|
if (typeof entity.getDebugInfo === 'function') {
|
|||
|
|
const debugInfo = entity.getDebugInfo();
|
|||
|
|
node = {
|
|||
|
|
...node,
|
|||
|
|
...debugInfo
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 收集所有组件详细属性信息
|
|||
|
|
if (entity.components && entity.components.length > 0) {
|
|||
|
|
(node as any).componentDetails = this.extractComponentDetails(entity.components);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return node;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 构建实体详情映射
|
|||
|
|
*/
|
|||
|
|
private buildEntityDetailsMap(entityList: { buffer?: Entity[] }): Record<number, any> {
|
|||
|
|
if (!entityList?.buffer) return {};
|
|||
|
|
|
|||
|
|
const entityDetailsMap: Record<number, any> = {};
|
|||
|
|
|
|||
|
|
// 批量处理实体,减少函数调用开销
|
|||
|
|
const entities = entityList.buffer;
|
|||
|
|
const batchSize = 50;
|
|||
|
|
|
|||
|
|
for (let i = 0; i < entities.length; i += batchSize) {
|
|||
|
|
const batch = entities.slice(i, i + batchSize);
|
|||
|
|
|
|||
|
|
batch.forEach((entity: Entity) => {
|
|||
|
|
// 优先使用Entity的getDebugInfo方法
|
|||
|
|
const baseDebugInfo = entity.getDebugInfo ?
|
|||
|
|
entity.getDebugInfo() :
|
|||
|
|
this.buildFallbackEntityInfo(entity);
|
|||
|
|
|
|||
|
|
// 利用组件缓存统计信息
|
|||
|
|
const componentCacheStats = (entity as any).getComponentCacheStats ?
|
|||
|
|
(entity as any).getComponentCacheStats() : null;
|
|||
|
|
|
|||
|
|
const componentDetails = this.extractComponentDetails(entity.components);
|
|||
|
|
|
|||
|
|
// 构建完整的实体详情对象
|
|||
|
|
entityDetailsMap[entity.id] = {
|
|||
|
|
...baseDebugInfo,
|
|||
|
|
parentName: entity.parent?.name || null,
|
|||
|
|
components: componentDetails,
|
|||
|
|
componentTypes: baseDebugInfo.componentTypes ||
|
|||
|
|
componentDetails.map((comp) => comp.typeName),
|
|||
|
|
// 添加缓存性能信息
|
|||
|
|
cachePerformance: componentCacheStats ? {
|
|||
|
|
hitRate: componentCacheStats.cacheStats.hitRate,
|
|||
|
|
size: componentCacheStats.cacheStats.size,
|
|||
|
|
maxSize: componentCacheStats.cacheStats.maxSize
|
|||
|
|
} : null
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return entityDetailsMap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 构建实体基础信息
|
|||
|
|
*/
|
|||
|
|
private buildFallbackEntityInfo(entity: Entity): any {
|
|||
|
|
return {
|
|||
|
|
name: entity.name || `Entity_${entity.id}`,
|
|||
|
|
id: entity.id,
|
|||
|
|
enabled: entity.enabled !== false,
|
|||
|
|
active: entity.active !== false,
|
|||
|
|
activeInHierarchy: entity.activeInHierarchy !== false,
|
|||
|
|
destroyed: entity.isDestroyed || false,
|
|||
|
|
componentCount: entity.components.length,
|
|||
|
|
componentTypes: entity.components.map((component: Component) => component.constructor.name),
|
|||
|
|
componentMask: entity.componentMask?.toString() || '0',
|
|||
|
|
parentId: entity.parent?.id || null,
|
|||
|
|
childCount: entity.children?.length || 0,
|
|||
|
|
childIds: entity.children.map((child: Entity) => child.id) || [],
|
|||
|
|
depth: entity.getDepth ? entity.getDepth() : 0,
|
|||
|
|
tag: entity.tag || 0,
|
|||
|
|
updateOrder: entity.updateOrder || 0
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 提取组件详细信息
|
|||
|
|
*/
|
|||
|
|
private extractComponentDetails(components: Component[]): Array<{
|
|||
|
|
typeName: string;
|
|||
|
|
properties: Record<string, any>;
|
|||
|
|
}> {
|
|||
|
|
return components.map((component: Component) => {
|
|||
|
|
// 获取组件类型名称,优先使用constructor.name
|
|||
|
|
let typeName = component.constructor.name;
|
|||
|
|
|
|||
|
|
// 如果constructor.name为空或者是通用名称,尝试其他方法
|
|||
|
|
if (!typeName || typeName === 'Object' || typeName === 'Function') {
|
|||
|
|
// 尝试从类型管理器获取
|
|||
|
|
try {
|
|||
|
|
const { ComponentTypeManager } = require('../../ECS/Utils/ComponentTypeManager');
|
|||
|
|
const typeManager = ComponentTypeManager.instance;
|
|||
|
|
const componentType = component.constructor as any;
|
|||
|
|
const typeId = typeManager.getTypeId(componentType);
|
|||
|
|
typeName = typeManager.getTypeName(typeId);
|
|||
|
|
} catch (error) {
|
|||
|
|
// 如果类型管理器不可用,使用默认名称
|
|||
|
|
typeName = 'UnknownComponent';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const componentDetail = {
|
|||
|
|
typeName: typeName,
|
|||
|
|
properties: {} as Record<string, any>
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 安全地提取组件属性
|
|||
|
|
try {
|
|||
|
|
const propertyKeys = Object.keys(component);
|
|||
|
|
propertyKeys.forEach(propertyKey => {
|
|||
|
|
// 跳过私有属性和实体引用,避免循环引用
|
|||
|
|
if (!propertyKey.startsWith('_') && propertyKey !== 'entity') {
|
|||
|
|
const propertyValue = (component as any)[propertyKey];
|
|||
|
|
if (propertyValue !== undefined && propertyValue !== null) {
|
|||
|
|
componentDetail.properties[propertyKey] = this.formatPropertyValue(propertyValue);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
componentDetail.properties['_extractionError'] = '属性提取失败';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return componentDetail;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 格式化属性值
|
|||
|
|
*/
|
|||
|
|
private formatPropertyValue(value: any, depth: number = 0): any {
|
|||
|
|
// 防止无限递归,限制最大深度
|
|||
|
|
if (depth > 5) {
|
|||
|
|
return value?.toString() || 'null';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (typeof value === 'object' && value !== null) {
|
|||
|
|
if (Array.isArray(value)) {
|
|||
|
|
// 对于数组,总是返回完整数组,让前端决定如何显示
|
|||
|
|
return value.map(item => this.formatPropertyValue(item, depth + 1));
|
|||
|
|
} else {
|
|||
|
|
// 通用对象处理:提取所有可枚举属性,不限制数量
|
|||
|
|
try {
|
|||
|
|
const keys = Object.keys(value);
|
|||
|
|
if (keys.length === 0) {
|
|||
|
|
return {};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result: any = {};
|
|||
|
|
keys.forEach(key => {
|
|||
|
|
const propValue = value[key];
|
|||
|
|
// 避免循环引用和函数属性
|
|||
|
|
if (propValue !== value && typeof propValue !== 'function') {
|
|||
|
|
try {
|
|||
|
|
result[key] = this.formatPropertyValue(propValue, depth + 1);
|
|||
|
|
} catch (error) {
|
|||
|
|
// 如果属性访问失败,记录错误信息
|
|||
|
|
result[key] = `[访问失败: ${error instanceof Error ? error.message : String(error)}]`;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
return result;
|
|||
|
|
} catch (error) {
|
|||
|
|
return `[对象解析失败: ${error instanceof Error ? error.message : String(error)}]`;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取Archetype分布
|
|||
|
|
*/
|
|||
|
|
private getArchetypeDistribution(entityContainer: any): Array<{ signature: string; count: number; memory: number }> {
|
|||
|
|
const distribution = new Map<string, { count: number; memory: number }>();
|
|||
|
|
|
|||
|
|
if (entityContainer && entityContainer.entities) {
|
|||
|
|
entityContainer.entities.forEach((entity: any) => {
|
|||
|
|
const signature = entity.componentMask?.toString() || '0';
|
|||
|
|
const existing = distribution.get(signature);
|
|||
|
|
const memory = this.estimateEntityMemoryUsage(entity);
|
|||
|
|
|
|||
|
|
if (existing) {
|
|||
|
|
existing.count++;
|
|||
|
|
existing.memory += memory;
|
|||
|
|
} else {
|
|||
|
|
distribution.set(signature, { count: 1, memory });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return Array.from(distribution.entries())
|
|||
|
|
.map(([signature, data]) => ({ signature, ...data }))
|
|||
|
|
.sort((a, b) => b.count - a.count);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取组件数量最多的实体
|
|||
|
|
*/
|
|||
|
|
private getTopEntitiesByComponents(entityContainer: any): Array<{ id: string; name: string; componentCount: number; memory: number }> {
|
|||
|
|
if (!entityContainer || !entityContainer.entities) {
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return entityContainer.entities
|
|||
|
|
.map((entity: any) => ({
|
|||
|
|
id: entity.id.toString(),
|
|||
|
|
name: entity.name || `Entity_${entity.id}`,
|
|||
|
|
componentCount: entity.components?.length || 0,
|
|||
|
|
memory: this.estimateEntityMemoryUsage(entity)
|
|||
|
|
}))
|
|||
|
|
.sort((a: any, b: any) => b.componentCount - a.componentCount);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 提取实体详细信息
|
|||
|
|
*/
|
|||
|
|
private extractEntityDetails(entity: any): any {
|
|||
|
|
const components = entity.components || [];
|
|||
|
|
const componentDetails = this.extractComponentDetails(components);
|
|||
|
|
|
|||
|
|
// 格式化组件掩码为更可读的形式
|
|||
|
|
const componentMask = entity.componentMask || entity._componentMask || 0;
|
|||
|
|
const componentMaskBinary = componentMask.toString(2).padStart(32, '0');
|
|||
|
|
const componentMaskHex = '0x' + componentMask.toString(16).toUpperCase().padStart(8, '0');
|
|||
|
|
|
|||
|
|
// 生成组件掩码的可读描述
|
|||
|
|
const maskDescription = this.generateComponentMaskDescription(componentMask, components);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
id: entity.id,
|
|||
|
|
name: entity.name || `Entity_${entity.id}`,
|
|||
|
|
active: entity.active !== false,
|
|||
|
|
enabled: entity.enabled !== false,
|
|||
|
|
activeInHierarchy: entity.activeInHierarchy !== false,
|
|||
|
|
updateOrder: entity.updateOrder || 0,
|
|||
|
|
tag: entity.tag || '未分配',
|
|||
|
|
depth: entity.depth || 0,
|
|||
|
|
componentMask: componentMask,
|
|||
|
|
componentMaskHex: componentMaskHex,
|
|||
|
|
componentMaskBinary: componentMaskBinary,
|
|||
|
|
componentMaskDescription: maskDescription,
|
|||
|
|
componentCount: components.length,
|
|||
|
|
components: componentDetails,
|
|||
|
|
|
|||
|
|
// 层次关系信息
|
|||
|
|
parentId: entity.parent?.id || null,
|
|||
|
|
parentName: entity.parent?.name || null,
|
|||
|
|
childCount: entity.children?.length || 0,
|
|||
|
|
childIds: entity.children?.map((child: any) => child.id).slice(0, 10) || [], // 最多显示10个子实体ID
|
|||
|
|
|
|||
|
|
// 内存信息
|
|||
|
|
estimatedMemory: this.estimateEntityMemoryUsage(entity),
|
|||
|
|
|
|||
|
|
// 额外的调试信息
|
|||
|
|
destroyed: entity.destroyed || false,
|
|||
|
|
scene: entity.scene?.name || '未知场景',
|
|||
|
|
transform: this.extractTransformInfo(entity),
|
|||
|
|
|
|||
|
|
// 实体状态描述
|
|||
|
|
statusDescription: this.generateEntityStatusDescription(entity)
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生成组件掩码的可读描述
|
|||
|
|
*/
|
|||
|
|
private generateComponentMaskDescription(mask: number, components: any[]): string {
|
|||
|
|
if (mask === 0) {
|
|||
|
|
return '无组件';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (components.length === 0) {
|
|||
|
|
return `掩码值: ${mask} (组件信息不可用)`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const componentNames = components.map(c => c.constructor.name);
|
|||
|
|
if (componentNames.length <= 3) {
|
|||
|
|
return `包含: ${componentNames.join(', ')}`;
|
|||
|
|
} else {
|
|||
|
|
return `包含: ${componentNames.slice(0, 3).join(', ')} 等${componentNames.length}个组件`;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生成实体状态描述
|
|||
|
|
*/
|
|||
|
|
private generateEntityStatusDescription(entity: any): string {
|
|||
|
|
const statuses = [];
|
|||
|
|
|
|||
|
|
if (entity.destroyed) {
|
|||
|
|
statuses.push('已销毁');
|
|||
|
|
} else {
|
|||
|
|
if (entity.active === false) {
|
|||
|
|
statuses.push('非活跃');
|
|||
|
|
}
|
|||
|
|
if (entity.enabled === false) {
|
|||
|
|
statuses.push('已禁用');
|
|||
|
|
}
|
|||
|
|
if (entity.activeInHierarchy === false) {
|
|||
|
|
statuses.push('层次非活跃');
|
|||
|
|
}
|
|||
|
|
if (statuses.length === 0) {
|
|||
|
|
statuses.push('正常运行');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return statuses.join(', ');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 提取变换信息
|
|||
|
|
*/
|
|||
|
|
private extractTransformInfo(entity: any): any {
|
|||
|
|
try {
|
|||
|
|
// 尝试获取Transform组件或position/rotation/scale信息
|
|||
|
|
const transform = entity.transform || entity.getComponent?.('Transform') || null;
|
|||
|
|
|
|||
|
|
if (transform) {
|
|||
|
|
return {
|
|||
|
|
position: this.formatVector3(transform.position || transform.localPosition),
|
|||
|
|
rotation: this.formatVector3(transform.rotation || transform.localRotation),
|
|||
|
|
scale: this.formatVector3(transform.scale || transform.localScale),
|
|||
|
|
worldPosition: this.formatVector3(transform.worldPosition),
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果没有Transform组件,检查是否有直接的位置信息
|
|||
|
|
if (entity.position || entity.x !== undefined) {
|
|||
|
|
return {
|
|||
|
|
position: this.formatVector3({
|
|||
|
|
x: entity.x || entity.position?.x || 0,
|
|||
|
|
y: entity.y || entity.position?.y || 0,
|
|||
|
|
z: entity.z || entity.position?.z || 0
|
|||
|
|
})
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
} catch (error) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 格式化Vector3对象
|
|||
|
|
*/
|
|||
|
|
private formatVector3(vector: any): string | null {
|
|||
|
|
if (!vector) return null;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const x = typeof vector.x === 'number' ? vector.x.toFixed(2) : '0.00';
|
|||
|
|
const y = typeof vector.y === 'number' ? vector.y.toFixed(2) : '0.00';
|
|||
|
|
const z = typeof vector.z === 'number' ? vector.z.toFixed(2) : '0.00';
|
|||
|
|
|
|||
|
|
return `(${x}, ${y}, ${z})`;
|
|||
|
|
} catch (error) {
|
|||
|
|
return vector.toString();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|