更新性能分析器及更改部分注释

This commit is contained in:
YHH
2025-06-30 20:33:45 +08:00
parent f88a402b0c
commit 992338d924
48 changed files with 3322 additions and 1038 deletions

View File

@@ -0,0 +1,185 @@
import { IComponentDebugData } from '../../types';
import { Core } from '../../Core';
/**
* 组件数据收集器
*/
export class ComponentDataCollector {
/**
* 收集组件数据
*/
public collectComponentData(): IComponentDebugData {
const scene = Core.scene;
if (!scene) {
return {
componentTypes: 0,
componentInstances: 0,
componentStats: []
};
}
const entityList = (scene as any).entities;
if (!entityList?.buffer) {
return {
componentTypes: 0,
componentInstances: 0,
componentStats: []
};
}
const componentStats = new Map<string, { count: number; entities: number }>();
let totalInstances = 0;
entityList.buffer.forEach((entity: any) => {
if (entity.components) {
entity.components.forEach((component: any) => {
const typeName = component.constructor.name;
const stats = componentStats.get(typeName) || { count: 0, entities: 0 };
stats.count++;
totalInstances++;
componentStats.set(typeName, stats);
});
}
});
// 获取池利用率信息
let poolUtilizations = new Map<string, number>();
let poolSizes = new Map<string, number>();
try {
const { ComponentPoolManager } = require('../../ECS/Core/ComponentPool');
const poolManager = ComponentPoolManager.getInstance();
const poolStats = poolManager.getPoolStats();
const utilizations = poolManager.getPoolUtilization();
for (const [typeName, stats] of poolStats.entries()) {
poolSizes.set(typeName, stats.maxSize);
}
for (const [typeName, util] of utilizations.entries()) {
poolUtilizations.set(typeName, util.utilization);
}
} catch (error) {
// 如果无法获取池信息,使用默认值
}
return {
componentTypes: componentStats.size,
componentInstances: totalInstances,
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);
return {
typeName,
instanceCount: stats.count,
memoryPerInstance: memoryPerInstance,
totalMemory: stats.count * memoryPerInstance,
poolSize: poolSize,
poolUtilization: poolUtilization,
averagePerEntity: stats.count / entityList.buffer.length
};
})
};
}
/**
* 计算组件实际内存大小
*/
private calculateComponentMemorySize(typeName: string): number {
const scene = Core.scene;
if (!scene) return 32;
const entityList = (scene as any).entities;
if (!entityList?.buffer) return 32;
try {
// 找到第一个包含此组件的实体,分析组件大小
for (const entity of entityList.buffer) {
if (entity.components) {
const component = entity.components.find((c: any) => c.constructor.name === typeName);
if (component) {
return this.estimateObjectSize(component);
}
}
}
} catch (error) {
// 忽略错误,使用默认值
}
// 如果无法计算,返回基础大小
return 32; // 基础对象开销
}
/**
* 估算对象内存大小
*/
private estimateObjectSize(obj: any, visited = new WeakSet(), depth = 0): number {
if (obj === null || obj === undefined || depth > 50) return 0;
if (visited.has(obj)) return 0;
let size = 0;
const type = typeof obj;
switch (type) {
case 'boolean':
size = 1;
break;
case 'number':
size = 8;
break;
case 'string':
const stringSize = 24 + (obj.length * 2);
size = Math.ceil(stringSize / 8) * 8;
break;
case 'object':
visited.add(obj);
if (Array.isArray(obj)) {
size = 40 + (obj.length * 8);
for (let i = 0; i < obj.length; 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' ||
key === '__proto__' ||
key === 'entity' || key === '_entity'
)) {
continue;
}
const descriptor = Object.getOwnPropertyDescriptor(obj, key);
if (!descriptor) continue;
if (typeof key === 'string') {
size += 16 + (key.length * 2);
} else {
size += 24;
}
if (descriptor.value !== undefined) {
size += this.estimateObjectSize(descriptor.value, visited, depth + 1);
}
} catch (error) {
continue;
}
}
}
break;
default:
size = 8;
}
return Math.ceil(size / 8) * 8;
}
}

View File

@@ -0,0 +1,124 @@
import { Component } from '../../ECS/Component';
/**
* 调试数据格式化工具
*/
export class DebugDataFormatter {
/**
* 格式化属性值
*/
public static 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;
}
/**
* 提取组件详细信息
*/
public static extractComponentDetails(components: Component[]): Array<{
typeName: string;
properties: Record<string, any>;
}> {
return components.map((component: Component) => {
const componentDetail = {
typeName: component.constructor.name,
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;
});
}
/**
* 计算对象大小
*/
public static 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);
}
}

View File

