更新性能分析器及更改部分注释
This commit is contained in:
668
src/Utils/Debug/EntityDataCollector.ts
Normal file
668
src/Utils/Debug/EntityDataCollector.ts
Normal file
@@ -0,0 +1,668 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user