新增cocos-debug-profiler

This commit is contained in:
YHH
2025-06-17 00:32:16 +08:00
parent 103f773286
commit 1b5363611d
46 changed files with 6757 additions and 37 deletions

View File

@@ -0,0 +1,54 @@
{
"codeGeneration": {
"template": "typescript",
"useStrictMode": true,
"generateComments": true,
"generateImports": true,
"componentSuffix": "Component",
"systemSuffix": "System",
"indentStyle": "spaces",
"indentSize": 4
},
"performance": {
"enableMonitoring": true,
"warningThreshold": 16.67,
"criticalThreshold": 33.33,
"memoryWarningMB": 100,
"memoryCriticalMB": 200,
"maxRecentSamples": 60,
"enableFpsMonitoring": true,
"targetFps": 120
},
"debugging": {
"enableDebugMode": true,
"showEntityCount": true,
"showSystemExecutionTime": true,
"enablePerformanceWarnings": true,
"logLevel": "info",
"enableDetailedLogs": false
},
"editor": {
"autoRefreshAssets": true,
"showWelcomePanelOnStartup": true,
"enableAutoUpdates": false,
"updateChannel": "stable",
"enableNotifications": true
},
"template": {
"defaultEntityName": "ModifiedEntity",
"defaultComponentName": "TestComponent",
"defaultSystemName": "TestSystem",
"createExampleFiles": true,
"includeDocumentation": true,
"useFactoryPattern": true
},
"events": {
"enableEventSystem": true,
"defaultEventPriority": 0,
"enableAsyncEvents": true,
"enableEventBatching": false,
"batchSize": 10,
"batchDelay": 16,
"maxEventListeners": 100
}
}

View File

@@ -25,22 +25,57 @@
"default": {
"title": "ECS Framework - 欢迎面板",
"type": "dockable",
"main": "dist/panels/default",
"main": "dist/panels/default/index.js",
"size": {
"min-width": 450,
"min-height": 600,
"width": 850,
"height": 800
}
},
"settings": {
"title": "ECS Framework - 设置",
"type": "dockable",
"main": "dist/panels/settings/index.js",
"size": {
"min-width": 600,
"min-height": 700,
"width": 800,
"height": 900
}
},
"debug": {
"title": "ECS Framework - 调试面板",
"type": "dockable",
"main": "dist/panels/debug/index.js",
"size": {
"min-width": 400,
"min-height": 500,
"width": 500,
"height": 600
}
}
},
"contributions": {
"scene": {
"script": "./dist/scene.js"
},
"menu": [
{
"path": "i18n:menu.panel/ECS Framework",
"label": "欢迎面板",
"message": "open-panel"
},
{
"path": "i18n:menu.panel/ECS Framework",
"label": "插件设置",
"message": "open-settings"
},
{
"path": "i18n:menu.panel/ECS Framework",
"label": "调试面板",
"message": "open-debug"
},
{
"path": "i18n:menu.develop/ECS Framework",
"label": "ECS 开发工具",
@@ -97,6 +132,16 @@
"methods": [
"open-github"
]
},
"settings-updated": {
"methods": [
"settings-updated"
]
},
"open-debug": {
"methods": [
"open-debug"
]
}
}
}

View File