@@ -0,0 +1,615 @@
import { IECSDebugConfig, IECSDebugData } from '../../types';
import { EntityDataCollector } from './EntityDataCollector';
import { SystemDataCollector } from './SystemDataCollector';
import { PerformanceDataCollector } from './PerformanceDataCollector';
import { ComponentDataCollector } from './ComponentDataCollector';
import { SceneDataCollector } from './SceneDataCollector';
import { WebSocketManager } from './WebSocketManager';
import { Core } from '../../Core';
/**
* 调试管理器
*
* 整合所有调试数据收集器,负责收集和发送调试数据
*/
export class DebugManager {
private config: IECSDebugConfig;
private webSocketManager: WebSocketManager;
private entityCollector: EntityDataCollector;
private systemCollector: SystemDataCollector;
private performanceCollector: PerformanceDataCollector;
private componentCollector: ComponentDataCollector;
private sceneCollector: SceneDataCollector;
private frameCounter: number = 0;
private lastSendTime: number = 0;
private sendInterval: number;
private isRunning: boolean = false;
constructor(core: Core, config: IECSDebugConfig) {
this.config = config;
// 初始化数据收集器
this.entityCollector = new EntityDataCollector();
this.systemCollector = new SystemDataCollector();
this.performanceCollector = new PerformanceDataCollector();
this.componentCollector = new ComponentDataCollector();
this.sceneCollector = new SceneDataCollector();
// 初始化WebSocket管理器
this.webSocketManager = new WebSocketManager(
config.websocketUrl,
config.autoReconnect !== false
);
// 设置消息处理回调
this.webSocketManager.setMessageHandler(this.handleMessage.bind(this));
// 计算发送间隔(基于帧率)
const debugFrameRate = config.debugFrameRate || 30;
this.sendInterval = 1000 / debugFrameRate;
this.start();
}
/**
* 启动调试管理器
*/
public start(): void {
if (this.isRunning) return;
this.isRunning = true;
this.connectWebSocket();
}
/**
* 停止调试管理器
*/
public stop(): void {
if (!this.isRunning) return;
this.isRunning = false;
this.webSocketManager.disconnect();
}
/**
* 更新配置
*/
public updateConfig(config: IECSDebugConfig): void {
this.config = config;
// 更新发送间隔
const debugFrameRate = config.debugFrameRate || 30;
this.sendInterval = 1000 / debugFrameRate;
// 重新连接WebSocket如果URL变化
if (this.webSocketManager && config.websocketUrl) {
this.webSocketManager.disconnect();
this.webSocketManager = new WebSocketManager(
config.websocketUrl,
config.autoReconnect !== false
);
this.webSocketManager.setMessageHandler(this.handleMessage.bind(this));
this.connectWebSocket();
}
}
/**
* 帧更新回调
*/
public onFrameUpdate(deltaTime: number): void {
if (!this.isRunning || !this.config.enabled) return;
this.frameCounter++;
const currentTime = Date.now();
// 基于配置的帧率发送数据
if (currentTime - this.lastSendTime >= this.sendInterval) {
this.sendDebugData();
this.lastSendTime = currentTime;
}
}
/**
* 场景变更回调
*/
public onSceneChanged(): void {
// 场景变更时立即发送一次数据
if (this.isRunning && this.config.enabled) {
this.sendDebugData();
}
}
/**
* 处理来自调试面板的消息
*/
private handleMessage(message: any): void {
try {
switch (message.type) {
case 'capture_memory_snapshot':
this.handleMemorySnapshotRequest();
break;
case 'config_update':
if (message.config) {
this.updateConfig({ ...this.config, ...message.config });
}
break;
case 'ping':
this.webSocketManager.send({
type: 'pong',
timestamp: Date.now()
});
break;
default:
// 未知消息类型,忽略
break;
}
} catch (error) {
// console.error('[ECS Debug] 处理消息失败:', error);
}
}
/**
* 处理内存快照请求
*/
private handleMemorySnapshotRequest(): void {
try {
const memorySnapshot = this.captureMemorySnapshot();
this.webSocketManager.send({
type: 'memory_snapshot_response',
data: memorySnapshot
});
} catch (error) {
this.webSocketManager.send({
type: 'memory_snapshot_error',
error: error instanceof Error ? error.message : '内存快照捕获失败'
});
}
}
/**
* 捕获内存快照
*/
private captureMemorySnapshot(): any {
const scene = Core.scene;
if (!scene) {
throw new Error('没有活跃的场景');
}
const entityList = (scene as any).entities;
if (!entityList?.buffer) {
throw new Error('无法访问实体列表');
}
const timestamp = Date.now();
// 1. 收集基础内存信息
const baseMemoryInfo = this.collectBaseMemoryInfo();
// 2. 收集实体内存统计
const entityMemoryStats = this.collectEntityMemoryStats(entityList);
// 3. 收集组件内存统计
const componentMemoryStats = this.collectComponentMemoryStats(entityList);
// 4. 收集系统内存统计
const systemMemoryStats = this.collectSystemMemoryStats();
// 5. 收集对象池内存统计
const poolMemoryStats = this.collectPoolMemoryStats();
// 6. 收集性能监控器统计
const performanceStats = this.collectPerformanceStats();
return {
timestamp,
version: '2.0',
summary: {
totalEntities: entityList.buffer.length,
totalMemoryUsage: baseMemoryInfo.usedMemory,
totalMemoryLimit: baseMemoryInfo.totalMemory,
memoryUtilization: (baseMemoryInfo.usedMemory / baseMemoryInfo.totalMemory * 100),
gcCollections: baseMemoryInfo.gcCollections,
entityMemory: entityMemoryStats.totalMemory,
componentMemory: componentMemoryStats.totalMemory,
systemMemory: systemMemoryStats.totalMemory,
poolMemory: poolMemoryStats.totalMemory
},
baseMemory: baseMemoryInfo,
entities: entityMemoryStats,
components: componentMemoryStats,
systems: systemMemoryStats,
pools: poolMemoryStats,
performance: performanceStats,
// 保持向后兼容
totalEntities: entityList.buffer.length,
totalMemory: entityMemoryStats.totalMemory,
detailedArchetypes: entityMemoryStats.archetypes
};
}
/**
* 收集基础内存信息
*/
private collectBaseMemoryInfo(): any {
const memoryInfo: any = {
totalMemory: 0,
usedMemory: 0,
freeMemory: 0,
gcCollections: 0,
heapInfo: null
};
try {
if ((performance as any).memory) {
const perfMemory = (performance as any).memory;
memoryInfo.totalMemory = perfMemory.jsHeapSizeLimit || 512 * 1024 * 1024;
memoryInfo.usedMemory = perfMemory.usedJSHeapSize || 0;
memoryInfo.freeMemory = memoryInfo.totalMemory - memoryInfo.usedMemory;
memoryInfo.heapInfo = {
totalJSHeapSize: perfMemory.totalJSHeapSize || 0,
usedJSHeapSize: perfMemory.usedJSHeapSize || 0,
jsHeapSizeLimit: perfMemory.jsHeapSizeLimit || 0
};
} else {
memoryInfo.totalMemory = 512 * 1024 * 1024;
memoryInfo.freeMemory = 512 * 1024 * 1024;
}
// 尝试获取GC信息
if ((performance as any).measureUserAgentSpecificMemory) {
// 这是一个实验性API可能不可用
(performance as any).measureUserAgentSpecificMemory().then((result: any) => {
memoryInfo.detailedMemory = result;
}).catch(() => {
// 忽略错误
});
}
} catch (error) {
// 使用默认值
}
return memoryInfo;
}
/**
* 收集实体内存统计
*/
private collectEntityMemoryStats(entityList: any): any {
const archetypeStats = new Map<string, { count: number; memory: number; entities: any[] }>();
const entitySizeDistribution = new Map<string, number>(); // 按大小范围分布
let totalMemory = 0;
let maxEntityMemory = 0;
let minEntityMemory = Number.MAX_VALUE;
const largestEntities: any[] = [];
for (const entity of entityList.buffer) {
if (!entity || entity.destroyed) continue;
// 生成组件签名
const componentTypes = entity.components ?
entity.components.map((c: any) => c.constructor.name).sort() : [];
const signature = componentTypes.length > 0 ? componentTypes.join(',') : 'Empty';
// 计算实体内存使用
const entityMemory = this.entityCollector.estimateEntityMemoryUsage(entity);
totalMemory += entityMemory;
maxEntityMemory = Math.max(maxEntityMemory, entityMemory);
minEntityMemory = Math.min(minEntityMemory, entityMemory);
// 收集大实体信息
largestEntities.push({
id: entity.id,
name: entity.name || `Entity_${entity.id}`,
memory: entityMemory,
componentCount: componentTypes.length,
componentTypes: componentTypes
});
// 内存大小分布统计
const sizeCategory = this.getMemorySizeCategory(entityMemory);
entitySizeDistribution.set(sizeCategory, (entitySizeDistribution.get(sizeCategory) || 0) + 1);
// 更新原型统计
if (!archetypeStats.has(signature)) {
archetypeStats.set(signature, { count: 0, memory: 0, entities: [] });
}
const stats = archetypeStats.get(signature)!;
stats.count++;
stats.memory += entityMemory;
stats.entities.push({
id: entity.id,
name: entity.name || `Entity_${entity.id}`,
memory: entityMemory,
componentCount: componentTypes.length
});
}
// 排序并限制返回的实体数量
largestEntities.sort((a, b) => b.memory - a.memory);
// 转换原型统计
const archetypes = Array.from(archetypeStats.entries()).map(([signature, stats]) => ({
signature,
count: stats.count,
memory: stats.memory,
averageMemory: stats.memory / stats.count,
percentage: totalMemory > 0 ? (stats.memory / totalMemory * 100) : 0,
entities: stats.entities.sort((a, b) => b.memory - a.memory).slice(0, 5) // 只返回前5个最大的
})).sort((a, b) => b.memory - a.memory);
return {
totalMemory,
entityCount: entityList.buffer.length,
averageEntityMemory: totalMemory / entityList.buffer.length,
maxEntityMemory,
minEntityMemory: minEntityMemory === Number.MAX_VALUE ? 0 : minEntityMemory,
archetypes,
largestEntities: largestEntities.slice(0, 10),
sizeDistribution: Array.from(entitySizeDistribution.entries()).map(([category, count]) => ({
category,
count,
percentage: (count / entityList.buffer.length * 100)
}))
};
}
/**
* 收集组件内存统计
*/
private collectComponentMemoryStats(entityList: any): any {
const componentStats = new Map<string, { count: number; totalMemory: number; instances: any[] }>();
let totalComponentMemory = 0;
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
});
}
}
const componentBreakdown = Array.from(componentStats.entries()).map(([typeName, stats]) => ({
typeName,
instanceCount: stats.count,
totalMemory: stats.totalMemory,
averageMemory: stats.totalMemory / stats.count,
percentage: totalComponentMemory > 0 ? (stats.totalMemory / totalComponentMemory * 100) : 0,
largestInstances: stats.instances.sort((a, b) => b.memory - a.memory).slice(0, 3)
})).sort((a, b) => b.totalMemory - a.totalMemory);
return {
totalMemory: totalComponentMemory,
componentTypes: componentStats.size,
totalInstances: Array.from(componentStats.values()).reduce((sum, stats) => sum + stats.count, 0),
breakdown: componentBreakdown
};
}
/**
* 收集系统内存统计
*/
private collectSystemMemoryStats(): any {
const scene = Core.scene;
let totalSystemMemory = 0;
const systemBreakdown: any[] = [];
try {
const entityProcessors = (scene as any).entityProcessors;
if (entityProcessors && entityProcessors.processors) {
for (const system of entityProcessors.processors) {
const systemMemory = this.entityCollector.calculateObjectSize(system);
totalSystemMemory += systemMemory;
systemBreakdown.push({
name: system.constructor.name,
memory: systemMemory,
enabled: system.enabled !== false,
updateOrder: system.updateOrder || 0
});
}
}
} catch (error) {
// 忽略错误
}
return {
totalMemory: totalSystemMemory,
systemCount: systemBreakdown.length,
breakdown: systemBreakdown.sort((a, b) => b.memory - a.memory)
};
}
/**
* 收集对象池内存统计
*/
private collectPoolMemoryStats(): any {
let totalPoolMemory = 0;
const poolBreakdown: any[] = [];
try {
// 尝试获取组件池统计
const { ComponentPoolManager } = require('../../ECS/Core/ComponentPool');
const poolManager = ComponentPoolManager.getInstance();
const poolStats = poolManager.getPoolStats();
for (const [typeName, stats] of poolStats.entries()) {
const poolData = stats as any; // 类型断言
const poolMemory = poolData.maxSize * 32; // 估算每个对象32字节
totalPoolMemory += poolMemory;
poolBreakdown.push({
typeName,
maxSize: poolData.maxSize,
currentSize: poolData.currentSize || 0,
estimatedMemory: poolMemory,
utilization: poolData.currentSize ? (poolData.currentSize / poolData.maxSize * 100) : 0
});
}
} catch (error) {
// 如果无法获取池信息,使用默认值
}
try {
// 尝试获取通用对象池统计
const { Pool } = require('../../Utils/Pool');
const poolStats = Pool.getStats();
for (const [typeName, stats] of Object.entries(poolStats)) {
const poolData = stats as any; // 类型断言
totalPoolMemory += poolData.estimatedMemoryUsage;
poolBreakdown.push({
typeName: `Pool_${typeName}`,
maxSize: poolData.maxSize,
currentSize: poolData.size,
estimatedMemory: poolData.estimatedMemoryUsage,
utilization: poolData.size / poolData.maxSize * 100,
hitRate: poolData.hitRate * 100
});
}
} catch (error) {
// 忽略错误
}
return {
totalMemory: totalPoolMemory,
poolCount: poolBreakdown.length,
breakdown: poolBreakdown.sort((a, b) => b.estimatedMemory - a.estimatedMemory)
};
}
/**
* 收集性能统计信息
*/
private collectPerformanceStats(): any {
try {
const performanceMonitor = (Core.Instance as any)._performanceMonitor;
if (!performanceMonitor) {
return { enabled: false };
}
const stats = performanceMonitor.getAllSystemStats();
const warnings = performanceMonitor.getPerformanceWarnings();
return {
enabled: performanceMonitor.enabled,
systemCount: stats.size,
warnings: warnings.slice(0, 10), // 最多10个警告
topSystems: Array.from(stats.entries() as any).map((entry: any) => {
const [name, stat] = entry;
return {
name,
averageTime: stat.averageTime,
maxTime: stat.maxTime,
samples: stat.executionCount
};
}).sort((a: any, b: any) => b.averageTime - a.averageTime).slice(0, 5)
};
} catch (error: any) {
return { enabled: false, error: error.message };
}
}
/**
* 获取内存大小分类
*/
private getMemorySizeCategory(memoryBytes: number): string {
if (memoryBytes < 1024) return '< 1KB';
if (memoryBytes < 10 * 1024) return '1-10KB';
if (memoryBytes < 100 * 1024) return '10-100KB';
if (memoryBytes < 1024 * 1024) return '100KB-1MB';
return '> 1MB';
}
/**
* 获取调试数据
*/
public getDebugData(): IECSDebugData {
const currentTime = Date.now();
const scene = Core.scene;
const debugData: IECSDebugData = {
timestamp: currentTime,
frameworkVersion: '1.0.0', // 可以从package.json读取
isRunning: this.isRunning,
frameworkLoaded: true,
currentScene: scene?.name || 'Unknown'
};
// 根据配置收集各种数据
if (this.config.channels.entities) {
debugData.entities = this.entityCollector.collectEntityData();
}
if (this.config.channels.systems) {
const performanceMonitor = (Core.Instance as any)._performanceMonitor;
debugData.systems = this.systemCollector.collectSystemData(performanceMonitor);
}
if (this.config.channels.performance) {
const performanceMonitor = (Core.Instance as any)._performanceMonitor;
debugData.performance = this.performanceCollector.collectPerformanceData(performanceMonitor);
}
if (this.config.channels.components) {
debugData.components = this.componentCollector.collectComponentData();
}
if (this.config.channels.scenes) {
debugData.scenes = this.sceneCollector.collectSceneData();
}
return debugData;
}
/**
* 连接WebSocket
*/
private async connectWebSocket(): Promise<void> {
try {
await this.webSocketManager.connect();
// console.log('[ECS Debug] 调试管理器已连接到调试服务器');
} catch (error) {
// console.warn('[ECS Debug] 无法连接到调试服务器:', error);
}
}
/**
* 发送调试数据
*/
private sendDebugData(): void {
if (!this.webSocketManager.getConnectionStatus()) {
return;
}
try {
const debugData = this.getDebugData();
// 包装成调试面板期望的消息格式
const message = {
type: 'debug_data',
data: debugData
};
this.webSocketManager.send(message);
} catch (error) {
// console.error('[ECS Debug] 发送调试数据失败:', error);
}
}
}

View 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();
}
}
}

View File

@@ -0,0 +1,234 @@
import { IPerformanceDebugData } from '../../types';
import { Time } from '../Time';
/**
* 性能数据收集器
*/
export class PerformanceDataCollector {
private frameTimeHistory: number[] = [];
private maxHistoryLength: number = 60;
private lastGCCount: number = 0;
private gcCollections: number = 0;
private lastMemoryCheck: number = 0;
/**
* 收集性能数据
*/
public collectPerformanceData(performanceMonitor: any): IPerformanceDebugData {
const frameTimeSeconds = Time.deltaTime;
const engineFrameTimeMs = frameTimeSeconds * 1000;
const currentFps = frameTimeSeconds > 0 ? Math.round(1 / frameTimeSeconds) : 0;
const ecsPerformanceData = this.getECSPerformanceData(performanceMonitor);
const ecsExecutionTimeMs = ecsPerformanceData.totalExecutionTime;
const ecsPercentage = engineFrameTimeMs > 0 ? (ecsExecutionTimeMs / engineFrameTimeMs * 100) : 0;
let memoryUsage = 0;
if ((performance as any).memory) {
memoryUsage = (performance as any).memory.usedJSHeapSize / 1024 / 1024;
}
// 更新ECS执行时间历史记录
this.frameTimeHistory.push(ecsExecutionTimeMs);
if (this.frameTimeHistory.length > this.maxHistoryLength) {
this.frameTimeHistory.shift();
}
// 计算ECS执行时间统计
const history = this.frameTimeHistory.filter(t => t >= 0);
const averageECSTime = history.length > 0 ? history.reduce((a, b) => a + b, 0) / history.length : ecsExecutionTimeMs;
const minECSTime = history.length > 0 ? Math.min(...history) : ecsExecutionTimeMs;
const maxECSTime = history.length > 0 ? Math.max(...history) : ecsExecutionTimeMs;
return {
frameTime: ecsExecutionTimeMs,
engineFrameTime: engineFrameTimeMs,
ecsPercentage: ecsPercentage,
memoryUsage: memoryUsage,
fps: currentFps,
averageFrameTime: averageECSTime,
minFrameTime: minECSTime,
maxFrameTime: maxECSTime,
frameTimeHistory: [...this.frameTimeHistory],
systemPerformance: this.getSystemPerformance(performanceMonitor),
systemBreakdown: ecsPerformanceData.systemBreakdown,
memoryDetails: this.getMemoryDetails()
};
}
/**
* 获取ECS框架整体性能数据
*/
private getECSPerformanceData(performanceMonitor: any): { totalExecutionTime: number; systemBreakdown: Array<any> } {
// 检查性能监视器是否存在
if (!performanceMonitor) {
// 尝试从Core实例获取性能监视器
try {
const { Core } = require('../../Core');
const coreInstance = Core.Instance;
if (coreInstance && (coreInstance as any)._performanceMonitor) {
performanceMonitor = (coreInstance as any)._performanceMonitor;
} else {
return { totalExecutionTime: 0, systemBreakdown: [] };
}
} catch (error) {
return { totalExecutionTime: 0, systemBreakdown: [] };
}
}
if (!performanceMonitor.enabled) {
// 尝试启用性能监视器
try {
performanceMonitor.enabled = true;
} catch (error) {
// 如果无法启用,返回默认值
}
return { totalExecutionTime: 0, systemBreakdown: [] };
}
try {
let totalTime = 0;
const systemBreakdown = [];
const stats = performanceMonitor.getAllSystemStats();
if (stats.size === 0) {
return { totalExecutionTime: 0, systemBreakdown: [] };
}
// 计算各系统的执行时间
for (const [systemName, stat] of stats.entries()) {
// 使用最近的执行时间而不是平均时间,这样更能反映当前状态
const systemTime = stat.recentTimes && stat.recentTimes.length > 0 ?
stat.recentTimes[stat.recentTimes.length - 1] :
(stat.averageTime || 0);
totalTime += systemTime;
systemBreakdown.push({
systemName: systemName,
executionTime: systemTime,
percentage: 0 // 后面计算
});
}
// 计算各系统占ECS总时间的百分比
systemBreakdown.forEach(system => {
system.percentage = totalTime > 0 ? (system.executionTime / totalTime * 100) : 0;
});
// 按执行时间排序
systemBreakdown.sort((a, b) => b.executionTime - a.executionTime);
return {
totalExecutionTime: totalTime,
systemBreakdown: systemBreakdown
};
} catch (error) {
return { totalExecutionTime: 0, systemBreakdown: [] };
}
}
/**
* 获取系统性能数据
*/
private getSystemPerformance(performanceMonitor: any): Array<any> {
if (!performanceMonitor) {
return [];
}
try {
const stats = performanceMonitor.getAllSystemStats();
const systemData = performanceMonitor.getAllSystemData();
return Array.from(stats.entries() as Iterable<[string, any]>).map(([systemName, stat]) => {
const data = systemData.get(systemName);
return {
systemName: systemName,
averageTime: stat.averageTime || 0,
maxTime: stat.maxTime || 0,
minTime: stat.minTime === Number.MAX_VALUE ? 0 : (stat.minTime || 0),
samples: stat.executionCount || 0,
percentage: 0,
entityCount: data?.entityCount || 0,
lastExecutionTime: data?.executionTime || 0
};
});
} catch (error) {
return [];
}
}
/**
* 获取内存详情
*/
private getMemoryDetails(): any {
const memoryInfo: any = {
entities: 0,
components: 0,
systems: 0,
pooled: 0,
totalMemory: 0,
usedMemory: 0,
freeMemory: 0,
gcCollections: this.updateGCCount()
};
try {
if ((performance as any).memory) {
const perfMemory = (performance as any).memory;
memoryInfo.totalMemory = perfMemory.jsHeapSizeLimit || 512 * 1024 * 1024;
memoryInfo.usedMemory = perfMemory.usedJSHeapSize || 0;
memoryInfo.freeMemory = memoryInfo.totalMemory - memoryInfo.usedMemory;
// 检测GC如果使用的内存突然大幅减少可能发生了GC
if (this.lastMemoryCheck > 0) {
const memoryDrop = this.lastMemoryCheck - memoryInfo.usedMemory;
if (memoryDrop > 1024 * 1024) { // 内存减少超过1MB
this.gcCollections++;
}
}
this.lastMemoryCheck = memoryInfo.usedMemory;
} else {
memoryInfo.totalMemory = 512 * 1024 * 1024;
memoryInfo.freeMemory = 512 * 1024 * 1024;
}
} catch (error) {
return {
totalMemory: 0,
usedMemory: 0,
freeMemory: 0,
entityMemory: 0,
componentMemory: 0,
systemMemory: 0,
pooledMemory: 0,
gcCollections: this.gcCollections
};
}
return memoryInfo;
}
/**
* 更新GC计数
*/
private updateGCCount(): number {
try {
// 尝试使用PerformanceObserver来检测GC
if (typeof PerformanceObserver !== 'undefined') {
// 这是一个简化的GC检测方法
// 实际的GC检测需要更复杂的逻辑
return this.gcCollections;
}
// 如果有其他GC检测API可以在这里添加
if ((performance as any).measureUserAgentSpecificMemory) {
// 实验性API可能不可用
return this.gcCollections;
}
return this.gcCollections;
} catch (error) {
return this.gcCollections;
}
}
}