@@ -26,7 +26,7 @@ export class TemplateGenerator {
*/
public getExistingFiles(): string[] {
if (!this.checkTemplateExists()) return [];
const files: string[] = [];
this.scanDirectory(this.ecsDir, '', files);
return files;
@@ -34,12 +34,12 @@ export class TemplateGenerator {
private scanDirectory(dirPath: string, relativePath: string, files: string[]): void {
if (!fs.existsSync(dirPath)) return;
const items = fs.readdirSync(dirPath);
for (const item of items) {
const fullPath = path.join(dirPath, item);
const relativeFilePath = relativePath ? `${relativePath}/${item}` : item;
if (fs.statSync(fullPath).isDirectory()) {
this.scanDirectory(fullPath, relativeFilePath, files);
} else {
@@ -64,16 +64,16 @@ export class TemplateGenerator {
public createTemplate(): void {
// 创建目录结构
this.createDirectories();
// 创建ECS启动管理器
this.createECSManager();
// 创建基础游戏场景
this.createBaseGameScene();
// 创建README文档
this.createReadme();
console.log('ECS启动模板创建成功');
}
@@ -141,8 +141,27 @@ export class ECSManager extends Component {
console.log('🎮 正在初始化ECS框架...');
try {
// 1. 创建Core实例
Core.create(this.debugMode);
// 1. 创建Core实例,启用调试功能
if (this.debugMode) {
Core.create({
debugConfig: {
enabled: true,
websocketUrl: 'ws://localhost:8080/ecs-debug',
autoReconnect: true,
updateInterval: 100,
channels: {
entities: true,
systems: true,
performance: true,
components: true,
scenes: true
}
}
});
console.log('🔧 ECS调试模式已启用可在Cocos Creator扩展面板中查看调试信息');
} else {
Core.create(false);
}
// 2. 创建游戏场景
const gameScene = new GameScene();
@@ -284,11 +303,25 @@ ECS框架已经配置完成您只需要
\`\`\`
🎮 正在初始化ECS框架...
🔧 ECS调试模式已启用可在Cocos Creator扩展面板中查看调试信息
🎯 游戏场景已创建
✅ ECS框架初始化成功
🚀 游戏场景已启动
\`\`\`
### 3. 使用调试面板
ECS框架已启用调试功能您可以
1. 在Cocos Creator编辑器菜单中选择 "扩展" → "ECS Framework" → "调试面板"
2. 调试面板将显示实时的ECS运行状态
- 实体数量和状态
- 系统执行信息
- 性能监控数据
- 组件统计信息
**注意**:调试功能会消耗一定性能,正式发布时建议关闭调试模式。
## 📚 下一步开发
### 创建您的第一个组件

View File

@@ -122,7 +122,7 @@ export const methods: { [key: string]: (...any: any) => any } = {
'open-documentation'() {
// 使用系统默认命令打开链接
const url = 'https://github.com/esengine/ecs-framework/blob/master/README.md';
exec(`start ${url}`, (error) => {
exec(`start "" "${url}"`, (error) => {
if (error) {
console.error('Failed to open documentation:', error);
Editor.Dialog.info('打开文档', {
@@ -201,10 +201,17 @@ export const methods: { [key: string]: (...any: any) => any } = {
* 打开设置
*/
'open-settings'() {
Editor.Dialog.info('插件设置', {
detail: '设置面板开发中...\n\n将在下个版本提供完整的插件配置功能。\n\n预计功能\n• 代码生成模板配置\n• 性能监控设置\n• 自动更新设置\n• 开发工具偏好',
buttons: ['好的'],
});
console.log('Opening ECS Framework settings panel...');
try {
// 正确的打开特定面板的方法
Editor.Panel.open(packageJSON.name + '.settings');
console.log('Settings panel opened successfully');
} catch (error) {
console.error('Failed to open settings panel:', error);
Editor.Dialog.error('打开设置失败', {
detail: `无法打开设置面板:\n\n${error}\n\n请尝试重启Cocos Creator编辑器。`,
});
}
},
/**
@@ -226,6 +233,74 @@ export const methods: { [key: string]: (...any: any) => any } = {
buttons: ['好的'],
});
},
/**
* 打开GitHub仓库
*/
'open-github'() {
const url = 'https://github.com/esengine/ecs-framework';
// 在Windows上使用正确的start命令语法
exec(`start "" "${url}"`, (error) => {
if (error) {
console.error('Failed to open GitHub:', error);
Editor.Dialog.info('打开GitHub', {
detail: `请手动访问以下链接:\n\n${url}`,
});
}
});
},
/**
* 打开调试面板
*/
'open-debug'() {
console.log('Opening ECS Framework debug panel...');
try {
// 正确的打开特定面板的方法
Editor.Panel.open(packageJSON.name + '.debug');
console.log('Debug panel opened successfully');
} catch (error) {
console.error('Failed to open debug panel:', error);
Editor.Dialog.error('打开调试面板失败', {
detail: `无法打开调试面板:\n\n${error}\n\n请尝试重启Cocos Creator编辑器。`,
});
}
},
/**
* 处理设置更新
*/
'settings-updated'(settings: any) {
console.log('ECS Framework settings updated:', settings);
// 这里可以根据设置更新做一些处理
// 比如重新配置框架、更新性能监控等
// 示例:根据设置更新性能监控
if (settings?.performance?.enableMonitoring) {
console.log('Performance monitoring enabled with thresholds:', {
warning: settings.performance.warningThreshold,
critical: settings.performance.criticalThreshold,
memoryWarning: settings.performance.memoryWarningMB,
memoryCritical: settings.performance.memoryCriticalMB
});
}
// 示例:根据设置更新调试模式
if (settings?.debugging?.enableDebugMode) {
console.log('Debug mode enabled with log level:', settings.debugging.logLevel);
}
// 示例:根据设置更新事件系统
if (settings?.events?.enableEventSystem) {
console.log('Event system configured:', {
asyncEvents: settings.events.enableAsyncEvents,
batching: settings.events.enableEventBatching,
batchSize: settings.events.batchSize,
maxListeners: settings.events.maxEventListeners
});
}
},
};
/**

View File

@@ -0,0 +1,612 @@
import { readFileSync } from 'fs-extra';
import { join } from 'path';
import { createApp, App, defineComponent, ref, reactive, onMounted, onUnmounted } from 'vue';
import { WebSocketServer } from 'ws';
const panelDataMap = new WeakMap<any, App>();
/**
* 游戏实例信息
*/
interface GameInstance {
id: string;
name: string;
connectTime: number;
lastUpdateTime: number;
isActive: boolean;
debugData?: any;
ws?: any; // WebSocket连接
}
/**
* 详细的调试信息接口
*/
interface DetailedDebugInfo {
// 基础信息
instanceId: string;
instanceName: string;
isRunning: boolean;
frameworkLoaded: boolean;
currentScene: string;
uptime: number;
// 性能信息
performance: {
frameTime: number;
fps: number;
averageFrameTime: number;
minFrameTime: number;
maxFrameTime: number;
frameTimeHistory: number[];
};
// 内存信息
memory: {
totalMemory: number;
usedMemory: number;
freeMemory: number;
entityMemory: number;
componentMemory: number;
systemMemory: number;
pooledMemory: number;
gcCollections: number;
};
// 实体信息
entities: {
total: number;
active: number;
inactive: number;
pendingAdd: number;
pendingRemove: number;
entitiesPerArchetype: Array<{
signature: string;
count: number;
memory: number;
}>;
topEntitiesByComponents: Array<{
id: string;
name: string;
componentCount: number;
memory: number;
}>;
};
// 组件信息
components: {
totalTypes: number;
totalInstances: number;
componentStats: Array<{
typeName: string;
instanceCount: number;
memoryPerInstance: number;
totalMemory: number;
poolSize: number;
poolUtilization: number;
}>;
};
// 系统信息
systems: {
total: number;
systemStats: Array<{
name: string;
type: string;
entityCount: number;
averageExecutionTime: number;
minExecutionTime: number;
maxExecutionTime: number;
executionTimeHistory: number[];
memoryUsage: number;
updateOrder: number;
enabled: boolean;
}>;
};
// 场景信息
scenes: {
currentScene: string;
sceneMemory: number;
sceneEntityCount: number;
sceneSystemCount: number;
sceneUptime: number;
};
}
/**
* ECS调试服务器
* 作为服务端,接收多个游戏实例的连接
*/
class ECSDebugServer {
private wss?: WebSocketServer;
private port: number = 8080;
private gameInstances = new Map<string, GameInstance>();
private isRunning: boolean = false;
constructor(port: number = 8080) {
this.port = port;
}
async start(): Promise<boolean> {
if (this.isRunning) return true;
try {
this.wss = new WebSocketServer({ port: this.port });
this.wss.on('connection', (ws, req) => {
const instanceId = this.generateInstanceId();
const instance: GameInstance = {
id: instanceId,
name: `游戏实例-${instanceId.substring(0, 8)}`,
connectTime: Date.now(),
lastUpdateTime: Date.now(),
isActive: true,
debugData: null,
ws: ws
};
this.gameInstances.set(instanceId, instance);
console.log(`[ECS Debug Server] New instance connected: ${instance.name}`);
ws.on('message', (data) => {
try {
const message = JSON.parse(data.toString());
this.handleMessage(instanceId, message);
} catch (error) {
console.error('[ECS Debug Server] Failed to parse message:', error);
}
});
ws.on('close', () => {
const instance = this.gameInstances.get(instanceId);
if (instance) {
instance.isActive = false;
console.log(`[ECS Debug Server] Instance disconnected: ${instance.name}`);
}
});
ws.on('error', (error) => {
console.error(`[ECS Debug Server] WebSocket error for ${instanceId}:`, error);
});
// 发送连接确认
this.sendToInstance(instanceId, {
type: 'connection_confirmed',
instanceId: instanceId,
serverTime: Date.now()
});
});
this.isRunning = true;
console.log(`[ECS Debug Server] Started on port ${this.port}`);
return true;
} catch (error) {
console.error('[ECS Debug Server] Failed to start:', error);
return false;
}
}
stop(): void {
if (this.wss) {
this.wss.close();
this.wss = undefined;
}
this.gameInstances.clear();
this.isRunning = false;
console.log('[ECS Debug Server] Stopped');
}
private generateInstanceId(): string {
return Math.random().toString(36).substring(2) + Date.now().toString(36);
}
private handleMessage(instanceId: string, message: any): void {
const instance = this.gameInstances.get(instanceId);
if (!instance) return;
switch (message.type) {
case 'debug_data':
instance.debugData = message.data;
instance.lastUpdateTime = Date.now();
break;
case 'instance_info':
if (message.name) {
instance.name = message.name;
}
break;
case 'ping':
this.sendToInstance(instanceId, { type: 'pong', timestamp: Date.now() });
break;
}
}
private sendToInstance(instanceId: string, message: any): void {
const instance = this.gameInstances.get(instanceId);
if (instance && instance.ws && instance.ws.readyState === 1) {
instance.ws.send(JSON.stringify(message));
}
}
get running(): boolean {
return this.isRunning;
}
get instances(): GameInstance[] {
return Array.from(this.gameInstances.values());
}
getInstance(instanceId: string): GameInstance | undefined {
return this.gameInstances.get(instanceId);
}
getInstanceDebugData(instanceId: string): DetailedDebugInfo | null {
const instance = this.gameInstances.get(instanceId);
if (!instance || !instance.debugData) {
return null;
}
return this.transformToDetailedDebugInfo(instance, instance.debugData);
}
private transformToDetailedDebugInfo(instance: GameInstance, rawData: any): DetailedDebugInfo {
const uptime = (Date.now() - instance.connectTime) / 1000;
return {
instanceId: instance.id,
instanceName: instance.name,
isRunning: rawData.isRunning || false,
frameworkLoaded: rawData.frameworkLoaded || false,
currentScene: rawData.currentScene || '未知',
uptime: uptime,
performance: {
frameTime: rawData.performance?.frameTime || 0,
fps: rawData.performance?.fps || 0,
averageFrameTime: rawData.performance?.averageFrameTime || rawData.performance?.frameTime || 0,
minFrameTime: rawData.performance?.minFrameTime || rawData.performance?.frameTime || 0,
maxFrameTime: rawData.performance?.maxFrameTime || rawData.performance?.frameTime || 0,
frameTimeHistory: rawData.performance?.frameTimeHistory || []
},
memory: {
totalMemory: rawData.performance?.memoryDetails?.totalMemory || rawData.memory?.totalMemory || 512 * 1024 * 1024,
usedMemory: rawData.performance?.memoryDetails?.usedMemory || (rawData.performance?.memoryUsage ? rawData.performance.memoryUsage * 1024 * 1024 : 0),
freeMemory: rawData.performance?.memoryDetails?.freeMemory || 0,
entityMemory: rawData.performance?.memoryDetails?.entities || rawData.memory?.entityMemory || 0,
componentMemory: rawData.performance?.memoryDetails?.components || rawData.memory?.componentMemory || 0,
systemMemory: rawData.performance?.memoryDetails?.systems || rawData.memory?.systemMemory || 0,
pooledMemory: rawData.performance?.memoryDetails?.pooled || rawData.memory?.pooledMemory || 0,
gcCollections: rawData.performance?.memoryDetails?.gcCollections || rawData.memory?.gcCollections || 0
},
entities: {
total: rawData.entities?.totalEntities || 0,
active: rawData.entities?.activeEntities || 0,
inactive: (rawData.entities?.totalEntities || 0) - (rawData.entities?.activeEntities || 0),
pendingAdd: rawData.entities?.pendingAdd || 0,
pendingRemove: rawData.entities?.pendingRemove || 0,
entitiesPerArchetype: rawData.entities?.entitiesPerArchetype || [],
topEntitiesByComponents: rawData.entities?.topEntitiesByComponents || []
},
components: {
totalTypes: rawData.components?.componentTypes || 0,
totalInstances: rawData.components?.componentInstances || 0,
componentStats: (rawData.components?.componentStats || []).map((comp: any) => ({
typeName: comp.typeName,
instanceCount: comp.instanceCount || 0,
memoryPerInstance: comp.memoryPerInstance || 0,
totalMemory: comp.totalMemory || (comp.instanceCount || 0) * (comp.memoryPerInstance || 0),
poolSize: comp.poolSize || 0,
poolUtilization: comp.poolSize > 0 ? (comp.instanceCount / comp.poolSize * 100) : 0
}))
},
systems: {
total: rawData.systems?.totalSystems || 0,
systemStats: (rawData.systems?.systemsInfo || []).map((sys: any) => ({
name: sys.name,
type: sys.type || 'Unknown',
entityCount: sys.entityCount || 0,
averageExecutionTime: sys.executionTime || 0,
minExecutionTime: sys.minExecutionTime || sys.executionTime || 0,
maxExecutionTime: sys.maxExecutionTime || sys.executionTime || 0,
executionTimeHistory: sys.executionTimeHistory || [],
memoryUsage: sys.memoryUsage || 0,
updateOrder: sys.updateOrder || 0,
enabled: sys.enabled !== false
}))
},
scenes: {
currentScene: rawData.currentScene || '未知',
sceneMemory: rawData.scenes?.sceneMemory || 0,
sceneEntityCount: rawData.entities?.totalEntities || 0,
sceneSystemCount: rawData.systems?.totalSystems || 0,
sceneUptime: rawData.scenes?.sceneUptime || uptime
}
};
}
}
/**
* 默认调试信息
*/
const defaultDebugInfo: DetailedDebugInfo = {
instanceId: '',
instanceName: '未选择实例',
isRunning: false,
frameworkLoaded: false,
currentScene: '未知',
uptime: 0,
performance: {
frameTime: 0,
fps: 0,
averageFrameTime: 0,
minFrameTime: 0,
maxFrameTime: 0,
frameTimeHistory: []
},
memory: {
totalMemory: 0,
usedMemory: 0,
freeMemory: 0,
entityMemory: 0,
componentMemory: 0,
systemMemory: 0,
pooledMemory: 0,
gcCollections: 0
},
entities: {
total: 0,
active: 0,
inactive: 0,
pendingAdd: 0,
pendingRemove: 0,
entitiesPerArchetype: [],
topEntitiesByComponents: []
},
components: {
totalTypes: 0,
totalInstances: 0,
componentStats: []
},
systems: {
total: 0,
systemStats: []
},
scenes: {
currentScene: '未知',
sceneMemory: 0,
sceneEntityCount: 0,
sceneSystemCount: 0,
sceneUptime: 0
}
};
// 全局调试服务器实例
let globalDebugServer: ECSDebugServer | null = null;
/**
* 启动调试服务器
*/
async function ensureDebugServer(): Promise<ECSDebugServer> {
if (!globalDebugServer) {
globalDebugServer = new ECSDebugServer(8080);
}
if (!globalDebugServer.running) {
await globalDebugServer.start();
}
return globalDebugServer;
}
module.exports = Editor.Panel.define({
listeners: {
show() { },
hide() { },
},
template: `<div id="app"></div>`,
style: readFileSync(join(__dirname, '../../../static/style/debug/index.css'), 'utf-8'),
$: {
app: '#app',
},
ready() {
if (this.$.app) {
const app = createApp(defineComponent({
setup() {
const debugInfo = reactive<DetailedDebugInfo>({ ...defaultDebugInfo });
const gameInstances = ref<GameInstance[]>([]);
const selectedInstanceId = ref<string>('');
const isAutoRefresh = ref(true);
const refreshInterval = ref(100);
const lastUpdateTime = ref('');
let intervalId: NodeJS.Timeout | null = null;
let debugServer: ECSDebugServer | null = null;
// 初始化调试服务器
const initializeServer = async () => {
try {
debugServer = await ensureDebugServer();
} catch (error) {
console.error('Failed to start debug server:', error);
}
};
// 更新游戏实例列表和调试信息
const updateDebugInfo = async () => {
if (!debugServer) return;
try {
// 更新实例列表
gameInstances.value = debugServer.instances;
// 如果有选中的实例,更新其调试信息
if (selectedInstanceId.value) {
const detailedInfo = debugServer.getInstanceDebugData(selectedInstanceId.value);
if (detailedInfo) {
Object.assign(debugInfo, detailedInfo);
} else {
// 实例已断开,重置选择
selectedInstanceId.value = '';
Object.assign(debugInfo, defaultDebugInfo);
}
}
lastUpdateTime.value = new Date().toLocaleTimeString();
} catch (error) {
console.error('Failed to update debug info:', error);
}
};
// 开始自动刷新
const startAutoRefresh = () => {
if (intervalId) clearInterval(intervalId);
if (isAutoRefresh.value) {
intervalId = setInterval(updateDebugInfo, refreshInterval.value);
}
};
// 停止自动刷新
const stopAutoRefresh = () => {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
};
// 手动刷新
const manualRefresh = () => {
updateDebugInfo();
};
// 切换自动刷新
const toggleAutoRefresh = () => {
if (isAutoRefresh.value) {
startAutoRefresh();
} else {
stopAutoRefresh();
}
};
// 更改刷新间隔
const changeRefreshInterval = () => {
if (isAutoRefresh.value) {
startAutoRefresh();
}
};
// 实例选择改变
const onInstanceChanged = () => {
if (selectedInstanceId.value) {
updateDebugInfo();
} else {
Object.assign(debugInfo, defaultDebugInfo);
}
};
// 格式化运行时间
const formatUptime = (seconds: number): string => {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
};
// 格式化内存大小
const formatMemory = (bytes: number): string => {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
return (bytes / (1024 * 1024 * 1024)).toFixed(1) + ' GB';
};
// 获取FPS颜色
const getFpsColor = (fps: number): string => {
if (fps >= 55) return 'good';
if (fps >= 30) return 'warning';
return 'critical';
};
// 获取内存颜色
const getMemoryColor = (percentage: number): string => {
if (percentage < 70) return 'good';
if (percentage < 85) return 'warning';
return 'critical';
};
// 获取ECS时间占比颜色
const getECSTimeColor = (percentage: number): string => {
if (!percentage) return 'good';
if (percentage <= 10) return 'good'; // ECS占用<=10%为绿色
if (percentage <= 30) return 'warning'; // ECS占用<=30%为黄色
return 'critical'; // ECS占用>30%为红色
};
// 获取执行时间颜色
const getExecutionTimeColor = (time: number): string => {
if (time < 1) return 'good'; // <1ms为绿色
if (time < 5) return 'warning'; // <5ms为黄色
return 'critical';
};
// 组件挂载时初始化
onMounted(async () => {
await initializeServer();
updateDebugInfo();
startAutoRefresh();
});
// 组件卸载时清理
onUnmounted(() => {
stopAutoRefresh();
});
return {
debugInfo,
gameInstances,
selectedInstanceId,
isAutoRefresh,
refreshInterval,
lastUpdateTime,
manualRefresh,
toggleAutoRefresh,
changeRefreshInterval,
onInstanceChanged,
formatUptime,
formatMemory,
getFpsColor,
getMemoryColor,
getECSTimeColor,
getExecutionTimeColor
};
},
template: readFileSync(join(__dirname, '../../../static/template/debug/index.html'), 'utf-8'),
}));
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ui-');
app.mount(this.$.app);
panelDataMap.set(this, app);
}
},
beforeClose() { },
close() {
const app = panelDataMap.get(this);
if (app) {
app.unmount();
panelDataMap.delete(this);
}
// 关闭调试服务器
if (globalDebugServer) {
globalDebugServer.stop();
globalDebugServer = null;
}
},
});

View File

@@ -0,0 +1,370 @@
import { readFileSync } from 'fs-extra';
import { join } from 'path';
import { createApp, App, defineComponent, ref, onMounted, reactive } from 'vue';
import * as fs from 'fs';
import * as path from 'path';
const panelDataMap = new WeakMap<any, App>();
/**
* ECS框架设置配置接口
*/
interface ECSSettings {
// 代码生成设置
codeGeneration: {
template: 'typescript' | 'javascript';
useStrictMode: boolean;
generateComments: boolean;
generateImports: boolean;
componentSuffix: string;
systemSuffix: string;
indentStyle: 'spaces' | 'tabs';
indentSize: number;
};
// 性能监控设置
performance: {
enableMonitoring: boolean;
warningThreshold: number; // 执行时间警告阈值(ms)
criticalThreshold: number; // 执行时间严重阈值(ms)
memoryWarningMB: number; // 内存警告阈值(MB)
memoryCriticalMB: number; // 内存严重阈值(MB)
maxRecentSamples: number; // 性能采样数量
enableFpsMonitoring: boolean;
targetFps: number;
};
// 调试设置
debugging: {
enableDebugMode: boolean;
showEntityCount: boolean;
showSystemExecutionTime: boolean;
enablePerformanceWarnings: boolean;
logLevel: 'none' | 'error' | 'warn' | 'info' | 'debug';
enableDetailedLogs: boolean;
};
// 编辑器集成
editor: {
autoRefreshAssets: boolean;
showWelcomePanelOnStartup: boolean;
enableAutoUpdates: boolean;
updateChannel: 'stable' | 'beta' | 'alpha';
enableNotifications: boolean;
};
// 项目模板设置
template: {
defaultEntityName: string;
defaultComponentName: string;
defaultSystemName: string;
createExampleFiles: boolean;
includeDocumentation: boolean;
useFactoryPattern: boolean;
};
// 事件系统设置
events: {
enableEventSystem: boolean;
defaultEventPriority: number;
enableAsyncEvents: boolean;
enableEventBatching: boolean;
batchSize: number;
batchDelay: number; // ms
maxEventListeners: number;
};
}
/**
* 默认设置
*/
const defaultSettings: ECSSettings = {
codeGeneration: {
template: 'typescript',
useStrictMode: true,
generateComments: true,
generateImports: true,
componentSuffix: 'Component',
systemSuffix: 'System',
indentStyle: 'spaces',
indentSize: 4
},
performance: {
enableMonitoring: true,
warningThreshold: 16.67, // 60fps
criticalThreshold: 33.33, // 30fps
memoryWarningMB: 100,
memoryCriticalMB: 200,
maxRecentSamples: 60,
enableFpsMonitoring: true,
targetFps: 60
},
debugging: {
enableDebugMode: true,
showEntityCount: true,
showSystemExecutionTime: true,
enablePerformanceWarnings: true,
logLevel: 'info',
enableDetailedLogs: false
},
editor: {
autoRefreshAssets: true,
showWelcomePanelOnStartup: true,
enableAutoUpdates: false,
updateChannel: 'stable',
enableNotifications: true
},
template: {
defaultEntityName: 'GameEntity',
defaultComponentName: 'CustomComponent',
defaultSystemName: 'CustomSystem',
createExampleFiles: true,
includeDocumentation: true,
useFactoryPattern: true
},
events: {
enableEventSystem: true,
defaultEventPriority: 0,
enableAsyncEvents: true,
enableEventBatching: false,
batchSize: 10,
batchDelay: 16,
maxEventListeners: 100
}
};
/**
* 获取设置文件路径
*/
function getSettingsPath(): string {
const projectPath = Editor.Project.path;
return path.join(projectPath, '.ecs-framework-settings.json');
}
/**
* 加载设置
*/
function loadSettings(): ECSSettings {
try {
const settingsPath = getSettingsPath();
if (fs.existsSync(settingsPath)) {
const data = fs.readFileSync(settingsPath, 'utf-8');
const loadedSettings = JSON.parse(data);
// 合并默认设置,确保所有字段都存在
return deepMerge(defaultSettings, loadedSettings);
}
} catch (error) {
console.warn('Failed to load ECS settings:', error);
}
return JSON.parse(JSON.stringify(defaultSettings));
}
/**
* 保存设置
*/
function saveSettings(settings: ECSSettings): boolean {
try {
const settingsPath = getSettingsPath();
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
return true;
} catch (error) {
console.error('Failed to save ECS settings:', error);
return false;
}
}
/**
* 深度合并对象
*/
function deepMerge(target: any, source: any): any {
if (source === null || typeof source !== 'object') return source;
if (target === null || typeof target !== 'object') return source;
const result = { ...target };
for (const key in source) {
if (source.hasOwnProperty(key)) {
if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
result[key] = deepMerge(target[key], source[key]);
} else {
result[key] = source[key];
}
}
}
return result;
}
/**
* 重置为默认设置
*/
function resetToDefaults(): ECSSettings {
return JSON.parse(JSON.stringify(defaultSettings));
}
module.exports = Editor.Panel.define({
listeners: {
show() { console.log('ECS Settings Panel Show'); },
hide() { console.log('ECS Settings Panel Hide'); },
},
template: `<div id="app"></div>`,
style: readFileSync(join(__dirname, '../../../static/style/settings/index.css'), 'utf-8'),
$: {
app: '#app',
},
ready() {
if (this.$.app) {
// 不要直接设置HTML内容让Vue来处理
const app = createApp(defineComponent({
setup() {
const settings = reactive(loadSettings());
const isDirty = ref(false);
const saving = ref(false);
const lastSaved = ref('');
// 监听设置变化
const markDirty = () => {
isDirty.value = true;
};
// 保存设置
const saveCurrentSettings = async () => {
saving.value = true;
try {
const success = saveSettings(settings);
if (success) {
isDirty.value = false;
lastSaved.value = new Date().toLocaleTimeString();
// 通知主进程设置已更新
Editor.Message.send('cocos-ecs-extension', 'settings-updated', settings);
Editor.Dialog.info('设置保存', {
detail: '✅ ECS框架设置已成功保存',
});
} else {
Editor.Dialog.error('保存失败', {
detail: '❌ 保存设置时发生错误,请检查文件权限。',
});
}
} catch (error) {
console.error('Save settings error:', error);
Editor.Dialog.error('保存失败', {
detail: `❌ 保存设置时发生错误:\n\n${error}`,
});
} finally {
saving.value = false;
}
};
// 重置设置
const resetSettings = () => {
Editor.Dialog.warn('重置设置', {
detail: '⚠️ 您确定要重置所有设置为默认值吗?\n\n此操作无法撤销。',
buttons: ['重置', '取消'],
}).then((result: any) => {
if (result.response === 0) {
const defaults = resetToDefaults();
Object.assign(settings, defaults);
isDirty.value = true;
Editor.Dialog.info('设置重置', {
detail: '✅ 设置已重置为默认值,请点击保存按钮确认更改。',
});
}
});
};
// 导出设置
const exportSettings = () => {
try {
const dataStr = JSON.stringify(settings, null, 2);
const blob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(blob);
// 创建下载链接
const a = document.createElement('a');
a.href = url;
a.download = 'ecs-framework-settings.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
Editor.Dialog.info('导出成功', {
detail: '✅ 设置已导出到下载文件夹。',
});
} catch (error) {
console.error('Export settings error:', error);
Editor.Dialog.error('导出失败', {
detail: `❌ 导出设置时发生错误:\n\n${error}`,
});
}
};
// 导入设置
const importSettings = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.style.display = 'none';
input.onchange = (e: any) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
try {
const importedSettings = JSON.parse(event.target?.result as string);
const mergedSettings = deepMerge(defaultSettings, importedSettings);
Object.assign(settings, mergedSettings);
isDirty.value = true;
Editor.Dialog.info('导入成功', {
detail: '✅ 设置已导入,请检查并保存。',
});
} catch (error) {
console.error('Import settings error:', error);
Editor.Dialog.error('导入失败', {
detail: `❌ 导入设置文件时发生错误:\n\n${error}\n\n请确保文件格式正确。`,
});
}
};
reader.readAsText(file);
};
document.body.appendChild(input);
input.click();
document.body.removeChild(input);
};
return {
settings,
isDirty,
saving,
lastSaved,
markDirty,
saveCurrentSettings,
resetSettings,
exportSettings,
importSettings
};
},
template: readFileSync(join(__dirname, '../../../static/template/settings/index.html'), 'utf-8'),
}));
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ui-');
app.mount(this.$.app);
panelDataMap.set(this, app);
}
},
beforeClose() { },
close() {
const app = panelDataMap.get(this);
if (app) {
app.unmount();
panelDataMap.delete(this);
}
},
});

View File

@@ -0,0 +1,187 @@
import { join } from 'path';
// 添加编辑器内的模块搜索路径
module.paths.push(join(Editor.App.path, 'node_modules'));
export function load() {
console.log('ECS Debug Scene Script loaded');
}
export function unload() {
console.log('ECS Debug Scene Script unloaded');
}
export const methods = {
/**
* 获取预览状态
* @returns {object} 预览状态信息
*/
getPreviewState() {
try {
// 检查是否在游戏运行状态
const { director } = require('cc');
if (director && director.getScene && director.getScene()) {
return {
isRunning: true,
engineLoaded: true
};
}
return {
isRunning: false,
engineLoaded: false
};
} catch (error) {
console.warn('Failed to get preview state:', error);
return {
isRunning: false,
engineLoaded: false
};
}
},
/**
* 检查ECS框架是否已加载
* @returns {boolean} ECS框架加载状态
*/
isECSFrameworkLoaded() {
try {
// 检查是否有ECS框架的全局对象
return typeof window !== 'undefined' && !!(window as any).ECSFramework;
} catch (error) {
console.warn('Failed to check ECS framework status:', error);
return false;
}
},
/**
* 获取场景基本信息
* @returns {object} 场景信息
*/
getSceneBasicInfo() {
try {
const { director } = require('cc');
if (director && director.getScene) {
const scene = director.getScene();
return {
sceneName: scene ? (scene.name || '当前场景') : '未知场景',
nodeCount: scene ? this.countNodes(scene) : 0,
isValid: scene ? scene.isValid : false
};
}
return {
sceneName: '未知场景',
nodeCount: 0,
isValid: false
};
} catch (error) {
console.warn('Failed to get scene basic info:', error);
return {
sceneName: '获取失败',
nodeCount: 0,
isValid: false
};
}
},
/**
* 获取ECS框架的调试信息
* @returns {object|null} ECS调试数据或null如果框架未加载
*/
getECSDebugInfo() {
try {
// 检查是否有ECS框架的全局对象
if (typeof window !== 'undefined' && (window as any).ECSFramework) {
const ecs = (window as any).ECSFramework;
// 获取当前场景和实体管理器
if (ecs.Core && ecs.Core.getCurrentScene) {
const scene = ecs.Core.getCurrentScene();
if (scene && scene.entityManager) {
const entityManager = scene.entityManager;
const systemManager = scene.systemManager;
// 收集调试信息
const debugInfo = {
timestamp: new Date().toISOString(),
frameworkLoaded: true,
currentScene: scene.name || '当前场景',
totalEntities: entityManager.entityCount || 0,
activeEntities: entityManager.activeEntityCount || 0,
pendingAdd: 0, // 需要具体API
pendingRemove: 0, // 需要具体API
totalSystems: systemManager ? systemManager.getSystemCount() : 0,
systemsInfo: [],
frameTime: 0, // 需要性能监控
memoryUsage: 0, // 需要内存监控
componentTypes: 0, // 需要组件统计
componentInstances: 0 // 需要组件实例统计
};
// 获取系统信息
if (systemManager && systemManager.getSystems) {
const systems = systemManager.getSystems();
debugInfo.systemsInfo = systems.map((system: any, index: number) => ({
name: system.constructor.name || `System${index}`,
entityCount: system.entities ? system.entities.length : 0,
executionTime: system.lastExecutionTime || 0,
updateOrder: index + 1
}));
}
return debugInfo;
}
}
}
// 检查是否直接导入了ECS模块
try {
// 这里需要根据实际的ECS框架导入方式调整
const { Core } = require('ecs-framework');
if (Core) {
const scene = Core.getCurrentScene();
if (scene) {
return {
timestamp: new Date().toISOString(),
frameworkLoaded: true,
currentScene: scene.name || '当前场景',
totalEntities: scene.entityManager?.entityCount || 0,
activeEntities: scene.entityManager?.activeEntityCount || 0,
pendingAdd: 0,
pendingRemove: 0,
totalSystems: scene.systemManager?.getSystemCount() || 0,
systemsInfo: [],
frameTime: 0,
memoryUsage: 0,
componentTypes: 0,
componentInstances: 0
};
}
}
} catch (error) {
// ECS框架未导入或未初始化
}
return null;
} catch (error) {
console.warn('Failed to get ECS debug info:', error);
return null;
}
},
/**
* 递归计算节点数量
* @param {any} node
* @returns {number}
*/
countNodes(node: any): number {
if (!node) return 0;
let count = 1; // 当前节点
if (node.children) {
for (const child of node.children) {
count += this.countNodes(child);
}
}
return count;
}
};

View File

@@ -0,0 +1,353 @@
/* ECS Framework 设置面板样式 */
.settings-container {
height: 100%;
display: flex;
flex-direction: column;
background: var(--color-normal-fill);
font-family: var(--font-family);
}
/* 头部样式 */
.settings-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 16px 20px;
background: var(--color-info-fill);
border-bottom: 1px solid var(--color-normal-border);
flex-shrink: 0;
}
.header-title h1 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: var(--color-default-text);
}
.header-title p {
margin: 4px 0 0 0;
font-size: 12px;
color: var(--color-focus-text);
opacity: 0.8;
}
.header-actions {
display: flex;
gap: 8px;
}
.header-actions ui-button {
min-width: 80px;
height: 28px;
font-size: 12px;
}
/* 保存按钮状态 */
.save-button.dirty {
background: var(--color-warn-fill) !important;
border-color: var(--color-warn-border) !important;
color: var(--color-warn-text) !important;
}
.save-button.saving {
background: var(--color-info-fill) !important;
opacity: 0.7;
}
/* 图标动画 */
.spin {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* 状态栏 */
.status-bar {
padding: 8px 20px;
background: var(--color-success-fill);
border-bottom: 1px solid var(--color-normal-border);
flex-shrink: 0;
}
.status-text {
font-size: 12px;
color: var(--color-success-text);
}
/* 设置内容区域 */
.settings-content {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 0 20px 20px 20px;
max-height: calc(100vh - 140px); /* 确保有固定的滚动区域 */
scroll-behavior: smooth; /* 平滑滚动 */
}
/* 设置分组 */
.settings-section {
margin-bottom: 32px;
background: var(--color-default-fill);
border: 1px solid var(--color-normal-border);
border-radius: 8px;
overflow: hidden;
}
.section-header {
padding: 16px 20px;
background: var(--color-focus-fill);
border-bottom: 1px solid var(--color-normal-border);
}
.section-header h2 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: var(--color-default-text);
}
.section-header p {
margin: 4px 0 0 0;
font-size: 12px;
color: var(--color-focus-text);
opacity: 0.8;
}
/* 设置网格 */
.settings-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 16px;
padding: 20px;
}
/* 设置项 */
.setting-item {
display: flex;
flex-direction: column;
gap: 6px;
}
.setting-item label {
font-size: 12px;
font-weight: 500;
color: var(--color-default-text);
}
.setting-item ui-input,
.setting-item ui-select,
.setting-item ui-num-input {
width: 100%;
height: 26px;
font-size: 12px;
}
/* 复选框项样式 */
.checkbox-item {
flex-direction: row;
align-items: center;
gap: 8px;
}
.checkbox-item ui-checkbox {
margin: 0;
}
/* 全宽设置项 */
.setting-item.full-width {
grid-column: 1 / -1;
}
/* 底部区域 */
.settings-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 20px;
background: var(--color-focus-fill);
border-top: 1px solid var(--color-normal-border);
flex-shrink: 0;
}
.footer-info {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--color-focus-text);
opacity: 0.8;
}
.footer-info ui-icon {
width: 14px;
height: 14px;
}
.footer-actions {
display: flex;
align-items: center;
}
.dirty-indicator {
font-size: 11px;
color: var(--color-warn-text);
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 768px) {
.settings-header {
flex-direction: column;
gap: 12px;
align-items: stretch;
}
.header-actions {
justify-content: flex-end;
}
.settings-grid {
grid-template-columns: 1fr;
}
.settings-footer {
flex-direction: column;
gap: 8px;
align-items: stretch;
text-align: center;
}
}
/* 深色主题支持 */
@media (prefers-color-scheme: dark) {
.settings-container {
background: #2c2c2c;
}
.settings-section {
background: #3c3c3c;
border-color: #555;
}
.section-header {
background: #404040;
border-color: #555;
}
.settings-header {
background: #333;
border-color: #555;
}
.status-bar {
background: #2d5a2d;
}
.settings-footer {
background: #333;
border-color: #555;
}
}
/* 优化滚动条样式 */
.settings-content::-webkit-scrollbar {
width: 10px;
}
.settings-content::-webkit-scrollbar-track {
background: var(--color-normal-fill);
border-radius: 5px;
margin: 2px;
}
.settings-content::-webkit-scrollbar-thumb {
background: var(--color-focus-border);
border-radius: 5px;
transition: background-color 0.2s ease;
min-height: 30px;
}
.settings-content::-webkit-scrollbar-thumb:hover {
background: var(--color-warn-border);
}
.settings-content::-webkit-scrollbar-thumb:active {
background: var(--color-danger-border);
}
/* 表单元素统一样式 */
.setting-item ui-input,
.setting-item ui-select,
.setting-item ui-num-input {
border: 1px solid var(--color-normal-border);
border-radius: 4px;
padding: 4px 8px;
background: var(--color-default-fill);
color: var(--color-default-text);
transition: border-color 0.2s ease;
}
.setting-item ui-input:focus,
.setting-item ui-select:focus,
.setting-item ui-num-input:focus {
border-color: var(--color-focus-border);
outline: none;
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
}
/* 复选框样式 */
.setting-item ui-checkbox {
font-size: 12px;
color: var(--color-default-text);
}
/* 按钮样式覆盖 */
.header-actions .export-button {
background: var(--color-info-fill);
border-color: var(--color-info-border);
color: var(--color-info-text);
}
.header-actions .import-button {
background: var(--color-warn-fill);
border-color: var(--color-warn-border);
color: var(--color-warn-text);
}
.header-actions .reset-button {
background: var(--color-danger-fill);
border-color: var(--color-danger-border);
color: var(--color-danger-text);
}
/* 工具提示样式 */
.setting-item[title] {
cursor: help;
}
/* 加载状态 */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid var(--color-normal-border);
border-top: 3px solid var(--color-focus-border);
border-radius: 50%;
animation: spin 1s linear infinite;
}

View File

@@ -0,0 +1,294 @@
<div class="debug-panel">
<!-- 顶部控制栏 -->
<div class="debug-toolbar">
<div class="toolbar-section">
<label>游戏实例:</label>
<select v-model="selectedInstanceId" @change="onInstanceChanged" class="instance-selector">
<option value="">-- 选择实例 --</option>
<option v-for="instance in gameInstances" :key="instance.id" :value="instance.id">
{{ instance.name }} ({{ instance.isActive ? '活跃' : '离线' }})
</option>
</select>
<span class="instance-count">{{ gameInstances.length }} 个实例</span>
</div>
<div class="toolbar-section">
<button @click="manualRefresh" class="btn-refresh" :disabled="!selectedInstanceId">
<ui-icon value="refresh"></ui-icon>
刷新
</button>
<label class="checkbox-label">
<input type="checkbox" v-model="isAutoRefresh" @change="toggleAutoRefresh">
自动刷新
</label>
<select v-model="refreshInterval" @change="changeRefreshInterval" :disabled="!isAutoRefresh">
<option :value="100">0.1秒</option>
<option :value="250">0.25秒</option>
<option :value="500">0.5秒</option>
<option :value="1000">1秒</option>
<option :value="2000">2秒</option>
</select>
</div>
<div class="toolbar-section">
<span class="last-update">{{ lastUpdateTime }}</span>
</div>
</div>
<!-- 连接状态和基础信息 -->
<div class="debug-section">
<div class="section-header">
<h2>📊 实例状态</h2>
</div>
<div class="status-grid" v-if="selectedInstanceId && debugInfo.instanceId">
<div class="status-item">
<span class="label">实例名称:</span>
<span class="value">{{ debugInfo.instanceName }}</span>
</div>
<div class="status-item">
<span class="label">运行状态:</span>
<span class="value" :class="{ online: debugInfo.isRunning, offline: !debugInfo.isRunning }">
{{ debugInfo.isRunning ? '运行中' : '已停止' }}
</span>
</div>
<div class="status-item">
<span class="label">当前场景:</span>
<span class="value">{{ debugInfo.currentScene }}</span>
</div>
<div class="status-item">
<span class="label">运行时间:</span>
<span class="value">{{ formatUptime(debugInfo.uptime) }}</span>
</div>
</div>
<div class="no-instance" v-else>
<ui-icon value="info" class="info-icon"></ui-icon>
<span>请选择一个游戏实例查看详细信息</span>
</div>
</div>
<!-- 性能监控 -->
<div class="debug-section" v-if="debugInfo.instanceId">
<div class="section-header">
<h2>⚡ 性能监控</h2>
</div>
<div class="performance-grid">
<div class="perf-card">
<div class="perf-title">帧率</div>
<div class="perf-value" :class="getFpsColor(debugInfo.performance.fps)">
{{ debugInfo.performance.fps }} FPS
</div>
<div class="perf-detail">
引擎帧时间: {{ debugInfo.performance.engineFrameTime?.toFixed(2) || '0.00' }}ms
</div>
</div>
<div class="perf-card">
<div class="perf-title">ECS执行时间</div>
<div class="perf-value" :class="getECSTimeColor(debugInfo.performance.ecsPercentage)">
{{ debugInfo.performance.frameTime.toFixed(3) }}ms
</div>
<div class="perf-detail">
占总帧时间: {{ debugInfo.performance.ecsPercentage?.toFixed(1) || '0.0' }}%
</div>
</div>
<div class="perf-card">
<div class="perf-title">总内存</div>
<div class="perf-value" :class="getMemoryColor(debugInfo.memory.usedMemory / debugInfo.memory.totalMemory * 100)">
{{ formatMemory(debugInfo.memory.usedMemory) }}
</div>
<div class="perf-detail">
/ {{ formatMemory(debugInfo.memory.totalMemory) }}
</div>
</div>
<div class="perf-card">
<div class="perf-title">GC回收</div>
<div class="perf-value">{{ debugInfo.memory.gcCollections }}</div>
<div class="perf-detail">次数</div>
</div>
</div>
</div>
<!-- 内存分析 -->
<div class="debug-section" v-if="debugInfo.instanceId">
<div class="section-header">
<h2>💾 内存分析</h2>
</div>
<div class="memory-breakdown">
<div class="memory-item">
<span class="memory-label">实体内存:</span>
<span class="memory-value">{{ formatMemory(debugInfo.memory.entityMemory) }}</span>
<div class="memory-bar">
<div class="memory-fill" :style="{ width: (debugInfo.memory.entityMemory / debugInfo.memory.usedMemory * 100) + '%' }"></div>
</div>
</div>
<div class="memory-item">
<span class="memory-label">组件内存:</span>
<span class="memory-value">{{ formatMemory(debugInfo.memory.componentMemory) }}</span>
<div class="memory-bar">
<div class="memory-fill" :style="{ width: (debugInfo.memory.componentMemory / debugInfo.memory.usedMemory * 100) + '%' }"></div>
</div>
</div>
<div class="memory-item">
<span class="memory-label">系统内存:</span>
<span class="memory-value">{{ formatMemory(debugInfo.memory.systemMemory) }}</span>
<div class="memory-bar">
<div class="memory-fill" :style="{ width: (debugInfo.memory.systemMemory / debugInfo.memory.usedMemory * 100) + '%' }"></div>
</div>
</div>
<div class="memory-item">
<span class="memory-label">对象池内存:</span>
<span class="memory-value">{{ formatMemory(debugInfo.memory.pooledMemory) }}</span>
<div class="memory-bar">
<div class="memory-fill" :style="{ width: (debugInfo.memory.pooledMemory / debugInfo.memory.usedMemory * 100) + '%' }"></div>
</div>
</div>
</div>
</div>
<!-- 实体统计 -->
<div class="debug-section" v-if="debugInfo.instanceId">
<div class="section-header">
<h2>👥 实体统计</h2>
</div>
<div class="entity-stats">
<div class="stat-card">
<div class="stat-number">{{ debugInfo.entities.total }}</div>
<div class="stat-label">总实体数</div>
</div>
<div class="stat-card">
<div class="stat-number">{{ debugInfo.entities.active }}</div>
<div class="stat-label">活跃实体</div>
</div>
<div class="stat-card">
<div class="stat-number">{{ debugInfo.entities.inactive }}</div>
<div class="stat-label">非活跃实体</div>
</div>
<div class="stat-card">
<div class="stat-number">{{ debugInfo.entities.pendingAdd }}</div>
<div class="stat-label">待添加</div>
</div>
</div>
<!-- Archetype分布 -->
<div class="archetype-section" v-if="debugInfo.entities.entitiesPerArchetype.length > 0">
<h3>Archetype 分布</h3>
<div class="archetype-list">
<div v-for="archetype in debugInfo.entities.entitiesPerArchetype" :key="archetype.signature" class="archetype-item">
<div class="archetype-signature">{{ archetype.signature }}</div>
<div class="archetype-stats">
<span class="entity-count">{{ archetype.count }} 实体</span>
<span class="memory-usage">{{ formatMemory(archetype.memory) }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- 组件分析 -->
<div class="debug-section" v-if="debugInfo.instanceId">
<div class="section-header">
<h2>🧩 组件分析</h2>
</div>
<div class="component-overview">
<div class="overview-item">
<span class="label">组件类型:</span>
<span class="value">{{ debugInfo.components.totalTypes }}</span>
</div>
<div class="overview-item">
<span class="label">组件实例:</span>
<span class="value">{{ debugInfo.components.totalInstances }}</span>
</div>
</div>
<div class="component-table" v-if="debugInfo.components.componentStats.length > 0">
<div class="table-header">
<div class="col-name">组件类型</div>
<div class="col-count">实例数</div>
<div class="col-memory">内存占用</div>
<div class="col-pool">对象池</div>
<div class="col-efficiency">利用率</div>
</div>
<div v-for="comp in debugInfo.components.componentStats" :key="comp.typeName" class="table-row">
<div class="col-name">{{ comp.typeName }}</div>
<div class="col-count">{{ comp.instanceCount }}</div>
<div class="col-memory">{{ formatMemory(comp.totalMemory) }}</div>
<div class="col-pool">{{ comp.poolSize }}</div>
<div class="col-efficiency">
<div class="efficiency-bar">
<div class="efficiency-fill" :style="{ width: comp.poolUtilization + '%' }"></div>
</div>
{{ comp.poolUtilization.toFixed(1) }}%
</div>
</div>
</div>
</div>
<!-- 系统性能 -->
<div class="debug-section" v-if="debugInfo.instanceId">
<div class="section-header">
<h2>⚙️ 系统性能</h2>
</div>
<div class="systems-table" v-if="debugInfo.systems.systemStats.length > 0">
<div class="table-header">
<div class="col-name">系统名称</div>
<div class="col-entities">实体数</div>
<div class="col-time">执行时间</div>
<div class="col-percentage">ECS占比</div>
<div class="col-order">优先级</div>
<div class="col-status">状态</div>
</div>
<div v-for="system in debugInfo.systems.systemStats" :key="system.name" class="table-row">
<div class="col-name">{{ system.name }}</div>
<div class="col-entities">{{ system.entityCount }}</div>
<div class="col-time">
<span :class="getExecutionTimeColor(system.averageExecutionTime)">
{{ system.averageExecutionTime.toFixed(3) }}ms
</span>
<div class="time-range">
({{ system.minExecutionTime.toFixed(3) }}-{{ system.maxExecutionTime.toFixed(3) }}ms)
</div>
</div>
<div class="col-percentage">
<div class="percentage-bar">
<div class="percentage-fill" :style="{ width: (system.percentage || 0) + '%' }"></div>
</div>
{{ (system.percentage || 0).toFixed(1) }}%
</div>
<div class="col-order">{{ system.updateOrder }}</div>
<div class="col-status">
<span :class="{ enabled: system.enabled, disabled: !system.enabled }">
{{ system.enabled ? '启用' : '禁用' }}
</span>
</div>
</div>
</div>
</div>
<!-- 服务器状态 -->
<div class="debug-section" v-if="gameInstances.length === 0">
<div class="section-header">
<h2>🔌 调试服务器</h2>
</div>
<div class="server-status">
<div class="status-message">
<ui-icon value="wifi" class="server-icon"></ui-icon>
<div class="message-content">
<h3>等待游戏实例连接...</h3>
<p>调试服务器正在 <strong>ws://localhost:8080</strong> 监听连接</p>
<p>请确保游戏中的ECS框架已启用调试模式</p>
</div>
</div>
<div class="connection-help">
<h4>连接说明:</h4>
<ul>
<li>在ECSManager组件中启用调试模式</li>
<li>运行游戏,框架会自动连接到调试服务器</li>
<li>支持多个游戏实例同时连接</li>
<li>可以在上方选择不同的实例进行调试</li>
</ul>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,322 @@
<div id="app">
<div class="settings-container">
<!-- 头部 -->
<div class="settings-header">
<div class="header-title">
<h1>🔧 ECS Framework 设置</h1>
<p>配置ECS框架的行为和性能选项</p>
</div>
<div class="header-actions">
<ui-button class="save-button" :class="{ dirty: isDirty, saving: saving }"
@click="saveCurrentSettings" :disabled="saving">
<ui-icon value="confirm" v-if="!saving"></ui-icon>
<ui-icon value="loading" class="spin" v-if="saving"></ui-icon>
{{ saving ? '保存中...' : (isDirty ? '保存 *' : '保存') }}
</ui-button>
<ui-button class="export-button" @click="exportSettings">
<ui-icon value="download"></ui-icon>
导出
</ui-button>
<ui-button class="import-button" @click="importSettings">
<ui-icon value="upload"></ui-icon>
导入
</ui-button>
<ui-button class="reset-button" @click="resetSettings">
<ui-icon value="reset"></ui-icon>
重置
</ui-button>
</div>
</div>
<!-- 状态栏 -->
<div class="status-bar" v-if="lastSaved">
<span class="status-text">最后保存: {{ lastSaved }}</span>
</div>
<!-- 设置内容 -->
<div class="settings-content">
<!-- 代码生成设置 -->
<div class="settings-section">
<div class="section-header">
<h2>📝 代码生成设置</h2>
<p>配置自动生成代码的格式和选项</p>
</div>
<div class="settings-grid">
<div class="setting-item">
<label>模板语言</label>
<ui-select v-model="settings.codeGeneration.template" @change="markDirty">
<option value="typescript">TypeScript</option>
<option value="javascript">JavaScript</option>
</ui-select>
</div>
<div class="setting-item">
<label>组件后缀</label>
<ui-input v-model="settings.codeGeneration.componentSuffix"
@change="markDirty" placeholder="Component"></ui-input>
</div>
<div class="setting-item">
<label>系统后缀</label>
<ui-input v-model="settings.codeGeneration.systemSuffix"
@change="markDirty" placeholder="System"></ui-input>
</div>
<div class="setting-item">
<label>缩进风格</label>
<ui-select v-model="settings.codeGeneration.indentStyle" @change="markDirty">
<option value="spaces">空格</option>
<option value="tabs">制表符</option>
</ui-select>
</div>
<div class="setting-item">
<label>缩进大小</label>
<ui-num-input v-model="settings.codeGeneration.indentSize"
@change="markDirty" :min="1" :max="8"></ui-num-input>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.codeGeneration.useStrictMode" @change="markDirty">
启用严格模式
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.codeGeneration.generateComments" @change="markDirty">
生成注释
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.codeGeneration.generateImports" @change="markDirty">
自动添加导入语句
</ui-checkbox>
</div>
</div>
</div>
<!-- 性能监控设置 -->
<div class="settings-section">
<div class="section-header">
<h2>⚡ 性能监控设置</h2>
<p>配置性能监控和优化建议</p>
</div>
<div class="settings-grid">
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.performance.enableMonitoring" @change="markDirty">
启用性能监控
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.performance.enableFpsMonitoring" @change="markDirty">
启用FPS监控
</ui-checkbox>
</div>
<div class="setting-item">
<label>目标FPS</label>
<ui-num-input v-model="settings.performance.targetFps"
@change="markDirty" :min="30" :max="120"></ui-num-input>
</div>
<div class="setting-item">
<label>执行时间警告阈值 (ms)</label>
<ui-num-input v-model="settings.performance.warningThreshold"
@change="markDirty" :min="1" :max="100" :step="0.1"></ui-num-input>
</div>
<div class="setting-item">
<label>执行时间严重阈值 (ms)</label>
<ui-num-input v-model="settings.performance.criticalThreshold"
@change="markDirty" :min="1" :max="200" :step="0.1"></ui-num-input>
</div>
<div class="setting-item">
<label>内存警告阈值 (MB)</label>
<ui-num-input v-model="settings.performance.memoryWarningMB"
@change="markDirty" :min="10" :max="1000"></ui-num-input>
</div>
<div class="setting-item">
<label>内存严重阈值 (MB)</label>
<ui-num-input v-model="settings.performance.memoryCriticalMB"
@change="markDirty" :min="50" :max="2000"></ui-num-input>
</div>
<div class="setting-item">
<label>性能采样数量</label>
<ui-num-input v-model="settings.performance.maxRecentSamples"
@change="markDirty" :min="10" :max="300"></ui-num-input>
</div>
</div>
</div>
<!-- 调试设置 -->
<div class="settings-section">
<div class="section-header">
<h2>🐛 调试设置</h2>
<p>配置调试模式和日志选项</p>
</div>
<div class="settings-grid">
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.debugging.enableDebugMode" @change="markDirty">
启用调试模式
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.debugging.showEntityCount" @change="markDirty">
显示实体数量
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.debugging.showSystemExecutionTime" @change="markDirty">
显示系统执行时间
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.debugging.enablePerformanceWarnings" @change="markDirty">
启用性能警告
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.debugging.enableDetailedLogs" @change="markDirty">
启用详细日志
</ui-checkbox>
</div>
<div class="setting-item">
<label>日志级别</label>
<ui-select v-model="settings.debugging.logLevel" @change="markDirty">
<option value="none"></option>
<option value="error">错误</option>
<option value="warn">警告</option>
<option value="info">信息</option>
<option value="debug">调试</option>
</ui-select>
</div>
</div>
</div>
<!-- 编辑器集成设置 -->
<div class="settings-section">
<div class="section-header">
<h2>⚙️ 编辑器集成</h2>
<p>配置与Cocos Creator的集成选项</p>
</div>
<div class="settings-grid">
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.editor.autoRefreshAssets" @change="markDirty">
自动刷新资源
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.editor.showWelcomePanelOnStartup" @change="markDirty">
启动时显示欢迎面板
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.editor.enableAutoUpdates" @change="markDirty">
启用自动更新
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.editor.enableNotifications" @change="markDirty">
启用通知
</ui-checkbox>
</div>
<div class="setting-item">
<label>更新频道</label>
<ui-select v-model="settings.editor.updateChannel" @change="markDirty">
<option value="stable">稳定版</option>
<option value="beta">测试版</option>
<option value="alpha">开发版</option>
</ui-select>
</div>
</div>
</div>
<!-- 项目模板设置 -->
<div class="settings-section">
<div class="section-header">
<h2>📁 项目模板设置</h2>
<p>配置生成项目模板的默认选项</p>
</div>
<div class="settings-grid">
<div class="setting-item">
<label>默认实体名称</label>
<ui-input v-model="settings.template.defaultEntityName"
@change="markDirty" placeholder="GameEntity"></ui-input>
</div>
<div class="setting-item">
<label>默认组件名称</label>
<ui-input v-model="settings.template.defaultComponentName"
@change="markDirty" placeholder="CustomComponent"></ui-input>
</div>
<div class="setting-item">
<label>默认系统名称</label>
<ui-input v-model="settings.template.defaultSystemName"
@change="markDirty" placeholder="CustomSystem"></ui-input>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.template.createExampleFiles" @change="markDirty">
创建示例文件
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.template.includeDocumentation" @change="markDirty">
包含文档
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.template.useFactoryPattern" @change="markDirty">
使用工厂模式
</ui-checkbox>
</div>
</div>
</div>
<!-- 事件系统设置 -->
<div class="settings-section">
<div class="section-header">
<h2>📡 事件系统设置</h2>
<p>配置事件系统的行为和性能</p>
</div>
<div class="settings-grid">
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.events.enableEventSystem" @change="markDirty">
启用事件系统
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.events.enableAsyncEvents" @change="markDirty">
启用异步事件
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.events.enableEventBatching" @change="markDirty">
启用事件批处理
</ui-checkbox>
</div>
<div class="setting-item">
<label>默认事件优先级</label>
<ui-num-input v-model="settings.events.defaultEventPriority"
@change="markDirty" :min="-100" :max="100"></ui-num-input>
</div>
<div class="setting-item">
<label>批处理大小</label>
<ui-num-input v-model="settings.events.batchSize"
@change="markDirty" :min="1" :max="100"></ui-num-input>
</div>
<div class="setting-item">
<label>批处理延迟 (ms)</label>
<ui-num-input v-model="settings.events.batchDelay"
@change="markDirty" :min="1" :max="1000"></ui-num-input>
</div>
<div class="setting-item">
<label>最大事件监听器数</label>
<ui-num-input v-model="settings.events.maxEventListeners"
@change="markDirty" :min="10" :max="1000"></ui-num-input>
</div>
</div>
</div>
</div>
<!-- 底部提示 -->
<div class="settings-footer">
<div class="footer-info">
<ui-icon value="info"></ui-icon>
<span>设置将保存到项目目录的 .ecs-framework-settings.json 文件中</span>
</div>
<div class="footer-actions">
<span class="dirty-indicator" v-if="isDirty">● 有未保存的更改</span>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,132 @@
const fs = require('fs');
const path = require('path');
// 模拟项目路径(实际会是真实的项目路径)
const projectPath = process.cwd();
const settingsPath = path.join(projectPath, '.ecs-framework-settings.json');
console.log('🧪 测试ECS框架设置功能...');
console.log('设置文件路径:', settingsPath);
// 默认设置
const testSettings = {
codeGeneration: {
template: 'typescript',
useStrictMode: true,
generateComments: true,
generateImports: true,
componentSuffix: 'Component',
systemSuffix: 'System',
indentStyle: 'spaces',
indentSize: 4
},
performance: {
enableMonitoring: true,
warningThreshold: 16.67,
criticalThreshold: 33.33,
memoryWarningMB: 100,
memoryCriticalMB: 200,
maxRecentSamples: 60,
enableFpsMonitoring: true,
targetFps: 60
},
debugging: {
enableDebugMode: true,
showEntityCount: true,
showSystemExecutionTime: true,
enablePerformanceWarnings: true,
logLevel: 'info',
enableDetailedLogs: false
},
editor: {
autoRefreshAssets: true,
showWelcomePanelOnStartup: true,
enableAutoUpdates: false,
updateChannel: 'stable',
enableNotifications: true
},
template: {
defaultEntityName: 'TestEntity', // 修改这个值来测试
defaultComponentName: 'TestComponent',
defaultSystemName: 'TestSystem',
createExampleFiles: true,
includeDocumentation: true,
useFactoryPattern: true
},
events: {
enableEventSystem: true,
defaultEventPriority: 0,
enableAsyncEvents: true,
enableEventBatching: false,
batchSize: 10,
batchDelay: 16,
maxEventListeners: 100
}
};
// 测试保存功能
console.log('✅ 测试保存设置...');
try {
fs.writeFileSync(settingsPath, JSON.stringify(testSettings, null, 2), 'utf-8');
console.log('✅ 设置已成功保存到:', settingsPath);
} catch (error) {
console.error('❌ 保存设置失败:', error);
}
// 测试加载功能
console.log('✅ 测试加载设置...');
try {
if (fs.existsSync(settingsPath)) {
const loadedData = fs.readFileSync(settingsPath, 'utf-8');
const loadedSettings = JSON.parse(loadedData);
console.log('✅ 设置已成功加载');
console.log('默认实体名称:', loadedSettings.template.defaultEntityName);
console.log('调试模式:', loadedSettings.debugging.enableDebugMode);
console.log('目标FPS:', loadedSettings.performance.targetFps);
// 验证数据完整性
const expectedKeys = Object.keys(testSettings);
const loadedKeys = Object.keys(loadedSettings);
if (expectedKeys.every(key => loadedKeys.includes(key))) {
console.log('✅ 数据完整性检查通过');
} else {
console.log('❌ 数据完整性检查失败');
}
} else {
console.log('❌ 设置文件不存在');
}
} catch (error) {
console.error('❌ 加载设置失败:', error);
}
// 测试修改和重新保存
console.log('✅ 测试修改设置...');
try {
const modifiedSettings = { ...testSettings };
modifiedSettings.template.defaultEntityName = 'ModifiedEntity';
modifiedSettings.performance.targetFps = 120;
fs.writeFileSync(settingsPath, JSON.stringify(modifiedSettings, null, 2), 'utf-8');
// 重新加载验证
const reloadedData = fs.readFileSync(settingsPath, 'utf-8');
const reloadedSettings = JSON.parse(reloadedData);
if (reloadedSettings.template.defaultEntityName === 'ModifiedEntity' &&
reloadedSettings.performance.targetFps === 120) {
console.log('✅ 设置修改测试通过');
} else {
console.log('❌ 设置修改测试失败');
}
} catch (error) {
console.error('❌ 修改设置测试失败:', error);
}
console.log('🎉 测试完成!设置功能工作正常。');
console.log('📁 设置文件位置:', settingsPath);
// 清理测试文件(可选)
// fs.unlinkSync(settingsPath);
// console.log('🧹 已清理测试文件');