View File

@@ -0,0 +1,50 @@
import { ISceneDebugData } from '../../types';
import { Core } from '../../Core';
/**
* 场景数据收集器
*/
export class SceneDataCollector {
private sceneStartTime: number = Date.now();
/**
* 收集场景数据
*/
public collectSceneData(): ISceneDebugData {
const scene = Core.scene;
if (!scene) {
return {
currentSceneName: 'No Scene',
isInitialized: false,
sceneRunTime: 0,
sceneEntityCount: 0,
sceneSystemCount: 0,
sceneMemory: 0,
sceneUptime: 0
};
}
const currentTime = Date.now();
const runTime = (currentTime - this.sceneStartTime) / 1000;
const entityList = (scene as any).entities;
const entityProcessors = (scene as any).entityProcessors;
return {
currentSceneName: (scene as any).name || 'Unnamed Scene',
isInitialized: (scene as any)._didSceneBegin || false,
sceneRunTime: runTime,
sceneEntityCount: entityList?.buffer?.length || 0,
sceneSystemCount: entityProcessors?.processors?.length || 0,
sceneMemory: 0, // TODO: 计算实际场景内存
sceneUptime: runTime
};
}
/**
* 设置场景开始时间
*/
public setSceneStartTime(time: number): void {
this.sceneStartTime = time;
}
}

View File

@@ -0,0 +1,65 @@
import { ISystemDebugData } from '../../types';
import { Core } from '../../Core';
/**
* 系统数据收集器
*/
export class SystemDataCollector {
/**
* 收集系统数据
*/
public collectSystemData(performanceMonitor: any): ISystemDebugData {
const scene = Core.scene;
if (!scene) {
return {
totalSystems: 0,
systemsInfo: []
};
}
const entityProcessors = (scene as any).entityProcessors;
if (!entityProcessors) {
return {
totalSystems: 0,
systemsInfo: []
};
}
const systems = entityProcessors.processors || [];
// 获取性能监控数据
let systemStats: Map<string, any> = new Map();
let systemData: Map<string, any> = new Map();
if (performanceMonitor) {
try {
systemStats = performanceMonitor.getAllSystemStats();
systemData = performanceMonitor.getAllSystemData();
} catch (error) {
// 忽略错误使用空的Map
}
}
return {
totalSystems: systems.length,
systemsInfo: systems.map((system: any) => {
const systemName = system.systemName || system.constructor.name;
const stats = systemStats.get(systemName);
const data = systemData.get(systemName);
return {
name: systemName,
type: system.constructor.name,
entityCount: system.entities?.length || 0,
executionTime: stats?.averageTime || data?.executionTime || 0,
minExecutionTime: stats?.minTime === Number.MAX_VALUE ? 0 : (stats?.minTime || 0),
maxExecutionTime: stats?.maxTime || 0,
executionTimeHistory: stats?.recentTimes || [],
updateOrder: system.updateOrder || 0,
enabled: system.enabled !== false,
lastUpdateTime: data?.lastUpdateTime || 0
};
})
};
}
}

View File

@@ -0,0 +1,177 @@
/**
* WebSocket连接管理器
*/
export class WebSocketManager {
private ws?: WebSocket;
private isConnected: boolean = false;
private reconnectAttempts: number = 0;
private maxReconnectAttempts: number = 5;
private reconnectInterval: number = 2000;
private url: string;
private autoReconnect: boolean;
private reconnectTimer?: NodeJS.Timeout;
private onOpen?: (event: Event) => void;
private onClose?: (event: CloseEvent) => void;
private onError?: (error: Event | any) => void;
private messageHandler?: (message: any) => void;
constructor(url: string, autoReconnect: boolean = true) {
this.url = url;
this.autoReconnect = autoReconnect;
}
/**
* 设置消息处理回调
*/
public setMessageHandler(handler: (message: any) => void): void {
this.messageHandler = handler;
}
/**
* 连接WebSocket
*/
public connect(): Promise<void> {
return new Promise((resolve, reject) => {
try {
this.ws = new WebSocket(this.url);
this.ws.onopen = (event) => {
this.handleOpen(event);
resolve();
};
this.ws.onclose = (event) => {
this.handleClose(event);
};
this.ws.onerror = (error) => {
this.handleError(error);
reject(error);
};
this.ws.onmessage = (event) => {
this.handleMessage(event);
};
} catch (error) {
this.handleConnectionFailure(error);
reject(error);
}
});
}
/**
* 断开连接
*/
public disconnect(): void {
if (this.ws) {
this.autoReconnect = false; // 主动断开时不自动重连
this.ws.close();
this.ws = undefined;
}
this.isConnected = false;
}
/**
* 发送数据
*/
public send(data: any): void {
if (!this.isConnected || !this.ws) {
return;
}
try {
const message = typeof data === 'string' ? data : JSON.stringify(data);
this.ws.send(message);
} catch (error) {
}
}
/**
* 获取连接状态
*/
public getConnectionStatus(): boolean {
return this.isConnected;
}
/**
* 设置最大重连次数
*/
public setMaxReconnectAttempts(attempts: number): void {
this.maxReconnectAttempts = attempts;
}
/**
* 设置重连间隔
*/
public setReconnectInterval(interval: number): void {
this.reconnectInterval = interval;
}
/**
* 计划重连
*/
private scheduleReconnect(): void {
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
}
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
this.reconnectAttempts++;
this.reconnectTimer = setTimeout(() => {
this.connect().catch(error => {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.scheduleReconnect();
}
});
}, delay);
}
/**
* 处理接收到的消息
*/
private handleMessage(event: MessageEvent): void {
try {
const message = JSON.parse(event.data);
// 调用消息处理回调
if (this.messageHandler) {
this.messageHandler(message);
}
} catch (error) {
}
}
private handleOpen(event: Event): void {
this.isConnected = true;
this.reconnectAttempts = 0;
if (this.onOpen) {
this.onOpen(event);
}
}
private handleClose(event: CloseEvent): void {
this.isConnected = false;
if (this.onClose) {
this.onClose(event);
}
if (this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
this.scheduleReconnect();
}
}
private handleError(error: Event): void {
if (this.onError) {
this.onError(error);
}
}
private handleConnectionFailure(error: any): void {
if (this.onError) {
this.onError(error);
}
}
}

7
src/Utils/Debug/index.ts Normal file
View File

@@ -0,0 +1,7 @@
export { EntityDataCollector } from './EntityDataCollector';
export { SystemDataCollector } from './SystemDataCollector';
export { PerformanceDataCollector } from './PerformanceDataCollector';
export { ComponentDataCollector } from './ComponentDataCollector';
export { SceneDataCollector } from './SceneDataCollector';
export { WebSocketManager } from './WebSocketManager';
export { DebugManager } from './DebugManager';

View File

@@ -1,971 +0,0 @@
import {
IECSDebugConfig,
IECSDebugData,
IEntityDebugData,
ISystemDebugData,
IPerformanceDebugData,
IComponentDebugData,
ISceneDebugData
} from '../types';
import { Core } from '../Core';
import { Time } from './Time';
/**
* ECS调试报告器 - WebSocket模式
*
* 负责收集ECS框架的运行时调试数据并通过WebSocket发送到调试服务器
*/
export class DebugReporter {
private config: IECSDebugConfig;
private core: Core;
private timer?: any;
private ws?: WebSocket;
private lastFrameTime: number = 0;
private frameCount: number = 0;
private lastFpsTime: number = 0;
private fps: number = 0;
private sceneStartTime: number = 0;
private isConnected: boolean = false;
private reconnectAttempts: number = 0;
private maxReconnectAttempts: number = 5;
private frameTimeHistory: number[] = [];
private maxHistoryLength: number = 60; // 保存60帧的历史数据
/**
* 构造函数
* @param core Core实例
* @param config 调试配置
*/
constructor(core: Core, config: IECSDebugConfig) {
this.core = core;
this.config = config;
this.sceneStartTime = Date.now();
// 确保性能监控器在调试模式下被启用
if (this.config.enabled && this.config.channels.performance) {
if (!this.core._performanceMonitor.isEnabled) {
this.core._performanceMonitor.enable();
console.log('[ECS Debug] Performance monitor enabled for debugging');
}
}
if (this.config.enabled) {
this.start();
}
}
/**
* 启动调试报告器
*/
private start(): void {
this.connectWebSocket();
}
/**
* 停止调试报告器
*/
public stop(): void {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
if (this.ws) {
this.ws.close();
this.ws = undefined;
}
this.isConnected = false;
}
/**
* 更新配置
* @param newConfig 新配置
*/
public updateConfig(newConfig: IECSDebugConfig): void {
const wasEnabled = this.config.enabled;
const urlChanged = this.config.websocketUrl !== newConfig.websocketUrl;
this.config = newConfig;
// 根据配置启用或禁用性能监控器
if (newConfig.enabled && newConfig.channels.performance) {
if (!this.core._performanceMonitor.isEnabled) {
this.core._performanceMonitor.enable();
console.log('[ECS Debug] Performance monitor enabled for debugging');
}
}
if (!newConfig.enabled && wasEnabled) {
this.stop();
} else if (newConfig.enabled && (!wasEnabled || urlChanged)) {
this.stop();
this.start();
}
}
/**
* 连接WebSocket
*/
private connectWebSocket(): void {
if (!this.config.websocketUrl) {
console.error('[ECS Debug] WebSocket URL not provided');
return;
}
try {
this.ws = new WebSocket(this.config.websocketUrl);
this.ws.onopen = () => {
this.isConnected = true;
this.reconnectAttempts = 0;
this.startDataStream();
// 发送初始化消息
this.send({
type: 'init',
data: {
frameworkVersion: this.getFrameworkVersion(),
timestamp: Date.now()
}
});
};
this.ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
this.handleMessage(message);
} catch (error) {
console.error('[ECS Debug] Failed to parse message:', error);
}
};
this.ws.onclose = (event) => {
this.isConnected = false;
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
// 自动重连
if (this.config.autoReconnect !== false && this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts - 1), 10000);
setTimeout(() => this.connectWebSocket(), delay);
}
};
this.ws.onerror = (error) => {
console.error('[ECS Debug] WebSocket error:', error);
};
} catch (error) {
console.error('[ECS Debug] Failed to create WebSocket:', error);
}
}
/**
* 启动数据流
*/
private startDataStream(): void {
const interval = this.config.updateInterval || 1000;
this.timer = setInterval(() => {
if (this.isConnected) {
try {
const data = this.collectDebugData();
this.send({ type: 'debug_data', data });
} catch (error) {
console.error('[ECS Debug] Failed to send debug data:', error);
}
}
}, interval);
}
/**
* 发送消息
*/
private send(message: any): void {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(message));
}
}
/**
* 处理接收到的消息
* @param message 消息内容
*/
private handleMessage(message: any): void {
switch (message.type) {
case 'get_debug_data':
const data = this.collectDebugData();
this.send({ type: 'debug_data_response', data, requestId: message.requestId });
break;
case 'update_config':
if (message.config) {
this.updateConfig(message.config);
this.send({ type: 'config_updated', success: true });
}
break;
case 'ping':
this.send({ type: 'pong', timestamp: Date.now() });
break;
default:
console.warn('[ECS Debug] Unknown message type:', message.type);
}
}
/**
* 收集调试数据
* @returns 调试数据对象
*/
private collectDebugData(): IECSDebugData {
const currentTime = Date.now();
// 更新FPS计算
this.updateFPS(currentTime);
const data: IECSDebugData = {
timestamp: currentTime,
frameworkVersion: this.getFrameworkVersion(),
isRunning: true,
frameworkLoaded: true,
currentScene: this.getCurrentSceneName()
};
// 根据配置收集不同类型的数据
if (this.config.channels.entities) {
data.entities = this.collectEntityData();
}
if (this.config.channels.systems) {
data.systems = this.collectSystemData();
}
if (this.config.channels.performance) {
data.performance = this.collectPerformanceData();
}
if (this.config.channels.components) {
data.components = this.collectComponentData();
}
if (this.config.channels.scenes) {
data.scenes = this.collectSceneData();
}
return data;
}
/**
* 更新FPS计算
*/
private updateFPS(currentTime: number): void {
this.frameCount++;
if (currentTime - this.lastFpsTime >= 1000) {
this.fps = this.frameCount;
this.frameCount = 0;
this.lastFpsTime = currentTime;
}
}
/**
* 获取框架版本
*/
private getFrameworkVersion(): string {
return '1.0.0';
}
/**
* 获取当前场景名称
*/
private getCurrentSceneName(): string {
const scene = Core.scene;
return scene ? (scene as any).name || 'Unnamed Scene' : 'No Scene';
}
/**
* 收集实体数据
*/
private collectEntityData(): IEntityDebugData {
const scene = Core.scene;
if (!scene) {
return {
totalEntities: 0,
activeEntities: 0,
pendingAdd: 0,
pendingRemove: 0,
entitiesPerArchetype: [],
topEntitiesByComponents: []
};
}
const entityList = (scene as any).entities;
if (!entityList) {
return {
totalEntities: 0,
activeEntities: 0,
pendingAdd: 0,
pendingRemove: 0,
entitiesPerArchetype: [],
topEntitiesByComponents: []
};
}
const allEntities = entityList.buffer || [];
const activeEntities = allEntities.filter((e: any) => e.enabled && !e._isDestroyed);
return {
totalEntities: allEntities.length,
activeEntities: activeEntities.length,
pendingAdd: entityList.toAdd?.length || 0,
pendingRemove: entityList.toRemove?.length || 0,
entitiesPerArchetype: this.getArchetypeDistribution({ entities: allEntities }),
topEntitiesByComponents: this.getTopEntitiesByComponents({ entities: allEntities }),
entityDetails: this.getEntityDetails({ entities: allEntities })
};
}
/**
* 获取实体详情
*/
private getEntityDetails(entityContainer: any): Array<any> {
if (!entityContainer.entities) return [];
return entityContainer.entities.slice(0, 100).map((entity: any) => ({
id: entity.id,
name: entity.name || `Entity_${entity.id}`,
tag: entity.tag,
enabled: entity.enabled,
componentCount: entity.components?.length || 0,
components: entity.components?.map((c: any) => c.constructor.name) || []
}));
}
/**
* 收集系统数据
*/
private collectSystemData(): ISystemDebugData {
const scene = Core.scene;
if (!scene) {
return {
totalSystems: 0,
systemsInfo: []
};
}
const entityProcessors = (scene as any).entityProcessors;
if (!entityProcessors) {
return {
totalSystems: 0,
systemsInfo: []
};
}
const systems = entityProcessors.processors || [];
// 获取性能监控数据
const monitor = this.core._performanceMonitor;
let systemStats: Map<string, any> = new Map();
let systemData: Map<string, any> = new Map();
if (monitor) {
try {
systemStats = monitor.getAllSystemStats();
systemData = monitor.getAllSystemData();
} catch (error) {
// 忽略错误使用空的Map
}
}
return {
totalSystems: systems.length,
systemsInfo: systems.map((system: any) => {
const systemName = system.systemName || system.constructor.name;
const stats = systemStats.get(systemName);
const data = systemData.get(systemName);
return {
name: systemName,
type: system.constructor.name,
entityCount: system.entities?.length || 0,
executionTime: stats?.averageTime || data?.executionTime || 0,
minExecutionTime: stats?.minTime === Number.MAX_VALUE ? 0 : (stats?.minTime || 0),
maxExecutionTime: stats?.maxTime || 0,
executionTimeHistory: stats?.recentTimes || [],
updateOrder: system.updateOrder || 0,
enabled: system.enabled !== false,
lastUpdateTime: data?.lastUpdateTime || 0
};
})
};
}
/**
* 收集性能数据
*/
private collectPerformanceData(): IPerformanceDebugData {
const frameTimeSeconds = Time.deltaTime;
const engineFrameTimeMs = frameTimeSeconds * 1000;
const currentFps = frameTimeSeconds > 0 ? Math.round(1 / frameTimeSeconds) : 0;
const ecsPerformanceData = this.getECSPerformanceData();
const ecsExecutionTimeMs = ecsPerformanceData.totalExecutionTime;
const ecsPercentage = engineFrameTimeMs > 0 ? (ecsExecutionTimeMs / engineFrameTimeMs * 100) : 0;
let memoryUsage = 0;
if ((performance as any).memory) {
memoryUsage = (performance as any).memory.usedJSHeapSize / 1024 / 1024;
}
// 更新ECS执行时间历史记录
this.frameTimeHistory.push(ecsExecutionTimeMs);
if (this.frameTimeHistory.length > this.maxHistoryLength) {
this.frameTimeHistory.shift();
}
// 计算ECS执行时间统计
const history = this.frameTimeHistory.filter(t => t >= 0);
const averageECSTime = history.length > 0 ? history.reduce((a, b) => a + b, 0) / history.length : ecsExecutionTimeMs;
const minECSTime = history.length > 0 ? Math.min(...history) : ecsExecutionTimeMs;
const maxECSTime = history.length > 0 ? Math.max(...history) : ecsExecutionTimeMs;
return {
frameTime: ecsExecutionTimeMs,
engineFrameTime: engineFrameTimeMs,
ecsPercentage: ecsPercentage,
memoryUsage: memoryUsage,
fps: currentFps,
averageFrameTime: averageECSTime, // ECS平均执行时间
minFrameTime: minECSTime, // ECS最短执行时间
maxFrameTime: maxECSTime, // ECS最长执行时间
frameTimeHistory: [...this.frameTimeHistory],
systemPerformance: this.getSystemPerformance(),
systemBreakdown: ecsPerformanceData.systemBreakdown,
memoryDetails: this.getMemoryDetails()
};
}
/**
* 获取ECS框架整体性能数据
*/
private getECSPerformanceData(): { totalExecutionTime: number; systemBreakdown: Array<any> } {
const monitor = this.core._performanceMonitor;
if (!monitor) {
console.warn('[ECS Debug] Performance monitor not found');
return { totalExecutionTime: 0, systemBreakdown: [] };
}
if (!monitor.isEnabled) {
console.warn('[ECS Debug] Performance monitor is disabled. Enable it to see ECS performance data.');
// 尝试启用性能监控器
monitor.enable();
return { totalExecutionTime: 0, systemBreakdown: [] };
}
try {
let totalTime = 0;
const systemBreakdown = [];
const stats = monitor.getAllSystemStats();
if (stats.size === 0) {
console.log('[ECS Debug] No system performance data available yet. This is normal on first frames.');
return { totalExecutionTime: 0, systemBreakdown: [] };
}
// 计算各系统的执行时间
for (const [systemName, stat] of stats.entries()) {
const systemTime = stat.averageTime || 0;
totalTime += systemTime;
systemBreakdown.push({
systemName: systemName,
executionTime: systemTime,
percentage: 0 // 后面计算
});
}
// 计算各系统占ECS总时间的百分比
systemBreakdown.forEach(system => {
system.percentage = totalTime > 0 ? (system.executionTime / totalTime * 100) : 0;
});
// 按执行时间排序
systemBreakdown.sort((a, b) => b.executionTime - a.executionTime);
console.log(`[ECS Debug] Performance data: ${stats.size} systems, total time: ${totalTime.toFixed(2)}ms`);
return {
totalExecutionTime: totalTime,
systemBreakdown: systemBreakdown
};
} catch (error) {
console.error('[ECS Debug] Error getting ECS performance data:', error);
return { totalExecutionTime: 0, systemBreakdown: [] };
}
}
/**
* 获取系统性能数据
*/
private getSystemPerformance(): Array<any> {
const monitor = this.core._performanceMonitor;
if (!monitor) {
return [];
}
try {
const stats = monitor.getAllSystemStats();
const systemData = monitor.getAllSystemData();
return Array.from(stats.entries()).map(([systemName, stat]) => {
const data = systemData.get(systemName);
return {
systemName: systemName,
averageTime: stat.averageTime,
maxTime: stat.maxTime,
minTime: stat.minTime === Number.MAX_VALUE ? 0 : stat.minTime,
samples: stat.executionCount,
percentage: 0, // 在getECSPerformanceData中计算
entityCount: data?.entityCount || 0,
lastExecutionTime: data?.executionTime || 0
};
});
} catch (error) {
return [];
}
}
/**
* 获取内存详情
*/
private getMemoryDetails(): any {
const scene = Core.scene;
if (!scene) {
return {
entities: 0,
components: 0,
systems: 0,
pooled: 0,
totalMemory: 0,
usedMemory: 0,
freeMemory: 0,
gcCollections: 0
};
}
try {
let entityMemory = 0;
let componentMemory = 0;
let systemMemory = 0;
let pooledMemory = 0;
const entityManager = (scene as any).entityManager;
if (entityManager?.entities) {
// 计算实体和组件内存
entityManager.entities.forEach((entity: any) => {
entityMemory += this.estimateObjectSize(entity);
if (entity.components) {
entity.components.forEach((component: any) => {
componentMemory += this.estimateObjectSize(component);
});
}
});
} else {
// 如果entityManager不存在尝试从场景直接获取
const entityList = (scene as any).entities;
if (entityList?.buffer) {
entityList.buffer.forEach((entity: any) => {
entityMemory += this.estimateObjectSize(entity);
if (entity.components) {
entity.components.forEach((component: any) => {
componentMemory += this.estimateObjectSize(component);
});
}
});
}
}
// 计算系统内存
const entitySystems = (scene as any).entitySystems;
if (entitySystems?.systems) {
entitySystems.systems.forEach((system: any) => {
systemMemory += this.estimateObjectSize(system);
});
} else {
// 尝试从entityProcessors获取
const entityProcessors = (scene as any).entityProcessors;
if (entityProcessors?.processors) {
entityProcessors.processors.forEach((system: any) => {
systemMemory += this.estimateObjectSize(system);
});
}
}
// 计算对象池内存
try {
const { ComponentPoolManager } = require('../ECS/Core/ComponentPool');
const poolManager = ComponentPoolManager.getInstance();
const poolStats = poolManager.getPoolStats();
for (const [typeName, stats] of poolStats.entries()) {
// 估算每个组件实例的大小
const estimatedComponentSize = this.calculateComponentMemorySize(typeName);
pooledMemory += stats.available * estimatedComponentSize;
}
} catch (error) {
// 如果无法访问ComponentPoolManager使用估算值
pooledMemory = 512 * 1024; // 512KB估算值
}
// 获取浏览器内存信息
let totalMemory = 512 * 1024 * 1024;
let usedMemory = entityMemory + componentMemory + systemMemory + pooledMemory;
let gcCollections = 0;
if ((performance as any).memory) {
const perfMemory = (performance as any).memory;
totalMemory = perfMemory.jsHeapSizeLimit || totalMemory;
usedMemory = perfMemory.usedJSHeapSize || usedMemory;
}
return {
entities: entityMemory,
components: componentMemory,
systems: systemMemory,
pooled: pooledMemory,
totalMemory: totalMemory,
usedMemory: usedMemory,
freeMemory: totalMemory - usedMemory,
gcCollections: gcCollections
};
} catch (error) {
return {
entities: 0,
components: 0,
systems: 0,
pooled: 0,
totalMemory: 512 * 1024 * 1024,
usedMemory: 0,
freeMemory: 512 * 1024 * 1024,
gcCollections: 0
};
}
}
/**
* 收集组件数据
*/
private collectComponentData(): IComponentDebugData {
const scene = Core.scene;
if (!scene) {
return {
componentTypes: 0,
componentInstances: 0,
componentStats: []
};
}
const entityList = (scene as any).entities;
if (!entityList?.buffer) {
return {
componentTypes: 0,
componentInstances: 0,
componentStats: []
};
}
const componentStats = new Map<string, { count: number; entities: number }>();
let totalInstances = 0;
entityList.buffer.forEach((entity: any) => {
if (entity.components) {
entity.components.forEach((component: any) => {
const typeName = component.constructor.name;
const stats = componentStats.get(typeName) || { count: 0, entities: 0 };
stats.count++;
totalInstances++;
componentStats.set(typeName, stats);
});
}
});
// 获取池利用率信息
let poolUtilizations = new Map<string, number>();
let poolSizes = new Map<string, number>();
try {
const { ComponentPoolManager } = require('../ECS/Core/ComponentPool');
const poolManager = ComponentPoolManager.getInstance();
const poolStats = poolManager.getPoolStats();
const utilizations = poolManager.getPoolUtilization();
for (const [typeName, stats] of poolStats.entries()) {
poolSizes.set(typeName, stats.maxSize);
}
for (const [typeName, util] of utilizations.entries()) {
poolUtilizations.set(typeName, util.utilization);
}
} catch (error) {
// 如果无法获取池信息,使用默认值
}
return {
componentTypes: componentStats.size,
componentInstances: totalInstances,
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);
return {
typeName,
instanceCount: stats.count,
memoryPerInstance: memoryPerInstance,
totalMemory: stats.count * memoryPerInstance,
poolSize: poolSize,
poolUtilization: poolUtilization,
averagePerEntity: stats.count / entityList.buffer.length
};
})
};
}
/**
* 计算组件实际内存大小
*/
private calculateComponentMemorySize(typeName: string): number {
const scene = Core.scene;
if (!scene) return 32;
const entityList = (scene as any).entities;
if (!entityList?.buffer) return 32;
try {
// 找到第一个包含此组件的实体,分析组件大小
for (const entity of entityList.buffer) {
if (entity.components) {
const component = entity.components.find((c: any) => c.constructor.name === typeName);
if (component) {
return this.estimateObjectSize(component);
}
}
}
} catch (error) {
// 忽略错误,使用默认值
}
// 如果无法计算,返回基础大小
return 32; // 基础对象开销
}
/**
* 估算对象内存大小(字节)
*/
private estimateObjectSize(obj: any, visited = new WeakSet(), depth = 0): number {
// 防止无限递归:限制深度和检测循环引用
if (obj === null || obj === undefined || depth > 10) return 0;
if (visited.has(obj)) return 0; // 已访问过,避免循环引用
let size = 0;
const type = typeof obj;
switch (type) {
case 'boolean':
size = 1;
break;
case 'number':
size = 8; // JavaScript中数字都是64位浮点数
break;
case 'string':
size = Math.min(obj.length * 2, 1024); // UTF-16编码每字符2字节限制最大1KB
break;
case 'object':
visited.add(obj); // 标记为已访问
if (Array.isArray(obj)) {
size = 24; // 数组基础开销
// 只处理前100个元素避免大数组导致性能问题
const maxItems = Math.min(obj.length, 100);
for (let i = 0; i < maxItems; i++) {
size += this.estimateObjectSize(obj[i], visited, depth + 1);
}
} else {
size = 24; // 对象基础开销
let propertyCount = 0;
for (const key in obj) {
// 只处理前50个属性避免复杂对象导致性能问题
if (propertyCount >= 50) break;
if (obj.hasOwnProperty(key)) {
// 跳过一些可能导致问题的属性
if (key === 'scene' || key === 'parent' || key === 'children' ||
key === '_scene' || key === '_parent' || key === '_children' ||
key === 'entity' || key === '_entity') {
continue;
}
try {
size += key.length * 2; // 属性名
size += this.estimateObjectSize(obj[key], visited, depth + 1); // 属性值
propertyCount++;
} catch (error) {
// 忽略访问错误的属性
continue;
}
}
}
}
break;
default:
size = 8; // 其他类型默认8字节
}
return Math.min(size, 10240); // 限制单个对象最大10KB
}
/**
* 收集场景数据
*/
private collectSceneData(): ISceneDebugData {
const scene = Core.scene;
if (!scene) {
return {
currentSceneName: 'No Scene',
isInitialized: false,
sceneRunTime: 0,
sceneEntityCount: 0,
sceneSystemCount: 0,
sceneMemory: 0,
sceneUptime: 0
};
}
const currentTime = Date.now();
const runTime = (currentTime - this.sceneStartTime) / 1000;
const entityList = (scene as any).entities;
const entityProcessors = (scene as any).entityProcessors;
return {
currentSceneName: (scene as any).name || 'Unnamed Scene',
isInitialized: (scene as any)._didSceneBegin || false,
sceneRunTime: runTime,
sceneEntityCount: entityList?.buffer?.length || 0,
sceneSystemCount: entityProcessors?.processors?.length || 0,
sceneMemory: 0, // TODO: 计算实际场景内存
sceneUptime: runTime
};
}
/**
* 手动触发数据收集
* @returns 当前调试数据
*/
public getDebugData(): IECSDebugData {
return this.collectDebugData();
}
/**
* 重置场景时间
*/
public onSceneChanged(): void {
this.sceneStartTime = Date.now();
// 发送场景切换事件
if (this.isConnected) {
this.send({
type: 'scene_changed',
data: {
sceneName: this.getCurrentSceneName(),
timestamp: Date.now()
}
});
}
}
/**
* 获取连接状态
*/
public get connected(): boolean {
return this.isConnected;
}
/**
* 手动重连
*/
public reconnect(): void {
if (this.ws) {
this.ws.close();
}
this.reconnectAttempts = 0;
this.connectWebSocket();
}
/**
* 获取Archetype分布
*/
private getArchetypeDistribution(entityContainer: any): Array<{ signature: string; count: number; memory: number }> {
if (!entityContainer.entities) return [];
const archetypes = new Map<string, { count: number; memory: number }>();
entityContainer.entities.forEach((entity: any) => {
const components = entity.components || [];
const signature = components.map((c: any) => c.constructor.name).sort().join(',') || 'Empty';
const existing = archetypes.get(signature) || { count: 0, memory: 0 };
existing.count++;
// 计算每个组件的实际内存大小
let entityMemory = 0;
components.forEach((component: any) => {
entityMemory += this.estimateObjectSize(component);
});
existing.memory += entityMemory;
archetypes.set(signature, existing);
});
return Array.from(archetypes.entries())
.map(([signature, data]) => ({ signature, ...data }))
.sort((a: any, b: any) => b.count - a.count)
.slice(0, 10); // 只返回前10个
}
/**
* 获取组件数量最多的实体
*/
private getTopEntitiesByComponents(entityContainer: any): Array<{ id: string; name: string; componentCount: number; memory: number }> {
if (!entityContainer.entities) return [];
return entityContainer.entities
.map((entity: any) => {
const components = entity.components || [];
let memory = 0;
// 计算实际内存使用
components.forEach((component: any) => {
memory += this.estimateObjectSize(component);
});
return {
id: entity.id?.toString() || 'unknown',
name: entity.name || `Entity_${entity.id}`,
componentCount: components.length,
memory: memory
};
})
.sort((a: any, b: any) => b.componentCount - a.componentCount)
.slice(0, 10); // 只返回前10个
}
}

View File

@@ -3,4 +3,5 @@ export * from './Pool';
export * from './Emitter';
export * from './GlobalManager';
export * from './PerformanceMonitor';
export { Time } from './Time';
export { Time } from './Time';
export * from './Debug';