From 80e2f7df71330530b58543ba6ab87b14cad50fe0 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Thu, 19 Jun 2025 21:00:42 +0800 Subject: [PATCH] remove cocos-extensions --- .gitmodules | 3 + .../.ecs-framework-settings.json | 54 - .../@types/schema/package/base/panels.json | 81 - .../schema/package/contributions/index.json | 9 - .../@types/schema/package/index.json | 64 - .../extensions/cocos-ecs-extension/README.md | 81 - .../cocos-ecs-extension/base.tsconfig.json | 22 - .../extensions/cocos-ecs-extension/i18n/en.js | 15 - .../extensions/cocos-ecs-extension/i18n/zh.js | 18 - .../cocos-ecs-extension/package-lock.json | 318 ---- .../cocos-ecs-extension/package.json | 234 --- .../cocos-ecs-extension/scripts/preinstall.js | 1 - .../source/CodeGenerator.ts | 274 --- .../source/TemplateGenerator.ts | 436 ----- .../cocos-ecs-extension/source/assets-menu.ts | 46 - .../source/handlers/BehaviorTreeHandler.ts | 351 ---- .../source/handlers/EcsFrameworkHandler.ts | 234 --- .../source/handlers/HotUpdateHandler.ts | 571 ------ .../source/handlers/PanelHandler.ts | 64 - .../source/handlers/index.ts | 4 - .../cocos-ecs-extension/source/main.ts | 229 --- .../behavior-tree/composables/useAppState.ts | 124 -- .../composables/useBehaviorTreeEditor.ts | 1074 ----------- .../composables/useBlackboard.ts | 474 ----- .../composables/useCanvasManager.ts | 203 --- .../composables/useCodeGeneration.ts | 490 ----- .../composables/useComputedProperties.ts | 372 ---- .../composables/useConditionAttachment.ts | 482 ----- .../composables/useConnectionManager.ts | 610 ------- .../composables/useFileOperations.ts | 538 ------ .../composables/useInstallation.ts | 60 - .../composables/useNodeDisplay.ts | 86 - .../composables/useNodeOperations.ts | 190 -- .../behavior-tree/data/nodeTemplates.ts | 1115 ------------ .../source/panels/behavior-tree/index.ts | 180 -- .../panels/behavior-tree/types/index.ts | 81 - .../behavior-tree/utils/EventManager.ts | 104 -- .../panels/behavior-tree/utils/canvasUtils.ts | 109 -- .../behavior-tree/utils/installUtils.ts | 95 - .../panels/behavior-tree/utils/nodeUtils.ts | 230 --- .../source/panels/debug/index.ts | 717 -------- .../source/panels/default/index.ts | 528 ------ .../source/panels/generator/index.ts | 240 --- .../cocos-ecs-extension/source/scene.ts | 187 -- .../cocos-ecs-extension/start-admin.bat | 50 - .../static/style/behavior-tree/base.css | 261 --- .../static/style/behavior-tree/canvas.css | 149 -- .../static/style/behavior-tree/conditions.css | 359 ---- .../static/style/behavior-tree/modals.css | 610 ------- .../static/style/behavior-tree/nodes.css | 393 ---- .../static/style/behavior-tree/panels.css | 1424 --------------- .../static/style/behavior-tree/toolbar.css | 299 --- .../static/style/debug/index.css | 1602 ----------------- .../static/style/default/index.css | 483 ----- .../static/style/generator/index.css | 378 ---- .../behavior-tree/BehaviorTreeEditor.html | 903 ---------- .../behavior-tree/BlackboardPanel.html | 194 -- .../template/behavior-tree/TreeNodeItem.html | 16 - .../static/template/behavior-tree/index.html | 3 - .../static/template/debug/index.html | 382 ---- .../static/template/default/index.html | 5 - .../static/template/generator/index.html | 111 -- .../static/template/vue/counter.html | 6 - .../static/template/vue/welcome.html | 162 -- .../cocos-ecs-extension/test-settings.js | 132 -- .../cocos-ecs-extension/tsconfig.json | 15 - thirdparty/admin-backend | 1 + 67 files changed, 4 insertions(+), 19332 deletions(-) delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/.ecs-framework-settings.json delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/base/panels.json delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/contributions/index.json delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/index.json delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/README.md delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/base.tsconfig.json delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/i18n/en.js delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/i18n/zh.js delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package-lock.json delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package.json delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/scripts/preinstall.js delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/CodeGenerator.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/TemplateGenerator.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/assets-menu.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/BehaviorTreeHandler.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/EcsFrameworkHandler.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/HotUpdateHandler.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/PanelHandler.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/index.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/main.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useAppState.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useBehaviorTreeEditor.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useBlackboard.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useCanvasManager.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useCodeGeneration.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useComputedProperties.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useConditionAttachment.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useConnectionManager.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useFileOperations.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useInstallation.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useNodeDisplay.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useNodeOperations.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/data/nodeTemplates.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/index.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/types/index.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/EventManager.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/canvasUtils.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/installUtils.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/nodeUtils.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/debug/index.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/default/index.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/generator/index.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/scene.ts delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/start-admin.bat delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/base.css delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/canvas.css delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/conditions.css delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/modals.css delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/nodes.css delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/panels.css delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/toolbar.css delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/debug/index.css delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/default/index.css delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/generator/index.css delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/BehaviorTreeEditor.html delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/BlackboardPanel.html delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/TreeNodeItem.html delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/index.html delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/debug/index.html delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/default/index.html delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/generator/index.html delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/vue/counter.html delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/vue/welcome.html delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/test-settings.js delete mode 100644 extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/tsconfig.json create mode 160000 thirdparty/admin-backend diff --git a/.gitmodules b/.gitmodules index 8ab1ab6b..e283929c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "thirdparty/BehaviourTree-ai"] path = thirdparty/BehaviourTree-ai url = https://github.com/esengine/BehaviourTree-ai.git +[submodule "thirdparty/admin-backend"] + path = thirdparty/admin-backend + url = https://github.com/esengine/admin-backend.git diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/.ecs-framework-settings.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/.ecs-framework-settings.json deleted file mode 100644 index 8632000f..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/.ecs-framework-settings.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "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 - } -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/base/panels.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/base/panels.json deleted file mode 100644 index 9e268fba..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/base/panels.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "description": "面板数据 / Panel data", - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9_-]+$": { - "type": "object", - "description": "面板名 / Panel name", - "properties": { - "title": { - "type": "string", - "default": "Default Panel", - "description": "面板标题,支持 i18n:key / Panel title, support for i18n:key (required)" - }, - "main": { - "type": "string", - "default": "dist/panels/default/index.js", - "description": "入口函数 / Entry function (required)" - }, - "icon": { - "type": "string", - "description": "面板图标存放相对目录 / Relative directory for panel icon storage" - }, - "type": { - "type": "string", - "enum": ["dockable", "simple"], - "default": "dockable", - "description": "面板类型(dockable | simple) / Panel type (dockable | simple)" - }, - "flags": { - "type": "object", - "properties": { - "resizable": { - "type": "boolean", - "default": true, - "description": "是否可以改变大小,默认 true / Whether the size can be changed, default true" - }, - "save": { - "type": "boolean", - "default": true, - "description": "是否需要保存,默认 false / Whether to save, default false" - }, - "alwaysOnTop": { - "type": "boolean", - "default": true, - "description": "是否保持顶层显示,默认 false / Whether to keep the top level display, default false" - } - } - }, - "size": { - "type": "object", - "description": "面板大小信息 / Panel size information", - "properties": { - "min-width": { - "type": "number", - "default": 200, - "description": "面板最小宽度 / Minimum panel width" - }, - "min-height": { - "type": "number", - "default": 200, - "description": "面板最小高度 / Minimum panel height" - }, - "width": { - "type": "number", - "default": 400, - "description": " 面板默认宽度 / Panel Default Width" - }, - "height": { - "type": "number", - "default": 600, - "description": "面板默认高度 / Panel Default Height" - } - } - } - }, - "required": ["title", "main"] - } - } -} diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/contributions/index.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/contributions/index.json deleted file mode 100644 index 185ad85f..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/contributions/index.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "description": "其他扩展插件的扩展配置 / Extended configuration for other extension plugins", - "properties": { - - }, - "required": [] -} diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/index.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/index.json deleted file mode 100644 index 0452158b..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/@types/schema/package/index.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "description": "插件定义文件 / Extension definition file", - "properties": { - "author": { - "type": "string", - "description": "作者 / Author", - "default": "Cocos Creator Developer" - }, - "contributions": { - "$ref": "./contributions/index.json" - }, - "dependencies": { - "type": "object", - "description": "发布时所需的依赖库 / Dependencies required for publishing" - }, - "description": { - "type": "string", - "description": "简要介绍扩展关键特性、用途,支持 i18n / Brief introduction of the key features and uses of the extension, supporting i18n" - }, - "devDependencies": { - "type": "object", - "description": "开发时所需的依赖库 / Dependencies required for development" - }, - "editor": { - "type": "string", - "description": "支持的 Cocos Creator 编辑器版本,支持 semver 格式 / Supported Cocos Creator editor version, supporting semver format" - }, - "main": { - "type": "string", - "description": "入口函数 / Entry function", - "default": "./dist/index.js" - }, - "name": { - "type": "string", - "description": "不能以 _ 或 . 开头、不能含有大写字母,也不能含有 URL 的非法字符例如 .、' 和 ,。 / Cannot start with _ or., cannot contain uppercase letters, and cannot contain URL illegal characters such as.,'and,", - "default": "Custom Extension" - }, - "package_version": { - "type": "number", - "description": "扩展系统预留版本号 / Extension system reserved version number", - "default": 2 - }, - "panels": { - "$ref": "./base/panels.json" - }, - "scripts": { - "type": "object", - "description": "NPM 脚本 / NPM scripts" - }, - "version": { - "type": "string", - "description": "版本号字符串 / Version number string", - "default": "1.0.0" - } - }, - "required": [ - "author", - "name", - "package_version", - "version" - ] -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/README.md b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/README.md deleted file mode 100644 index a75e1491..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# ECS Framework for Cocos Creator - 开发扩展插件 - -专业的ECS框架开发助手,为Cocos Creator提供完整的实体组件系统(ECS)开发工具链。 - -## 🎯 主要功能 - -### 📦 一键安装管理 -- **自动检测**:实时检测ECS框架安装状态和版本信息 -- **一键安装**:快速安装 `@esengine/ecs-framework` 到当前项目 -- **版本管理**:自动检查更新,支持一键更新到最新版本 -- **智能卸载**:安全卸载框架,保护项目完整性 - -### 🚀 代码生成器 -- **智能生成**:输入功能名称,自动生成对应的组件和系统代码 -- **多种系统类型**:支持EntitySystem、ProcessingSystem、IntervalSystem、PassiveSystem -- **组件配置**:可选择添加属性、注释等定制化选项 -- **组件过滤**:支持生成带组件过滤的高级系统 - -### 🛠️ 项目模板 -- **快速启动**:一键生成完整的ECS项目结构 -- **预设组件**:包含位置、速度、Cocos节点等常用组件 -- **系统示例**:提供移动系统、节点同步系统等实用示例 -- **工厂模式**:包含实体工厂和场景管理器模板 - -### 🔍 调试工具 -- **实时监控**:查看ECS框架运行状态和性能数据 -- **组件池监控**:实时监控组件对象池使用情况 -- **性能分析**:提供详细的性能统计和优化建议 - -## 📋 面板介绍 - -### 欢迎面板 -- ECS框架安装状态检测 -- 一键安装、更新、卸载操作 -- 项目模板生成 -- 快速访问文档和GitHub - -### 代码生成器 -- 可视化代码生成界面 -- 实时预览生成的代码结构 -- 支持批量生成多个文件 - -### 调试面板 -- 实时性能监控 -- 组件池状态查看 -- 系统运行统计 - -## 🔧 开发环境 - -- **Cocos Creator**: >= 3.8.6 -- **Node.js**: >= 14.0.0 -- **依赖框架**: @esengine/ecs-framework - -## 📥 安装使用 - -1. 将插件复制到项目的 `extensions` 目录 -2. 在Cocos Creator中启用插件 -3. 通过菜单 `面板 -> ECS Framework -> 欢迎面板` 打开主界面 -4. 按照界面提示安装ECS框架并开始开发 - -## 🚀 快速开始 - -1. **安装框架**:在欢迎面板点击"安装 ECS Framework" -2. **创建模板**:点击"创建ECS模板"生成项目结构 -3. **生成代码**:使用代码生成器快速创建组件和系统 -4. **开始开发**:基于生成的模板开始您的ECS游戏开发 - -## 📚 更多资源 - -- **GitHub仓库**:[https://github.com/esengine/ecs-framework](https://github.com/esengine/ecs-framework) -- **完整文档**:包含详细的API文档和教程 -- **技术交流**:加入QQ群获取技术支持和交流 - -## ⭐ 特色优势 - -- **零配置**:开箱即用,无需复杂配置 -- **可视化**:图形化界面,操作简单直观 -- **高效率**:大幅减少重复代码编写 -- **专业性**:基于成熟的ECS框架设计模式 - -让ECS开发变得简单高效,专注于游戏逻辑而非框架配置! diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/base.tsconfig.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/base.tsconfig.json deleted file mode 100644 index 6fd63022..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/base.tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json", - "compilerOptions": { - "target": "ES2017", - "module": "CommonJS", - "moduleResolution": "node", - "inlineSourceMap": true, - "inlineSources": true, - "esModuleInterop": true, - "skipLibCheck": true, - "strict": true, - "experimentalDecorators": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "outDir": "./dist", - "rootDir": "./source", - "types": [ - "node", - "@cocos/creator-types/editor", - ] - } -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/i18n/en.js b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/i18n/en.js deleted file mode 100644 index de152f1f..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/i18n/en.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -module.exports = { - description: "Professional ECS Framework Development Assistant: One-click installation of @esengine/ecs-framework, intelligent code generator for quick creation of components and systems, project template generation, real-time status detection and version management. Provides welcome panel, debug panel, code generator and behavior tree AI component library to make ECS development in Cocos Creator more efficient and convenient.", - - open_panel: "Default Panel", - send_to_panel: "Send message to panel", - - menu: { - panel: "Panel", - develop: "Develop", - create: "Create", - open: "Open" - } -}; \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/i18n/zh.js b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/i18n/zh.js deleted file mode 100644 index 35ead1f5..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/i18n/zh.js +++ /dev/null @@ -1,18 +0,0 @@ -"use strict"; - -module.exports = { - // 插件描述 - description: "专业的ECS框架开发助手:一键安装@esengine/ecs-framework,智能代码生成器快速创建组件和系统,项目模板生成,实时状态检测和版本管理。提供欢迎面板、调试面板、代码生成器和行为树AI组件库,让Cocos Creator的ECS开发更高效便捷。", - - // 面板相关 - open_panel: "默认面板", - send_to_panel: "发送消息给面板", - - // 菜单相关 - menu: { - panel: "面板", - develop: "开发", - create: "创建", - open: "打开" - } -}; \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package-lock.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package-lock.json deleted file mode 100644 index eaa3a068..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package-lock.json +++ /dev/null @@ -1,318 +0,0 @@ -{ - "name": "cocos-ecs-extension", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "cocos-ecs-extension", - "version": "1.0.0", - "hasInstallScript": true, - "dependencies": { - "fs-extra": "^10.0.0", - "vue": "^3.1.4", - "ws": "^8.14.2" - }, - "devDependencies": { - "@cocos/creator-types": "^3.8.6", - "@types/fs-extra": "^9.0.5", - "@types/node": "^18.17.1", - "@types/ws": "^8.5.10", - "typescript": "^5.8.2" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.0", - "license": "MIT", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@cocos/creator-types": { - "version": "3.8.6", - "resolved": "https://registry.npmjs.org/@cocos/creator-types/-/creator-types-3.8.6.tgz", - "integrity": "sha512-hyZ4aoqqLxoRtKbBLSJM5RgtK3oGOlTEryHDcyH4znq3h9cFk+MSbQC2aJHvK5/bMlJzsZ641/hD77RGSrvo8Q==", - "dev": true - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "license": "MIT" - }, - "node_modules/@types/fs-extra": { - "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "18.19.111", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.111.tgz", - "integrity": "sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@vue/compiler-core": { - "version": "3.3.4", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.21.3", - "@vue/shared": "3.3.4", - "estree-walker": "^2.0.2", - "source-map-js": "^1.0.2" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.3.4", - "license": "MIT", - "dependencies": { - "@vue/compiler-core": "3.3.4", - "@vue/shared": "3.3.4" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.3.4", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.15", - "@vue/compiler-core": "3.3.4", - "@vue/compiler-dom": "3.3.4", - "@vue/compiler-ssr": "3.3.4", - "@vue/reactivity-transform": "3.3.4", - "@vue/shared": "3.3.4", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.0", - "postcss": "^8.1.10", - "source-map-js": "^1.0.2" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.3.4", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.3.4", - "@vue/shared": "3.3.4" - } - }, - "node_modules/@vue/reactivity": { - "version": "3.3.4", - "license": "MIT", - "dependencies": { - "@vue/shared": "3.3.4" - } - }, - "node_modules/@vue/reactivity-transform": { - "version": "3.3.4", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.15", - "@vue/compiler-core": "3.3.4", - "@vue/shared": "3.3.4", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.0" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.3.4", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.3.4", - "@vue/shared": "3.3.4" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.3.4", - "license": "MIT", - "dependencies": { - "@vue/runtime-core": "3.3.4", - "@vue/shared": "3.3.4", - "csstype": "^3.1.1" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.3.4", - "license": "MIT", - "dependencies": { - "@vue/compiler-ssr": "3.3.4", - "@vue/shared": "3.3.4" - }, - "peerDependencies": { - "vue": "3.3.4" - } - }, - "node_modules/@vue/shared": { - "version": "3.3.4", - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.1.2", - "license": "MIT" - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "license": "MIT" - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "license": "ISC" - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/magic-string": { - "version": "0.30.3", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/nanoid": { - "version": "3.3.6", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "license": "ISC" - }, - "node_modules/postcss": { - "version": "8.4.30", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/universalify": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/vue": { - "version": "3.3.4", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.3.4", - "@vue/compiler-sfc": "3.3.4", - "@vue/runtime-dom": "3.3.4", - "@vue/server-renderer": "3.3.4", - "@vue/shared": "3.3.4" - } - }, - "node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - } - } -} diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package.json deleted file mode 100644 index 3767a492..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package.json +++ /dev/null @@ -1,234 +0,0 @@ -{ - "$schema": "./@types/schema/package/index.json", - "package_version": 2, - "name": "cocos-ecs-extension", - "version": "1.0.0", - "author": "esengine", - "editor": ">=3.8.6", - "scripts": { - "preinstall": "node ./scripts/preinstall.js", - "build": "npx tsc" - }, - "description": "i18n:cocos-ecs-extension.description", - "main": "./dist/main.js", - "dependencies": { - "vue": "^3.1.4", - "fs-extra": "^10.0.0", - "ws": "^8.14.2", - "adm-zip": "^0.5.10" - }, - "devDependencies": { - "@cocos/creator-types": "^3.8.6", - "@types/fs-extra": "^9.0.5", - "@types/node": "^18.17.1", - "@types/ws": "^8.5.10", - "@types/adm-zip": "^0.5.0", - "typescript": "^5.8.2" - }, - "panels": { - "default": { - "title": "ECS Framework - 欢迎面板", - "type": "dockable", - "main": "dist/panels/default/index.js", - "size": { - "min-width": 450, - "min-height": 600, - "width": 850, - "height": 800 - } - }, - "debug": { - "title": "ECS Framework - 调试面板", - "type": "dockable", - "main": "dist/panels/debug/index.js", - "size": { - "min-width": 400, - "min-height": 500, - "width": 500, - "height": 600 - } - }, - "generator": { - "title": "ECS Framework - 代码生成器", - "type": "dockable", - "main": "dist/panels/generator/index.js", - "size": { - "min-width": 600, - "min-height": 500, - "width": 900, - "height": 700 - } - }, - "behavior-tree": { - "title": "ECS Framework - 行为树AI组件库", - "type": "dockable", - "main": "dist/panels/behavior-tree/index.js", - "size": { - "min-width": 700, - "min-height": 600, - "width": 1000, - "height": 800 - } - } - }, - "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-debug" - }, - { - "path": "i18n:menu.panel/ECS Framework", - "label": "代码生成器", - "message": "open-generator" - }, - { - "path": "i18n:menu.panel/ECS Framework", - "label": "行为树AI组件库", - "message": "open-behavior-tree" - }, - { - "path": "i18n:menu.develop/ECS Framework", - "label": "ECS 开发工具", - "message": "open-panel" - }, - { - "path": "i18n:menu.panel/ECS Framework", - "label": "检查更新", - "message": "check-plugin-updates" - } - ], - "assets": { - "menu": { - "methods": "./dist/assets-menu.js", - "assetMenu": "onAssetMenu" - } - }, - "messages": { - "open-panel": { - "methods": [ - "openPanel" - ] - }, - "install-ecs-framework": { - "methods": [ - "install-ecs-framework" - ] - }, - "update-ecs-framework": { - "methods": [ - "update-ecs-framework" - ] - }, - "uninstall-ecs-framework": { - "methods": [ - "uninstall-ecs-framework" - ] - }, - "open-documentation": { - "methods": [ - "open-documentation" - ] - }, - "create-ecs-template": { - "methods": [ - "create-ecs-template" - ] - }, - "open-github": { - "methods": [ - "open-github" - ] - }, - "open-qq-group": { - "methods": [ - "open-qq-group" - ] - }, - "open-debug": { - "methods": [ - "open-debug" - ] - }, - "open-generator": { - "methods": [ - "open-generator" - ] - }, - "open-behavior-tree": { - "methods": [ - "open-behavior-tree" - ] - }, - "install-behavior-tree": { - "methods": [ - "install-behavior-tree" - ] - }, - "update-behavior-tree": { - "methods": [ - "update-behavior-tree" - ] - }, - "check-behavior-tree-installed": { - "methods": [ - "check-behavior-tree-installed" - ] - }, - "check-plugin-updates": { - "methods": [ - "check-plugin-updates" - ] - }, - "set-hot-update-config": { - "methods": [ - "set-hot-update-config" - ] - }, - "get-hot-update-config": { - "methods": [ - "get-hot-update-config" - ] - }, - "open-behavior-tree-docs": { - "methods": [ - "open-behavior-tree-docs" - ] - }, - "create-behavior-tree-file": { - "methods": [ - "create-behavior-tree-file" - ] - }, - "load-behavior-tree-file": { - "methods": [ - "load-behavior-tree-file" - ] - }, - "create-behavior-tree-from-editor": { - "methods": [ - "create-behavior-tree-from-editor" - ] - }, - "overwrite-behavior-tree-file": { - "methods": [ - "overwrite-behavior-tree-file" - ] - }, - "behavior-tree-panel-load-file": { - "methods": [ - "behavior-tree.loadBehaviorTreeFile" - ] - } - } - } -} diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/scripts/preinstall.js b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/scripts/preinstall.js deleted file mode 100644 index 3876bba6..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/scripts/preinstall.js +++ /dev/null @@ -1 +0,0 @@ -const readFileSync=require("fs")["readFileSync"],join=require("path")["join"],spawnSync=require("child_process")["spawnSync"],PATH={packageJSON:join(__dirname,"../package.json")};function checkCreatorTypesVersion(e){var o="win32"===process.platform?"npm.cmd":"npm";let n=spawnSync(o,["view","@cocos/creator-types","versions"]).stdout.toString();try{n=JSON.parse(listString)}catch(e){}return!!n.includes(e)}try{const e=readFileSync(PATH.packageJSON,"utf8"),f=JSON.parse(e),g=f.devDependencies["@cocos/creator-types"].replace(/^[^\d]+/,"");checkCreatorTypesVersion(g)||(console.log("Warning:"),console.log(" @en"),console.log(" Version check of @cocos/creator-types failed."),console.log(` The definition of ${g} has not been released yet. Please export the definition to the ./node_modules directory by selecting "Developer -> Export Interface Definition" in the menu of the Creator editor.`),console.log(" The definition of the corresponding version will be released on npm after the editor is officially released."),console.log(" @zh"),console.log(" @cocos/creator-types 版本检查失败。"),console.log(` ${g} 定义还未发布,请先通过 Creator 编辑器菜单 "开发者 -> 导出接口定义",导出定义到 ./node_modules 目录。`),console.log(" 对应版本的定义会在编辑器正式发布后同步发布到 npm 上。"))}catch(e){console.error(e)} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/CodeGenerator.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/CodeGenerator.ts deleted file mode 100644 index a5bf258f..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/CodeGenerator.ts +++ /dev/null @@ -1,274 +0,0 @@ -import { ensureDir, writeFile } from 'fs-extra'; -import { join } from 'path'; - -/** - * 代码生成器工具类 - * 用于生成基础的ECS框架代码 - */ - -interface ComponentOptions { - includeComments: boolean; - addProperties: string[]; -} - -interface SystemOptions { - includeComments: boolean; - systemType: 'EntitySystem' | 'ProcessingSystem' | 'IntervalSystem' | 'PassiveSystem'; - requiredComponents: string[]; -} - -export class CodeGenerator { - - /** - * 生成组件代码 - */ - public async generateComponent( - name: string, - targetDir: string, - options: ComponentOptions = { - includeComments: true, - addProperties: [] - } - ): Promise { - const className = `${name}Component`; - const fileName = `${className}.ts`; - const filePath = join(targetDir, fileName); - - await ensureDir(targetDir); - - const comments = options.includeComments ? this.generateComponentComments(className) : ''; - const properties = this.generateComponentProperties(options.addProperties); - - const content = `import { Component } from '@esengine/ecs-framework'; - -${comments} -export class ${className} extends Component { -${properties} - - constructor() { - super(); - } - - /** - * 重置组件状态 - */ - public reset(): void { - // 重置组件属性到默认值 -${this.generateResetCode(options.addProperties)} - } -} -`; - - await writeFile(filePath, content, 'utf-8'); - } - - /** - * 生成系统代码 - */ - public async generateSystem( - name: string, - targetDir: string, - options: SystemOptions = { - includeComments: true, - systemType: 'EntitySystem', - requiredComponents: [] - } - ): Promise { - const className = `${name}System`; - const fileName = `${className}.ts`; - const filePath = join(targetDir, fileName); - - await ensureDir(targetDir); - - const comments = options.includeComments ? this.generateSystemComments(className, options.systemType) : ''; - const imports = this.getSystemImports(options.systemType, options.requiredComponents); - const matcherSetup = options.requiredComponents.length > 0 ? - `Matcher.empty().all(${options.requiredComponents.join(', ')})` : - `Matcher.empty()`; - - const processMethod = this.generateProcessMethod(options.systemType, options.requiredComponents, className); - - const content = `${imports} - -${comments} -export class ${className} extends ${options.systemType} { - - constructor() { - super(${matcherSetup}${options.systemType === 'IntervalSystem' ? ', 1000 / 60' : ''})${options.systemType === 'IntervalSystem' ? '; // 60fps' : ';'} - } - -${processMethod} - - /** - * 系统开始时调用 - */ - public begin(): void { - super.begin(); - // 添加系统初始化逻辑 - } - - /** - * 系统结束时调用 - */ - public end(): void { - // 添加系统清理逻辑 - super.end(); - } -} -`; - - await writeFile(filePath, content, 'utf-8'); - } - - // ============ 辅助方法 ============ - - private generateComponentComments(className: string): string { - return `/** - * ${className} - * - * 组件描述 - * - * @example - * \`\`\`typescript - * const entity = scene.createEntity("Example"); - * const component = entity.addComponent(new ${className}()); - * \`\`\` - */`; - } - - private generateSystemComments(className: string, systemType: string): string { - const descriptions = { - 'EntitySystem': '处理拥有特定组件的实体', - 'ProcessingSystem': '执行全局游戏逻辑', - 'IntervalSystem': '按时间间隔处理实体', - 'PassiveSystem': '被动响应事件或手动调用' - }; - - return `/** - * ${className} - * - * ${descriptions[systemType as keyof typeof descriptions] || '处理游戏逻辑'} - * - * @example - * \`\`\`typescript - * const system = new ${className}(); - * scene.addEntityProcessor(system); - * \`\`\` - */`; - } - - private generateComponentProperties(properties: string[]): string { - if (properties.length === 0) { - return ' // 添加组件属性\n // public value: number = 0;'; - } - - return properties.map(prop => { - const [name, type = 'number', defaultValue = '0'] = prop.split(':'); - return ` public ${name}: ${type} = ${defaultValue};`; - }).join('\n'); - } - - private generateResetCode(properties: string[]): string { - if (properties.length === 0) { - return ' // this.value = 0;'; - } - - return properties.map(prop => { - const [name, , defaultValue = '0'] = prop.split(':'); - return ` this.${name} = ${defaultValue};`; - }).join('\n'); - } - - private getSystemImports(systemType: string, requiredComponents: string[]): string { - const imports = [systemType, 'Entity']; - - // 所有系统类型都可能需要Matcher来过滤组件 - if (requiredComponents.length > 0 || systemType === 'EntitySystem' || systemType === 'IntervalSystem' || systemType === 'PassiveSystem') { - imports.push('Matcher'); - } - - return `import { ${imports.join(', ')} } from '@esengine/ecs-framework';${requiredComponents.length > 0 ? '\n' + this.generateComponentImports(requiredComponents) : ''}`; - } - - private generateComponentImports(components: string[]): string { - return components.map(comp => `import { ${comp} } from '../components/${comp}';`).join('\n'); - } - - private generateProcessMethod(systemType: string, requiredComponents: string[], className: string): string { - switch (systemType) { - case 'EntitySystem': - return ` protected process(entities: Entity[]): void { - for (const entity of entities) { - this.processEntity(entity); - } - } - - private processEntity(entity: Entity): void { -${this.generateProcessingLogic(requiredComponents)} - }`; - - case 'ProcessingSystem': - return ` public processSystem(): void { - // 添加全局系统逻辑 - console.log('${className} processSystem called'); - }`; - - case 'IntervalSystem': - return ` protected process(entities: Entity[]): void { - const intervalDelta = this.getIntervalDelta(); - console.log(\`${className} executing with interval delta: \${intervalDelta}\`); - - for (const entity of entities) { - this.processEntity(entity, intervalDelta); - } - } - - private processEntity(entity: Entity, delta: number): void { -${this.generateProcessingLogic(requiredComponents)} - }`; - - case 'PassiveSystem': - return ` /** - * 被动系统不主动处理实体 - * 通常用于响应事件或被其他系统调用 - */ - public processEntity(entity: Entity): void { -${this.generateProcessingLogic(requiredComponents)} - } - - /** - * 手动触发处理 - */ - public trigger(): void { - for (const entity of this.entities) { - this.processEntity(entity); - } - }`; - - default: - return ''; - } - } - - private generateProcessingLogic(requiredComponents: string[]): string { - if (requiredComponents.length === 0) { - return ' // 添加处理逻辑'; - } - - const componentVars = requiredComponents.map((comp: string) => { - const varName = comp.replace('Component', '').toLowerCase(); - return ` const ${varName} = entity.getComponent(${comp});`; - }).join('\n'); - - const nullCheck = requiredComponents.map((comp: string) => { - const varName = comp.replace('Component', '').toLowerCase(); - return varName; - }).join(' && '); - - return `${componentVars} - - if (${nullCheck}) { - // 添加处理逻辑 - }`; - } -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/TemplateGenerator.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/TemplateGenerator.ts deleted file mode 100644 index a86aae8d..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/TemplateGenerator.ts +++ /dev/null @@ -1,436 +0,0 @@ -import * as path from 'path'; -import * as fs from 'fs'; - -/** - * ECS启动模板生成器 - * 生成最基础的ECS框架启动模板,不包含业务逻辑 - */ -export class TemplateGenerator { - private projectPath: string; - private ecsDir: string; - - constructor(projectPath: string) { - this.projectPath = projectPath; - this.ecsDir = path.join(projectPath, 'assets', 'scripts', 'ecs'); - } - - /** - * 检查是否已经存在ECS模板 - */ - public checkTemplateExists(): boolean { - return fs.existsSync(this.ecsDir); - } - - /** - * 获取已存在的文件列表 - */ - public getExistingFiles(): string[] { - if (!this.checkTemplateExists()) return []; - - const files: string[] = []; - this.scanDirectory(this.ecsDir, '', files); - return files; - } - - 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 { - files.push(relativeFilePath); - } - } - } - - /** - * 删除现有的ECS模板 - */ - public removeExistingTemplate(): void { - if (fs.existsSync(this.ecsDir)) { - fs.rmSync(this.ecsDir, { recursive: true, force: true }); - console.log('Removed existing ECS template'); - } - } - - /** - * 创建ECS启动模板 - */ - public createTemplate(): void { - // 创建目录结构 - this.createDirectories(); - - // 创建ECS启动管理器 - this.createECSManager(); - - // 创建基础游戏场景 - this.createBaseGameScene(); - - // 创建README文档 - this.createReadme(); - - console.log('ECS启动模板创建成功'); - } - - /** - * 创建目录结构 - */ - private createDirectories(): void { - const dirs = [ - this.ecsDir, - path.join(this.ecsDir, 'scenes'), - path.join(this.ecsDir, 'components'), - path.join(this.ecsDir, 'systems') - ]; - - dirs.forEach(dir => { - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - console.log(`Created directory: ${path.relative(this.projectPath, dir)}`); - } - }); - } - - /** - * 创建ECS管理器 - */ - private createECSManager(): void { - this.writeFile(path.join(this.ecsDir, 'ECSManager.ts'), `import { Core } from '@esengine/ecs-framework'; -import { Component, _decorator } from 'cc'; -import { GameScene } from './scenes/GameScene'; - -const { ccclass, property } = _decorator; - -/** - * ECS管理器 - Cocos Creator组件 - * 将此组件添加到场景中的任意节点上即可启动ECS框架 - * - * 使用说明: - * 1. 在Cocos Creator场景中创建一个空节点 - * 2. 将此ECSManager组件添加到该节点 - * 3. 运行场景即可自动启动ECS框架 - */ -@ccclass('ECSManager') -export class ECSManager extends Component { - - @property({ - tooltip: '是否启用调试模式(建议开发阶段开启)' - }) - public debugMode: boolean = true; - - private isInitialized: boolean = false; - - /** - * 组件启动时初始化ECS - */ - start() { - this.initializeECS(); - } - - /** - * 初始化ECS框架 - */ - private initializeECS(): void { - if (this.isInitialized) return; - - console.log('🎮 正在初始化ECS框架...'); - - try { - // 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(); - - // 3. 设置为当前场景(会自动调用scene.begin()) - Core.scene = gameScene; - - this.isInitialized = true; - console.log('✅ ECS框架初始化成功!'); - console.log('📖 请查看 assets/scripts/ecs/README.md 了解如何添加组件和系统'); - - } catch (error) { - console.error('❌ ECS框架初始化失败:', error); - } - } - - /** - * 每帧更新ECS框架 - */ - update(deltaTime: number) { - if (this.isInitialized) { - // 更新ECS核心系统 - Core.update(deltaTime); - } - } - - /** - * 组件销毁时清理ECS - */ - onDestroy() { - if (this.isInitialized) { - console.log('🧹 清理ECS框架...'); - // ECS框架会自动处理场景清理 - this.isInitialized = false; - } - } -} -`); - } - - /** - * 创建基础游戏场景 - */ - private createBaseGameScene(): void { - this.writeFile(path.join(this.ecsDir, 'scenes', 'GameScene.ts'), `import { Scene } from '@esengine/ecs-framework'; - -/** - * 游戏场景 - * - * 这是您的主游戏场景。在这里可以: - * - 添加游戏系统 - * - 创建初始实体 - * - 设置场景参数 - */ -export class GameScene extends Scene { - - /** - * 场景初始化 - * 在场景创建时调用,用于设置基础配置 - */ - public initialize(): void { - super.initialize(); - - // 设置场景名称 - this.name = "MainGameScene"; - - console.log('🎯 游戏场景已创建'); - - // TODO: 在这里添加您的游戏系统 - // 例如:this.addEntityProcessor(new MovementSystem()); - - // TODO: 在这里创建初始实体 - // 例如:this.createEntity("Player"); - } - - /** - * 场景开始运行 - * 在场景开始时调用,用于执行启动逻辑 - */ - public onStart(): void { - super.onStart(); - - console.log('🚀 游戏场景已启动'); - - // TODO: 在这里添加场景启动逻辑 - // 例如:创建UI、播放音乐、初始化游戏状态等 - } - - /** - * 场景卸载 - * 在场景结束时调用,用于清理资源 - */ - public unload(): void { - console.log('🛑 游戏场景已结束'); - - // TODO: 在这里添加清理逻辑 - // 例如:清理缓存、释放资源等 - - super.unload(); - } -} -`); - } - - /** - * 创建README文档 - */ - private createReadme(): void { - this.writeFile(path.join(this.ecsDir, 'README.md'), `# ECS框架启动模板 - -欢迎使用ECS框架!这是一个最基础的启动模板,帮助您快速开始ECS项目开发。 - -## 📁 项目结构 - -\`\`\` -ecs/ -├── components/ # 组件目录(请在此添加您的组件) -├── systems/ # 系统目录(请在此添加您的系统) -├── scenes/ # 场景目录 -│ └── GameScene.ts # 主游戏场景 -├── ECSManager.ts # ECS管理器组件 -└── README.md # 本文档 -\`\`\` - -## 🚀 快速开始 - -### 1. 启动ECS框架 - -ECS框架已经配置完成!您只需要: - -1. 在Cocos Creator中打开您的场景 -2. 创建一个空节点(例如命名为"ECSManager") -3. 将 \`ECSManager\` 组件添加到该节点 -4. 运行场景,ECS框架将自动启动 - -### 2. 查看控制台输出 - -如果一切正常,您将在控制台看到: - -\`\`\` -🎮 正在初始化ECS框架... -🔧 ECS调试模式已启用,可在Cocos Creator扩展面板中查看调试信息 -🎯 游戏场景已创建 -✅ ECS框架初始化成功! -🚀 游戏场景已启动 -\`\`\` - -### 3. 使用调试面板 - -ECS框架已启用调试功能,您可以: - -1. 在Cocos Creator编辑器菜单中选择 "扩展" → "ECS Framework" → "调试面板" -2. 调试面板将显示实时的ECS运行状态: - - 实体数量和状态 - - 系统执行信息 - - 性能监控数据 - - 组件统计信息 - -**注意**:调试功能会消耗一定性能,正式发布时建议关闭调试模式。 - -## 📚 下一步开发 - -### 创建您的第一个组件 - -在 \`components/\` 目录下创建组件: - -\`\`\`typescript -// components/PositionComponent.ts -import { Component } from '@esengine/ecs-framework'; -import { Vec3 } from 'cc'; - -export class PositionComponent extends Component { - public position: Vec3 = new Vec3(); - - constructor(x: number = 0, y: number = 0, z: number = 0) { - super(); - this.position.set(x, y, z); - } -} -\`\`\` - -### 创建您的第一个系统 - -在 \`systems/\` 目录下创建系统: - -\`\`\`typescript -// systems/MovementSystem.ts -import { EntitySystem, Entity, Matcher } from '@esengine/ecs-framework'; -import { PositionComponent } from '../components/PositionComponent'; - -export class MovementSystem extends EntitySystem { - constructor() { - super(Matcher.empty().all(PositionComponent)); - } - - protected process(entities: Entity[]): void { - for (const entity of entities) { - const position = entity.getComponent(PositionComponent); - if (position) { - // TODO: 在这里编写移动逻辑 - console.log(\`实体 \${entity.name} 位置: \${position.position}\`); - } - } - } -} -\`\`\` - -### 在场景中注册系统 - -在 \`scenes/GameScene.ts\` 的 \`initialize()\` 方法中添加: - -\`\`\`typescript -import { MovementSystem } from '../systems/MovementSystem'; - -public initialize(): void { - super.initialize(); - this.name = "MainGameScene"; - - // 添加系统 - this.addEntityProcessor(new MovementSystem()); - - // 创建测试实体 - const testEntity = this.createEntity("TestEntity"); - testEntity.addComponent(new PositionComponent(0, 0, 0)); -} -\`\`\` - -## 🔗 学习资源 - -- [ECS框架完整文档](https://github.com/esengine/ecs-framework) -- [ECS概念详解](https://github.com/esengine/ecs-framework/blob/master/docs/concepts-explained.md) -- [新手教程](https://github.com/esengine/ecs-framework/blob/master/docs/beginner-tutorials.md) -- [组件设计指南](https://github.com/esengine/ecs-framework/blob/master/docs/component-design-guide.md) -- [系统开发指南](https://github.com/esengine/ecs-framework/blob/master/docs/system-guide.md) - -## 💡 开发提示 - -1. **组件只存储数据**:避免在组件中编写复杂逻辑 -2. **系统处理逻辑**:所有业务逻辑应该在系统中实现 -3. **使用Matcher过滤实体**:系统通过Matcher指定需要处理的实体类型 -4. **性能优化**:大量实体时考虑使用位掩码查询和组件索引 - -## ❓ 常见问题 - -### Q: 如何创建实体? -A: 在场景中使用 \`this.createEntity("实体名称")\` - -### Q: 如何给实体添加组件? -A: 使用 \`entity.addComponent(new YourComponent())\` - -### Q: 如何获取实体的组件? -A: 使用 \`entity.getComponent(YourComponent)\` - -### Q: 如何删除实体? -A: 使用 \`entity.destroy()\` 或 \`this.destroyEntity(entity)\` - ---- - -🎮 **开始您的ECS开发之旅吧!** - -如有问题,请查阅官方文档或提交Issue。 -`); - } - - /** - * 写入文件 - */ - private writeFile(filePath: string, content: string): void { - fs.writeFileSync(filePath, content, 'utf8'); - console.log(`Created file: ${path.relative(this.projectPath, filePath)}`); - } -} diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/assets-menu.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/assets-menu.ts deleted file mode 100644 index bbbe2517..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/assets-menu.ts +++ /dev/null @@ -1,46 +0,0 @@ -export function onAssetMenu(assetInfo: any) { - console.log('[AssetMenu] onAssetMenu 被调用,资源信息:', assetInfo); - console.log('[AssetMenu] assetInfo 完整结构:', JSON.stringify(assetInfo, null, 2)); - - const menuItems = []; - - // 检查是否为行为树文件 - const isTargetFile = (assetInfo && assetInfo.name && assetInfo.name.endsWith('.bt.json')) || - (assetInfo && assetInfo.file && assetInfo.file.endsWith('.bt.json')); - - if (isTargetFile) { - console.log('[AssetMenu] 发现 .bt.json 文件,添加菜单项'); - menuItems.push({ - label: '用行为树编辑器打开', - click() { - console.log('[AssetMenu] 菜单项被点击,文件信息:', assetInfo); - - // 直接调用主进程的方法,不需要复杂的序列化 - try { - Editor.Message.send('cocos-ecs-extension', 'load-behavior-tree-file', assetInfo); - console.log('[AssetMenu] 消息发送成功'); - } catch (error) { - console.error('[AssetMenu] 消息发送失败:', error); - } - } - }); - } - - // 在目录中添加创建选项 - if (assetInfo && assetInfo.isDirectory) { - menuItems.push({ - label: '创建行为树文件', - click() { - console.log('[AssetMenu] 在目录中创建行为树文件:', assetInfo); - try { - Editor.Message.send('cocos-ecs-extension', 'create-behavior-tree-file'); - } catch (error) { - console.error('[AssetMenu] 创建消息发送失败:', error); - } - } - }); - } - - console.log('[AssetMenu] 返回菜单项数量:', menuItems.length); - return menuItems; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/BehaviorTreeHandler.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/BehaviorTreeHandler.ts deleted file mode 100644 index 9399be61..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/BehaviorTreeHandler.ts +++ /dev/null @@ -1,351 +0,0 @@ -import { exec } from 'child_process'; -import * as path from 'path'; -import * as fs from 'fs'; -import * as fsExtra from 'fs-extra'; - -/** - * 行为树相关的处理器 - */ -export class BehaviorTreeHandler { - /** - * 安装行为树AI系统 - */ - static async install(): Promise { - const projectPath = Editor.Project.path; - const command = 'npm install @esengine/ai'; - - return new Promise((resolve) => { - exec(command, { cwd: projectPath }, (error, stdout, stderr) => { - if (error) { - console.error('AI系统安装失败:', error.message); - resolve(false); - } else { - // 验证安装是否成功 - const nodeModulesPath = path.join(projectPath, 'node_modules', '@esengine', 'ai'); - const installSuccess = fs.existsSync(nodeModulesPath); - - if (!installSuccess) { - console.warn('安装完成但未找到AI系统目录,请检查网络连接'); - } - - resolve(installSuccess); - } - }); - }); - } - - /** - * 更新行为树AI系统 - */ - static async update(): Promise { - const projectPath = Editor.Project.path; - const command = 'npm update @esengine/ai'; - - return new Promise((resolve) => { - exec(command, { cwd: projectPath }, (error, stdout, stderr) => { - if (error) { - console.error('AI系统更新失败:', error.message); - resolve(false); - } else { - // 验证更新是否成功 - const nodeModulesPath = path.join(projectPath, 'node_modules', '@esengine', 'ai'); - const updateSuccess = fs.existsSync(nodeModulesPath); - - if (!updateSuccess) { - console.warn('更新完成但未找到AI系统目录'); - } - - resolve(updateSuccess); - } - }); - }); - } - - /** - * 检查行为树AI是否已安装 - */ - static checkInstalled(): boolean { - try { - const projectPath = Editor.Project.path; - const packageJsonPath = path.join(projectPath, 'package.json'); - - if (!fs.existsSync(packageJsonPath)) { - return false; - } - - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); - const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies }; - - return '@esengine/ai' in dependencies; - } catch (error) { - console.error('检查AI系统安装状态失败:', error); - return false; - } - } - - /** - * 打开行为树文档 - */ - static openDocumentation(): void { - const url = 'https://github.com/esengine/ai/blob/master/README.md'; - - try { - const { shell } = require('electron'); - shell.openExternal(url); - console.log('Behavior Tree documentation opened successfully'); - } catch (error) { - console.error('Failed to open Behavior Tree documentation:', error); - Editor.Dialog.info('打开行为树文档', { - detail: `请手动访问以下链接查看文档:\n\n${url}`, - }); - } - } - - /** - * 创建行为树文件 - */ - static async createFile(assetInfo?: any): Promise { - try { - const projectPath = Editor.Project.path; - const assetsPath = path.join(projectPath, 'assets'); - - // 生成唯一文件名 - let fileName = 'NewBehaviorTree'; - let counter = 1; - let filePath = path.join(assetsPath, `${fileName}.bt.json`); - - while (fs.existsSync(filePath)) { - fileName = `NewBehaviorTree_${counter}`; - filePath = path.join(assetsPath, `${fileName}.bt.json`); - counter++; - } - - // 创建默认的行为树配置 - const defaultConfig = { - version: "1.0.0", - type: "behavior-tree", - metadata: { - createdAt: new Date().toISOString(), - nodeCount: 1 - }, - tree: { - id: "root", - type: "sequence", - namespace: "behaviourTree/composites", - properties: {}, - children: [] - } - }; - - // 写入文件 - await fsExtra.writeFile(filePath, JSON.stringify(defaultConfig, null, 2)); - - // 刷新资源管理器 - 使用正确的资源路径 - const relativeAssetPath = path.relative(projectPath, filePath).replace(/\\/g, '/'); - const dbAssetPath = 'db://' + relativeAssetPath; - await Editor.Message.request('asset-db', 'refresh-asset', dbAssetPath); - - console.log(`Behavior tree file created: ${filePath}`); - - Editor.Dialog.info('创建成功', { - detail: `行为树文件 "${fileName}.bt.json" 已创建完成!\n\n文件位置:assets/${fileName}.bt.json\n\n您可以右键点击文件选择"用行为树编辑器打开"来编辑它。`, - }); - - } catch (error) { - console.error('Failed to create behavior tree file:', error); - Editor.Dialog.error('创建失败', { - detail: `创建行为树文件失败:\n\n${error instanceof Error ? error.message : String(error)}`, - }); - } - } - - /** - * 打开行为树文件 - */ - static async openFile(assetInfo: any): Promise { - try { - if (!assetInfo || !assetInfo.file) { - throw new Error('无效的文件信息'); - } - - const filePath = assetInfo.file; - const fileData = await this.loadFileData(filePath); - await this.openPanel(); - await this.sendDataToPanel(fileData); - - } catch (error) { - Editor.Dialog.error('打开失败', { - detail: `打开行为树文件失败:\n\n${error instanceof Error ? error.message : String(error)}` - }); - } - } - - /** - * 读取并解析文件数据 - */ - private static async loadFileData(filePath: string): Promise { - try { - let assetPath = filePath; - - if (path.isAbsolute(filePath)) { - const projectPath = Editor.Project.path; - if (filePath.startsWith(projectPath)) { - assetPath = path.relative(projectPath, filePath); - assetPath = assetPath.replace(/\\/g, '/'); - } - } - - if (!assetPath.startsWith('db://')) { - assetPath = 'db://' + assetPath; - } - - try { - const assetInfo = await Editor.Message.request('asset-db', 'query-asset-info', assetPath); - - if (assetInfo && assetInfo.source) { - const content = await fsExtra.readFile(assetInfo.source, 'utf8'); - let fileContent: any; - - try { - fileContent = JSON.parse(content); - } catch (parseError) { - fileContent = { - version: "1.0.0", - type: "behavior-tree", - rawContent: content - }; - } - - const fileData = { - ...fileContent, - _fileInfo: { - fileName: path.basename(assetInfo.source, path.extname(assetInfo.source)), - filePath: assetInfo.source, - assetPath: assetPath - } - }; - - return fileData; - } - } catch (assetError) { - // 资源系统读取失败,尝试直接文件读取 - } - - const actualFilePath = path.isAbsolute(filePath) ? filePath : path.join(Editor.Project.path, filePath); - - if (!fs.existsSync(actualFilePath)) { - throw new Error(`文件不存在: ${actualFilePath}`); - } - - const content = await fsExtra.readFile(actualFilePath, 'utf8'); - let fileContent: any; - - try { - fileContent = JSON.parse(content); - } catch (parseError) { - fileContent = { - version: "1.0.0", - type: "behavior-tree", - rawContent: content - }; - } - - const fileData = { - ...fileContent, - _fileInfo: { - fileName: path.basename(actualFilePath, path.extname(actualFilePath)), - filePath: actualFilePath - } - }; - - return fileData; - - } catch (error) { - throw new Error(`文件读取失败: ${error instanceof Error ? error.message : String(error)}`); - } - } - - /** - * 打开行为树面板 - */ - private static async openPanel(): Promise { - await Editor.Panel.open('cocos-ecs-extension.behavior-tree'); - await new Promise(resolve => setTimeout(resolve, 300)); - } - - /** - * 发送数据到面板 - */ - private static async sendDataToPanel(fileData: any): Promise { - try { - const result = await Editor.Message.request('cocos-ecs-extension.behavior-tree', 'loadBehaviorTreeFile', fileData); - } catch (error) { - setTimeout(() => { - try { - Editor.Message.send('cocos-ecs-extension.behavior-tree', 'loadBehaviorTreeFile', fileData); - } catch (delayError) { - // 静默失败 - } - }, 100); - } - } - - /** - * 从编辑器创建行为树文件 - */ - static async createFromEditor(data: { fileName: string, content: string }): Promise { - try { - const projectPath = Editor.Project.path; - const assetsPath = path.join(projectPath, 'assets'); - - let fileName = data.fileName; - let counter = 1; - let filePath = path.join(assetsPath, `${fileName}.bt.json`); - - while (fs.existsSync(filePath)) { - fileName = `${data.fileName}_${counter}`; - filePath = path.join(assetsPath, `${fileName}.bt.json`); - counter++; - } - - await fsExtra.writeFile(filePath, data.content); - - const relativeAssetPath = path.relative(projectPath, filePath).replace(/\\/g, '/'); - const dbAssetPath = 'db://' + relativeAssetPath; - await Editor.Message.request('asset-db', 'refresh-asset', dbAssetPath); - - Editor.Dialog.info('保存成功', { - detail: `行为树文件 "${fileName}.bt.json" 已保存到 assets 目录中!`, - }); - - } catch (error) { - Editor.Dialog.error('保存失败', { - detail: `保存行为树文件失败:\n\n${error instanceof Error ? error.message : String(error)}`, - }); - } - } - - /** - * 覆盖现有行为树文件 - */ - static async overwriteFile(data: { filePath: string, content: string }): Promise { - try { - await fsExtra.writeFile(data.filePath, data.content); - - const projectPath = Editor.Project.path; - const relativeAssetPath = path.relative(projectPath, data.filePath).replace(/\\/g, '/'); - const dbAssetPath = 'db://' + relativeAssetPath; - await Editor.Message.request('asset-db', 'refresh-asset', dbAssetPath); - - const fileName = path.basename(data.filePath, path.extname(data.filePath)); - Editor.Dialog.info('覆盖成功', { - detail: `行为树文件 "${fileName}.bt.json" 已更新!`, - }); - - } catch (error) { - Editor.Dialog.error('覆盖失败', { - detail: `覆盖行为树文件失败:\n\n${error instanceof Error ? error.message : String(error)}`, - }); - } - } -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/EcsFrameworkHandler.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/EcsFrameworkHandler.ts deleted file mode 100644 index b7b313fc..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/EcsFrameworkHandler.ts +++ /dev/null @@ -1,234 +0,0 @@ -import { exec } from 'child_process'; -import * as path from 'path'; -import * as fs from 'fs'; -import { TemplateGenerator } from '../TemplateGenerator'; - -/** - * ECS框架相关的处理器 - */ -export class EcsFrameworkHandler { - /** - * 安装ECS Framework - */ - static async install(): Promise { - const projectPath = Editor.Project.path; - const command = 'npm install @esengine/ecs-framework'; - - console.log(`Installing ECS Framework to project: ${projectPath}`); - - return new Promise((resolve, reject) => { - exec(command, { cwd: projectPath }, (error, stdout, stderr) => { - console.log('Install stdout:', stdout); - if (stderr) console.log('Install stderr:', stderr); - - if (error) { - console.error('Installation failed:', error); - reject(error); - } else { - console.log('Installation completed successfully'); - - // 验证安装是否成功 - const nodeModulesPath = path.join(projectPath, 'node_modules', '@esengine', 'ecs-framework'); - const installSuccess = fs.existsSync(nodeModulesPath); - - if (installSuccess) { - console.log('ECS Framework installed successfully'); - resolve(); - } else { - console.warn('ECS Framework directory not found after install'); - reject(new Error('安装验证失败')); - } - } - }); - }); - } - - /** - * 更新ECS Framework - */ - static async update(targetVersion?: string): Promise { - const projectPath = Editor.Project.path; - const version = targetVersion ? `@${targetVersion}` : '@latest'; - const command = `npm install @esengine/ecs-framework${version}`; - - console.log(`Updating ECS Framework to ${version} in project: ${projectPath}`); - - return new Promise((resolve, reject) => { - exec(command, { cwd: projectPath }, (error, stdout, stderr) => { - console.log('Update stdout:', stdout); - if (stderr) console.log('Update stderr:', stderr); - - if (error) { - console.error('Update failed:', error); - reject(error); - } else { - console.log('Update completed successfully'); - - // 验证更新是否成功 - const nodeModulesPath = path.join(projectPath, 'node_modules', '@esengine', 'ecs-framework'); - const updateSuccess = fs.existsSync(nodeModulesPath); - - if (updateSuccess) { - console.log(`ECS Framework updated successfully to ${version}`); - resolve(); - } else { - console.warn('ECS Framework directory not found after update'); - reject(new Error('更新验证失败')); - } - } - }); - }); - } - - /** - * 卸载ECS Framework - */ - static async uninstall(): Promise { - const projectPath = Editor.Project.path; - const command = 'npm uninstall @esengine/ecs-framework'; - - console.log(`Uninstalling ECS Framework from project: ${projectPath}`); - - return new Promise((resolve, reject) => { - exec(command, { cwd: projectPath }, (error, stdout, stderr) => { - console.log('Uninstall stdout:', stdout); - if (stderr) console.log('Uninstall stderr:', stderr); - - if (error) { - console.error('Uninstall failed:', error); - reject(error); - } else { - console.log('Uninstall completed successfully'); - - // 检查是否真的卸载了 - const nodeModulesPath = path.join(projectPath, 'node_modules', '@esengine', 'ecs-framework'); - const stillExists = fs.existsSync(nodeModulesPath); - - if (stillExists) { - console.warn('ECS Framework directory still exists after uninstall'); - reject(new Error('卸载验证失败')); - } else { - console.log('ECS Framework uninstalled successfully'); - resolve(); - } - } - }); - }); - } - - /** - * 打开文档 - */ - static openDocumentation(): void { - const url = 'https://github.com/esengine/ecs-framework/blob/master/README.md'; - - try { - // 使用Electron的shell模块打开外部链接 - const { shell } = require('electron'); - shell.openExternal(url); - console.log('Documentation link opened successfully'); - } catch (error) { - console.error('Failed to open documentation:', error); - Editor.Dialog.info('打开文档', { - detail: `请手动访问以下链接查看文档:\n\n${url}`, - }); - } - } - - /** - * 创建ECS模板 - */ - static createTemplate(): void { - const projectPath = Editor.Project.path; - console.log(`Creating ECS template in project: ${projectPath}`); - - try { - const templateGenerator = new TemplateGenerator(projectPath); - - // 检查是否已存在模板 - if (templateGenerator.checkTemplateExists()) { - const existingFiles = templateGenerator.getExistingFiles(); - const fileList = existingFiles.length > 0 ? existingFiles.join('\n• ') : '未检测到具体文件'; - - Editor.Dialog.warn('模板已存在', { - detail: `检测到已存在ECS模板,包含以下文件:\n\n• ${fileList}\n\n是否要覆盖现有模板?`, - buttons: ['覆盖', '取消'], - }).then((result: any) => { - if (result.response === 0) { - // 用户选择覆盖 - console.log('User chose to overwrite existing template'); - templateGenerator.removeExistingTemplate(); - templateGenerator.createTemplate(); - this.showTemplateCreatedDialog(); - } else { - console.log('User cancelled template creation'); - } - }); - return; - } - - // 创建新模板 - templateGenerator.createTemplate(); - console.log('ECS template created successfully'); - this.showTemplateCreatedDialog(); - - } catch (error) { - console.error('Failed to create ECS template:', error); - const errorMessage = error instanceof Error ? error.message : String(error); - Editor.Dialog.error('模板创建失败', { - detail: `创建ECS模板时发生错误:\n\n${errorMessage}\n\n请检查项目权限和目录结构。`, - }); - } - } - - /** - * 显示模板创建成功的对话框 - */ - private static showTemplateCreatedDialog(): void { - Editor.Dialog.info('模板创建成功', { - detail: '✅ ECS项目模板已创建完成!\n\n已为您的Cocos Creator项目生成了完整的ECS架构模板,包括:\n\n' + - '• 位置、速度、Cocos节点组件\n' + - '• 移动系统和节点同步系统\n' + - '• 实体工厂和场景管理器\n' + - '• ECS管理器组件(可直接添加到节点)\n' + - '• 完整的使用文档\n\n' + - '请刷新资源管理器查看新创建的文件。', - }); - } - - /** - * 打开GitHub仓库 - */ - static openGitHub(): void { - const url = 'https://github.com/esengine/ecs-framework'; - - try { - const { shell } = require('electron'); - shell.openExternal(url); - console.log('GitHub repository opened successfully'); - } catch (error) { - console.error('Failed to open GitHub repository:', error); - Editor.Dialog.info('打开GitHub', { - detail: `请手动访问以下链接:\n\n${url}`, - }); - } - } - - /** - * 打开QQ群 - */ - static openQQGroup(): void { - const url = 'https://qm.qq.com/cgi-bin/qm/qr?k=your-qq-group-key'; - - try { - const { shell } = require('electron'); - shell.openExternal(url); - console.log('QQ group opened successfully'); - } catch (error) { - console.error('Failed to open QQ group:', error); - Editor.Dialog.info('QQ群', { - detail: '请手动搜索QQ群号或访问相关链接加入讨论群。', - }); - } - } -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/HotUpdateHandler.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/HotUpdateHandler.ts deleted file mode 100644 index 8bd571a5..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/HotUpdateHandler.ts +++ /dev/null @@ -1,571 +0,0 @@ -import * as fs from 'fs-extra'; -import * as path from 'path'; -import * as crypto from 'crypto'; -import { exec } from 'child_process'; - -/** - * 热更新配置接口 - */ -interface HotUpdateConfig { - serverUrl: string; - currentVersion: string; - updateChannel: 'stable' | 'beta' | 'dev'; - autoCheck: boolean; - checkInterval: number; // 分钟 -} - -/** - * 版本信息接口 - */ -interface VersionInfo { - version: string; - releaseDate: string; - description: string; - downloadUrl: string; - fileSize: number; - checksum: string; - mandatory: boolean; // 是否强制更新 - files: UpdateFile[]; -} - -/** - * 更新文件接口 - */ -interface UpdateFile { - path: string; - hash: string; - size: number; - action: 'add' | 'update' | 'delete'; -} - -/** - * 热更新处理器 - */ -export class HotUpdateHandler { - private static readonly CONFIG_FILE = 'hot-update-config.json'; - private static readonly VERSION_FILE = 'version-info.json'; - private static readonly EXTENSION_PATH = Editor.Package.getPath('cocos-ecs-extension') || ''; - - private static config: HotUpdateConfig; - private static updateTimer: NodeJS.Timeout | null = null; - - /** - * 初始化热更新系统 - */ - static async initialize(): Promise { - console.log('[HotUpdate] 初始化热更新系统...'); - - try { - await this.loadConfig(); - await this.startAutoCheck(); - console.log('[HotUpdate] 热更新系统初始化完成'); - } catch (error) { - console.error('[HotUpdate] 初始化失败:', error); - } - } - - /** - * 加载配置 - */ - private static async loadConfig(): Promise { - const configPath = path.join(this.EXTENSION_PATH, this.CONFIG_FILE); - - try { - if (await fs.pathExists(configPath)) { - this.config = await fs.readJSON(configPath); - } else { - // 创建默认配置 - this.config = { - serverUrl: 'https://earthonline-game.cn/api/plugin-updates', - currentVersion: this.getCurrentVersion(), - updateChannel: 'stable', - autoCheck: true, - checkInterval: 60 // 60分钟检查一次 - }; - await this.saveConfig(); - } - } catch (error) { - console.error('[HotUpdate] 配置加载失败:', error); - throw error; - } - } - - /** - * 保存配置 - */ - private static async saveConfig(): Promise { - const configPath = path.join(this.EXTENSION_PATH, this.CONFIG_FILE); - await fs.writeJSON(configPath, this.config, { spaces: 2 }); - } - - /** - * 获取当前版本 - */ - private static getCurrentVersion(): string { - try { - const packagePath = path.join(this.EXTENSION_PATH, 'package.json'); - const packageInfo = fs.readJSONSync(packagePath); - return packageInfo.version; - } catch (error) { - console.error('[HotUpdate] 无法获取当前版本:', error); - return '1.0.0'; - } - } - - /** - * 开始自动检查 - */ - private static async startAutoCheck(): Promise { - if (!this.config.autoCheck) { - return; - } - - // 立即检查一次 - await this.checkForUpdates(true); - - // 设置定时检查 - if (this.updateTimer) { - clearInterval(this.updateTimer); - } - - this.updateTimer = setInterval(async () => { - await this.checkForUpdates(true); - }, this.config.checkInterval * 60 * 1000); - } - - /** - * 检查更新 - */ - static async checkForUpdates(silent: boolean = false): Promise { - console.log('[HotUpdate] 检查更新中...'); - - try { - const response = await fetch(`${this.config.serverUrl}/check`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - currentVersion: this.config.currentVersion, - pluginId: 'cocos-ecs-extension', // 当前插件ID - channel: this.config.updateChannel, - platform: process.platform, - editorVersion: Editor.App.version - }) - }); - - if (!response.ok) { - throw new Error(`服务器响应错误: ${response.status}`); - } - - const versionInfo: VersionInfo = await response.json(); - - if (this.isNewerVersion(versionInfo.version, this.config.currentVersion)) { - console.log(`[HotUpdate] 发现新版本: ${versionInfo.version}`); - - if (!silent) { - await this.showUpdateDialog(versionInfo); - } - - return versionInfo; - } else { - if (!silent) { - Editor.Dialog.info('检查更新', { - detail: '当前已是最新版本!' - }); - } - return null; - } - } catch (error) { - console.error('[HotUpdate] 检查更新失败:', error); - - if (!silent) { - Editor.Dialog.error('检查更新失败', { - detail: `无法连接到更新服务器:\n\n${error instanceof Error ? error.message : String(error)}` - }); - } - return null; - } - } - - /** - * 比较版本号 - */ - private static isNewerVersion(newVersion: string, currentVersion: string): boolean { - const parseVersion = (version: string) => { - return version.split('.').map(Number); - }; - - const newParts = parseVersion(newVersion); - const currentParts = parseVersion(currentVersion); - const maxLength = Math.max(newParts.length, currentParts.length); - - for (let i = 0; i < maxLength; i++) { - const newPart = newParts[i] || 0; - const currentPart = currentParts[i] || 0; - - if (newPart > currentPart) return true; - if (newPart < currentPart) return false; - } - - return false; - } - - /** - * 显示更新对话框 - */ - private static async showUpdateDialog(versionInfo: VersionInfo): Promise { - const message = `发现新版本 ${versionInfo.version}!\n\n` + - `发布时间: ${versionInfo.releaseDate}\n\n` + - `更新内容:\n${versionInfo.description}\n\n` + - `文件大小: ${this.formatFileSize(versionInfo.fileSize)}`; - - const buttons = versionInfo.mandatory ? ['立即更新'] : ['立即更新', '稍后提醒', '跳过此版本']; - - const result = await Editor.Dialog.info('插件更新', { - detail: message, - buttons: buttons - }); - - switch (result.response) { - case 0: // 立即更新 - await this.downloadAndInstallUpdate(versionInfo); - break; - case 1: // 稍后提醒 - if (!versionInfo.mandatory) { - console.log('[HotUpdate] 用户选择稍后更新'); - } - break; - case 2: // 跳过此版本 - if (!versionInfo.mandatory) { - await this.skipVersion(versionInfo.version); - } - break; - } - } - - /** - * 下载并安装更新 - */ - private static async downloadAndInstallUpdate(versionInfo: VersionInfo): Promise { - console.log(`[HotUpdate] 开始下载更新: ${versionInfo.version}`); - - try { - // 显示进度对话框 - const progressDialog = this.showProgressDialog('正在下载更新...'); - - // 下载更新包 - const updatePath = await this.downloadUpdate(versionInfo, (progress) => { - // 更新进度 - console.log(`[HotUpdate] 下载进度: ${progress}%`); - }); - - progressDialog.detail = '正在验证文件...'; - - // 验证文件完整性 - const isValid = await this.verifyUpdate(updatePath, versionInfo.checksum); - if (!isValid) { - throw new Error('文件校验失败,更新包可能已损坏'); - } - - progressDialog.detail = '正在安装更新...'; - - // 安装更新 - await this.installUpdate(updatePath, versionInfo); - - // 更新版本信息 - this.config.currentVersion = versionInfo.version; - await this.saveConfig(); - - // 显示安装完成对话框 - const result = await Editor.Dialog.info('更新完成', { - detail: `插件已成功更新到版本 ${versionInfo.version}!\n\n为了使更新生效,需要重启Cocos Creator编辑器。`, - buttons: ['立即重启', '稍后重启'] - }); - - if (result.response === 0) { - this.restartEditor(); - } - - } catch (error) { - console.error('[HotUpdate] 更新失败:', error); - Editor.Dialog.error('更新失败', { - detail: `更新过程中发生错误:\n\n${error instanceof Error ? error.message : String(error)}` - }); - } - } - - /** - * 下载更新 - */ - private static async downloadUpdate(versionInfo: VersionInfo, onProgress?: (progress: number) => void): Promise { - const response = await fetch(versionInfo.downloadUrl); - - if (!response.ok) { - throw new Error(`下载失败: ${response.status}`); - } - - const totalSize = parseInt(response.headers.get('content-length') || '0'); - let downloadedSize = 0; - - const tempPath = path.join(this.EXTENSION_PATH, 'temp', `update-${versionInfo.version}.zip`); - await fs.ensureDir(path.dirname(tempPath)); - - const writer = fs.createWriteStream(tempPath); - const reader = response.body?.getReader(); - - if (!reader) { - throw new Error('无法创建下载流'); - } - - try { - while (true) { - const { done, value } = await reader.read(); - - if (done) break; - - writer.write(value); - downloadedSize += value.length; - - if (onProgress && totalSize > 0) { - const progress = Math.round((downloadedSize / totalSize) * 100); - onProgress(progress); - } - } - - writer.end(); - return tempPath; - - } catch (error) { - writer.destroy(); - await fs.remove(tempPath).catch(() => {}); // 忽略删除错误 - throw error; - } - } - - /** - * 验证更新包 - */ - private static async verifyUpdate(filePath: string, expectedChecksum: string): Promise { - try { - const fileBuffer = await fs.readFile(filePath); - const hash = crypto.createHash('sha256'); - hash.update(fileBuffer); - const actualChecksum = hash.digest('hex'); - - return actualChecksum === expectedChecksum; - } catch (error) { - console.error('[HotUpdate] 文件校验失败:', error); - return false; - } - } - - /** - * 安装更新 - */ - private static async installUpdate(updatePath: string, versionInfo: VersionInfo): Promise { - const extractPath = path.join(this.EXTENSION_PATH, 'temp', 'extract'); - - try { - // 清理临时目录 - await fs.remove(extractPath); - await fs.ensureDir(extractPath); - - // 解压更新包 - await this.extractZip(updatePath, extractPath); - - // 备份当前版本 - const backupPath = path.join(this.EXTENSION_PATH, 'backup', this.config.currentVersion); - await this.createBackup(backupPath); - - // 应用更新文件 - await this.applyUpdateFiles(extractPath, versionInfo.files); - - // 清理临时文件 - await fs.remove(path.dirname(updatePath)); - - } catch (error) { - console.error('[HotUpdate] 安装更新失败:', error); - // 尝试恢复备份 - await this.restoreBackup(); - throw error; - } - } - - /** - * 解压ZIP文件 - */ - private static async extractZip(zipPath: string, extractPath: string): Promise { - return new Promise((resolve, reject) => { - // 这里使用node的解压库,您可能需要安装 yauzl 或 adm-zip - const AdmZip = require('adm-zip'); - - try { - const zip = new AdmZip(zipPath); - zip.extractAllTo(extractPath, true); - resolve(); - } catch (error) { - reject(error); - } - }); - } - - /** - * 应用更新文件 - */ - private static async applyUpdateFiles(extractPath: string, files: UpdateFile[]): Promise { - for (const file of files) { - const sourcePath = path.join(extractPath, file.path); - const targetPath = path.join(this.EXTENSION_PATH, file.path); - - try { - switch (file.action) { - case 'add': - case 'update': - await fs.ensureDir(path.dirname(targetPath)); - await fs.copy(sourcePath, targetPath, { overwrite: true }); - break; - case 'delete': - await fs.remove(targetPath); - break; - } - } catch (error) { - console.error(`[HotUpdate] 处理文件失败 ${file.path}:`, error); - throw error; - } - } - } - - /** - * 创建备份 - */ - private static async createBackup(backupPath: string): Promise { - await fs.ensureDir(backupPath); - - const sourceFiles = [ - 'source', - 'static', - 'package.json', - 'README.md' - ]; - - for (const file of sourceFiles) { - const sourcePath = path.join(this.EXTENSION_PATH, file); - const targetPath = path.join(backupPath, file); - - if (await fs.pathExists(sourcePath)) { - await fs.copy(sourcePath, targetPath); - } - } - } - - /** - * 恢复备份 - */ - private static async restoreBackup(): Promise { - const backupPath = path.join(this.EXTENSION_PATH, 'backup', this.config.currentVersion); - - if (await fs.pathExists(backupPath)) { - console.log('[HotUpdate] 正在恢复备份...'); - - const backupFiles = await fs.readdir(backupPath); - for (const file of backupFiles) { - const sourcePath = path.join(backupPath, file); - const targetPath = path.join(this.EXTENSION_PATH, file); - - await fs.copy(sourcePath, targetPath, { overwrite: true }); - } - } - } - - /** - * 跳过版本 - */ - private static async skipVersion(version: string): Promise { - const skipPath = path.join(this.EXTENSION_PATH, 'skipped-versions.json'); - let skippedVersions: string[] = []; - - if (await fs.pathExists(skipPath)) { - skippedVersions = await fs.readJSON(skipPath); - } - - if (!skippedVersions.includes(version)) { - skippedVersions.push(version); - await fs.writeJSON(skipPath, skippedVersions); - } - } - - /** - * 显示进度对话框 - */ - private static showProgressDialog(message: string) { - // 这是一个简化版本,实际可能需要创建自定义进度条 - return { - detail: message - }; - } - - /** - * 格式化文件大小 - */ - private static formatFileSize(bytes: number): string { - const units = ['B', 'KB', 'MB', 'GB']; - let size = bytes; - let unitIndex = 0; - - while (size >= 1024 && unitIndex < units.length - 1) { - size /= 1024; - unitIndex++; - } - - return `${size.toFixed(1)} ${units[unitIndex]}`; - } - - /** - * 重启编辑器 - */ - private static restartEditor(): void { - // 注意:这个功能需要特殊权限,可能需要用户手动重启 - console.log('[HotUpdate] 请求重启编辑器'); - - try { - // 尝试重启编辑器(可能不会成功) - Editor.App.quit(); - } catch (error) { - console.warn('[HotUpdate] 无法自动重启编辑器:', error); - } - } - - /** - * 设置更新配置 - */ - static async setConfig(newConfig: Partial): Promise { - this.config = { ...this.config, ...newConfig }; - await this.saveConfig(); - - // 重新启动自动检查 - if (this.config.autoCheck) { - await this.startAutoCheck(); - } else if (this.updateTimer) { - clearInterval(this.updateTimer); - this.updateTimer = null; - } - } - - /** - * 获取配置 - */ - static getConfig(): HotUpdateConfig { - return { ...this.config }; - } - - /** - * 清理资源 - */ - static cleanup(): void { - if (this.updateTimer) { - clearInterval(this.updateTimer); - this.updateTimer = null; - } - } -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/PanelHandler.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/PanelHandler.ts deleted file mode 100644 index 9bb78015..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/PanelHandler.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** - * 面板管理相关的处理器 - */ -export class PanelHandler { - /** - * 打开默认面板 - */ - static openDefaultPanel(): void { - try { - Editor.Panel.open('cocos-ecs-extension'); - console.log('Default panel opened successfully'); - } catch (error) { - console.error('Failed to open default panel:', error); - Editor.Dialog.error('打开面板失败', { - detail: `无法打开面板:\n\n${error}\n\n请尝试重启Cocos Creator编辑器。`, - }); - } - } - - /** - * 打开调试面板 - */ - static openDebugPanel(): void { - try { - Editor.Panel.open('cocos-ecs-extension.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编辑器。`, - }); - } - } - - /** - * 打开代码生成器面板 - */ - static openGeneratorPanel(): void { - try { - Editor.Panel.open('cocos-ecs-extension.generator'); - console.log('Generator panel opened successfully'); - } catch (error) { - console.error('Failed to open generator panel:', error); - Editor.Dialog.error('打开代码生成器失败', { - detail: `无法打开代码生成器面板:\n\n${error}\n\n请尝试重启Cocos Creator编辑器。`, - }); - } - } - - /** - * 打开行为树面板 - */ - static openBehaviorTreePanel(): void { - try { - Editor.Panel.open('cocos-ecs-extension.behavior-tree'); - console.log('Behavior Tree panel opened successfully'); - } catch (error) { - console.error('Failed to open behavior tree panel:', error); - Editor.Dialog.error('打开行为树面板失败', { - detail: `无法打开行为树AI组件库面板:\n\n${error}\n\n请尝试重启Cocos Creator编辑器。`, - }); - } - } -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/index.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/index.ts deleted file mode 100644 index 646b8c3f..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { EcsFrameworkHandler } from './EcsFrameworkHandler'; -export { BehaviorTreeHandler } from './BehaviorTreeHandler'; -export { PanelHandler } from './PanelHandler'; -export { HotUpdateHandler } from './HotUpdateHandler'; \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/main.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/main.ts deleted file mode 100644 index 1f0d2a80..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/main.ts +++ /dev/null @@ -1,229 +0,0 @@ -// @ts-ignore -import packageJSON from '../package.json'; -import { EcsFrameworkHandler, BehaviorTreeHandler, PanelHandler, HotUpdateHandler } from './handlers'; -import { readJSON } from 'fs-extra'; -import * as path from 'path'; -import { AssetInfo } from '@cocos/creator-types/editor/packages/asset-db/@types/public'; - -/** - * @en Registration method for the main process of Extension - * @zh 为扩展的主进程的注册方法 - */ -export const methods: { [key: string]: (...any: any) => any } = { - // ================ 面板管理 ================ - /** - * 打开默认面板 - */ - openPanel() { - PanelHandler.openDefaultPanel(); - }, - - /** - * 打开调试面板 - */ - 'open-debug'() { - PanelHandler.openDebugPanel(); - }, - - /** - * 打开代码生成器面板 - */ - 'open-generator'() { - PanelHandler.openGeneratorPanel(); - }, - - /** - * 打开行为树面板 - */ - 'open-behavior-tree'() { - PanelHandler.openBehaviorTreePanel(); - }, - - // ================ ECS框架管理 ================ - /** - * 安装ECS Framework - */ - 'install-ecs-framework'() { - EcsFrameworkHandler.install(); - }, - - /** - * 更新ECS Framework - */ - 'update-ecs-framework'() { - EcsFrameworkHandler.update(); - }, - - /** - * 卸载ECS Framework - */ - 'uninstall-ecs-framework'() { - EcsFrameworkHandler.uninstall(); - }, - - /** - * 打开文档 - */ - 'open-documentation'() { - EcsFrameworkHandler.openDocumentation(); - }, - - /** - * 创建ECS模板 - */ - 'create-ecs-template'() { - EcsFrameworkHandler.createTemplate(); - }, - - /** - * 打开GitHub仓库 - */ - 'open-github'() { - EcsFrameworkHandler.openGitHub(); - }, - - /** - * 打开QQ群 - */ - 'open-qq-group'() { - EcsFrameworkHandler.openQQGroup(); - }, - - // ================ 行为树管理 ================ - /** - * 安装行为树AI系统 - */ - async 'install-behavior-tree'() { - try { - return await BehaviorTreeHandler.install(); - } catch (error) { - console.error('安装行为树AI系统失败:', error); - return false; - } - }, - - /** - * 更新行为树AI系统 - */ - async 'update-behavior-tree'() { - try { - return await BehaviorTreeHandler.update(); - } catch (error) { - console.error('更新行为树AI系统失败:', error); - return false; - } - }, - - /** - * 检查行为树AI是否已安装 - */ - 'check-behavior-tree-installed'() { - return BehaviorTreeHandler.checkInstalled(); - }, - - /** - * 打开行为树文档 - */ - 'open-behavior-tree-docs'() { - BehaviorTreeHandler.openDocumentation(); - }, - - /** - * 创建行为树文件 - */ - 'create-behavior-tree-file'() { - BehaviorTreeHandler.createFile(); - }, - - /** - * 加载行为树文件到编辑器 - */ - async 'load-behavior-tree-file'(...args: any[]) { - const assetInfo = args.length >= 2 ? args[1] : args[0]; - - try { - if (!assetInfo || (!assetInfo.file && !assetInfo.path)) { - throw new Error('无效的文件信息'); - } - - await Editor.Panel.open('cocos-ecs-extension.behavior-tree'); - await new Promise(resolve => setTimeout(resolve, 500)); - - const result = await Editor.Message.request('cocos-ecs-extension', 'behavior-tree-panel-load-file', assetInfo); - - } catch (error) { - Editor.Dialog.error('打开失败', { - detail: `打开行为树文件失败:\n\n${error instanceof Error ? error.message : String(error)}` - }); - } - }, - - /** - * 从编辑器创建行为树文件 - */ - 'create-behavior-tree-from-editor'(event: any, data: any) { - BehaviorTreeHandler.createFromEditor(data); - }, - - /** - * 覆盖现有行为树文件 - */ - 'overwrite-behavior-tree-file'(...args: any[]) { - const data = args.length >= 2 ? args[1] : args[0]; - - if (data && data.filePath) { - BehaviorTreeHandler.overwriteFile(data); - } else { - throw new Error('文件路径不存在或数据无效'); - } - }, - - // ================ 热更新管理 ================ - /** - * 检查插件更新 - */ - 'check-plugin-updates'() { - return HotUpdateHandler.checkForUpdates(false); - }, - - /** - * 设置热更新配置 - */ - 'set-hot-update-config'(...args: any[]) { - const config = args.length >= 2 ? args[1] : args[0]; - return HotUpdateHandler.setConfig(config); - }, - - /** - * 获取热更新配置 - */ - 'get-hot-update-config'() { - return HotUpdateHandler.getConfig(); - }, -}; - - - -/** - * @en Method triggered when the extension is started - * @zh 启动扩展时触发的方法 - */ -export function load() { - console.log('[Cocos ECS Extension] 扩展已加载'); - - // 初始化热更新系统 - HotUpdateHandler.initialize().catch(error => { - console.error('[Cocos ECS Extension] 热更新初始化失败:', error); - }); -} - -/** - * @en Method triggered when the extension is uninstalled - * @zh 卸载扩展时触发的方法 - */ -export function unload() { - console.log('[Cocos ECS Extension] 扩展已卸载'); - - // 清理热更新资源 - HotUpdateHandler.cleanup(); -} diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useAppState.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useAppState.ts deleted file mode 100644 index ce1406a6..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useAppState.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { ref } from 'vue'; -import { TreeNode, DragState, Connection } from '../types'; -import { allNodeTemplates as nodeTemplates } from '../data/nodeTemplates'; - -/** - * 应用状态管理 - */ -export function useAppState() { - // 安装状态 - const checkingStatus = ref(true); - const isInstalled = ref(false); - const version = ref(null); - const isInstalling = ref(false); - - // 编辑器状态 - const nodeTemplates_ = ref(nodeTemplates); - const treeNodes = ref([]); - const selectedNodeId = ref(null); - const selectedConditionNodeId = ref(null); // 选中的条件节点ID - const nodeSearchText = ref(''); - - // 调试:检查条件节点模板 - console.log('🔍 条件节点模板检查:'); - nodeTemplates.filter(t => t.category === 'condition').forEach(template => { - console.log(` ${template.name}: isDraggableCondition=${template.isDraggableCondition}`); - }); - - console.log('🎭 装饰器节点模板检查:'); - nodeTemplates.filter(t => t.category === 'decorator').forEach(template => { - console.log(` ${template.name}: type=${template.type}`); - }); - - // 画布状态 - const canvasWidth = ref(800); - const canvasHeight = ref(600); - const zoomLevel = ref(1); - const panX = ref(0); - const panY = ref(0); - - const dragState = ref({ - isDraggingCanvas: false, - isDraggingNode: false, - isConnecting: false, - dragStartX: 0, - dragStartY: 0, - dragNodeId: null, - dragNodeStartX: 0, - dragNodeStartY: 0, - connectionStart: null, - connectionEnd: { x: 0, y: 0 } - }); - - // 连接状态 - const connections = ref([]); - const tempConnection = ref({ path: '' }); - - // UI状态 - const showExportModal = ref(false); - const exportFormat = ref('json'); // 默认JSON格式,TypeScript暂时禁用 - - // 工具函数 - const getNodeByIdLocal = (id: string): TreeNode | undefined => { - return treeNodes.value.find(node => node.id === id); - }; - - const selectNode = (nodeId: string) => { - selectedNodeId.value = nodeId; - }; - - const newBehaviorTree = () => { - treeNodes.value = []; - selectedNodeId.value = null; - connections.value = []; - tempConnection.value.path = ''; - }; - - const updateCanvasSize = () => { - const canvasArea = document.querySelector('.canvas-area') as HTMLElement; - if (canvasArea) { - const rect = canvasArea.getBoundingClientRect(); - if (rect.width > 0 && rect.height > 0) { - canvasWidth.value = Math.max(rect.width, 800); - canvasHeight.value = Math.max(rect.height, 600); - } - } - }; - - return { - // 安装状态 - checkingStatus, - isInstalled, - version, - isInstalling, - - // 编辑器状态 - nodeTemplates: nodeTemplates_, - treeNodes, - selectedNodeId, - selectedConditionNodeId, - nodeSearchText, - - // 画布状态 - canvasWidth, - canvasHeight, - zoomLevel, - panX, - panY, - dragState, - - // 连接状态 - connections, - tempConnection, - - // UI状态 - showExportModal, - exportFormat, - - // 工具函数 - getNodeByIdLocal, - selectNode, - newBehaviorTree, - updateCanvasSize - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useBehaviorTreeEditor.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useBehaviorTreeEditor.ts deleted file mode 100644 index 3e92a4ee..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useBehaviorTreeEditor.ts +++ /dev/null @@ -1,1074 +0,0 @@ -import { ref, computed, reactive, onMounted, onUnmounted, nextTick } from 'vue'; -import { useAppState } from './useAppState'; -import { useComputedProperties } from './useComputedProperties'; -import { useNodeOperations } from './useNodeOperations'; -import { useCodeGeneration } from './useCodeGeneration'; -import { useInstallation } from './useInstallation'; -import { useFileOperations } from './useFileOperations'; -import { useConnectionManager } from './useConnectionManager'; -import { useCanvasManager } from './useCanvasManager'; -import { useNodeDisplay } from './useNodeDisplay'; -import { useConditionAttachment } from './useConditionAttachment'; -import { useBlackboard } from './useBlackboard'; -import { validateTree as validateTreeStructure } from '../utils/nodeUtils'; - -/** - * 主要的行为树编辑器组合功能 - */ -export function useBehaviorTreeEditor() { - // Vue Refs for DOM elements - const canvasAreaRef = ref(null); - const svgRef = ref(null); - - // 获取其他组合功能 - const appState = useAppState(); - - // 临时根节点获取函数 - const getRootNode = () => { - return appState.treeNodes.value.find(node => - !appState.treeNodes.value.some(otherNode => - otherNode.children?.includes(node.id) - ) - ) || null; - }; - - const codeGen = useCodeGeneration( - appState.treeNodes, - appState.nodeTemplates, - appState.getNodeByIdLocal, - getRootNode, - computed(() => blackboard.blackboardVariables.value.reduce((map, variable) => { - map.set(variable.name, variable); - return map; - }, new Map())) - ); - - const computedProps = useComputedProperties( - appState.nodeTemplates, - appState.nodeSearchText, - appState.treeNodes, - appState.selectedNodeId, - appState.selectedConditionNodeId, - appState.checkingStatus, - appState.isInstalling, - appState.isInstalled, - appState.version, - appState.exportFormat, - appState.panX, - appState.panY, - appState.zoomLevel, - appState.getNodeByIdLocal, - { - generateConfigJSON: codeGen.generateConfigJSON, - generateTypeScriptCode: codeGen.generateTypeScriptCode - } - ); - - const nodeOps = useNodeOperations( - appState.treeNodes, - appState.selectedNodeId, - appState.connections, - appState.panX, - appState.panY, - appState.zoomLevel, - appState.getNodeByIdLocal, - () => connectionManager.updateConnections() - ); - - const installation = useInstallation( - appState.checkingStatus, - appState.isInstalled, - appState.version, - appState.isInstalling - ); - - // Blackboard功能 - const blackboard = useBlackboard(); - - // Blackboard常驻侧边面板状态 - const blackboardSidebarState = reactive({ - collapsed: false, - transparent: true - }); - - const connectionState = reactive({ - isConnecting: false, - startNodeId: null as string | null, - startPortType: null as 'input' | 'output' | null, - tempPath: '', - currentMousePos: null as { x: number, y: number } | null, - startPortPos: null as { x: number, y: number } | null, - hoveredPort: null as { nodeId: string, portType: 'input' | 'output' } | null - }); - - const connectionManager = useConnectionManager( - appState.treeNodes, - appState.connections, - connectionState, - canvasAreaRef, - svgRef, - appState.panX, - appState.panY, - appState.zoomLevel - ); - - const fileOps = useFileOperations({ - treeNodes: appState.treeNodes, - selectedNodeId: appState.selectedNodeId, - connections: appState.connections, - tempConnection: appState.tempConnection, - showExportModal: appState.showExportModal, - codeGeneration: codeGen, - updateConnections: connectionManager.updateConnections, - blackboardOperations: { - getBlackboardVariables: () => blackboard.blackboardVariables.value, - loadBlackboardVariables: (variables: any[]) => { - blackboard.loadBlackboardFromArray(variables); - }, - clearBlackboard: blackboard.clearBlackboard - } - }); - - const canvasManager = useCanvasManager( - appState.panX, - appState.panY, - appState.zoomLevel, - appState.treeNodes, - appState.selectedNodeId, - canvasAreaRef, - connectionManager.updateConnections - ); - - const nodeDisplay = useNodeDisplay(); - - const conditionAttachment = useConditionAttachment( - appState.treeNodes, - appState.getNodeByIdLocal - ); - - const dragState = reactive({ - isDragging: false, - dragNode: null as any, - dragElement: null as HTMLElement | null, - dragOffset: { x: 0, y: 0 }, - startPosition: { x: 0, y: 0 }, - updateCounter: 0 - }); - - // Blackboard拖拽相关功能 - const isBlackboardDroppable = (prop: any): boolean => { - return prop && (prop.type === 'string' || prop.type === 'number' || prop.type === 'boolean'); - }; - - const isBlackboardReference = (value: string): boolean => { - return typeof value === 'string' && value.startsWith('{{') && value.endsWith('}}'); - }; - - const handleBlackboardDrop = (event: DragEvent, propertyKey: string) => { - event.preventDefault(); - event.stopPropagation(); - - try { - const blackboardData = event.dataTransfer?.getData('application/blackboard-variable'); - - if (!blackboardData) { - return; - } - - const variable = JSON.parse(blackboardData); - - // 检查当前是否在编辑条件节点 - if (appState.selectedConditionNodeId.value) { - // 条件节点:直接更新装饰器的属性 - const decoratorNode = appState.getNodeByIdLocal(appState.selectedConditionNodeId.value); - if (decoratorNode) { - const referenceValue = `{{${variable.name}}}`; - - if (!decoratorNode.properties) { - decoratorNode.properties = {}; - } - decoratorNode.properties[propertyKey] = referenceValue; - - // 强制触发响应式更新 - const nodeIndex = appState.treeNodes.value.findIndex(n => n.id === decoratorNode.id); - if (nodeIndex > -1) { - const newNodes = [...appState.treeNodes.value]; - newNodes[nodeIndex] = { ...decoratorNode }; - appState.treeNodes.value = newNodes; - } - } - } else { - // 普通节点:使用原来的逻辑 - const activeNode = computedProps.activeNode.value; - - if (!activeNode || !activeNode.properties) { - return; - } - - const property = activeNode.properties[propertyKey]; - - if (!property) { - return; - } - - // 设置Blackboard引用 - const referenceValue = `{{${variable.name}}}`; - nodeOps.updateNodeProperty(`properties.${propertyKey}.value`, referenceValue); - } - - } catch (error) { - console.error('处理Blackboard拖拽失败:', error); - } - }; - - const handleBlackboardDragOver = (event: DragEvent) => { - event.preventDefault(); - event.stopPropagation(); - - const hasBlackboardData = event.dataTransfer?.types.includes('application/blackboard-variable'); - - if (hasBlackboardData) { - event.dataTransfer!.dropEffect = 'copy'; - const element = event.currentTarget as HTMLElement; - element.classList.add('drag-over'); - } - }; - - const handleBlackboardDragLeave = (event: DragEvent) => { - const element = event.currentTarget as HTMLElement; - element.classList.remove('drag-over'); - }; - - const clearBlackboardReference = (propertyKey: string) => { - // 检查当前是否在编辑条件节点 - if (appState.selectedConditionNodeId.value) { - // 条件节点:直接清除装饰器的属性 - const decoratorNode = appState.getNodeByIdLocal(appState.selectedConditionNodeId.value); - if (decoratorNode && decoratorNode.properties) { - decoratorNode.properties[propertyKey] = ''; - - // 强制触发响应式更新 - const nodeIndex = appState.treeNodes.value.findIndex(n => n.id === decoratorNode.id); - if (nodeIndex > -1) { - const newNodes = [...appState.treeNodes.value]; - newNodes[nodeIndex] = { ...decoratorNode }; - appState.treeNodes.value = newNodes; - } - } - } else { - // 普通节点:使用原来的逻辑 - nodeOps.updateNodeProperty(`properties.${propertyKey}.value`, ''); - } - }; - - // 节点类型识别相关方法 - const getOriginalNodeName = (nodeType: string): string => { - const template = appState.nodeTemplates.value.find(t => t.type === nodeType); - return template?.name || nodeType; - }; - - const getNodeTemplate = (nodeType: string) => { - return appState.nodeTemplates.value.find(t => t.type === nodeType); - }; - - const getNodeCategory = (nodeType: string): string => { - const template = getNodeTemplate(nodeType); - if (!template) return 'unknown'; - - const category = template.category || 'unknown'; - const categoryMap: Record = { - 'root': '根节点', - 'composite': '组合', - 'decorator': '装饰器', - 'action': '动作', - 'condition': '条件', - 'ecs': 'ECS' - }; - - return categoryMap[category] || category; - }; - - const isNodeNameCustomized = (node: any): boolean => { - if (!node) return false; - const originalName = getOriginalNodeName(node.type); - return node.name !== originalName; - }; - - const resetNodeToOriginalName = () => { - if (!appState.selectedNodeId.value) return; - - const selectedNode = appState.getNodeByIdLocal(appState.selectedNodeId.value); - if (!selectedNode) return; - - const originalName = getOriginalNodeName(selectedNode.type); - nodeOps.updateNodeProperty('name', originalName); - - console.log(`节点名称已重置为原始名称: ${originalName}`); - }; - - const startNodeDrag = (event: MouseEvent, node: any) => { - event.stopPropagation(); - event.preventDefault(); - - dragState.isDragging = true; - dragState.dragNode = node; - dragState.startPosition = { x: event.clientX, y: event.clientY }; - - dragState.dragElement = document.querySelector(`[data-node-id="${node.id}"]`) as HTMLElement; - if (dragState.dragElement) { - dragState.dragElement.classList.add('dragging'); - } - - dragState.dragOffset = { - x: node.x, - y: node.y - }; - - document.addEventListener('mousemove', onNodeDrag); - document.addEventListener('mouseup', onNodeDragEnd); - }; - - const onNodeDrag = (event: MouseEvent) => { - if (!dragState.isDragging || !dragState.dragNode) return; - - const deltaX = (event.clientX - dragState.startPosition.x) / appState.zoomLevel.value; - const deltaY = (event.clientY - dragState.startPosition.y) / appState.zoomLevel.value; - - dragState.dragNode.x = dragState.dragOffset.x + deltaX; - dragState.dragNode.y = dragState.dragOffset.y + deltaY; - - connectionManager.updateConnections(); - }; - - const onNodeDragEnd = (event: MouseEvent) => { - if (!dragState.isDragging) return; - - if (dragState.dragElement) { - dragState.dragElement.classList.remove('dragging'); - } - - dragState.isDragging = false; - dragState.dragNode = null; - dragState.dragElement = null; - - document.removeEventListener('mousemove', onNodeDrag); - document.removeEventListener('mouseup', onNodeDragEnd); - - connectionManager.updateConnections(); - dragState.updateCounter = 0; - }; - - const handleInstall = () => { - installation.handleInstall(); - }; - - // 紧凑子树布局算法 - 体现行为树的层次结构 - const autoLayout = () => { - if (appState.treeNodes.value.length === 0) { - return; - } - - // 找到根节点 - const rootNode = appState.treeNodes.value.find(node => - !appState.treeNodes.value.some(otherNode => - otherNode.children?.includes(node.id) - ) - ); - - if (!rootNode) { - console.warn('未找到根节点,无法进行自动布局'); - return; - } - - // 计算节点尺寸 - const getNodeSize = (node: any) => { - let width = 180; - let height = 100; - - // 根据节点类型调整基础尺寸 - switch (node.category || node.type) { - case 'root': - width = 200; height = 70; - break; - case 'composite': - width = 160; height = 90; - break; - case 'decorator': - width = 140; height = 80; - break; - case 'action': - width = 180; height = 100; - break; - case 'condition': - width = 150; height = 85; - break; - } - - // 根据属性数量动态调整 - if (node.properties) { - const propertyCount = Object.keys(node.properties).length; - height += propertyCount * 20; - } - - // 根据名称长度调整宽度 - if (node.name) { - const nameWidth = node.name.length * 8 + 40; - width = Math.max(width, nameWidth); - } - - return { width, height }; - }; - - // 紧凑子树布局核心算法 - const layoutSubtree = (node: any, parentX = 0, parentY = 0, depth = 0): { width: number, height: number } => { - const nodeSize = getNodeSize(node); - - // 如果是叶子节点,直接返回自身尺寸 - if (!node.children || node.children.length === 0) { - node.x = parentX; - node.y = parentY; - return { width: nodeSize.width, height: nodeSize.height }; - } - - // 递归布局所有子节点,收集子树信息 - const childSubtrees: Array<{ node: any, width: number, height: number }> = []; - let totalChildrenWidth = 0; - let maxChildHeight = 0; - - const childY = parentY + nodeSize.height + 60; // 子节点距离父节点的垂直间距 - const siblingSpacing = 40; // 同级子节点间的水平间距 - - // 先计算每个子树的尺寸 - node.children.forEach((childId: string) => { - const childNode = appState.treeNodes.value.find(n => n.id === childId); - if (childNode) { - const subtreeInfo = layoutSubtree(childNode, 0, childY, depth + 1); - childSubtrees.push({ node: childNode, ...subtreeInfo }); - totalChildrenWidth += subtreeInfo.width; - maxChildHeight = Math.max(maxChildHeight, subtreeInfo.height); - } - }); - - // 添加子节点间的间距 - if (childSubtrees.length > 1) { - totalChildrenWidth += (childSubtrees.length - 1) * siblingSpacing; - } - - // 计算父节点的最终位置(在子节点的中心上方) - const subtreeWidth = Math.max(nodeSize.width, totalChildrenWidth); - node.x = parentX + subtreeWidth / 2 - nodeSize.width / 2; - node.y = parentY; - - // 布局子节点(以父节点为中心分布) - let currentX = parentX + subtreeWidth / 2 - totalChildrenWidth / 2; - - childSubtrees.forEach(({ node: childNode, width: childWidth }) => { - // 将子节点定位到其子树的中心 - const childCenterOffset = childWidth / 2; - childNode.x = currentX + childCenterOffset - getNodeSize(childNode).width / 2; - - // 递归调整子树中所有节点的位置 - adjustSubtreePosition(childNode, currentX, childY); - - currentX += childWidth + siblingSpacing; - }); - - // 返回整个子树的尺寸 - const subtreeHeight = nodeSize.height + 60 + maxChildHeight; - return { width: subtreeWidth, height: subtreeHeight }; - }; - - // 递归调整子树位置 - const adjustSubtreePosition = (node: any, baseX: number, baseY: number) => { - const nodeSize = getNodeSize(node); - - if (!node.children || node.children.length === 0) { - return; - } - - // 计算子节点的总宽度 - let totalChildrenWidth = 0; - const siblingSpacing = 40; - - node.children.forEach((childId: string) => { - const childNode = appState.treeNodes.value.find(n => n.id === childId); - if (childNode) { - const childSubtreeWidth = calculateSubtreeWidth(childNode); - totalChildrenWidth += childSubtreeWidth; - } - }); - - if (node.children.length > 1) { - totalChildrenWidth += (node.children.length - 1) * siblingSpacing; - } - - // 重新定位子节点 - let currentX = baseX + Math.max(nodeSize.width, totalChildrenWidth) / 2 - totalChildrenWidth / 2; - const childY = baseY + nodeSize.height + 60; - - node.children.forEach((childId: string) => { - const childNode = appState.treeNodes.value.find(n => n.id === childId); - if (childNode) { - const childSubtreeWidth = calculateSubtreeWidth(childNode); - const childCenterOffset = childSubtreeWidth / 2; - childNode.x = currentX + childCenterOffset - getNodeSize(childNode).width / 2; - childNode.y = childY; - - adjustSubtreePosition(childNode, currentX, childY); - currentX += childSubtreeWidth + siblingSpacing; - } - }); - }; - - // 计算子树宽度 - const calculateSubtreeWidth = (node: any): number => { - const nodeSize = getNodeSize(node); - - if (!node.children || node.children.length === 0) { - return nodeSize.width; - } - - let totalChildrenWidth = 0; - const siblingSpacing = 40; - - node.children.forEach((childId: string) => { - const childNode = appState.treeNodes.value.find(n => n.id === childId); - if (childNode) { - totalChildrenWidth += calculateSubtreeWidth(childNode); - } - }); - - if (node.children.length > 1) { - totalChildrenWidth += (node.children.length - 1) * siblingSpacing; - } - - return Math.max(nodeSize.width, totalChildrenWidth); - }; - - // 开始布局 - 从根节点开始 - const startX = 400; // 画布中心X - const startY = 50; // 顶部留白 - - const treeInfo = layoutSubtree(rootNode, startX, startY); - - // 处理孤立节点 - const connectedNodeIds = new Set(); - const collectConnectedNodes = (node: any) => { - connectedNodeIds.add(node.id); - if (node.children) { - node.children.forEach((childId: string) => { - const childNode = appState.treeNodes.value.find(n => n.id === childId); - if (childNode) { - collectConnectedNodes(childNode); - } - }); - } - }; - collectConnectedNodes(rootNode); - - const orphanNodes = appState.treeNodes.value.filter(node => !connectedNodeIds.has(node.id)); - if (orphanNodes.length > 0) { - const orphanY = startY + treeInfo.height + 100; - orphanNodes.forEach((node, index) => { - node.x = startX + (index - orphanNodes.length / 2) * 200; - node.y = orphanY + Math.floor(index / 5) * 120; - }); - } - - // 强制更新连接线 - const forceUpdateConnections = () => { - connectionManager.updateConnections(); - - nextTick(() => { - connectionManager.updateConnections(); - - setTimeout(() => { - connectionManager.updateConnections(); - }, 150); - }); - }; - - forceUpdateConnections(); - - console.log(`紧凑子树布局完成:${appState.treeNodes.value.length} 个节点已重新排列`); - }; - - - - // 验证树结构 - const validateTree = () => { - // 使用改进的验证函数 - const validationResult = validateTreeStructure(appState.treeNodes.value); - - const errors: string[] = []; - const warnings: string[] = []; - - if (!validationResult.isValid) { - errors.push(validationResult.message); - } - - // 检查孤立节点(除了根节点) - appState.treeNodes.value.forEach(node => { - if (node.type !== 'root') { - const hasParent = appState.treeNodes.value.some(otherNode => - otherNode.children?.includes(node.id) - ); - const hasChildren = node.children && node.children.length > 0; - - if (!hasParent && !hasChildren && appState.treeNodes.value.length > 1) { - warnings.push(`节点 "${node.name}" 是孤立节点`); - } - } - }); - - // 检查连接完整性 - appState.connections.value.forEach(conn => { - const sourceNode = appState.treeNodes.value.find(n => n.id === conn.sourceId); - const targetNode = appState.treeNodes.value.find(n => n.id === conn.targetId); - - if (!sourceNode) { - errors.push(`连接 ${conn.id} 的源节点不存在`); - } - if (!targetNode) { - errors.push(`连接 ${conn.id} 的目标节点不存在`); - } - }); - - // 检查节点类型一致性 - appState.treeNodes.value.forEach(node => { - if (node.type === 'root' && node.parent) { - errors.push(`根节点 "${node.name}" 不应该有父节点`); - } - - // 检查装饰器节点的限制 - if (node.type.includes('decorator') || node.type.includes('Decorator')) { - if (node.children.length > 1) { - warnings.push(`装饰器节点 "${node.name}" 建议只连接一个子节点,当前有 ${node.children.length} 个`); - } - } - }); - - let message = '🔍 树结构验证完成!\n\n'; - - if (errors.length > 0) { - message += `❌ 错误 (${errors.length}):\n${errors.map(e => `• ${e}`).join('\n')}\n\n`; - } - - if (warnings.length > 0) { - message += `⚠️ 警告 (${warnings.length}):\n${warnings.map(w => `• ${w}`).join('\n')}\n\n`; - } - - if (errors.length === 0 && warnings.length === 0) { - message += '✅ 没有发现问题!树结构完全符合行为树规范。'; - } else if (errors.length === 0) { - message += '✅ 树结构基本有效,但有一些建议优化的地方。'; - } - - alert(message); - }; - - // 清除所有连接线 - const clearAllConnections = () => { - if (appState.connections.value.length === 0) { - alert('当前没有连接线需要清除!'); - return; - } - - if (confirm(`确定要清除所有 ${appState.connections.value.length} 条连接线吗?此操作不可撤销。`)) { - // 清除所有节点的父子关系 - appState.treeNodes.value.forEach(node => { - node.parent = undefined; - node.children = []; - }); - - // 清空连接数组 - appState.connections.value = []; - - alert('已清除所有连接线!'); - } - }; - - // 复制到剪贴板 - const copyToClipboard = async () => { - try { - const code = computedProps.exportedCode(); - await navigator.clipboard.writeText(code); - - // 显示成功消息 - const toast = document.createElement('div'); - toast.style.cssText = ` - position: fixed; - top: 20px; - right: 20px; - padding: 12px 20px; - background: #4caf50; - color: white; - border-radius: 4px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); - z-index: 10001; - opacity: 0; - transform: translateX(100%); - transition: all 0.3s ease; - `; - toast.textContent = '已复制到剪贴板!'; - - document.body.appendChild(toast); - - setTimeout(() => { - toast.style.opacity = '1'; - toast.style.transform = 'translateX(0)'; - }, 10); - - setTimeout(() => { - toast.style.opacity = '0'; - toast.style.transform = 'translateX(100%)'; - setTimeout(() => { - if (document.body.contains(toast)) { - document.body.removeChild(toast); - } - }, 300); - }, 2000); - } catch (error) { - alert('复制到剪贴板失败: ' + error); - } - }; - - // 保存到文件 - 使用Cocos Creator扩展API提供保存路径选择 - const saveToFile = async () => { - try { - const code = computedProps.exportedCode(); - const format = appState.exportFormat.value; - const extension = format === 'json' ? '.json' : '.ts'; - const fileType = format === 'json' ? 'JSON配置文件' : 'TypeScript文件'; - - // 使用Cocos Creator的文件保存对话框 - const result = await Editor.Dialog.save({ - title: `保存${fileType}`, - filters: [ - { - name: fileType, - extensions: extension === '.json' ? ['json'] : ['ts'] - }, - { - name: '所有文件', - extensions: ['*'] - } - ] - }); - - if (result.canceled || !result.filePath) { - return; // 用户取消了保存 - } - - // 写入文件 - const fs = require('fs-extra'); - await fs.writeFile(result.filePath, code, 'utf8'); - - // 显示成功消息 - const toast = document.createElement('div'); - toast.style.cssText = ` - position: fixed; - top: 20px; - right: 20px; - padding: 12px 20px; - background: #4caf50; - color: white; - border-radius: 4px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); - z-index: 10001; - opacity: 0; - transform: translateX(100%); - transition: all 0.3s ease; - max-width: 400px; - word-wrap: break-word; - `; - - const path = require('path'); - const fileName = path.basename(result.filePath); - toast.innerHTML = ` -
✅ 文件保存成功
-
文件名: ${fileName}
-
路径: ${result.filePath}
- `; - - document.body.appendChild(toast); - - setTimeout(() => { - toast.style.opacity = '1'; - toast.style.transform = 'translateX(0)'; - }, 10); - - setTimeout(() => { - toast.style.opacity = '0'; - toast.style.transform = 'translateX(100%)'; - setTimeout(() => { - if (document.body.contains(toast)) { - document.body.removeChild(toast); - } - }, 300); - }, 4000); - - } catch (error: any) { - console.error('保存文件失败:', error); - - // 显示错误消息 - const errorToast = document.createElement('div'); - errorToast.style.cssText = ` - position: fixed; - top: 20px; - right: 20px; - padding: 12px 20px; - background: #f56565; - color: white; - border-radius: 4px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); - z-index: 10001; - opacity: 0; - transform: translateX(100%); - transition: all 0.3s ease; - max-width: 400px; - word-wrap: break-word; - `; - errorToast.innerHTML = ` -
❌ 保存失败
-
${error?.message || error}
- `; - - document.body.appendChild(errorToast); - - setTimeout(() => { - errorToast.style.opacity = '1'; - errorToast.style.transform = 'translateX(0)'; - }, 10); - - setTimeout(() => { - errorToast.style.opacity = '0'; - errorToast.style.transform = 'translateX(100%)'; - setTimeout(() => { - if (document.body.contains(errorToast)) { - document.body.removeChild(errorToast); - } - }, 300); - }, 5000); - } - }; - - - - onMounted(() => { - // 自动检查安装状态 - installation.checkInstallStatus(); - - const appContainer = document.querySelector('#behavior-tree-app'); - if (appContainer) { - (appContainer as any).loadFileContent = fileOps.loadFileContent; - (appContainer as any).showError = (errorMessage: string) => { - alert('文件加载失败: ' + errorMessage); - }; - } - - const handleLoadBehaviorTreeFile = (event: CustomEvent) => { - fileOps.loadFileContent(event.detail); - }; - - const handleFileLoadError = (event: CustomEvent) => { - console.error('[BehaviorTreeEditor] DOM事件错误:', event.detail); - alert('文件加载失败: ' + event.detail.error); - }; - - // 键盘快捷键处理 - const handleKeydown = (event: KeyboardEvent) => { - // Delete键删除选中的节点 - if (event.key === 'Delete' && appState.selectedNodeId.value) { - event.preventDefault(); - nodeOps.deleteNode(appState.selectedNodeId.value); - } - // Escape键取消连接 - if (event.key === 'Escape' && connectionState.isConnecting) { - event.preventDefault(); - connectionManager.cancelConnection(); - } - // Escape键取消条件拖拽 - if (event.key === 'Escape' && conditionAttachment.dragState.isDraggingCondition) { - event.preventDefault(); - conditionAttachment.resetDragState(); - } - }; - - // 全局拖拽结束处理 - const handleGlobalDragEnd = (event: DragEvent) => { - if (conditionAttachment.dragState.isDraggingCondition) { - setTimeout(() => { - conditionAttachment.resetDragState(); - }, 100); - } - }; - - const handleGlobalDragOver = (event: DragEvent) => { - // 静默处理拖拽悬停 - }; - - const handleGlobalDrop = (event: DragEvent) => { - // 静默处理拖拽放置 - }; - - document.addEventListener('load-behavior-tree-file', handleLoadBehaviorTreeFile as EventListener); - document.addEventListener('file-load-error', handleFileLoadError as EventListener); - document.addEventListener('keydown', handleKeydown); - document.addEventListener('dragend', handleGlobalDragEnd); - document.addEventListener('dragover', handleGlobalDragOver); - document.addEventListener('drop', handleGlobalDrop); - - onUnmounted(() => { - document.removeEventListener('load-behavior-tree-file', handleLoadBehaviorTreeFile as EventListener); - document.removeEventListener('file-load-error', handleFileLoadError as EventListener); - document.removeEventListener('keydown', handleKeydown); - document.removeEventListener('dragend', handleGlobalDragEnd); - document.removeEventListener('dragover', handleGlobalDragOver); - document.removeEventListener('drop', handleGlobalDrop); - - // 清理暴露的方法 - if (appContainer) { - delete (appContainer as any).loadFileContent; - delete (appContainer as any).showError; - } - }); - }); - - onUnmounted(() => { - document.removeEventListener('mousemove', onNodeDrag); - document.removeEventListener('mouseup', onNodeDragEnd); - }); - - return { - canvasAreaRef, - svgRef, - ...appState, - ...computedProps, - ...nodeOps, - ...fileOps, - ...codeGen, - ...installation, - ...blackboard, - handleInstall, - connectionState, - ...connectionManager, - ...canvasManager, - ...nodeDisplay, - startNodeDrag, - dragState, - autoLayout, - validateTree, - clearAllConnections, - copyToClipboard, - saveToFile, - selectNode: (nodeId: string) => { - appState.selectedNodeId.value = nodeId; - appState.selectedConditionNodeId.value = null; - }, - selectConditionNode: (decoratorNode: any) => { - appState.selectedNodeId.value = null; - appState.selectedConditionNodeId.value = decoratorNode.id; - }, - updateNodeProperty: (path: string, value: any) => { - if (appState.selectedConditionNodeId.value) { - // 条件节点的属性更新 - 需要同步到装饰器 - const decoratorNode = appState.getNodeByIdLocal(appState.selectedConditionNodeId.value); - if (decoratorNode) { - // 解析路径,例如 "properties.variableName.value" -> "variableName" - const pathParts = path.split('.'); - if (pathParts[0] === 'properties' && pathParts[2] === 'value') { - const propertyName = pathParts[1]; - - // 直接更新装饰器的属性 - if (!decoratorNode.properties) { - decoratorNode.properties = {}; - } - decoratorNode.properties[propertyName] = value; - - // 强制触发响应式更新 - const nodeIndex = appState.treeNodes.value.findIndex(n => n.id === decoratorNode.id); - if (nodeIndex > -1) { - const newNodes = [...appState.treeNodes.value]; - newNodes[nodeIndex] = { ...decoratorNode }; - appState.treeNodes.value = newNodes; - } - } - } - } else { - // 普通节点属性更新 - nodeOps.updateNodeProperty(path, value); - } - }, - conditionDragState: conditionAttachment.dragState, - startConditionDrag: conditionAttachment.startConditionDrag, - handleDecoratorDragOver: conditionAttachment.handleDecoratorDragOver, - handleDecoratorDragLeave: conditionAttachment.handleDecoratorDragLeave, - attachConditionToDecorator: conditionAttachment.attachConditionToDecorator, - getConditionDisplayText: conditionAttachment.getConditionDisplayText, - getConditionProperties: conditionAttachment.getConditionProperties, - removeConditionFromDecorator: conditionAttachment.removeConditionFromDecorator, - canAcceptCondition: conditionAttachment.canAcceptCondition, - resetDragState: conditionAttachment.resetDragState, - toggleConditionExpanded: conditionAttachment.toggleConditionExpanded, - - handleCanvasDrop: (event: DragEvent) => { - if (conditionAttachment.handleCanvasDrop(event)) { - return; - } - nodeOps.onCanvasDrop(event); - }, - - handleConditionNodeDragStart: (event: DragEvent, template: any) => { - if (template.isDraggableCondition) { - conditionAttachment.startConditionDrag(event, template); - } else { - nodeOps.onNodeDragStart(event, template); - } - }, - - handleNodeDrop: (event: DragEvent, node: any) => { - if (node.type === 'conditional-decorator') { - event.preventDefault(); - event.stopPropagation(); - return conditionAttachment.attachConditionToDecorator(event, node); - } - }, - - handleNodeDragOver: (event: DragEvent, node: any) => { - if (node.type === 'conditional-decorator') { - event.preventDefault(); - event.stopPropagation(); - return conditionAttachment.handleDecoratorDragOver(event, node); - } - }, - - handleNodeDragLeave: (event: DragEvent, node: any) => { - if (node.type === 'conditional-decorator') { - conditionAttachment.handleDecoratorDragLeave(node); - } - }, - isBlackboardDroppable, - isBlackboardReference, - handleBlackboardDrop, - handleBlackboardDragOver, - handleBlackboardDragLeave, - clearBlackboardReference, - - // 节点类型识别方法 - getOriginalNodeName, - getNodeTemplate, - getNodeCategory, - isNodeNameCustomized, - resetNodeToOriginalName, - - blackboardCollapsed: computed({ - get: () => blackboardSidebarState.collapsed, - set: (value: boolean) => blackboardSidebarState.collapsed = value - }), - blackboardTransparent: computed({ - get: () => blackboardSidebarState.transparent, - set: (value: boolean) => blackboardSidebarState.transparent = value - }) - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useBlackboard.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useBlackboard.ts deleted file mode 100644 index a425fffc..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useBlackboard.ts +++ /dev/null @@ -1,474 +0,0 @@ -import { ref, computed, reactive } from 'vue'; - -export interface BlackboardVariable { - name: string; - type: 'string' | 'number' | 'boolean' | 'vector2' | 'vector3' | 'object' | 'array'; - value: any; - defaultValue: any; - description?: string; - group?: string; - readOnly?: boolean; - constraints?: { - min?: number; - max?: number; - step?: number; - allowedValues?: string[]; - }; -} - -export interface BlackboardModalData { - name: string; - type: 'string' | 'number' | 'boolean' | 'vector2' | 'vector3' | 'object' | 'array'; - defaultValue: any; - description: string; - group: string; - readOnly: boolean; - constraints: { - min?: number; - max?: number; - step?: number; - }; - useAllowedValues: boolean; - allowedValuesText: string; -} - -export function useBlackboard() { - const blackboardVariables = ref>(new Map()); - const expandedGroups = ref>(new Set(['未分组'])); - const selectedVariable = ref(null); - const showBlackboardModal = ref(false); - const editingBlackboardVariable = ref(null); - - const blackboardModalData = reactive({ - name: '', - type: 'string', - defaultValue: '', - description: '', - group: '', - readOnly: false, - constraints: {}, - useAllowedValues: false, - allowedValuesText: '' - }); - - const blackboardCollapsed = ref(false); - const blackboardTransparent = ref(true); - - const blackboardVariablesArray = computed(() => { - return Array.from(blackboardVariables.value.values()); - }); - - const blackboardVariableGroups = computed(() => { - const groups: Record = {}; - - blackboardVariables.value.forEach(variable => { - const groupName = variable.group || '未分组'; - if (!groups[groupName]) { - groups[groupName] = []; - } - groups[groupName].push(variable); - }); - - const sortedGroups: Record = {}; - const groupNames = Object.keys(groups).sort((a, b) => { - if (a === '未分组') return -1; - if (b === '未分组') return 1; - return a.localeCompare(b); - }); - - groupNames.forEach(groupName => { - groups[groupName].sort((a, b) => a.name.localeCompare(b.name)); - sortedGroups[groupName] = groups[groupName]; - }); - - return sortedGroups; - }); - - const groupedBlackboardVariables = () => { - return Object.entries(blackboardVariableGroups.value); - }; - - const isGroupExpanded = (groupName: string): boolean => { - return expandedGroups.value.has(groupName); - }; - - const toggleGroup = (groupName: string) => { - if (expandedGroups.value.has(groupName)) { - expandedGroups.value.delete(groupName); - } else { - expandedGroups.value.add(groupName); - } - }; - - const getVariableTypeIcon = (type: string): string => { - const iconMap: Record = { - string: '📝', - number: '🔢', - boolean: '☑️', - vector2: '📐', - vector3: '🧊', - object: '📦', - array: '📋' - }; - return iconMap[type] || '❓'; - }; - - const formatBlackboardValue = (variable: BlackboardVariable): string => { - if (variable.value === null || variable.value === undefined) { - return 'null'; - } - - switch (variable.type) { - case 'boolean': - return variable.value ? 'true' : 'false'; - case 'string': - return `"${variable.value}"`; - case 'number': - return variable.value.toString(); - default: - return String(variable.value); - } - }; - - const hasVisibleConstraints = (variable: BlackboardVariable): boolean => { - if (!variable.constraints) return false; - - return !!( - variable.constraints.min !== undefined || - variable.constraints.max !== undefined || - variable.constraints.allowedValues?.length - ); - }; - - const formatConstraints = (constraints: BlackboardVariable['constraints']): string => { - const parts: string[] = []; - - if (constraints?.min !== undefined) { - parts.push(`最小: ${constraints.min}`); - } - if (constraints?.max !== undefined) { - parts.push(`最大: ${constraints.max}`); - } - if (constraints?.allowedValues?.length) { - parts.push(`可选: ${constraints.allowedValues.join(', ')}`); - } - - return parts.join(', '); - }; - - const getTypeDisplayName = (type: string): string => { - const typeMap: Record = { - string: 'STR', - number: 'NUM', - boolean: 'BOOL', - vector2: 'VEC2', - vector3: 'VEC3', - object: 'OBJ', - array: 'ARR' - }; - return typeMap[type] || type.toUpperCase(); - }; - - const getDisplayValue = (variable: BlackboardVariable): string => { - if (variable.value === null || variable.value === undefined) { - return 'null'; - } - - switch (variable.type) { - case 'string': - return String(variable.value); - case 'number': - return variable.value.toString(); - case 'boolean': - return variable.value ? 'true' : 'false'; - case 'vector2': - if (typeof variable.value === 'object' && variable.value.x !== undefined && variable.value.y !== undefined) { - return `(${variable.value.x}, ${variable.value.y})`; - } - return String(variable.value); - case 'vector3': - if (typeof variable.value === 'object' && variable.value.x !== undefined && variable.value.y !== undefined && variable.value.z !== undefined) { - return `(${variable.value.x}, ${variable.value.y}, ${variable.value.z})`; - } - return String(variable.value); - case 'object': - case 'array': - try { - const jsonStr = JSON.stringify(variable.value); - return jsonStr.length > 20 ? jsonStr.substring(0, 17) + '...' : jsonStr; - } catch { - return String(variable.value); - } - default: - return String(variable.value); - } - }; - - const saveBlackboardVariable = () => { - if (!blackboardModalData.name.trim()) { - alert('请输入变量名称'); - return; - } - - let finalValue = blackboardModalData.defaultValue; - - if (blackboardModalData.type === 'object' || blackboardModalData.type === 'array') { - try { - if (typeof blackboardModalData.defaultValue === 'string') { - finalValue = blackboardModalData.defaultValue ? JSON.parse(blackboardModalData.defaultValue) : (blackboardModalData.type === 'array' ? [] : {}); - } - } catch (error) { - alert('JSON格式错误,请检查输入'); - return; - } - } - - const constraints: BlackboardVariable['constraints'] = {}; - if (blackboardModalData.constraints.min !== undefined) constraints.min = blackboardModalData.constraints.min; - if (blackboardModalData.constraints.max !== undefined) constraints.max = blackboardModalData.constraints.max; - if (blackboardModalData.constraints.step !== undefined) constraints.step = blackboardModalData.constraints.step; - - if (blackboardModalData.useAllowedValues && blackboardModalData.allowedValuesText.trim()) { - constraints.allowedValues = blackboardModalData.allowedValuesText - .split('\n') - .map(val => val.trim()) - .filter(val => val.length > 0); - } - - const variable: BlackboardVariable = { - name: blackboardModalData.name, - type: blackboardModalData.type, - value: finalValue, - defaultValue: finalValue, - description: blackboardModalData.description, - group: blackboardModalData.group || undefined, - readOnly: blackboardModalData.readOnly, - constraints: Object.keys(constraints).length > 0 ? constraints : undefined - }; - - blackboardVariables.value.set(variable.name, variable); - - const groupName = variable.group || '未分组'; - expandedGroups.value.add(groupName); - - showBlackboardModal.value = false; - editingBlackboardVariable.value = null; - - Object.assign(blackboardModalData, { - name: '', - type: 'string', - defaultValue: '', - description: '', - group: '', - readOnly: false, - constraints: {}, - useAllowedValues: false, - allowedValuesText: '' - }); - }; - - const deleteBlackboardVariable = (variableName: string) => { - if (confirm(`确定要删除变量 "${variableName}" 吗?`)) { - blackboardVariables.value.delete(variableName); - } - }; - - const updateBlackboardVariable = (variableName: string, newValue: any) => { - const variable = blackboardVariables.value.get(variableName); - if (!variable) return; - - if (variable.readOnly) { - alert('该变量为只读,无法修改'); - return; - } - - const updatedVariable = { ...variable, value: newValue }; - blackboardVariables.value.set(variableName, updatedVariable); - }; - - const selectVariable = (variable: BlackboardVariable) => { - selectedVariable.value = variable; - }; - - const clearBlackboard = () => { - if (confirm('确定要清空所有变量吗?此操作不可恢复。')) { - blackboardVariables.value.clear(); - selectedVariable.value = null; - } - }; - - const resetBlackboardToDefaults = () => { - if (confirm('确定要重置所有变量到默认值吗?')) { - blackboardVariables.value.forEach((variable, name) => { - if (variable.defaultValue !== undefined) { - variable.value = variable.defaultValue; - blackboardVariables.value.set(name, { ...variable }); - } - }); - } - }; - - const exportBlackboard = () => { - const data = Array.from(blackboardVariables.value.values()); - const json = JSON.stringify(data, null, 2); - - try { - navigator.clipboard.writeText(json); - alert('Blackboard配置已复制到剪贴板'); - } catch (error) { - console.error('复制失败:', error); - } - }; - - const loadBlackboardFromArray = (variables: BlackboardVariable[]) => { - blackboardVariables.value.clear(); - variables.forEach(variable => { - if (variable.name && variable.type) { - blackboardVariables.value.set(variable.name, variable); - - // 展开变量所在的组 - const groupName = variable.group || '未分组'; - expandedGroups.value.add(groupName); - } - }); - }; - - const importBlackboard = () => { - const input = document.createElement('input'); - input.type = 'file'; - input.accept = '.json'; - - input.onchange = (event) => { - const file = (event.target as HTMLInputElement).files?.[0]; - if (!file) return; - - const reader = new FileReader(); - reader.onload = (e) => { - try { - const data = JSON.parse(e.target?.result as string); - if (!Array.isArray(data)) { - throw new Error('格式错误:期望数组格式'); - } - - let importCount = 0; - data.forEach(varData => { - if (varData.name && varData.type) { - blackboardVariables.value.set(varData.name, varData); - importCount++; - } - }); - - alert(`成功导入 ${importCount} 个变量`); - } catch (error) { - alert('导入失败:' + (error as Error).message); - } - }; - reader.readAsText(file); - }; - - input.click(); - }; - - const onVariableDragStart = (event: DragEvent, variable: BlackboardVariable) => { - if (!event.dataTransfer) { - return; - } - - const dragData = { - name: variable.name, - type: variable.type, - value: variable.value - }; - - event.dataTransfer.setData('application/blackboard-variable', JSON.stringify(dragData)); - event.dataTransfer.effectAllowed = 'copy'; - - // 添加视觉反馈 - const dragElement = event.currentTarget as HTMLElement; - if (dragElement) { - dragElement.style.opacity = '0.8'; - setTimeout(() => { - dragElement.style.opacity = '1'; - }, 100); - } - }; - - const editVariable = (variable: BlackboardVariable) => { - editingBlackboardVariable.value = variable; - - Object.assign(blackboardModalData, { - name: variable.name, - type: variable.type, - defaultValue: (variable.type === 'object' || variable.type === 'array') ? JSON.stringify(variable.value, null, 2) : variable.value, - description: variable.description || '', - group: variable.group || '', - readOnly: variable.readOnly || false, - constraints: { - min: variable.constraints?.min, - max: variable.constraints?.max, - step: variable.constraints?.step - }, - useAllowedValues: !!(variable.constraints?.allowedValues?.length), - allowedValuesText: variable.constraints?.allowedValues?.join('\n') || '' - }); - - showBlackboardModal.value = true; - }; - - const addBlackboardVariable = () => { - editingBlackboardVariable.value = null; - Object.assign(blackboardModalData, { - name: '', - type: 'string', - defaultValue: '', - description: '', - group: '', - readOnly: false, - constraints: {}, - useAllowedValues: false, - allowedValuesText: '' - }); - showBlackboardModal.value = true; - }; - - return { - blackboardVariables: blackboardVariablesArray, - selectedVariable, - showBlackboardModal, - editingBlackboardVariable, - blackboardModalData, - expandedGroups, - blackboardVariableGroups, - blackboardCollapsed, - blackboardTransparent, - - groupedBlackboardVariables, - isGroupExpanded, - toggleGroup, - getVariableTypeIcon, - formatBlackboardValue, - hasVisibleConstraints, - formatConstraints, - getTypeDisplayName, - getDisplayValue, - - addBlackboardVariable, - saveBlackboardVariable, - deleteBlackboardVariable, - removeBlackboardVariable: deleteBlackboardVariable, - updateBlackboardVariable, - editVariable, - selectVariable, - clearBlackboard, - resetBlackboardToDefaults, - loadBlackboardFromArray, - exportBlackboard, - importBlackboard, - - onBlackboardDragStart: onVariableDragStart, - editBlackboardVariable: editVariable, - onBlackboardValueChange: (variable: BlackboardVariable) => { - updateBlackboardVariable(variable.name, variable.value); - } - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useCanvasManager.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useCanvasManager.ts deleted file mode 100644 index f9c47d0b..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useCanvasManager.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { Ref, ref } from 'vue'; -import { TreeNode, DragState } from '../types'; - -/** - * 画布管理功能 - */ -export function useCanvasManager( - panX: Ref, - panY: Ref, - zoomLevel: Ref, - treeNodes: Ref, - selectedNodeId: Ref, - canvasAreaRef: Ref, - updateConnections: () => void -) { - // 画布尺寸 - 使用默认值或从DOM获取 - const canvasWidth = ref(800); - const canvasHeight = ref(600); - - // 拖拽状态 - const dragState = ref({ - isDraggingCanvas: false, - isDraggingNode: false, - dragNodeId: null, - dragStartX: 0, - dragStartY: 0, - dragNodeStartX: 0, - dragNodeStartY: 0, - isConnecting: false, - connectionStart: null, - connectionEnd: { x: 0, y: 0 } - }); - - // 如果有canvas引用,更新尺寸 - if (canvasAreaRef.value) { - const rect = canvasAreaRef.value.getBoundingClientRect(); - canvasWidth.value = rect.width; - canvasHeight.value = rect.height; - } - - // 画布操作功能 - const onCanvasWheel = (event: WheelEvent) => { - event.preventDefault(); - - const zoomSpeed = 0.1; - const delta = event.deltaY > 0 ? -zoomSpeed : zoomSpeed; - const newZoom = Math.max(0.1, Math.min(3, zoomLevel.value + delta)); - - zoomLevel.value = newZoom; - }; - - const onCanvasMouseDown = (event: MouseEvent) => { - // 只在空白区域开始画布拖拽 - if (event.target === event.currentTarget) { - dragState.value.isDraggingCanvas = true; - dragState.value.dragStartX = event.clientX; - dragState.value.dragStartY = event.clientY; - - document.addEventListener('mousemove', onCanvasMouseMove); - document.addEventListener('mouseup', onCanvasMouseUp); - } - }; - - const onCanvasMouseMove = (event: MouseEvent) => { - if (dragState.value.isDraggingCanvas) { - const deltaX = event.clientX - dragState.value.dragStartX; - const deltaY = event.clientY - dragState.value.dragStartY; - - panX.value += deltaX; - panY.value += deltaY; - - dragState.value.dragStartX = event.clientX; - dragState.value.dragStartY = event.clientY; - } - }; - - const onCanvasMouseUp = (event: MouseEvent) => { - if (dragState.value.isDraggingCanvas) { - dragState.value.isDraggingCanvas = false; - - document.removeEventListener('mousemove', onCanvasMouseMove); - document.removeEventListener('mouseup', onCanvasMouseUp); - } - }; - - // 缩放控制 - const zoomIn = () => { - zoomLevel.value = Math.min(3, zoomLevel.value + 0.1); - }; - - const zoomOut = () => { - zoomLevel.value = Math.max(0.1, zoomLevel.value - 0.1); - }; - - const resetZoom = () => { - zoomLevel.value = 1; - }; - - const centerView = () => { - if (treeNodes.value.length === 0) { - panX.value = 0; - panY.value = 0; - return; - } - - let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; - - treeNodes.value.forEach(node => { - // 尝试从DOM获取实际节点尺寸,否则使用默认值 - const nodeElement = document.querySelector(`[data-node-id="${node.id}"]`); - let nodeWidth = 150; - let nodeHeight = 80; // 使用基础高度 - - if (nodeElement) { - const rect = nodeElement.getBoundingClientRect(); - nodeWidth = rect.width / zoomLevel.value; - nodeHeight = rect.height / zoomLevel.value; - } - - minX = Math.min(minX, node.x); - minY = Math.min(minY, node.y); - maxX = Math.max(maxX, node.x + nodeWidth); - maxY = Math.max(maxY, node.y + nodeHeight); - }); - - const centerX = (minX + maxX) / 2; - const centerY = (minY + maxY) / 2; - - panX.value = canvasWidth.value / 2 - centerX * zoomLevel.value; - panY.value = canvasHeight.value / 2 - centerY * zoomLevel.value; - }; - - // 网格样式计算 - const gridStyle = () => { - const gridSize = 20 * zoomLevel.value; - return { - backgroundSize: `${gridSize}px ${gridSize}px`, - backgroundPosition: `${panX.value % gridSize}px ${panY.value % gridSize}px` - }; - }; - - // 节点拖拽功能 - const startNodeDrag = (event: MouseEvent, node: any) => { - event.preventDefault(); - event.stopPropagation(); - - dragState.value.isDraggingNode = true; - dragState.value.dragNodeId = node.id; - dragState.value.dragStartX = event.clientX; - dragState.value.dragStartY = event.clientY; - dragState.value.dragNodeStartX = node.x; - dragState.value.dragNodeStartY = node.y; - - const nodeElement = event.currentTarget as HTMLElement; - nodeElement.classList.add('dragging'); - - document.addEventListener('mousemove', onNodeDrag); - document.addEventListener('mouseup', onNodeDragEnd); - }; - - const onNodeDrag = (event: MouseEvent) => { - if (!dragState.value.isDraggingNode || !dragState.value.dragNodeId) return; - - const deltaX = (event.clientX - dragState.value.dragStartX) / zoomLevel.value; - const deltaY = (event.clientY - dragState.value.dragStartY) / zoomLevel.value; - - const node = treeNodes.value.find(n => n.id === dragState.value.dragNodeId); - if (node) { - node.x = dragState.value.dragNodeStartX + deltaX; - node.y = dragState.value.dragNodeStartY + deltaY; - - updateConnections(); - } - }; - - const onNodeDragEnd = (event: MouseEvent) => { - if (dragState.value.isDraggingNode) { - const draggingNodes = document.querySelectorAll('.tree-node.dragging'); - draggingNodes.forEach(node => node.classList.remove('dragging')); - - dragState.value.isDraggingNode = false; - dragState.value.dragNodeId = null; - - updateConnections(); - - document.removeEventListener('mousemove', onNodeDrag); - document.removeEventListener('mouseup', onNodeDragEnd); - } - }; - - return { - onCanvasWheel, - onCanvasMouseDown, - onCanvasMouseMove, - onCanvasMouseUp, - zoomIn, - zoomOut, - resetZoom, - centerView, - gridStyle, - startNodeDrag - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useCodeGeneration.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useCodeGeneration.ts deleted file mode 100644 index 755ad7ce..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useCodeGeneration.ts +++ /dev/null @@ -1,490 +0,0 @@ -import { Ref } from 'vue'; -import { TreeNode } from '../types'; -import { NodeTemplate } from '../data/nodeTemplates'; - -/** - * 代码生成管理 - */ -export function useCodeGeneration( - treeNodes: Ref, - nodeTemplates: Ref, - getNodeByIdLocal: (id: string) => TreeNode | undefined, - rootNode: () => TreeNode | null, - blackboardVariables?: Ref> -) { - - // 生成行为树配置JSON - const generateBehaviorTreeConfig = () => { - const root = rootNode(); - - if (!root) { - return null; - } - - const config: any = { - version: "1.0.0", - type: "behavior-tree", - metadata: { - createdAt: new Date().toISOString(), - hasECSNodes: hasECSNodes(), - nodeCount: treeNodes.value.length - }, - tree: generateNodeConfig(root) - }; - - // 包含黑板数据 - if (blackboardVariables && blackboardVariables.value.size > 0) { - config.blackboard = Array.from(blackboardVariables.value.values()); - } - - return config; - }; - - // 生成可读的配置JSON字符串 - const generateConfigJSON = (): string => { - const config = generateBehaviorTreeConfig(); - - if (!config) { - return '// 请先添加根节点'; - } - - return JSON.stringify(config, null, 2); - }; - - // 生成TypeScript构建代码(用于运行时从配置创建行为树) - const generateTypeScriptCode = (): string => { - const config = generateBehaviorTreeConfig(); - - if (!config) { - return '// 请先添加根节点'; - } - - const { behaviorTreeImports, ecsImports } = getRequiredImports(); - - let importsCode = ''; - if (behaviorTreeImports.length > 0) { - importsCode += `import { ${behaviorTreeImports.join(', ')}, BehaviorTreeBuilder } from '@esengine/ai';\n`; - } - if (ecsImports.length > 0) { - importsCode += `import { ${ecsImports.join(', ')} } from '@esengine/ecs-framework';\n`; - } - - const contextType = hasECSNodes() ? 'Entity' : 'any'; - const configString = JSON.stringify(config, null, 4); - - return `${importsCode} -// 行为树配置 -const behaviorTreeConfig = ${configString}; - -// 从配置创建行为树 -export function createBehaviorTree(context?: T): BehaviorTree { - return BehaviorTreeBuilder.fromConfig(behaviorTreeConfig, context); -} - -// 直接导出配置(用于序列化保存) -export const config = behaviorTreeConfig;`; - }; - - const getRequiredImports = (): { behaviorTreeImports: string[], ecsImports: string[] } => { - const behaviorTreeImports = new Set(); - const ecsImports = new Set(); - - // 总是需要这些基础类 - behaviorTreeImports.add('BehaviorTree'); - behaviorTreeImports.add('TaskStatus'); - - treeNodes.value.forEach(node => { - const template = nodeTemplates.value.find(t => t.className === node.type || t.type === node.type); - if (template?.className) { - if (template.namespace?.includes('ecs-integration')) { - behaviorTreeImports.add(template.className); - ecsImports.add('Entity'); - ecsImports.add('Component'); - } else { - behaviorTreeImports.add(template.className); - } - } - }); - - return { - behaviorTreeImports: Array.from(behaviorTreeImports), - ecsImports: Array.from(ecsImports) - }; - }; - - const hasECSNodes = (): boolean => { - return treeNodes.value.some(node => { - const template = nodeTemplates.value.find(t => t.className === node.type || t.type === node.type); - return template?.namespace?.includes('ecs-integration'); - }); - }; - - // 生成节点配置对象 - const generateNodeConfig = (node: TreeNode): any => { - const template = nodeTemplates.value.find(t => t.className === node.type || t.type === node.type); - - if (!template || !template.className) { - return { - type: node.type, - error: "未知节点类型" - }; - } - - const nodeConfig: any = { - id: node.id, - type: template.className, - namespace: template.namespace || 'behaviourTree', - properties: {} - }; - - // 处理节点属性 - if (node.properties) { - Object.entries(node.properties).forEach(([key, prop]) => { - if (prop.value !== undefined && prop.value !== '') { - nodeConfig.properties[key] = { - type: prop.type, - value: prop.value - }; - } - }); - } - - // 处理子节点 - if (node.children && node.children.length > 0) { - nodeConfig.children = node.children - .map(childId => getNodeByIdLocal(childId)) - .filter(Boolean) - .map(child => generateNodeConfig(child!)); - } - - return nodeConfig; - }; - - const generateNodeCode = (node: TreeNode, indent: number = 0): string => { - const spaces = ' '.repeat(indent); - const template = nodeTemplates.value.find(t => t.className === node.type || t.type === node.type); - - if (!template || !template.className) { - return `${spaces}// 未知节点类型: ${node.type}`; - } - - let code = `${spaces}new ${template.className}(`; - const params: string[] = []; - - // 处理特定节点的构造函数参数 - if (template.namespace?.includes('ecs-integration')) { - // ECS节点的特殊处理 - switch (template.className) { - case 'HasComponentCondition': - case 'AddComponentAction': - case 'RemoveComponentAction': - case 'ModifyComponentAction': - if (node.properties?.componentType?.value) { - params.push(node.properties.componentType.value); - } - if (template.className === 'AddComponentAction' && node.properties?.componentFactory?.value) { - params.push(node.properties.componentFactory.value); - } - if (template.className === 'ModifyComponentAction' && node.properties?.modifierCode?.value) { - params.push(node.properties.modifierCode.value); - } - break; - case 'HasTagCondition': - if (node.properties?.tag?.value !== undefined) { - params.push(node.properties.tag.value.toString()); - } - break; - case 'IsActiveCondition': - if (node.properties?.checkHierarchy?.value !== undefined) { - params.push(node.properties.checkHierarchy.value.toString()); - } - break; - case 'WaitTimeAction': - if (node.properties?.waitTime?.value !== undefined) { - params.push(node.properties.waitTime.value.toString()); - } - break; - } - } else { - // 普通行为树节点的处理 - switch (template.className) { - case 'ExecuteAction': - case 'ExecuteActionConditional': - if (node.properties?.actionCode?.value || node.properties?.conditionCode?.value) { - const code = node.properties.actionCode?.value || node.properties.conditionCode?.value; - params.push(code); - if (node.properties?.actionName?.value) { - params.push(`{ name: "${node.properties.actionName.value}" }`); - } - } - break; - case 'WaitAction': - if (node.properties?.waitTime?.value !== undefined) { - params.push(node.properties.waitTime.value.toString()); - } - break; - case 'LogAction': - if (node.properties?.message?.value) { - params.push(`"${node.properties.message.value}"`); - } - break; - case 'Repeater': - if (node.properties?.repeatCount?.value !== undefined) { - params.push(node.properties.repeatCount.value.toString()); - } - break; - case 'Sequence': - case 'Selector': - if (node.properties?.abortType?.value && node.properties.abortType.value !== 'None') { - params.push(`AbortTypes.${node.properties.abortType.value}`); - } - break; - } - } - - code += params.join(', '); - code += ')'; - - // 处理子节点(对于复合节点和装饰器) - if (template.canHaveChildren && node.children && node.children.length > 0) { - const children = node.children - .map(childId => getNodeByIdLocal(childId)) - .filter(Boolean) - .map(child => generateNodeCode(child!, indent + 1)); - - if (children.length > 0) { - const className = template.className; // 保存到局部变量 - if (template.category === 'decorator') { - // 装饰器只有一个子节点 - code = code.slice(0, -1); // 移除最后的 ')' - const varName = className.toLowerCase(); - code += `;\n${spaces}${varName}.child = ${children[0].trim()};\n${spaces}return ${varName}`; - } else if (template.category === 'composite') { - // 复合节点需要添加子节点 - code = code.slice(0, -1); // 移除最后的 ')' - code += `;\n`; - children.forEach(child => { - code += `${spaces}${className.toLowerCase()}.addChild(${child.trim()});\n`; - }); - code += `${spaces}return ${className.toLowerCase()}`; - } - } - } - - return code; - }; - - // 从配置创建行为树节点 - const createTreeFromConfig = (config: any): TreeNode[] => { - console.log('createTreeFromConfig被调用,接收到的配置:', config); - console.log('nodeTemplates当前数量:', nodeTemplates.value.length); - - // 处理两种不同的文件格式 - if (config.nodes && Array.isArray(config.nodes)) { - console.log('使用nodes格式处理,节点数量:', config.nodes.length); - const result = createTreeFromNodesFormat(config); - console.log('nodes格式处理结果:', result); - return result; - } else if (config.tree) { - console.log('使用tree格式处理'); - const result = createTreeFromTreeFormat(config); - console.log('tree格式处理结果:', result); - return result; - } else { - console.log('配置格式不匹配,返回空数组'); - return []; - } - }; - - // 处理新格式(nodes数组格式) - const createTreeFromNodesFormat = (config: any): TreeNode[] => { - console.log('createTreeFromNodesFormat开始处理'); - - if (!config.nodes || !Array.isArray(config.nodes)) { - console.log('nodes数据无效'); - return []; - } - - const nodes: TreeNode[] = []; - - config.nodes.forEach((nodeConfig: any, index: number) => { - console.log(`处理第${index + 1}个节点:`, nodeConfig); - - const template = findTemplateByType(nodeConfig.type); - console.log(`为节点类型 "${nodeConfig.type}" 找到的模板:`, template); - - if (!template) { - console.warn(`未找到节点类型 "${nodeConfig.type}" 的模板`); - return; - } - - const node: TreeNode = { - id: nodeConfig.id || generateNodeId(), - type: template.type, - name: nodeConfig.name || template.name, - icon: nodeConfig.icon || template.icon, - description: nodeConfig.description || template.description, - canHaveChildren: template.canHaveChildren, - canHaveParent: template.canHaveParent, - x: nodeConfig.x || 400, - y: nodeConfig.y || 100, - properties: {}, - children: nodeConfig.children || [], - parent: nodeConfig.parent, - hasError: false - }; - - // 恢复属性 - if (nodeConfig.properties && template.properties) { - Object.entries(nodeConfig.properties).forEach(([key, propConfig]: [string, any]) => { - if (template.properties![key]) { - node.properties![key] = { - ...template.properties![key], - value: propConfig.value !== undefined ? propConfig.value : template.properties![key].value - }; - } - }); - } - - // 确保所有模板属性都有默认值 - if (template.properties) { - Object.entries(template.properties).forEach(([key, propDef]) => { - if (!node.properties![key]) { - node.properties![key] = { ...propDef }; - } - }); - } - - console.log(`创建的节点:`, node); - nodes.push(node); - }); - - console.log(`createTreeFromNodesFormat完成,总共创建了${nodes.length}个节点`); - return nodes; - }; - - // 处理旧格式(tree对象格式) - const createTreeFromTreeFormat = (config: any): TreeNode[] => { - if (!config.tree) { - return []; - } - - const nodes: TreeNode[] = []; - const processNode = (nodeConfig: any, parent?: TreeNode): TreeNode => { - const template = findTemplateByType(nodeConfig.type); - if (!template) { - throw new Error(`未知节点类型: ${nodeConfig.type}`); - } - - const node: TreeNode = { - id: nodeConfig.id || generateNodeId(), - type: template.type, - name: template.name, - icon: template.icon, - description: template.description, - canHaveChildren: template.canHaveChildren, - canHaveParent: template.canHaveParent, - x: 400, - y: 100, - properties: {}, - children: [], - parent: parent?.id, - hasError: false - }; - - // 恢复属性 - if (nodeConfig.properties && template.properties) { - Object.entries(nodeConfig.properties).forEach(([key, propConfig]: [string, any]) => { - if (template.properties![key]) { - node.properties![key] = { - ...template.properties![key], - value: propConfig.value !== undefined ? propConfig.value : template.properties![key].value - }; - } - }); - } - - // 确保所有模板属性都有默认值 - if (template.properties) { - Object.entries(template.properties).forEach(([key, propDef]) => { - if (!node.properties![key]) { - node.properties![key] = { ...propDef }; - } - }); - } - - nodes.push(node); - - // 处理子节点 - if (nodeConfig.children && Array.isArray(nodeConfig.children)) { - nodeConfig.children.forEach((childConfig: any) => { - const childNode = processNode(childConfig, node); - node.children!.push(childNode.id); - }); - } - - return node; - }; - - processNode(config.tree); - return nodes; - }; - - // 通过类型名查找模板(支持多种匹配方式) - const findTemplateByType = (typeName: string): NodeTemplate | undefined => { - // 直接匹配 type 字段 - let template = nodeTemplates.value.find(t => t.type === typeName); - if (template) return template; - - // 匹配 className 字段 - template = nodeTemplates.value.find(t => t.className === typeName); - if (template) return template; - - // 大小写不敏感匹配 type - template = nodeTemplates.value.find(t => t.type.toLowerCase() === typeName.toLowerCase()); - if (template) return template; - - // 大小写不敏感匹配 className - template = nodeTemplates.value.find(t => t.className && t.className.toLowerCase() === typeName.toLowerCase()); - if (template) return template; - - // 特殊映射处理 - const typeMapping: Record = { - 'Sequence': 'sequence', - 'Selector': 'selector', - 'Parallel': 'parallel', - 'Inverter': 'inverter', - 'Repeater': 'repeater', - 'AlwaysSucceed': 'always-succeed', - 'AlwaysFail': 'always-fail', - 'UntilSuccess': 'until-success', - 'UntilFail': 'until-fail', - 'ExecuteAction': 'execute-action', - 'LogAction': 'log-action', - 'WaitAction': 'wait-action' - }; - - const mappedType = typeMapping[typeName]; - if (mappedType) { - template = nodeTemplates.value.find(t => t.type === mappedType); - if (template) return template; - } - - return undefined; - }; - - // 生成唯一节点ID - const generateNodeId = (): string => { - return 'node_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); - }; - - return { - generateBehaviorTreeConfig, - generateConfigJSON, - generateTypeScriptCode, - generateNodeCode, - generateNodeConfig, - createTreeFromConfig, - getRequiredImports - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useComputedProperties.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useComputedProperties.ts deleted file mode 100644 index 7385b080..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useComputedProperties.ts +++ /dev/null @@ -1,372 +0,0 @@ -import { Ref, computed } from 'vue'; -import { TreeNode } from '../types'; -import { NodeTemplate } from '../data/nodeTemplates'; -import { getRootNode } from '../utils/nodeUtils'; -import { getInstallStatusText, getInstallStatusClass } from '../utils/installUtils'; -import { getGridStyle } from '../utils/canvasUtils'; - -/** - * 计算属性管理 - */ -export function useComputedProperties( - nodeTemplates: Ref, - nodeSearchText: Ref, - treeNodes: Ref, - selectedNodeId: Ref, - selectedConditionNodeId: Ref, - checkingStatus: Ref, - isInstalling: Ref, - isInstalled: Ref, - version: Ref, - exportFormat: Ref, - panX: Ref, - panY: Ref, - zoomLevel: Ref, - getNodeByIdLocal: (id: string) => TreeNode | undefined, - codeGeneration?: { - generateConfigJSON: () => string; - generateTypeScriptCode: () => string; - } -) { - // 过滤节点 - const filteredRootNodes = () => { - return nodeTemplates.value.filter(node => - node.category === 'root' && - node.name.toLowerCase().includes(nodeSearchText.value.toLowerCase()) - ); - }; - - const filteredCompositeNodes = () => { - return nodeTemplates.value.filter(node => - node.category === 'composite' && - node.name.toLowerCase().includes(nodeSearchText.value.toLowerCase()) - ); - }; - - const filteredDecoratorNodes = () => { - return nodeTemplates.value.filter(node => - node.category === 'decorator' && - node.name.toLowerCase().includes(nodeSearchText.value.toLowerCase()) - ); - }; - - const filteredActionNodes = () => { - return nodeTemplates.value.filter(node => - node.category === 'action' && - node.name.toLowerCase().includes(nodeSearchText.value.toLowerCase()) - ); - }; - - const filteredConditionNodes = () => { - return nodeTemplates.value.filter(node => - node.category === 'condition' && - node.name.toLowerCase().includes(nodeSearchText.value.toLowerCase()) - ); - }; - - const filteredECSNodes = () => { - return nodeTemplates.value.filter(node => - node.category === 'ecs' && - node.name.toLowerCase().includes(nodeSearchText.value.toLowerCase()) - ); - }; - - // 选中的节点 - 使用computed确保响应式更新 - const selectedNode = computed(() => { - if (!selectedNodeId.value) return null; - - // 直接从treeNodes数组中查找,确保获取最新的节点状态 - const node = treeNodes.value.find(n => n.id === selectedNodeId.value); - return node || null; - }); - - // 当前选中的条件节点(用于编辑条件属性) - const selectedConditionNode = computed(() => { - if (!selectedConditionNodeId.value) return null; - const decoratorNode = treeNodes.value.find(n => n.id === selectedConditionNodeId.value); - if (!decoratorNode || !decoratorNode.attachedCondition) return null; - - // 根据条件类型重新构建属性结构 - const conditionProperties = reconstructConditionProperties( - decoratorNode.attachedCondition.type, - decoratorNode.properties || {} - ); - - // 创建一个虚拟的条件节点对象,用于属性编辑 - return { - id: decoratorNode.id + '_condition', - name: decoratorNode.attachedCondition.name + '(条件)', - type: decoratorNode.attachedCondition.type, - icon: decoratorNode.attachedCondition.icon, - properties: conditionProperties, - isConditionNode: true, - parentDecorator: decoratorNode - }; - }); - - /** - * 根据条件类型重新构建属性结构 - * 将装饰器的扁平属性转换回条件模板的属性结构 - */ - const reconstructConditionProperties = (conditionType: string, decoratorProperties: Record) => { - switch (conditionType) { - case 'condition-random': - return { - successProbability: { - type: 'number', - name: '成功概率', - value: decoratorProperties.successProbability || 0.5, - description: '条件成功的概率 (0.0 - 1.0)' - } - }; - - case 'condition-component': - return { - componentType: { - type: 'string', - name: '组件类型', - value: decoratorProperties.componentType || '', - description: '要检查的组件类型名称' - } - }; - - case 'condition-tag': - return { - tagValue: { - type: 'number', - name: '标签值', - value: decoratorProperties.tagValue || 0, - description: '要检查的标签值' - } - }; - - case 'condition-active': - return { - checkHierarchy: { - type: 'boolean', - name: '检查层级激活', - value: decoratorProperties.checkHierarchy || false, - description: '是否检查整个层级的激活状态' - } - }; - - case 'condition-numeric': - return { - propertyPath: { - type: 'string', - name: '属性路径', - value: decoratorProperties.propertyPath || 'context.someValue', - description: '要比较的数值属性路径' - }, - compareOperator: { - type: 'select', - name: '比较操作符', - value: decoratorProperties.compareOperator || 'greater', - options: ['greater', 'less', 'equal', 'greaterEqual', 'lessEqual', 'notEqual'], - description: '数值比较的操作符' - }, - compareValue: { - type: 'number', - name: '比较值', - value: decoratorProperties.compareValue || 0, - description: '用于比较的目标值' - } - }; - - case 'condition-property': - return { - propertyPath: { - type: 'string', - name: '属性路径', - value: decoratorProperties.propertyPath || 'context.someProperty', - description: '要检查的属性路径' - } - }; - - case 'condition-custom': - return { - conditionCode: { - type: 'code', - name: '条件代码', - value: decoratorProperties.conditionCode || '(context) => true', - description: '自定义条件判断函数' - } - }; - - // Blackboard相关条件(使用实际的模板类型名) - case 'blackboard-variable-exists': - return { - variableName: { - type: 'string', - name: '变量名', - value: decoratorProperties.variableName || '', - description: '要检查的黑板变量名' - }, - invert: { - type: 'boolean', - name: '反转结果', - value: decoratorProperties.invert || false, - description: '是否反转检查结果' - } - }; - - case 'blackboard-value-comparison': - return { - variableName: { - type: 'string', - name: '变量名', - value: decoratorProperties.variableName || '', - description: '要比较的黑板变量名' - }, - operator: { - type: 'select', - name: '比较操作符', - value: decoratorProperties.operator || 'equal', - options: ['equal', 'notEqual', 'greater', 'greaterOrEqual', 'less', 'lessOrEqual', 'contains', 'notContains'], - description: '比较操作类型' - }, - compareValue: { - type: 'string', - name: '比较值', - value: decoratorProperties.compareValue || '', - description: '用于比较的值(留空则使用比较变量)' - }, - compareVariable: { - type: 'string', - name: '比较变量名', - value: decoratorProperties.compareVariable || '', - description: '用于比较的另一个黑板变量名' - } - }; - - case 'blackboard-variable-type-check': - return { - variableName: { - type: 'string', - name: '变量名', - value: decoratorProperties.variableName || '', - description: '要检查的黑板变量名' - }, - expectedType: { - type: 'select', - name: '期望类型', - value: decoratorProperties.expectedType || 'string', - options: ['string', 'number', 'boolean', 'vector2', 'vector3', 'object', 'array'], - description: '期望的变量类型' - } - }; - - case 'blackboard-variable-range-check': - return { - variableName: { - type: 'string', - name: '变量名', - value: decoratorProperties.variableName || '', - description: '要检查的数值型黑板变量名' - }, - minValue: { - type: 'number', - name: '最小值', - value: decoratorProperties.minValue || 0, - description: '范围的最小值(包含)' - }, - maxValue: { - type: 'number', - name: '最大值', - value: decoratorProperties.maxValue || 100, - description: '范围的最大值(包含)' - } - }; - - default: - // 对于未知的条件类型,尝试从装饰器属性中推断 - const reconstructed: Record = {}; - Object.keys(decoratorProperties).forEach(key => { - if (key !== 'conditionType') { - reconstructed[key] = { - type: typeof decoratorProperties[key] === 'number' ? 'number' : - typeof decoratorProperties[key] === 'boolean' ? 'boolean' : 'string', - name: key, - value: decoratorProperties[key], - description: `${key}参数` - }; - } - }); - return reconstructed; - } - }; - - // 当前显示在属性面板的节点(普通节点或条件节点) - const activeNode = computed(() => selectedConditionNode.value || selectedNode.value); - - // 根节点 - const rootNode = () => { - return getRootNode(treeNodes.value); - }; - - // 安装状态 - const installStatusClass = () => { - return getInstallStatusClass(isInstalling.value, isInstalled.value); - }; - - const installStatusText = () => { - return getInstallStatusText( - checkingStatus.value, - isInstalling.value, - isInstalled.value, - version.value - ); - }; - - // 验证结果 - const validationResult = () => { - if (treeNodes.value.length === 0) { - return { isValid: false, message: '行为树为空' }; - } - const root = rootNode(); - if (!root) { - return { isValid: false, message: '缺少根节点' }; - } - return { isValid: true, message: '行为树结构有效' }; - }; - - // 导出代码 - const exportedCode = () => { - if (!codeGeneration) { - return '// 代码生成器未初始化'; - } - - try { - if (exportFormat.value === 'json') { - return codeGeneration.generateConfigJSON(); - } else { - return codeGeneration.generateTypeScriptCode(); - } - } catch (error) { - return `// 代码生成失败: ${error}`; - } - }; - - // 网格样式 - const gridStyle = () => { - return getGridStyle(panX.value, panY.value, zoomLevel.value); - }; - - return { - filteredRootNodes, - filteredCompositeNodes, - filteredDecoratorNodes, - filteredActionNodes, - filteredConditionNodes, - filteredECSNodes, - selectedNode, - selectedConditionNode, - activeNode, - rootNode, - installStatusClass, - installStatusText, - validationResult, - exportedCode, - gridStyle - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useConditionAttachment.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useConditionAttachment.ts deleted file mode 100644 index c66c76ac..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useConditionAttachment.ts +++ /dev/null @@ -1,482 +0,0 @@ -import { ref, reactive, Ref } from 'vue'; -import { TreeNode } from '../types'; -import { NodeTemplate } from '../data/nodeTemplates'; - -/** - * 拖拽状态 - */ -interface DragState { - isDraggingCondition: boolean; - conditionTemplate: NodeTemplate | null; - mousePosition: { x: number, y: number } | null; - hoveredDecoratorId: string | null; -} - -/** - * 条件节点吸附功能 - */ -export function useConditionAttachment( - treeNodes: Ref, - getNodeByIdLocal: (id: string) => TreeNode | undefined -) { - - const dragState = reactive({ - isDraggingCondition: false, - conditionTemplate: null, - mousePosition: null, - hoveredDecoratorId: null - }); - - /** - * 检查节点是否为条件装饰器 - */ - const isConditionalDecorator = (node: TreeNode): boolean => { - return node.type === 'conditional-decorator'; - }; - - /** - * 开始拖拽条件节点 - */ - const startConditionDrag = (event: DragEvent, template: NodeTemplate) => { - console.log('🎯 开始条件拖拽:', template.name, template.isDraggableCondition); - - if (!template.isDraggableCondition) { - console.warn('节点不是可拖拽条件:', template.name); - return; - } - - dragState.isDraggingCondition = true; - dragState.conditionTemplate = template; - - if (event.dataTransfer) { - event.dataTransfer.setData('application/json', JSON.stringify({ - ...template, - isConditionDrag: true - })); - event.dataTransfer.effectAllowed = 'copy'; - } - - console.log('✅ 条件拖拽状态已设置:', dragState); - }; - - /** - * 处理拖拽悬停在装饰器上 - */ - const handleDecoratorDragOver = (event: DragEvent, decoratorNode: TreeNode) => { - console.log('🔀 装饰器拖拽悬停:', decoratorNode.name, decoratorNode.type, 'isDragging:', dragState.isDraggingCondition); - - // 检查传输数据 - const transferData = event.dataTransfer?.getData('application/json'); - if (transferData) { - try { - const data = JSON.parse(transferData); - console.log('📦 传输数据:', data.isConditionDrag, data.isDraggableCondition, data.name); - } catch (e) { - console.log('❌ 传输数据解析失败:', transferData); - } - } - - if (!dragState.isDraggingCondition || !isConditionalDecorator(decoratorNode)) { - console.log('❌ 不符合条件:', { - isDragging: dragState.isDraggingCondition, - isDecorator: isConditionalDecorator(decoratorNode), - nodeType: decoratorNode.type - }); - return false; - } - - event.preventDefault(); - event.stopPropagation(); - - dragState.hoveredDecoratorId = decoratorNode.id; - - if (event.dataTransfer) { - event.dataTransfer.dropEffect = 'copy'; - } - - console.log('✅ 装饰器可接受拖拽:', decoratorNode.name); - return true; - }; - - /** - * 处理拖拽离开装饰器 - */ - const handleDecoratorDragLeave = (decoratorNode: TreeNode) => { - if (dragState.hoveredDecoratorId === decoratorNode.id) { - dragState.hoveredDecoratorId = null; - } - }; - - /** - * 条件到装饰器属性的映射 - */ - const mapConditionToDecoratorProperties = (conditionTemplate: NodeTemplate): Record => { - const baseConfig = { - conditionType: getConditionTypeFromTemplate(conditionTemplate), - shouldReevaluate: true - }; - - switch (conditionTemplate.type) { - case 'condition-random': - return { - ...baseConfig, - successProbability: conditionTemplate.properties?.successProbability?.value || 0.5 - }; - - case 'condition-component': - return { - ...baseConfig, - componentType: conditionTemplate.properties?.componentType?.value || 'Component' - }; - - case 'condition-tag': - return { - ...baseConfig, - tagValue: conditionTemplate.properties?.tagValue?.value || 0 - }; - - case 'condition-active': - return { - ...baseConfig, - checkHierarchy: conditionTemplate.properties?.checkHierarchy?.value || true - }; - - case 'condition-numeric': - return { - ...baseConfig, - propertyPath: conditionTemplate.properties?.propertyPath?.value || 'context.someValue', - compareOperator: conditionTemplate.properties?.compareOperator?.value || 'greater', - compareValue: conditionTemplate.properties?.compareValue?.value || 0 - }; - - case 'condition-property': - return { - ...baseConfig, - propertyPath: conditionTemplate.properties?.propertyPath?.value || 'context.someProperty' - }; - - case 'condition-custom': - return { - ...baseConfig, - conditionCode: conditionTemplate.properties?.conditionCode?.value || '(context) => true' - }; - - // Blackboard相关条件支持 - case 'blackboard-variable-exists': - return { - ...baseConfig, - variableName: conditionTemplate.properties?.variableName?.value || '', - invert: conditionTemplate.properties?.invert?.value || false - }; - - case 'blackboard-value-comparison': - return { - ...baseConfig, - variableName: conditionTemplate.properties?.variableName?.value || '', - operator: conditionTemplate.properties?.operator?.value || 'equal', - compareValue: conditionTemplate.properties?.compareValue?.value || '', - compareVariable: conditionTemplate.properties?.compareVariable?.value || '' - }; - - case 'blackboard-variable-type-check': - return { - ...baseConfig, - variableName: conditionTemplate.properties?.variableName?.value || '', - expectedType: conditionTemplate.properties?.expectedType?.value || 'string' - }; - - case 'blackboard-variable-range-check': - return { - ...baseConfig, - variableName: conditionTemplate.properties?.variableName?.value || '', - minValue: conditionTemplate.properties?.minValue?.value || 0, - maxValue: conditionTemplate.properties?.maxValue?.value || 100 - }; - - default: - return baseConfig; - } - }; - - /** - * 获取条件类型字符串 - */ - const getConditionTypeFromTemplate = (template: NodeTemplate): string => { - const typeMap: Record = { - 'condition-random': 'random', - 'condition-component': 'hasComponent', - 'condition-tag': 'hasTag', - 'condition-active': 'isActive', - 'condition-numeric': 'numericCompare', - 'condition-property': 'propertyExists', - 'condition-custom': 'custom', - // Blackboard相关条件 - 'blackboard-variable-exists': 'blackboardExists', - 'blackboard-value-comparison': 'blackboardCompare', - 'blackboard-variable-type-check': 'blackboardTypeCheck', - 'blackboard-variable-range-check': 'blackboardRangeCheck' - }; - - return typeMap[template.type] || 'custom'; - }; - - /** - * 执行条件吸附到装饰器 - */ - const attachConditionToDecorator = ( - event: DragEvent, - decoratorNode: TreeNode - ): boolean => { - event.preventDefault(); - event.stopPropagation(); - - if (!dragState.isDraggingCondition || !dragState.conditionTemplate) { - return false; - } - - if (!isConditionalDecorator(decoratorNode)) { - return false; - } - - // 获取条件配置 - const conditionConfig = mapConditionToDecoratorProperties(dragState.conditionTemplate); - - // 更新装饰器属性 - if (!decoratorNode.properties) { - decoratorNode.properties = {}; - } - - Object.assign(decoratorNode.properties, conditionConfig); - - // 标记装饰器已附加条件 - decoratorNode.attachedCondition = { - type: dragState.conditionTemplate.type, - name: dragState.conditionTemplate.name, - icon: dragState.conditionTemplate.icon - }; - - // 初始化为收缩状态 - if (decoratorNode.conditionExpanded === undefined) { - decoratorNode.conditionExpanded = false; - } - - // 重置拖拽状态 - resetDragState(); - - return true; - }; - - /** - * 处理画布拖拽事件(阻止条件节点创建为独立节点) - */ - const handleCanvasDrop = (event: DragEvent): boolean => { - const templateData = event.dataTransfer?.getData('application/json'); - if (!templateData) return false; - - try { - const data = JSON.parse(templateData); - // 如果是条件拖拽,阻止创建独立节点 - if (data.isConditionDrag || data.isDraggableCondition) { - event.preventDefault(); - resetDragState(); - return true; - } - } catch (error) { - // 忽略解析错误 - } - - return false; - }; - - /** - * 重置拖拽状态 - */ - const resetDragState = () => { - dragState.isDraggingCondition = false; - dragState.conditionTemplate = null; - dragState.mousePosition = null; - dragState.hoveredDecoratorId = null; - }; - - /** - * 获取条件显示文本(简化版始终显示条件名称) - */ - const getConditionDisplayText = (decoratorNode: TreeNode, expanded: boolean = false): string => { - if (!decoratorNode.attachedCondition) { - return ''; - } - - // 始终返回条件名称,不管是否展开 - return decoratorNode.attachedCondition.name; - }; - - /** - * 获取条件的可见属性(用于展开时显示) - */ - const getConditionProperties = (decoratorNode: TreeNode): Record => { - if (!decoratorNode.attachedCondition || !decoratorNode.properties) { - return {}; - } - - const conditionType = decoratorNode.attachedCondition.type; - const visibleProps: Record = {}; - - // 根据条件类型筛选相关属性 - switch (conditionType) { - case 'condition-random': - if ('successProbability' in decoratorNode.properties) { - visibleProps['成功概率'] = `${(decoratorNode.properties.successProbability * 100).toFixed(1)}%`; - } - break; - - case 'condition-component': - if ('componentType' in decoratorNode.properties) { - visibleProps['组件类型'] = decoratorNode.properties.componentType; - } - break; - - case 'condition-tag': - if ('tagValue' in decoratorNode.properties) { - visibleProps['标签值'] = decoratorNode.properties.tagValue; - } - break; - - case 'condition-active': - if ('checkHierarchy' in decoratorNode.properties) { - visibleProps['检查层级'] = decoratorNode.properties.checkHierarchy ? '是' : '否'; - } - break; - - case 'condition-numeric': - if ('propertyPath' in decoratorNode.properties) { - visibleProps['属性路径'] = decoratorNode.properties.propertyPath; - } - if ('compareOperator' in decoratorNode.properties) { - visibleProps['比较操作'] = decoratorNode.properties.compareOperator; - } - if ('compareValue' in decoratorNode.properties) { - visibleProps['比较值'] = decoratorNode.properties.compareValue; - } - break; - - case 'condition-property': - if ('propertyPath' in decoratorNode.properties) { - visibleProps['属性路径'] = decoratorNode.properties.propertyPath; - } - break; - - case 'blackboard-variable-exists': - if ('variableName' in decoratorNode.properties) { - visibleProps['变量名'] = decoratorNode.properties.variableName; - } - if ('invert' in decoratorNode.properties) { - visibleProps['反转结果'] = decoratorNode.properties.invert ? '是' : '否'; - } - break; - - case 'blackboard-value-comparison': - if ('variableName' in decoratorNode.properties) { - visibleProps['变量名'] = decoratorNode.properties.variableName; - } - if ('operator' in decoratorNode.properties) { - visibleProps['操作符'] = decoratorNode.properties.operator; - } - if ('compareValue' in decoratorNode.properties) { - visibleProps['比较值'] = decoratorNode.properties.compareValue; - } - if ('compareVariable' in decoratorNode.properties) { - visibleProps['比较变量'] = decoratorNode.properties.compareVariable; - } - break; - - case 'blackboard-variable-type-check': - if ('variableName' in decoratorNode.properties) { - visibleProps['变量名'] = decoratorNode.properties.variableName; - } - if ('expectedType' in decoratorNode.properties) { - visibleProps['期望类型'] = decoratorNode.properties.expectedType; - } - break; - - case 'blackboard-variable-range-check': - if ('variableName' in decoratorNode.properties) { - visibleProps['变量名'] = decoratorNode.properties.variableName; - } - if ('minValue' in decoratorNode.properties) { - visibleProps['最小值'] = decoratorNode.properties.minValue; - } - if ('maxValue' in decoratorNode.properties) { - visibleProps['最大值'] = decoratorNode.properties.maxValue; - } - break; - } - - return visibleProps; - }; - - /** - * 切换条件展开状态 - */ - const toggleConditionExpanded = (decoratorNode: TreeNode) => { - decoratorNode.conditionExpanded = !decoratorNode.conditionExpanded; - }; - - /** - * 移除装饰器的条件 - */ - const removeConditionFromDecorator = (decoratorNode: TreeNode) => { - if (decoratorNode.attachedCondition) { - // 删除附加的条件信息 - delete decoratorNode.attachedCondition; - - // 重置展开状态 - decoratorNode.conditionExpanded = false; - - // 保留装饰器的基础属性,只删除条件相关的属性 - const preservedProperties: Record = {}; - - // 条件装饰器的基础属性 - const baseDecoratorProperties = [ - 'executeWhenTrue', - 'executeWhenFalse', - 'checkInterval', - 'abortType' - ]; - - // 保留基础属性 - if (decoratorNode.properties) { - baseDecoratorProperties.forEach(key => { - if (key in decoratorNode.properties!) { - preservedProperties[key] = decoratorNode.properties![key]; - } - }); - } - - // 重置为只包含基础属性的对象 - decoratorNode.properties = preservedProperties; - } - }; - - /** - * 检查装饰器是否可以接受条件吸附 - */ - const canAcceptCondition = (decoratorNode: TreeNode): boolean => { - return isConditionalDecorator(decoratorNode); - }; - - return { - dragState, - startConditionDrag, - handleDecoratorDragOver, - handleDecoratorDragLeave, - attachConditionToDecorator, - handleCanvasDrop, - resetDragState, - getConditionDisplayText, - removeConditionFromDecorator, - canAcceptCondition, - isConditionalDecorator, - toggleConditionExpanded, - getConditionProperties - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useConnectionManager.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useConnectionManager.ts deleted file mode 100644 index 805a7a77..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useConnectionManager.ts +++ /dev/null @@ -1,610 +0,0 @@ -import { Ref } from 'vue'; -import { TreeNode, Connection, ConnectionState } from '../types'; - -/** - * 连接线管理功能 - */ -export function useConnectionManager( - treeNodes: Ref, - connections: Ref, - connectionState: ConnectionState, - canvasAreaRef: Ref, - svgRef: Ref, - panX: Ref, - panY: Ref, - zoomLevel: Ref -) { - - const getPortPosition = (nodeId: string, portType: 'input' | 'output') => { - const node = treeNodes.value.find(n => n.id === nodeId); - if (!node) return null; - - const canvasArea = canvasAreaRef.value; - if (!canvasArea) { - return getCalculatedPortPosition(node, portType); - } - - const selectors = [ - `[data-node-id="${nodeId}"]`, - `.tree-node[data-node-id="${nodeId}"]`, - `div[data-node-id="${nodeId}"]` - ]; - - let nodeElement: HTMLElement | null = null; - - for (const selector of selectors) { - try { - const doc = canvasArea.ownerDocument || document; - const foundElement = doc.querySelector(selector); - if (foundElement && canvasArea.contains(foundElement)) { - nodeElement = foundElement as HTMLElement; - break; - } - } catch (error) { - continue; - } - } - - if (!nodeElement) { - try { - const allTreeNodes = canvasArea.querySelectorAll('.tree-node'); - for (let i = 0; i < allTreeNodes.length; i++) { - const el = allTreeNodes[i] as HTMLElement; - const dataNodeId = el.getAttribute('data-node-id'); - if (dataNodeId === nodeId) { - nodeElement = el; - break; - } - } - } catch (error) { - // Fallback to calculated position - } - } - - if (!nodeElement) { - return getCalculatedPortPosition(node, portType); - } - - const portSelectors = [ - `.port.port-${portType}`, - `.port-${portType}`, - `.port.${portType}`, - `.${portType}-port` - ]; - - let portElement: HTMLElement | null = null; - - for (const portSelector of portSelectors) { - try { - portElement = nodeElement.querySelector(portSelector) as HTMLElement; - if (portElement) { - break; - } - } catch (error) { - continue; - } - } - - if (!portElement) { - return getNodeEdgePortPosition(nodeElement, node, portType); - } - - const portRect = portElement.getBoundingClientRect(); - const canvasRect = canvasAreaRef.value?.getBoundingClientRect(); - - if (!canvasRect) { - return getCalculatedPortPosition(node, portType); - } - - const relativeX = portRect.left + portRect.width / 2 - canvasRect.left; - const relativeY = portRect.top + portRect.height / 2 - canvasRect.top; - - const svgX = (relativeX - panX.value) / zoomLevel.value; - const svgY = (relativeY - panY.value) / zoomLevel.value; - - return { x: svgX, y: svgY }; - }; - - const getCalculatedPortPosition = (node: any, portType: 'input' | 'output') => { - let nodeWidth = 150; - let nodeHeight = 80; - - if (node.properties) { - const propertyCount = Object.keys(node.properties).length; - if (propertyCount > 0) { - nodeHeight += propertyCount * 20 + 20; - nodeWidth = Math.max(150, nodeWidth + 50); - } - } - - const portX = node.x + nodeWidth / 2; - const portY = portType === 'input' - ? node.y - 8 - : node.y + nodeHeight + 8; - - return { x: portX, y: portY }; - }; - - const getNodeEdgePortPosition = (nodeElement: HTMLElement, node: any, portType: 'input' | 'output') => { - const nodeRect = nodeElement.getBoundingClientRect(); - const canvasRect = canvasAreaRef.value?.getBoundingClientRect(); - - if (!canvasRect) { - return getCalculatedPortPosition(node, portType); - } - - // 计算节点在SVG坐标系中的实际大小和位置 - const nodeWidth = nodeRect.width / zoomLevel.value; - const nodeHeight = nodeRect.height / zoomLevel.value; - - // 端口位于节点的水平中心 - const portX = node.x + nodeWidth / 2; - const portY = portType === 'input' - ? node.y - 5 - : node.y + nodeHeight + 5; - - return { x: portX, y: portY }; - }; - - const startConnection = (event: MouseEvent, nodeId: string, portType: 'input' | 'output') => { - event.preventDefault(); - event.stopPropagation(); - - connectionState.isConnecting = true; - connectionState.startNodeId = nodeId; - connectionState.startPortType = portType; - connectionState.currentMousePos = { x: event.clientX, y: event.clientY }; - - const startPos = getPortPosition(nodeId, portType); - if (startPos) { - connectionState.startPortPos = startPos; - } - - document.addEventListener('mousemove', onConnectionDrag); - document.addEventListener('mouseup', onConnectionEnd); - - if (canvasAreaRef.value) { - canvasAreaRef.value.classList.add('connecting'); - } - }; - - // 连接拖拽 - const onConnectionDrag = (event: MouseEvent) => { - if (!connectionState.isConnecting || !connectionState.startNodeId || !connectionState.startPortType) return; - - connectionState.currentMousePos = { x: event.clientX, y: event.clientY }; - - const svgPos = clientToSVGCoordinates(event.clientX, event.clientY); - const startPos = getPortPosition(connectionState.startNodeId, connectionState.startPortType); - - if (startPos && svgPos) { - const controlOffset = Math.abs(svgPos.y - startPos.y) * 0.5; - let path: string; - - if (connectionState.startPortType === 'output') { - path = `M ${startPos.x} ${startPos.y} C ${startPos.x} ${startPos.y + controlOffset} ${svgPos.x} ${svgPos.y - controlOffset} ${svgPos.x} ${svgPos.y}`; - } else { - path = `M ${startPos.x} ${startPos.y} C ${startPos.x} ${startPos.y - controlOffset} ${svgPos.x} ${svgPos.y + controlOffset} ${svgPos.x} ${svgPos.y}`; - } - - if ('tempPath' in connectionState) { - (connectionState as any).tempPath = path; - } - } - const targetPort = findTargetPort(event.clientX, event.clientY); - if (targetPort && targetPort.nodeId !== connectionState.startNodeId) { - connectionState.hoveredPort = targetPort; - } else { - connectionState.hoveredPort = null; - } - }; - - // 结束连接 - const onConnectionEnd = (event: MouseEvent) => { - if (!connectionState.isConnecting) return; - - // 检查是否落在有效的端口上 - const targetPort = findTargetPort(event.clientX, event.clientY); - - if (targetPort && connectionState.startNodeId && connectionState.startPortType) { - const canConnectResult = canConnect( - connectionState.startNodeId, - connectionState.startPortType, - targetPort.nodeId, - targetPort.portType - ); - - if (canConnectResult) { - let parentId: string, childId: string; - - if (connectionState.startPortType === 'output') { - parentId = connectionState.startNodeId; - childId = targetPort.nodeId; - } else { - parentId = targetPort.nodeId; - childId = connectionState.startNodeId; - } - - createConnection(parentId, childId); - } - } - - // 清理连接状态 - cancelConnection(); - }; - - // 取消连接 - const cancelConnection = () => { - connectionState.isConnecting = false; - connectionState.startNodeId = null; - connectionState.startPortType = null; - connectionState.currentMousePos = null; - connectionState.startPortPos = null; - connectionState.hoveredPort = null; - - if ('tempPath' in connectionState) { - (connectionState as any).tempPath = ''; - } - - document.removeEventListener('mousemove', onConnectionDrag); - document.removeEventListener('mouseup', onConnectionEnd); - - if (canvasAreaRef.value) { - canvasAreaRef.value.classList.remove('connecting'); - } - // 清除画布内的拖拽目标样式 - if (canvasAreaRef.value) { - const allPorts = canvasAreaRef.value.querySelectorAll('.port.drag-target'); - allPorts.forEach(port => port.classList.remove('drag-target')); - } - }; - - const clientToSVGCoordinates = (clientX: number, clientY: number) => { - if (!canvasAreaRef.value) return null; - - try { - // 获取canvas容器的边界 - const canvasRect = canvasAreaRef.value.getBoundingClientRect(); - - // 转换为相对于canvas的坐标 - const canvasX = clientX - canvasRect.left; - const canvasY = clientY - canvasRect.top; - - // 撤销SVG的transform,转换为SVG坐标 - // SVG transform: translate(panX, panY) scale(zoomLevel) - const svgX = (canvasX - panX.value) / zoomLevel.value; - const svgY = (canvasY - panY.value) / zoomLevel.value; - - return { x: svgX, y: svgY }; - } catch (e) { - return null; - } - }; - - // 查找目标端口 - const findTargetPort = (clientX: number, clientY: number) => { - if (!canvasAreaRef.value) return null; - - try { - const elementAtPoint = document.elementFromPoint(clientX, clientY); - if (elementAtPoint?.classList.contains('port') && canvasAreaRef.value.contains(elementAtPoint)) { - return getPortInfo(elementAtPoint as HTMLElement); - } - } catch (error) { - // 查询出错时静默处理 - } - - const allPorts = canvasAreaRef.value.querySelectorAll('.port'); - for (const port of allPorts) { - const rect = port.getBoundingClientRect(); - const margin = 10; - - if (clientX >= rect.left - margin && clientX <= rect.right + margin && - clientY >= rect.top - margin && clientY <= rect.bottom + margin) { - return getPortInfo(port as HTMLElement); - } - } - - return null; - }; - - // 从端口元素获取端口信息 - const getPortInfo = (portElement: HTMLElement) => { - const nodeElement = portElement.closest('.tree-node'); - if (!nodeElement) return null; - - const nodeId = nodeElement.getAttribute('data-node-id'); - const portType = portElement.classList.contains('port-input') ? 'input' : 'output' as 'input' | 'output'; - - return nodeId ? { nodeId, portType } : null; - }; - - // 端口悬停处理 - const onPortHover = (nodeId: string, portType: 'input' | 'output') => { - if (connectionState.isConnecting && connectionState.startNodeId !== nodeId) { - connectionState.hoveredPort = { nodeId, portType }; - - if (canConnect(connectionState.startNodeId!, connectionState.startPortType!, nodeId, portType)) { - // 在画布区域内查找端口元素 - if (canvasAreaRef.value) { - const portElement = canvasAreaRef.value.querySelector(`[data-node-id="${nodeId}"] .port.port-${portType}`); - if (portElement) { - portElement.classList.add('drag-target'); - } - } - } - } - }; - - const onPortLeave = () => { - if (connectionState.isConnecting) { - connectionState.hoveredPort = null; - // 清除画布内的拖拽目标样式 - if (canvasAreaRef.value) { - const allPorts = canvasAreaRef.value.querySelectorAll('.port.drag-target'); - allPorts.forEach(port => port.classList.remove('drag-target')); - } - } - }; - - // 验证连接目标是否有效 - const isValidConnectionTarget = (nodeId: string, portType: 'input' | 'output') => { - if (!connectionState.isConnecting || !connectionState.startNodeId || connectionState.startNodeId === nodeId) { - return false; - } - - return canConnect(connectionState.startNodeId, connectionState.startPortType!, nodeId, portType); - }; - - // 检查是否可以连接 - const canConnect = (sourceNodeId: string, sourcePortType: string, targetNodeId: string, targetPortType: string) => { - if (sourceNodeId === targetNodeId) return false; - if (sourcePortType === targetPortType) return false; - - let parentNodeId: string, childNodeId: string; - - if (sourcePortType === 'output') { - parentNodeId = sourceNodeId; - childNodeId = targetNodeId; - } else { - parentNodeId = targetNodeId; - childNodeId = sourceNodeId; - } - - const childNode = treeNodes.value.find(n => n.id === childNodeId); - if (childNode && childNode.parent && childNode.parent !== parentNodeId) { - return false; - } - - const parentNode = treeNodes.value.find(n => n.id === parentNodeId); - if (!parentNode || !parentNode.canHaveChildren) return false; - if (!childNode || !childNode.canHaveParent) return false; - - // 检查子节点数量限制 - if (parentNode.maxChildren !== undefined) { - const currentChildrenCount = parentNode.children ? parentNode.children.length : 0; - if (currentChildrenCount >= parentNode.maxChildren) { - return false; // 已达到最大子节点数量 - } - } - - // 检查根节点限制:根节点不能有父节点 - if (childNode.type === 'root') { - return false; // 根节点不能作为其他节点的子节点 - } - - // 检查是否只能有一个根节点 - if (parentNode.type === 'root') { - // 根节点只能连接一个子节点 - const rootNodes = treeNodes.value.filter(n => n.type === 'root'); - if (rootNodes.length > 1) { - return false; // 不能有多个根节点 - } - } - - if (wouldCreateCycle(parentNodeId, childNodeId)) return false; - if (isDescendant(childNodeId, parentNodeId)) return false; - - return true; - }; - - // 检查是否会创建循环 - const wouldCreateCycle = (parentId: string, childId: string) => { - return isDescendant(parentId, childId); - }; - - const isDescendant = (ancestorId: string, descendantId: string): boolean => { - const visited = new Set(); - - function checkPath(currentId: string): boolean { - if (currentId === ancestorId) return true; - if (visited.has(currentId)) return false; - - visited.add(currentId); - - const currentNode = treeNodes.value.find(n => n.id === currentId); - if (currentNode?.children) { - for (const childId of currentNode.children) { - if (checkPath(childId)) return true; - } - } - - return false; - } - - return checkPath(descendantId); - }; - - // 创建连接 - const createConnection = (parentId: string, childId: string) => { - const parentNode = treeNodes.value.find(n => n.id === parentId); - const childNode = treeNodes.value.find(n => n.id === childId); - - if (!parentNode || !childNode) return; - - // 移除子节点的旧父子关系 - if (childNode.parent) { - const oldParent = treeNodes.value.find(n => n.id === childNode.parent); - if (oldParent) { - const index = oldParent.children.indexOf(childId); - if (index > -1) { - oldParent.children.splice(index, 1); - } - } - } - - // 建立新的父子关系 - childNode.parent = parentId; - if (!parentNode.children.includes(childId)) { - parentNode.children.push(childId); - } - - updateConnections(); - }; - - // 改进的连接线更新方法 - const updateConnections = () => { - // 立即清空现有连接 - connections.value.length = 0; - - // 创建新的连接数据 - const newConnections: Connection[] = []; - - // 遍历所有节点建立连接 - treeNodes.value.forEach(node => { - if (node.children && node.children.length > 0) { - node.children.forEach(childId => { - const childNode = treeNodes.value.find(n => n.id === childId); - if (childNode) { - // 尝试获取端口位置 - const parentPos = getPortPosition(node.id, 'output'); - const childPos = getPortPosition(childId, 'input'); - - if (parentPos && childPos) { - // 计算贝塞尔曲线路径 - const deltaY = Math.abs(childPos.y - parentPos.y); - const controlOffset = Math.max(30, Math.min(deltaY * 0.5, 80)); - - const path = `M ${parentPos.x} ${parentPos.y} C ${parentPos.x} ${parentPos.y + controlOffset} ${childPos.x} ${childPos.y - controlOffset} ${childPos.x} ${childPos.y}`; - - newConnections.push({ - id: `${node.id}-${childId}`, - sourceId: node.id, - targetId: childId, - path: path, - active: false - }); - } else { - // 如果无法获取实际位置,使用计算位置作为后备 - const fallbackParentPos = getCalculatedPortPosition(node, 'output'); - const fallbackChildPos = getCalculatedPortPosition(childNode, 'input'); - - const deltaY = Math.abs(fallbackChildPos.y - fallbackParentPos.y); - const controlOffset = Math.max(30, Math.min(deltaY * 0.5, 80)); - - const path = `M ${fallbackParentPos.x} ${fallbackParentPos.y} C ${fallbackParentPos.x} ${fallbackParentPos.y + controlOffset} ${fallbackChildPos.x} ${fallbackChildPos.y - controlOffset} ${fallbackChildPos.x} ${fallbackChildPos.y}`; - - newConnections.push({ - id: `${node.id}-${childId}`, - sourceId: node.id, - targetId: childId, - path: path, - active: false - }); - } - } - }); - } - }); - - // 批量更新连接 - connections.value.push(...newConnections); - - // 如果有DOM元素,进行二次精确更新 - if (canvasAreaRef.value) { - setTimeout(() => { - // 二次更新,使用实际DOM位置 - const updatedConnections: Connection[] = []; - - treeNodes.value.forEach(node => { - if (node.children && node.children.length > 0) { - node.children.forEach(childId => { - const childNode = treeNodes.value.find(n => n.id === childId); - if (childNode) { - const parentPos = getPortPosition(node.id, 'output'); - const childPos = getPortPosition(childId, 'input'); - - if (parentPos && childPos) { - const deltaY = Math.abs(childPos.y - parentPos.y); - const controlOffset = Math.max(30, Math.min(deltaY * 0.5, 80)); - - const path = `M ${parentPos.x} ${parentPos.y} C ${parentPos.x} ${parentPos.y + controlOffset} ${childPos.x} ${childPos.y - controlOffset} ${childPos.x} ${childPos.y}`; - - updatedConnections.push({ - id: `${node.id}-${childId}`, - sourceId: node.id, - targetId: childId, - path: path, - active: false - }); - } - } - }); - } - }); - - // 如果二次更新得到了有效结果,替换连接数据 - if (updatedConnections.length > 0) { - connections.value.length = 0; - connections.value.push(...updatedConnections); - } - }, 100); // 100ms延迟,确保DOM完全渲染 - } - }; - - // 删除连接线 - const removeConnection = (connectionId: string) => { - const connection = connections.value.find(conn => conn.id === connectionId); - if (!connection) return; - - const parentNode = treeNodes.value.find(n => n.id === connection.sourceId); - const childNode = treeNodes.value.find(n => n.id === connection.targetId); - - if (parentNode && childNode) { - // 从父节点的children中移除 - const index = parentNode.children.indexOf(connection.targetId); - if (index > -1) { - parentNode.children.splice(index, 1); - } - - // 清除子节点的parent - childNode.parent = undefined; - - // 更新连接线 - updateConnections(); - } - }; - - // 连接线点击事件处理 - const onConnectionClick = (event: MouseEvent, connectionId: string) => { - event.preventDefault(); - event.stopPropagation(); - - // 询问用户是否要删除连接 - if (confirm('确定要删除这条连接线吗?')) { - removeConnection(connectionId); - } - }; - - return { - getPortPosition, - startConnection, - cancelConnection, - updateConnections, - removeConnection, - onConnectionClick, - onPortHover, - onPortLeave, - isValidConnectionTarget - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useFileOperations.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useFileOperations.ts deleted file mode 100644 index aa4eca9b..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useFileOperations.ts +++ /dev/null @@ -1,538 +0,0 @@ -import { Ref, ref, watch } from 'vue'; -import { TreeNode, Connection } from '../types'; - -interface FileOperationOptions { - treeNodes: Ref; - selectedNodeId: Ref; - connections: Ref; - tempConnection: Ref<{ path: string }>; - showExportModal: Ref; - codeGeneration?: { - createTreeFromConfig: (config: any) => TreeNode[]; - }; - updateConnections?: () => void; - blackboardOperations?: { - getBlackboardVariables: () => any[]; - loadBlackboardVariables: (variables: any[]) => void; - clearBlackboard: () => void; - }; -} - -interface FileData { - nodes: TreeNode[]; - connections: Connection[]; - blackboard?: any[]; - metadata: { - name: string; - created: string; - version: string; - }; -} - -export function useFileOperations(options: FileOperationOptions) { - const { - treeNodes, - selectedNodeId, - connections, - tempConnection, - showExportModal, - codeGeneration, - updateConnections, - blackboardOperations - } = options; - - const hasUnsavedChanges = ref(false); - const lastSavedState = ref(''); - const currentFileName = ref(''); - const currentFilePath = ref(''); - - const updateUnsavedStatus = () => { - const currentState = JSON.stringify({ - nodes: treeNodes.value, - connections: connections.value - }); - hasUnsavedChanges.value = currentState !== lastSavedState.value; - }; - - watch([treeNodes, connections], updateUnsavedStatus, { deep: true }); - - const markAsSaved = () => { - const currentState = JSON.stringify({ - nodes: treeNodes.value, - connections: connections.value - }); - lastSavedState.value = currentState; - hasUnsavedChanges.value = false; - }; - - const setCurrentFile = (fileName: string, filePath: string = '') => { - currentFileName.value = fileName; - currentFilePath.value = filePath; - markAsSaved(); - }; - - const clearCurrentFile = () => { - currentFileName.value = ''; - currentFilePath.value = ''; - }; - - const exportBehaviorTreeData = (): FileData => { - const data: FileData = { - nodes: treeNodes.value, - connections: connections.value, - metadata: { - name: currentFileName.value || 'untitled', - created: new Date().toISOString(), - version: '1.0' - } - }; - - // 包含黑板数据 - if (blackboardOperations) { - const blackboardVariables = blackboardOperations.getBlackboardVariables(); - if (blackboardVariables.length > 0) { - data.blackboard = blackboardVariables; - } - } - - return data; - }; - - const showMessage = (message: string, type: 'success' | 'error' = 'success') => { - const toast = document.createElement('div'); - toast.style.cssText = ` - position: fixed; - top: 20px; - right: 20px; - padding: 12px 20px; - background: ${type === 'success' ? '#4caf50' : '#f44336'}; - color: white; - border-radius: 4px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); - z-index: 10001; - opacity: 0; - transform: translateX(100%); - transition: all 0.3s ease; - `; - toast.textContent = message; - - document.body.appendChild(toast); - - setTimeout(() => { - toast.style.opacity = '1'; - toast.style.transform = 'translateX(0)'; - }, 10); - - setTimeout(() => { - toast.style.opacity = '0'; - toast.style.transform = 'translateX(100%)'; - setTimeout(() => { - if (document.body.contains(toast)) { - document.body.removeChild(toast); - } - }, 300); - }, 3000); - }; - - const sendToMain = (message: string, data: any): Promise => { - return new Promise((resolve, reject) => { - try { - Editor.Message.request('cocos-ecs-extension', message, data) - .then((result) => { - resolve(); - }) - .catch((error) => { - reject(error); - }); - } catch (error) { - reject(error); - } - }); - }; - - const checkUnsavedChanges = (): Promise => { - return new Promise((resolve) => { - if (!hasUnsavedChanges.value) { - resolve(true); - return; - } - - const result = confirm( - '当前行为树有未保存的更改,是否要保存?\n\n' + - '点击"确定"保存更改\n' + - '点击"取消"丢弃更改' - ); - - if (result) { - saveBehaviorTree().then(() => { - resolve(true); - }).catch(() => { - resolve(false); - }); - } else { - resolve(true); - } - }); - }; - - const newBehaviorTree = async () => { - const canProceed = await checkUnsavedChanges(); - if (canProceed) { - treeNodes.value = []; - selectedNodeId.value = null; - connections.value = []; - tempConnection.value.path = ''; - - // 清空黑板 - if (blackboardOperations) { - blackboardOperations.clearBlackboard(); - } - - clearCurrentFile(); - markAsSaved(); - } - }; - - const saveBehaviorTree = async (): Promise => { - if (currentFilePath.value) { - return await saveToCurrentFile(); - } else { - return await saveAsBehaviorTree(); - } - }; - - const saveToCurrentFile = async (): Promise => { - if (!currentFilePath.value) { - return await saveAsBehaviorTree(); - } - - try { - const data = exportBehaviorTreeData(); - const jsonString = JSON.stringify(data, null, 2); - - await sendToMain('overwrite-behavior-tree-file', { - filePath: currentFilePath.value, - content: jsonString - }); - - markAsSaved(); - showMessage('保存成功!'); - return true; - } catch (error) { - showMessage('保存失败: ' + error, 'error'); - return false; - } - }; - - const saveAsBehaviorTree = async (): Promise => { - try { - const data = exportBehaviorTreeData(); - const jsonString = JSON.stringify(data, null, 2); - - const result = await Editor.Dialog.save({ - title: '保存行为树文件', - filters: [ - { name: '行为树文件', extensions: ['bt.json', 'json'] }, - { name: '所有文件', extensions: ['*'] } - ] - }); - - if (result.canceled || !result.filePath) { - return false; - } - - const fs = require('fs-extra'); - await fs.writeFile(result.filePath, jsonString); - - const path = require('path'); - const fileName = path.basename(result.filePath, path.extname(result.filePath)); - setCurrentFile(fileName, result.filePath); - showMessage(`保存成功!文件: ${result.filePath}`); - - return true; - } catch (error) { - showMessage('另存为失败: ' + error, 'error'); - return false; - } - }; - - const saveToFile = async (fileName: string, jsonString: string): Promise => { - try { - await sendToMain('create-behavior-tree-from-editor', { - fileName: fileName + '.json', - content: jsonString - }); - - setCurrentFile(fileName, `assets/${fileName}.bt.json`); - showMessage(`保存成功!文件名: ${fileName}.json`); - return true; - } catch (error) { - showMessage('保存失败: ' + error, 'error'); - return false; - } - }; - - const getFileNameFromUser = (): Promise => { - return new Promise((resolve) => { - const overlay = document.createElement('div'); - overlay.style.cssText = ` - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - z-index: 10000; - `; - - const dialog = document.createElement('div'); - dialog.style.cssText = ` - background: #2d2d2d; - color: #ffffff; - padding: 20px; - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); - min-width: 300px; - `; - - dialog.innerHTML = ` -

保存行为树

-

请输入文件名(不含扩展名):

- -
- - -
- `; - - overlay.appendChild(dialog); - document.body.appendChild(overlay); - - const input = dialog.querySelector('#filename-input') as HTMLInputElement; - const saveBtn = dialog.querySelector('#save-btn') as HTMLButtonElement; - const cancelBtn = dialog.querySelector('#cancel-btn') as HTMLButtonElement; - - input.focus(); - input.select(); - - const cleanup = () => { - document.body.removeChild(overlay); - }; - - saveBtn.onclick = () => { - const fileName = input.value.trim(); - cleanup(); - resolve(fileName || null); - }; - - cancelBtn.onclick = () => { - cleanup(); - resolve(null); - }; - - input.onkeydown = (e) => { - if (e.key === 'Enter') { - const fileName = input.value.trim(); - cleanup(); - resolve(fileName || null); - } else if (e.key === 'Escape') { - cleanup(); - resolve(null); - } - }; - }); - }; - - const loadFileContent = (fileData: any, filePath: string = '') => { - try { - if (!fileData) { - return; - } - - let parsedData = fileData; - - if (fileData.rawContent) { - try { - parsedData = JSON.parse(fileData.rawContent); - } catch (e) { - parsedData = { - nodes: [], - connections: [] - }; - } - } - - if (parsedData.nodes && Array.isArray(parsedData.nodes)) { - treeNodes.value = parsedData.nodes.map((node: any) => ({ - ...node, - x: node.x || 0, - y: node.y || 0, - children: node.children || [], - properties: node.properties || {}, - canHaveChildren: node.canHaveChildren !== false, - canHaveParent: node.canHaveParent !== false, - hasError: node.hasError || false - })); - } else if (parsedData.tree) { - const treeNode = parsedData.tree; - const nodes = [treeNode]; - - const extractNodes = (node: any): any[] => { - const allNodes = [node]; - if (node.children && Array.isArray(node.children)) { - node.children.forEach((child: any) => { - if (typeof child === 'object') { - allNodes.push(...extractNodes(child)); - } - }); - } - return allNodes; - }; - - const allNodes = extractNodes(treeNode); - treeNodes.value = allNodes.map((node: any, index: number) => ({ - ...node, - x: node.x || (300 + index * 150), - y: node.y || (100 + Math.floor(index / 3) * 200), - children: Array.isArray(node.children) - ? node.children.filter((child: any) => typeof child === 'string') - : [], - properties: node.properties || {}, - canHaveChildren: true, - canHaveParent: node.id !== 'root', - hasError: false - })); - } else { - treeNodes.value = []; - } - - if (parsedData.connections && Array.isArray(parsedData.connections)) { - connections.value = parsedData.connections.map((conn: any) => ({ - id: conn.id || Math.random().toString(36).substr(2, 9), - sourceId: conn.sourceId, - targetId: conn.targetId, - path: conn.path || '', - active: conn.active || false - })); - } else { - connections.value = []; - } - - if (fileData._fileInfo) { - const fileName = fileData._fileInfo.fileName || 'untitled'; - const fullPath = fileData._fileInfo.filePath || filePath; - setCurrentFile(fileName, fullPath); - } else if (parsedData.metadata?.name) { - setCurrentFile(parsedData.metadata.name, filePath); - } else { - setCurrentFile('untitled', filePath); - } - - // 加载黑板数据 - if (blackboardOperations && parsedData.blackboard && Array.isArray(parsedData.blackboard)) { - blackboardOperations.loadBlackboardVariables(parsedData.blackboard); - } - - selectedNodeId.value = null; - tempConnection.value.path = ''; - - if (updateConnections) { - setTimeout(() => { - updateConnections(); - }, 100); - } - - } catch (error) { - console.error('文件加载失败:', error); - showMessage('文件加载失败: ' + error, 'error'); - treeNodes.value = []; - connections.value = []; - selectedNodeId.value = null; - setCurrentFile('untitled', ''); - } - }; - - const loadBehaviorTree = async () => { - const canProceed = await checkUnsavedChanges(); - if (!canProceed) return; - - const input = document.createElement('input'); - input.type = 'file'; - input.accept = '.json,.bt'; - input.onchange = (e) => { - const file = (e.target as HTMLInputElement).files?.[0]; - if (file) { - const reader = new FileReader(); - reader.onload = (event) => { - try { - const configText = event.target?.result as string; - const config = JSON.parse(configText); - - if (codeGeneration) { - const newNodes = codeGeneration.createTreeFromConfig(config); - treeNodes.value = newNodes; - selectedNodeId.value = null; - - if (config.connections && Array.isArray(config.connections)) { - connections.value = config.connections.map((conn: any) => ({ - id: conn.id, - sourceId: conn.sourceId, - targetId: conn.targetId, - path: conn.path || '', - active: conn.active || false - })); - } else { - connections.value = []; - } - - tempConnection.value.path = ''; - - // 加载黑板数据 - if (blackboardOperations && config.blackboard && Array.isArray(config.blackboard)) { - blackboardOperations.loadBlackboardVariables(config.blackboard); - } - - const fileName = file.name.replace(/\.(json|bt)$/, ''); - setCurrentFile(fileName, ''); - - setTimeout(() => { - if (updateConnections) { - updateConnections(); - } - }, 100); - } else { - showMessage('代码生成器未初始化', 'error'); - } - } catch (error) { - showMessage('配置文件格式错误', 'error'); - } - }; - reader.readAsText(file); - } - }; - input.click(); - }; - - const exportConfig = () => { - showExportModal.value = true; - }; - - return { - newBehaviorTree, - saveBehaviorTree, - saveAsBehaviorTree, - loadBehaviorTree, - loadFileContent, - exportConfig, - hasUnsavedChanges, - markAsSaved, - setCurrentFile, - clearCurrentFile, - currentFileName, - currentFilePath - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useInstallation.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useInstallation.ts deleted file mode 100644 index 66c89089..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useInstallation.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Ref } from 'vue'; -import { checkBehaviorTreeInstalled, installBehaviorTreeAI } from '../utils/installUtils'; - -/** - * 安装管理 - */ -export function useInstallation( - checkingStatus: Ref, - isInstalled: Ref, - version: Ref, - isInstalling: Ref -) { - - // 检查安装状态 - const checkInstallStatus = async () => { - checkingStatus.value = true; - try { - const result = await checkBehaviorTreeInstalled(Editor.Project.path); - isInstalled.value = result.installed; - version.value = result.version; - } catch (error) { - console.error('检查AI系统安装状态失败:', error); - isInstalled.value = false; - version.value = null; - } finally { - checkingStatus.value = false; - } - }; - - // 处理安装 - const handleInstall = async () => { - isInstalling.value = true; - try { - const result = await installBehaviorTreeAI(Editor.Project.path); - - if (result) { - // 等待文件系统更新 - await new Promise(resolve => setTimeout(resolve, 2000)); - await checkInstallStatus(); - - // 如果第一次检查失败,再次尝试 - if (!isInstalled.value) { - await new Promise(resolve => setTimeout(resolve, 1000)); - await checkInstallStatus(); - } - } else { - console.error('AI系统安装失败'); - } - } catch (error) { - console.error('安装AI系统时发生错误:', error); - } finally { - isInstalling.value = false; - } - }; - - return { - checkInstallStatus, - handleInstall - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useNodeDisplay.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useNodeDisplay.ts deleted file mode 100644 index d8d576aa..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useNodeDisplay.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * 节点显示管理功能 - */ -export function useNodeDisplay() { - - // 检查节点是否有可见属性 - const hasVisibleProperties = (node: any) => { - if (!node.properties) return false; - return Object.keys(getVisibleProperties(node)).length > 0; - }; - - // 获取可见属性 - const getVisibleProperties = (node: any) => { - if (!node.properties) return {}; - - const visibleProps: any = {}; - for (const [key, prop] of Object.entries(node.properties)) { - if (shouldShowProperty(prop as any, key)) { - visibleProps[key] = prop; - } - } - return visibleProps; - }; - - // 判断属性是否应该显示 - const shouldShowProperty = (prop: any, key: string) => { - // 总是显示这些重要属性 - const alwaysShow = ['abortType', 'repeatCount', 'priority']; - if (alwaysShow.includes(key)) { - return true; - } - - // 对于其他属性,只在非默认值时显示 - if (prop.type === 'string' && prop.value && prop.value.trim() !== '') { - return true; - } - if (prop.type === 'number' && prop.value !== 0 && prop.value !== -1) { - return true; - } - if (prop.type === 'boolean' && prop.value === true) { - return true; - } - if (prop.type === 'select' && prop.value !== 'None' && prop.value !== '') { - return true; - } - if (prop.type === 'code' && prop.value && prop.value.trim() !== '' && prop.value !== '(context) => true') { - return true; - } - - return false; - }; - - // 格式化属性值显示 - const formatPropertyValue = (prop: any) => { - switch (prop.type) { - case 'boolean': - return prop.value ? '✓' : '✗'; - case 'number': - return prop.value.toString(); - case 'select': - return prop.value; - case 'string': - return prop.value.length > 15 ? prop.value.substring(0, 15) + '...' : prop.value; - case 'code': - const code = prop.value || ''; - if (code.length > 20) { - // 尝试提取函数体的关键部分 - const bodyMatch = code.match(/=>\s*(.+)/) || code.match(/{\s*(.+?)\s*}/); - if (bodyMatch) { - const body = bodyMatch[1].trim(); - return body.length > 15 ? body.substring(0, 15) + '...' : body; - } - return code.substring(0, 20) + '...'; - } - return code; - default: - return prop.value?.toString() || ''; - } - }; - - return { - hasVisibleProperties, - getVisibleProperties, - formatPropertyValue - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useNodeOperations.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useNodeOperations.ts deleted file mode 100644 index 4e68edab..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/composables/useNodeOperations.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { Ref, nextTick } from 'vue'; -import { TreeNode, Connection } from '../types'; -import { NodeTemplate } from '../data/nodeTemplates'; -import { createNodeFromTemplate } from '../utils/nodeUtils'; -import { getCanvasCoordinates } from '../utils/canvasUtils'; - -/** - * 节点操作管理 - */ -export function useNodeOperations( - treeNodes: Ref, - selectedNodeId: Ref, - connections: Ref, - panX: Ref, - panY: Ref, - zoomLevel: Ref, - getNodeByIdLocal: (id: string) => TreeNode | undefined, - updateConnections?: () => void -) { - - // 获取相对于画布的坐标(用于节点拖放等操作) - const getCanvasCoords = (event: MouseEvent, canvasElement: HTMLElement | null) => { - return getCanvasCoordinates(event, canvasElement, panX.value, panY.value, zoomLevel.value); - }; - - // 拖拽事件处理 - const onNodeDragStart = (event: DragEvent, template: NodeTemplate) => { - if (event.dataTransfer) { - // 检查是否为条件节点,如果是则标记为条件拖拽 - const dragData = { - ...template, - isConditionDrag: template.isDraggableCondition || false - }; - event.dataTransfer.setData('application/json', JSON.stringify(dragData)); - event.dataTransfer.effectAllowed = 'copy'; - } - }; - - const onCanvasDragOver = (event: DragEvent) => { - event.preventDefault(); - if (event.dataTransfer) { - event.dataTransfer.dropEffect = 'copy'; - } - }; - - const onCanvasDrop = (event: DragEvent) => { - event.preventDefault(); - - const templateData = event.dataTransfer?.getData('application/json'); - if (!templateData) return; - - try { - const dragData = JSON.parse(templateData); - - // 如果是条件节点拖拽,阻止创建独立节点 - if (dragData.isConditionDrag || dragData.isDraggableCondition) { - return; // 条件节点不能作为独立节点创建 - } - - const template: NodeTemplate = dragData; - const canvasElement = event.currentTarget as HTMLElement; - const { x, y } = getCanvasCoords(event, canvasElement); - - const newNode = createNodeFromTemplate(template, x, y); - treeNodes.value.push(newNode); - selectedNodeId.value = newNode.id; - - } catch (error) { - // 节点创建失败时静默处理 - } - }; - - // 节点删除(递归删除子节点) - const deleteNode = (nodeId: string) => { - const deleteRecursive = (id: string) => { - const node = getNodeByIdLocal(id); - if (!node) return; - - // 递归删除子节点 - node.children.forEach(childId => deleteRecursive(childId)); - - // 从父节点的children中移除 - if (node.parent) { - const parent = getNodeByIdLocal(node.parent); - if (parent) { - const index = parent.children.indexOf(id); - if (index > -1) { - parent.children.splice(index, 1); - } - } - } - - // 移除连接 - connections.value = connections.value.filter(conn => - conn.sourceId !== id && conn.targetId !== id - ); - - // 从树中移除节点 - const nodeIndex = treeNodes.value.findIndex(n => n.id === id); - if (nodeIndex > -1) { - treeNodes.value.splice(nodeIndex, 1); - } - }; - - deleteRecursive(nodeId); - - if (selectedNodeId.value === nodeId) { - selectedNodeId.value = null; - } - - // 更新连接线 - if (updateConnections) { - updateConnections(); - } - }; - - // 通用的属性更新方法 - const setNestedProperty = (obj: any, path: string, value: any) => { - const keys = path.split('.'); - let current = obj; - - // 导航到目标属性的父对象 - for (let i = 0; i < keys.length - 1; i++) { - const key = keys[i]; - if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) { - current[key] = {}; - } - current = current[key]; - } - - // 设置最终值 - const finalKey = keys[keys.length - 1]; - current[finalKey] = value; - }; - - // 节点属性更新 - const updateNodeProperty = (path: string, value: any) => { - const selectedNode = selectedNodeId.value ? getNodeByIdLocal(selectedNodeId.value) : null; - if (!selectedNode) return; - - // 检查是否是条件节点的属性更新 - if (selectedNode.isConditionNode && selectedNode.parentDecorator) { - // 条件节点的属性更新需要同步到装饰器 - updateConditionNodeProperty(selectedNode.parentDecorator, path, value); - } else { - // 普通节点的属性更新 - setNestedProperty(selectedNode, path, value); - - // 强制触发响应式更新 - const nodeIndex = treeNodes.value.findIndex(n => n.id === selectedNode.id); - if (nodeIndex > -1) { - const newNodes = [...treeNodes.value]; - newNodes[nodeIndex] = { ...selectedNode }; - treeNodes.value = newNodes; - } - } - }; - - // 更新条件节点属性到装饰器 - const updateConditionNodeProperty = (decoratorNode: TreeNode, path: string, value: any) => { - // 解析属性路径,例如 "properties.variableName.value" -> "variableName" - const pathParts = path.split('.'); - if (pathParts[0] === 'properties' && pathParts[2] === 'value') { - const propertyName = pathParts[1]; - - // 直接更新装饰器的属性 - if (!decoratorNode.properties) { - decoratorNode.properties = {}; - } - decoratorNode.properties[propertyName] = value; - - // 强制触发响应式更新 - const nodeIndex = treeNodes.value.findIndex(n => n.id === decoratorNode.id); - if (nodeIndex > -1) { - const newNodes = [...treeNodes.value]; - newNodes[nodeIndex] = { ...decoratorNode }; - treeNodes.value = newNodes; - } - } - }; - - return { - getCanvasCoords, - onNodeDragStart, - onCanvasDragOver, - onCanvasDrop, - deleteNode, - updateNodeProperty - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/data/nodeTemplates.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/data/nodeTemplates.ts deleted file mode 100644 index 34b5bf83..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/data/nodeTemplates.ts +++ /dev/null @@ -1,1115 +0,0 @@ -/** - * 节点属性定义接口 - */ -export interface PropertyDefinition { - name: string; - type: 'string' | 'number' | 'boolean' | 'select' | 'code'; - value: any; - description?: string; - options?: string[]; // 用于select类型 - required?: boolean; -} - -/** - * 节点模板接口 - */ -export interface NodeTemplate { - type: string; - name: string; - icon: string; - category: 'composite' | 'decorator' | 'action' | 'condition' | 'ecs' | 'root'; - description: string; - canHaveChildren: boolean; - canHaveParent: boolean; - maxChildren?: number; // 最大子节点数量限制 - minChildren?: number; // 最小子节点数量要求 - properties?: Record; - className?: string; // 对应的实际类名 - namespace?: string; // 命名空间 - // 条件节点相关 - isDraggableCondition?: boolean; // 是否为可拖拽的条件节点 - attachableToDecorator?: boolean; // 是否可以吸附到条件装饰器 -} - -/** - * 基于项目实际行为树系统的节点模板定义 - */ -export const nodeTemplates: NodeTemplate[] = [ - // 根节点 - { - type: 'root', - name: '根节点', - icon: '🌳', - category: 'root', - description: '行为树的根节点,每棵树只能有一个根节点', - canHaveChildren: true, - canHaveParent: false, - maxChildren: 1, - minChildren: 1, - className: 'BehaviorTree', - namespace: 'behaviourTree' - }, - - // 复合节点 (Composites) - { - type: 'sequence', - name: '序列器', - icon: '→', - category: 'composite', - description: '按顺序执行子节点,任一失败则整体失败', - canHaveChildren: true, - canHaveParent: true, - minChildren: 1, - className: 'Sequence', - namespace: 'behaviourTree/composites', - properties: { - abortType: { - name: '中止类型', - type: 'select', - value: 'None', - options: ['None', 'LowerPriority', 'Self', 'Both'], - description: '决定节点在何种情况下会被中止', - required: false - } - } - }, - { - type: 'selector', - name: '选择器', - icon: '?', - category: 'composite', - description: '按顺序执行子节点,任一成功则整体成功', - canHaveChildren: true, - canHaveParent: true, - minChildren: 1, - className: 'Selector', - namespace: 'behaviourTree/composites', - properties: { - abortType: { - name: '中止类型', - type: 'select', - value: 'None', - options: ['None', 'LowerPriority', 'Self', 'Both'], - description: '决定节点在何种情况下会被中止', - required: false - } - } - }, - { - type: 'parallel', - name: '并行器', - icon: '||', - category: 'composite', - description: '并行执行所有子节点', - canHaveChildren: true, - canHaveParent: true, - minChildren: 2, - className: 'Parallel', - namespace: 'behaviourTree/composites' - }, - { - type: 'parallel-selector', - name: '并行选择器', - icon: '⫸', - category: 'composite', - description: '并行执行子节点,任一成功则成功', - canHaveChildren: true, - canHaveParent: true, - minChildren: 2, - className: 'ParallelSelector', - namespace: 'behaviourTree/composites' - }, - { - type: 'random-selector', - name: '随机选择器', - icon: '🎲?', - category: 'composite', - description: '随机顺序执行子节点,任一成功则成功', - canHaveChildren: true, - canHaveParent: true, - minChildren: 2, - className: 'RandomSelector', - namespace: 'behaviourTree/composites', - properties: { - reshuffleOnRestart: { - name: '重启时重新洗牌', - type: 'boolean', - value: true, - description: '是否在每次重新开始时都重新洗牌子节点顺序', - required: false - }, - abortType: { - name: '中止类型', - type: 'select', - value: 'None', - options: ['None', 'LowerPriority', 'Self', 'Both'], - description: '决定节点在何种情况下会被中止', - required: false - } - } - }, - { - type: 'random-sequence', - name: '随机序列器', - icon: '🎲→', - category: 'composite', - description: '随机顺序执行子节点,任一失败则失败', - canHaveChildren: true, - canHaveParent: true, - minChildren: 2, - className: 'RandomSequence', - namespace: 'behaviourTree/composites', - properties: { - reshuffleOnRestart: { - name: '重启时重新洗牌', - type: 'boolean', - value: true, - description: '是否在每次重新开始时都重新洗牌子节点顺序', - required: false - }, - abortType: { - name: '中止类型', - type: 'select', - value: 'None', - options: ['None', 'LowerPriority', 'Self', 'Both'], - description: '决定节点在何种情况下会被中止', - required: false - } - } - }, - - // 装饰器节点 (Decorators) - 只能有一个子节点 - { - type: 'repeater', - name: '重复器', - icon: '🔄', - category: 'decorator', - description: '重复执行子节点指定次数或无限次', - canHaveChildren: true, - canHaveParent: true, - maxChildren: 1, - minChildren: 1, - className: 'Repeater', - namespace: 'behaviourTree/decorators', - properties: { - count: { - name: '重复次数', - type: 'number', - value: -1, - description: '重复执行次数,-1表示无限重复,必须是正整数', - required: true - }, - endOnFailure: { - name: '失败时停止', - type: 'boolean', - value: false, - description: '子节点失败时是否停止重复', - required: false - }, - endOnSuccess: { - name: '成功时停止', - type: 'boolean', - value: false, - description: '子节点成功时是否停止重复', - required: false - } - } - }, - { - type: 'inverter', - name: '反转器', - icon: '⚡', - category: 'decorator', - description: '反转子节点的执行结果', - canHaveChildren: true, - canHaveParent: true, - maxChildren: 1, - minChildren: 1, - className: 'Inverter', - namespace: 'behaviourTree/decorators' - }, - { - type: 'always-succeed', - name: '总是成功', - icon: '✅', - category: 'decorator', - description: '无论子节点结果如何都返回成功', - canHaveChildren: true, - canHaveParent: true, - maxChildren: 1, - minChildren: 1, - className: 'AlwaysSucceed', - namespace: 'behaviourTree/decorators' - }, - { - type: 'always-fail', - name: '总是失败', - icon: '❌', - category: 'decorator', - description: '无论子节点结果如何都返回失败', - canHaveChildren: true, - canHaveParent: true, - maxChildren: 1, - minChildren: 1, - className: 'AlwaysFail', - namespace: 'behaviourTree/decorators' - }, - { - type: 'until-success', - name: '直到成功', - icon: '🔁✅', - category: 'decorator', - description: '重复执行子节点直到成功', - canHaveChildren: true, - canHaveParent: true, - maxChildren: 1, - minChildren: 1, - className: 'UntilSuccess', - namespace: 'behaviourTree/decorators' - }, - { - type: 'until-fail', - name: '直到失败', - icon: '🔁❌', - category: 'decorator', - description: '重复执行子节点直到失败', - canHaveChildren: true, - canHaveParent: true, - maxChildren: 1, - minChildren: 1, - className: 'UntilFail', - namespace: 'behaviourTree/decorators' - }, - { - type: 'conditional-decorator', - name: '条件装饰器', - icon: '🔀', - category: 'decorator', - description: '基于条件执行子节点(拖拽条件节点到此装饰器来配置条件)', - canHaveChildren: true, - canHaveParent: true, - maxChildren: 1, - minChildren: 1, - className: 'ConditionalDecorator', - namespace: 'behaviourTree/decorators', - properties: { - conditionType: { - name: '条件类型', - type: 'select', - value: 'custom', - options: ['custom', 'random', 'hasComponent', 'hasTag', 'isActive', 'numericCompare', 'propertyExists'], - description: '装饰器使用的条件类型', - required: false - }, - executeWhenTrue: { - name: '条件为真时执行', - type: 'boolean', - value: true, - description: '条件为真时是否执行子节点', - required: false - }, - executeWhenFalse: { - name: '条件为假时执行', - type: 'boolean', - value: false, - description: '条件为假时是否执行子节点', - required: false - }, - checkInterval: { - name: '检查间隔', - type: 'number', - value: 0, - description: '条件检查间隔时间(秒),0表示每帧检查', - required: false - }, - abortType: { - name: '中止类型', - type: 'select', - value: 'None', - options: ['None', 'LowerPriority', 'Self', 'Both'], - description: '决定节点在何种情况下会被中止', - required: false - } - } - }, - - // 动作节点 (Actions) - 叶子节点,不能有子节点 - { - type: 'execute-action', - name: '执行动作', - icon: '⚡', - category: 'action', - description: '执行自定义代码逻辑', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'ExecuteAction', - namespace: 'behaviourTree/actions', - properties: { - actionCode: { - name: '动作代码', - type: 'code', - value: '(context) => {\n // 在这里编写动作逻辑\n return TaskStatus.Success;\n}', - description: '要执行的动作函数代码', - required: true - }, - actionName: { - name: '动作名称', - type: 'string', - value: '', - description: '用于调试的动作名称', - required: false - } - } - }, - { - type: 'wait-action', - name: '等待动作', - icon: '⏰', - category: 'action', - description: '等待指定时间后完成', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'WaitAction', - namespace: 'behaviourTree/actions', - properties: { - waitTime: { - name: '等待时间', - type: 'number', - value: 1.0, - description: '等待时间(秒),必须大于0', - required: true - }, - useExternalTime: { - name: '使用外部时间', - type: 'boolean', - value: false, - description: '是否使用上下文提供的deltaTime,否则使用内部时间计算', - required: false - } - } - }, - { - type: 'log-action', - name: '日志动作', - icon: '📝', - category: 'action', - description: '输出日志信息', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'LogAction', - namespace: 'behaviourTree/actions', - properties: { - message: { - name: '日志消息', - type: 'string', - value: 'Hello from behavior tree!', - description: '要输出的日志消息', - required: true - }, - logLevel: { - name: '日志级别', - type: 'select', - value: 'info', - options: ['debug', 'info', 'warn', 'error'], - description: '日志输出级别', - required: false - } - } - }, - { - type: 'behavior-tree-reference', - name: '行为树引用', - icon: '🔗', - category: 'action', - description: '运行另一个行为树', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'BehaviorTreeReference', - namespace: 'behaviourTree/actions', - properties: { - treeName: { - name: '树名称', - type: 'string', - value: '', - description: '要引用的行为树名称', - required: true - } - } - }, - - - - - // 条件节点 (可拖拽到条件装饰器上吸附) - { - type: 'condition-random', - name: '随机概率', - icon: '🎲', - category: 'condition', - description: '基于概率的随机条件 (拖拽到条件装饰器上使用)', - canHaveChildren: false, - canHaveParent: false, // 不能作为常规子节点 - maxChildren: 0, - isDraggableCondition: true, // 标记为可拖拽的条件 - attachableToDecorator: true, // 可以吸附到装饰器 - className: 'RandomProbability', - namespace: 'behaviourTree/conditionals', - properties: { - successProbability: { - name: '成功概率', - type: 'number', - value: 0.5, - description: '返回成功的概率 (0.0 - 1.0)', - required: true - } - } - }, - { - type: 'condition-component', - name: '组件检查', - icon: '🔍📦', - category: 'condition', - description: '检查实体是否有指定组件 (拖拽到条件装饰器上使用)', - canHaveChildren: false, - canHaveParent: false, - maxChildren: 0, - isDraggableCondition: true, - attachableToDecorator: true, - className: 'HasComponentCondition', - namespace: 'ecs-integration/behaviors', - properties: { - componentType: { - name: '组件类型', - type: 'string', - value: 'Component', - description: '要检查的组件类型名称', - required: true - } - } - }, - { - type: 'condition-tag', - name: '标签检查', - icon: '🏷️', - category: 'condition', - description: '检查实体标签 (拖拽到条件装饰器上使用)', - canHaveChildren: false, - canHaveParent: false, - maxChildren: 0, - isDraggableCondition: true, - attachableToDecorator: true, - className: 'HasTagCondition', - namespace: 'ecs-integration/behaviors', - properties: { - tagValue: { - name: '标签值', - type: 'number', - value: 0, - description: '要检查的标签值', - required: true - } - } - }, - { - type: 'condition-active', - name: '激活状态', - icon: '👁️', - category: 'condition', - description: '检查实体激活状态 (拖拽到条件装饰器上使用)', - canHaveChildren: false, - canHaveParent: false, - maxChildren: 0, - isDraggableCondition: true, - attachableToDecorator: true, - className: 'IsActiveCondition', - namespace: 'ecs-integration/behaviors', - properties: { - checkHierarchy: { - name: '检查层级', - type: 'boolean', - value: true, - description: '是否检查层级激活状态', - required: false - } - } - }, - { - type: 'condition-numeric', - name: '数值比较', - icon: '🔢', - category: 'condition', - description: '数值比较条件 (拖拽到条件装饰器上使用)', - canHaveChildren: false, - canHaveParent: false, - maxChildren: 0, - isDraggableCondition: true, - attachableToDecorator: true, - className: 'NumericComparison', - namespace: 'behaviourTree/conditionals', - properties: { - propertyPath: { - name: '属性路径', - type: 'string', - value: 'context.someValue', - description: '要比较的属性路径', - required: true - }, - compareOperator: { - name: '比较操作符', - type: 'select', - value: 'greater', - options: ['greater', 'less', 'equal', 'greaterEqual', 'lessEqual', 'notEqual'], - description: '数值比较操作符', - required: true - }, - compareValue: { - name: '比较值', - type: 'number', - value: 0, - description: '用于比较的数值', - required: true - } - } - }, - { - type: 'condition-property', - name: '属性存在', - icon: '📋', - category: 'condition', - description: '检查属性是否存在 (拖拽到条件装饰器上使用)', - canHaveChildren: false, - canHaveParent: false, - maxChildren: 0, - isDraggableCondition: true, - attachableToDecorator: true, - className: 'PropertyExists', - namespace: 'behaviourTree/conditionals', - properties: { - propertyPath: { - name: '属性路径', - type: 'string', - value: 'context.someProperty', - description: '要检查的属性路径', - required: true - } - } - }, - { - type: 'condition-custom', - name: '自定义条件', - icon: '⚙️', - category: 'condition', - description: '自定义代码条件 (拖拽到条件装饰器上使用)', - canHaveChildren: false, - canHaveParent: false, - maxChildren: 0, - isDraggableCondition: true, - attachableToDecorator: true, - className: 'ExecuteActionConditional', - namespace: 'behaviourTree/conditionals', - properties: { - conditionCode: { - name: '条件代码', - type: 'code', - value: '(context) => {\n // 条件判断逻辑\n return true; // 返回 true/false\n}', - description: '条件判断函数代码', - required: true - }, - conditionName: { - name: '条件名称', - type: 'string', - value: '', - description: '用于调试的条件名称', - required: false - } - } - }, - - // ECS专用节点 - 动作节点 - { - type: 'add-component', - name: '添加组件', - icon: '➕', - category: 'ecs', - description: '为实体添加组件', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'AddComponentAction', - namespace: 'ecs-integration/behaviors', - properties: { - componentType: { - name: '组件类型', - type: 'string', - value: 'Component', - description: '要添加的组件类型名称', - required: true - }, - componentFactory: { - name: '组件工厂函数', - type: 'code', - value: '() => new Component()', - description: '创建组件实例的函数(可选)', - required: false - } - } - }, - { - type: 'remove-component', - name: '移除组件', - icon: '➖', - category: 'ecs', - description: '从实体移除组件', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'RemoveComponentAction', - namespace: 'ecs-integration/behaviors', - properties: { - componentType: { - name: '组件类型', - type: 'string', - value: 'Component', - description: '要移除的组件类型名称', - required: true - } - } - }, - { - type: 'modify-component', - name: '修改组件', - icon: '✏️', - category: 'ecs', - description: '修改实体组件的属性', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'ModifyComponentAction', - namespace: 'ecs-integration/behaviors', - properties: { - componentType: { - name: '组件类型', - type: 'string', - value: 'Component', - description: '要修改的组件类型名称', - required: true - }, - modifierCode: { - name: '修改代码', - type: 'code', - value: '(component) => {\n // 在这里修改组件属性\n // component.someProperty = newValue;\n}', - description: '组件修改函数代码', - required: true - } - } - }, - - { - type: 'wait-time', - name: 'ECS等待', - icon: '⏱️', - category: 'ecs', - description: 'ECS优化的等待动作', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'WaitTimeAction', - namespace: 'ecs-integration/behaviors', - properties: { - waitTime: { - name: '等待时间', - type: 'number', - value: 1.0, - description: '等待时间(秒)', - required: true - } - } - }, - { - type: 'destroy-entity', - name: '销毁实体', - icon: '💥', - category: 'ecs', - description: '销毁当前实体', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'DestroyEntityAction', - namespace: 'ecs-integration/behaviors' - }, - - // 黑板相关节点 - 动作节点 - { - type: 'set-blackboard-value', - name: '设置黑板变量', - icon: '📝', - category: 'action', - description: '设置黑板变量的值', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'SetBlackboardValue', - namespace: 'behaviourTree/actions', - properties: { - variableName: { - name: '变量名', - type: 'string', - value: '', - description: '黑板变量名', - required: true - }, - value: { - name: '设置值', - type: 'string', - value: '', - description: '要设置的值(留空则使用源变量)', - required: false - }, - sourceVariable: { - name: '源变量名', - type: 'string', - value: '', - description: '从另一个黑板变量复制值', - required: false - }, - force: { - name: '强制设置', - type: 'boolean', - value: false, - description: '是否忽略只读限制', - required: false - } - } - }, - { - type: 'add-blackboard-value', - name: '增加数值变量', - icon: '➕', - category: 'action', - description: '增加数值型黑板变量的值', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'AddToBlackboardValue', - namespace: 'behaviourTree/actions', - properties: { - variableName: { - name: '变量名', - type: 'string', - value: '', - description: '数值型黑板变量名', - required: true - }, - increment: { - name: '增量', - type: 'number', - value: 1, - description: '增加的数值', - required: true - }, - incrementVariable: { - name: '增量变量名', - type: 'string', - value: '', - description: '从另一个变量获取增量值', - required: false - } - } - }, - { - type: 'toggle-blackboard-bool', - name: '切换布尔变量', - icon: '🔄', - category: 'action', - description: '切换布尔型黑板变量的值', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'ToggleBlackboardBool', - namespace: 'behaviourTree/actions', - properties: { - variableName: { - name: '变量名', - type: 'string', - value: '', - description: '布尔型黑板变量名', - required: true - } - } - }, - { - type: 'reset-blackboard-variable', - name: '重置变量', - icon: '🔄', - category: 'action', - description: '重置黑板变量到默认值', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'ResetBlackboardVariable', - namespace: 'behaviourTree/actions', - properties: { - variableName: { - name: '变量名', - type: 'string', - value: '', - description: '要重置的黑板变量名', - required: true - } - } - }, - { - type: 'wait-blackboard-condition', - name: '等待黑板条件', - icon: '⏳', - category: 'action', - description: '等待黑板变量满足指定条件', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'WaitForBlackboardCondition', - namespace: 'behaviourTree/actions', - properties: { - variableName: { - name: '变量名', - type: 'string', - value: '', - description: '要监听的黑板变量名', - required: true - }, - expectedValue: { - name: '期望值', - type: 'string', - value: '', - description: '期望的变量值', - required: true - } - } - }, - { - type: 'log-blackboard-value', - name: '记录黑板变量', - icon: '📊', - category: 'action', - description: '将黑板变量值记录到控制台', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'LogBlackboardValue', - namespace: 'behaviourTree/actions', - properties: { - variableName: { - name: '变量名', - type: 'string', - value: '', - description: '要记录的黑板变量名', - required: true - }, - prefix: { - name: '日志前缀', - type: 'string', - value: '[Blackboard]', - description: '日志消息的前缀', - required: false - } - } - }, - { - type: 'math-blackboard-operation', - name: '数学运算', - icon: '🧮', - category: 'action', - description: '对黑板变量执行数学运算', - canHaveChildren: false, - canHaveParent: true, - maxChildren: 0, - className: 'MathBlackboardOperation', - namespace: 'behaviourTree/actions', - properties: { - targetVariable: { - name: '目标变量', - type: 'string', - value: '', - description: '存储结果的变量名', - required: true - }, - operand1Variable: { - name: '操作数1变量', - type: 'string', - value: '', - description: '第一个操作数的变量名', - required: true - }, - operand2: { - name: '操作数2', - type: 'string', - value: '', - description: '第二个操作数(数值或变量名)', - required: true - }, - operation: { - name: '运算类型', - type: 'select', - value: 'add', - options: ['add', 'subtract', 'multiply', 'divide', 'modulo', 'power', 'min', 'max'], - description: '要执行的数学运算', - required: true - } - } - }, - - // 黑板相关节点 - 条件节点 - { - type: 'blackboard-value-comparison', - name: '黑板值比较', - icon: '⚖️', - category: 'condition', - description: '比较黑板变量与指定值或另一个变量 (拖拽到条件装饰器上使用)', - canHaveChildren: false, - canHaveParent: false, - maxChildren: 0, - isDraggableCondition: true, - attachableToDecorator: true, - className: 'BlackboardValueComparison', - namespace: 'behaviourTree/conditionals', - properties: { - variableName: { - name: '变量名', - type: 'string', - value: '', - description: '要比较的黑板变量名', - required: true - }, - operator: { - name: '比较操作符', - type: 'select', - value: 'equal', - options: ['equal', 'notEqual', 'greater', 'greaterOrEqual', 'less', 'lessOrEqual', 'contains', 'notContains'], - description: '比较操作类型', - required: true - }, - compareValue: { - name: '比较值', - type: 'string', - value: '', - description: '用于比较的值(留空则使用比较变量)', - required: false - }, - compareVariable: { - name: '比较变量名', - type: 'string', - value: '', - description: '用于比较的另一个黑板变量名', - required: false - } - } - }, - { - type: 'blackboard-variable-exists', - name: '黑板变量存在', - icon: '✅', - category: 'condition', - description: '检查黑板变量是否存在且有效 (拖拽到条件装饰器上使用)', - canHaveChildren: false, - canHaveParent: false, - maxChildren: 0, - isDraggableCondition: true, - attachableToDecorator: true, - className: 'BlackboardVariableExists', - namespace: 'behaviourTree/conditionals', - properties: { - variableName: { - name: '变量名', - type: 'string', - value: '', - description: '要检查的黑板变量名', - required: true - }, - invert: { - name: '反转结果', - type: 'boolean', - value: false, - description: '是否反转检查结果', - required: false - } - } - }, - { - type: 'blackboard-variable-type-check', - name: '黑板变量类型检查', - icon: '🔍', - category: 'condition', - description: '检查黑板变量是否为指定类型 (拖拽到条件装饰器上使用)', - canHaveChildren: false, - canHaveParent: false, - maxChildren: 0, - isDraggableCondition: true, - attachableToDecorator: true, - className: 'BlackboardVariableTypeCheck', - namespace: 'behaviourTree/conditionals', - properties: { - variableName: { - name: '变量名', - type: 'string', - value: '', - description: '要检查的黑板变量名', - required: true - }, - expectedType: { - name: '期望类型', - type: 'select', - value: 'string', - options: ['string', 'number', 'boolean', 'vector2', 'vector3', 'object', 'array'], - description: '期望的变量类型', - required: true - } - } - }, - { - type: 'blackboard-variable-range-check', - name: '黑板变量范围检查', - icon: '📏', - category: 'condition', - description: '检查数值型黑板变量是否在指定范围内 (拖拽到条件装饰器上使用)', - canHaveChildren: false, - canHaveParent: false, - maxChildren: 0, - isDraggableCondition: true, - attachableToDecorator: true, - className: 'BlackboardVariableRangeCheck', - namespace: 'behaviourTree/conditionals', - properties: { - variableName: { - name: '变量名', - type: 'string', - value: '', - description: '要检查的数值型黑板变量名', - required: true - }, - minValue: { - name: '最小值', - type: 'number', - value: 0, - description: '范围的最小值(包含)', - required: true - }, - maxValue: { - name: '最大值', - type: 'number', - value: 100, - description: '范围的最大值(包含)', - required: true - } - } - } -]; - -// 导出所有节点模板 -export const allNodeTemplates: NodeTemplate[] = nodeTemplates; - -// 为了保持向后兼容,保留原来的导出 -export { nodeTemplates as default }; \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/index.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/index.ts deleted file mode 100644 index ffc09d42..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/index.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { readFileSync } from 'fs-extra'; -import { join } from 'path'; -import { createApp, App, defineComponent } from 'vue'; -import { useBehaviorTreeEditor } from './composables/useBehaviorTreeEditor'; -import { EventManager } from './utils/EventManager'; - -// Vue应用实例 -let panelDataMap = new WeakMap(); - -// 待处理的文件队列 -let pendingFileData: any = null; -// Vue应用是否已挂载完成 -let vueAppMounted: boolean = false; -// 存储面板实例,用于访问面板的DOM元素 -let currentPanelInstance: any = null; - -/** - * 面板定义 - */ -const panelDefinition = { - template: readFileSync(join(__dirname, '../../../static/template/behavior-tree/index.html'), 'utf-8'), - - style: [ - readFileSync(join(__dirname, '../../../static/style/behavior-tree/base.css'), 'utf-8'), - readFileSync(join(__dirname, '../../../static/style/behavior-tree/toolbar.css'), 'utf-8'), - readFileSync(join(__dirname, '../../../static/style/behavior-tree/panels.css'), 'utf-8'), - readFileSync(join(__dirname, '../../../static/style/behavior-tree/canvas.css'), 'utf-8'), - readFileSync(join(__dirname, '../../../static/style/behavior-tree/nodes.css'), 'utf-8'), - readFileSync(join(__dirname, '../../../static/style/behavior-tree/conditions.css'), 'utf-8'), - readFileSync(join(__dirname, '../../../static/style/behavior-tree/modals.css'), 'utf-8') - ].join('\n'), - - $: { - app: '#behavior-tree-app', - }, - - methods: { - async loadBehaviorTreeFile(assetInfo: any) { - try { - const filePath = assetInfo?.file || assetInfo?.path; - if (!filePath) { - throw new Error('无法获取文件路径'); - } - - const fs = require('fs-extra'); - const path = require('path'); - - if (!fs.existsSync(filePath)) { - throw new Error(`文件不存在: ${filePath}`); - } - - const content = await fs.readFile(filePath, 'utf8'); - let fileContent: any; - - try { - fileContent = JSON.parse(content); - } catch (parseError) { - fileContent = { - version: "1.0.0", - type: "behavior-tree", - tree: { id: "root", type: "sequence", children: [] } - }; - } - - const fileInfo = { - ...fileContent, - _fileInfo: { - fileName: path.basename(filePath, path.extname(filePath)), - filePath: filePath - } - }; - - const notifyVueComponent = () => { - const appContainer = currentPanelInstance?.$.app; - - if (appContainer && vueAppMounted) { - if (typeof (appContainer as any).loadFileContent === 'function') { - (appContainer as any).loadFileContent(fileInfo); - } else { - const event = new CustomEvent('load-behavior-tree-file', { detail: fileInfo }); - document.dispatchEvent(event); - } - } else { - pendingFileData = fileInfo; - } - }; - - notifyVueComponent(); - - if (pendingFileData) { - setTimeout(() => { - if (pendingFileData) { - notifyVueComponent(); - } - }, 500); - } - - return { success: true, message: '文件加载成功' }; - - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - const event = new CustomEvent('file-load-error', { detail: { error: errorMessage } }); - document.dispatchEvent(event); - return { success: false, error: errorMessage }; - } - }, - }, - - ready() { - currentPanelInstance = this; - - if (this.$.app) { - try { - const BehaviorTreeEditor = defineComponent({ - setup() { - const editor = useBehaviorTreeEditor(); - return editor; - }, - template: readFileSync(join(__dirname, '../../../static/template/behavior-tree/BehaviorTreeEditor.html'), 'utf-8') - }); - - const app = createApp(BehaviorTreeEditor); - - app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ui-'); - - app.config.errorHandler = (err, instance, info) => { - console.error('[BehaviorTreePanel] Vue错误:', err, info); - }; - - app.component('tree-node-item', defineComponent({ - props: ['node', 'level', 'getNodeByIdLocal'], - emits: ['node-select'], - template: ` -
- {{ node.icon || '●' }} - {{ node.name || node.type }} - {{ node.type }} -
- ` - })); - - app.mount(this.$.app); - panelDataMap.set(this, app); - vueAppMounted = true; - - if (pendingFileData) { - const event = new CustomEvent('load-behavior-tree-file', { detail: pendingFileData }); - document.dispatchEvent(event); - pendingFileData = null; - } - - } catch (error) { - console.error('[BehaviorTreePanel] 初始化失败:', error); - } - } - }, - - /** - * 面板关闭时调用 - */ - close() { - try { - const app = panelDataMap.get(this); - if (app) { - app.unmount(); - panelDataMap.delete(this); - } - - EventManager.getInstance().cleanup(); - - } catch (error) { - console.error('[BehaviorTreePanel] 清理资源时发生错误:', error); - } - } -}; - -// 导出面板定义 - 使用Editor.Panel.define()包装 -module.exports = Editor.Panel.define(panelDefinition); \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/types/index.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/types/index.ts deleted file mode 100644 index 49a90d1c..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/types/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { PropertyDefinition } from '../data/nodeTemplates'; - -export interface TreeNode { - id: string; - type: string; - name: string; - icon: string; - description: string; - x: number; - y: number; - children: string[]; - parent?: string; - properties?: Record; // 改为any以支持动态属性值 - canHaveChildren: boolean; - canHaveParent: boolean; - maxChildren?: number; // 最大子节点数量限制 - minChildren?: number; // 最小子节点数量要求 - hasError?: boolean; - // 条件装饰器相关 - attachedCondition?: { - type: string; - name: string; - icon: string; - }; - // 条件节点相关(用于虚拟条件节点) - isConditionNode?: boolean; - parentDecorator?: TreeNode; - // 条件显示状态 - conditionExpanded?: boolean; // 条件是否展开显示详细信息 -} - -export interface Connection { - id: string; - sourceId: string; - targetId: string; - path: string; - active: boolean; -} - -export interface DragState { - isDraggingCanvas: boolean; - isDraggingNode: boolean; - isConnecting: boolean; - dragStartX: number; - dragStartY: number; - dragNodeId: string | null; - dragNodeStartX: number; - dragNodeStartY: number; - connectionStart: { nodeId: string; portType: string } | null; - connectionEnd: { x: number; y: number }; -} - -export interface InstallStatus { - installed: boolean; - version: string | null; - packageExists: boolean; -} - -export interface ValidationResult { - isValid: boolean; - message: string; -} - -export interface ConnectionPort { - nodeId: string; - portType: string; -} - -export interface CanvasCoordinates { - x: number; - y: number; -} - -export interface ConnectionState { - isConnecting: boolean; - startNodeId: string | null; - startPortType: 'input' | 'output' | null; - currentMousePos: { x: number; y: number } | null; - startPortPos: { x: number; y: number } | null; - hoveredPort: { nodeId: string; portType: 'input' | 'output' } | null; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/EventManager.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/EventManager.ts deleted file mode 100644 index 00c22189..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/EventManager.ts +++ /dev/null @@ -1,104 +0,0 @@ -/** - * 事件管理器 - 统一处理面板的事件通信 - */ -export class EventManager { - private static instance: EventManager; - private eventListeners: Map = new Map(); - - private constructor() {} - - static getInstance(): EventManager { - if (!EventManager.instance) { - EventManager.instance = new EventManager(); - } - return EventManager.instance; - } - - /** - * 添加事件监听器 - */ - addEventListener(eventType: string, listener: EventListener): void { - if (!this.eventListeners.has(eventType)) { - this.eventListeners.set(eventType, []); - } - - const listeners = this.eventListeners.get(eventType)!; - listeners.push(listener); - - // 添加到DOM - document.addEventListener(eventType, listener); - - console.log(`[EventManager] 添加事件监听器: ${eventType}`); - } - - /** - * 移除事件监听器 - */ - removeEventListener(eventType: string, listener: EventListener): void { - const listeners = this.eventListeners.get(eventType); - if (listeners) { - const index = listeners.indexOf(listener); - if (index > -1) { - listeners.splice(index, 1); - document.removeEventListener(eventType, listener); - console.log(`[EventManager] 移除事件监听器: ${eventType}`); - } - } - } - - /** - * 移除特定类型的所有监听器 - */ - removeAllListeners(eventType: string): void { - const listeners = this.eventListeners.get(eventType); - if (listeners) { - listeners.forEach(listener => { - document.removeEventListener(eventType, listener); - }); - this.eventListeners.delete(eventType); - console.log(`[EventManager] 移除所有 ${eventType} 事件监听器`); - } - } - - /** - * 清理所有事件监听器 - */ - cleanup(): void { - this.eventListeners.forEach((listeners, eventType) => { - listeners.forEach(listener => { - document.removeEventListener(eventType, listener); - }); - }); - this.eventListeners.clear(); - console.log('[EventManager] 清理所有事件监听器'); - } - - /** - * 发送消息到主进程 - */ - static sendToMain(message: string, ...args: any[]): void { - try { - if (typeof (window as any).sendToMain === 'function') { - (window as any).sendToMain(message, ...args); - console.log(`[EventManager] 发送消息到主进程: ${message}`, args); - } else { - console.error('[EventManager] sendToMain 方法不可用'); - } - } catch (error) { - console.error('[EventManager] 发送消息失败:', error); - } - } - - /** - * 触发自定义事件 - */ - static dispatch(eventType: string, detail?: any): void { - try { - const event = new CustomEvent(eventType, { detail }); - document.dispatchEvent(event); - console.log(`[EventManager] 触发事件: ${eventType}`, detail); - } catch (error) { - console.error(`[EventManager] 触发事件失败: ${eventType}`, error); - } - } -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/canvasUtils.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/canvasUtils.ts deleted file mode 100644 index 2dd8cab3..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/canvasUtils.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { CanvasCoordinates } from '../types'; - -/** - * 获取相对于画布的坐标(考虑缩放和平移) - */ -export function getCanvasCoordinates( - event: MouseEvent, - canvasElement: HTMLElement | null, - panX: number, - panY: number, - zoomLevel: number -): CanvasCoordinates { - if (!canvasElement) { - return { x: 0, y: 0 }; - } - - try { - const rect = canvasElement.getBoundingClientRect(); - const x = (event.clientX - rect.left - panX) / zoomLevel; - const y = (event.clientY - rect.top - panY) / zoomLevel; - return { x, y }; - } catch (error) { - return { x: 0, y: 0 }; - } -} - -/** - * 计算网格样式 - */ -export function getGridStyle(panX: number, panY: number, zoomLevel: number) { - const gridSize = 20 * zoomLevel; - return { - backgroundSize: `${gridSize}px ${gridSize}px`, - backgroundPosition: `${panX % gridSize}px ${panY % gridSize}px` - }; -} - -/** - * 计算视图居中的平移值 - */ -export function calculateCenterView( - nodes: any[], - canvasWidth: number, - canvasHeight: number, - zoomLevel: number -): { panX: number; panY: number } { - if (nodes.length === 0) { - return { panX: 0, panY: 0 }; - } - - // 计算所有节点的边界 - let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; - - nodes.forEach(node => { - minX = Math.min(minX, node.x); - minY = Math.min(minY, node.y); - maxX = Math.max(maxX, node.x + 150); - maxY = Math.max(maxY, node.y + 100); - }); - - // 计算中心点 - const centerX = (minX + maxX) / 2; - const centerY = (minY + maxY) / 2; - - // 设置平移,使内容居中 - const panX = canvasWidth / 2 - centerX * zoomLevel; - const panY = canvasHeight / 2 - centerY * zoomLevel; - - return { panX, panY }; -} - -/** - * 约束缩放级别 - */ -export function constrainZoom(zoom: number): number { - return Math.max(0.3, Math.min(zoom, 3)); -} - -/** - * 计算缩放后的坐标 - */ -export function transformCoordinate( - x: number, - y: number, - panX: number, - panY: number, - zoomLevel: number -): { x: number; y: number } { - return { - x: x * zoomLevel + panX, - y: y * zoomLevel + panY - }; -} - -/** - * 计算逆向变换的坐标(从屏幕坐标到画布坐标) - */ -export function inverseTransformCoordinate( - screenX: number, - screenY: number, - panX: number, - panY: number, - zoomLevel: number -): { x: number; y: number } { - return { - x: (screenX - panX) / zoomLevel, - y: (screenY - panY) / zoomLevel - }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/installUtils.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/installUtils.ts deleted file mode 100644 index 4cea1245..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/installUtils.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { InstallStatus } from '../types'; -import * as fs from 'fs'; -import * as path from 'path'; - -/** - * 检查行为树AI系统是否已安装 - * 通过主进程检查项目中是否安装了@esengine/ai包 - */ -export async function checkBehaviorTreeInstalled(projectPath: string): Promise { - try { - // 通过Editor.Message请求主进程检查安装状态 - const isInstalled = await Editor.Message.request('cocos-ecs-extension', 'check-behavior-tree-installed'); - - if (isInstalled) { - // 如果已安装,读取版本信息 - const packageJsonPath = path.join(projectPath, 'package.json'); - if (fs.existsSync(packageJsonPath)) { - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); - const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies }; - const aiPackage = dependencies['@esengine/ai']; - - return { - installed: true, - version: aiPackage || null, - packageExists: true - }; - } - } - - return { - installed: false, - version: null, - packageExists: fs.existsSync(path.join(projectPath, 'package.json')) - }; - } catch (error) { - return { - installed: false, - version: null, - packageExists: false - }; - } -} - -/** - * 格式化安装状态文本 - */ -export function getInstallStatusText( - isChecking: boolean, - isInstalling: boolean, - isInstalled: boolean, - version: string | null -): string { - if (isChecking) return '检查状态中...'; - if (isInstalling) return '正在安装AI系统...'; - return isInstalled ? 'AI系统已安装' : 'AI系统未安装'; -} - -/** - * 获取安装状态CSS类 - */ -export function getInstallStatusClass( - isInstalling: boolean, - isInstalled: boolean -): string { - if (isInstalling) return 'installing'; - return isInstalled ? 'installed' : 'not-installed'; -} - -/** - * 安装行为树AI系统 - * 通过发送消息到主进程来执行真实的npm安装命令 - */ -export async function installBehaviorTreeAI(projectPath: string): Promise { - try { - const result = await Editor.Message.request('cocos-ecs-extension', 'install-behavior-tree'); - return Boolean(result); - } catch (error) { - console.error('请求安装AI系统失败:', error); - return false; - } -} - -/** - * 更新行为树AI系统 - * 通过发送消息到主进程来执行真实的npm更新命令 - */ -export async function updateBehaviorTreeAI(projectPath: string): Promise { - try { - const result = await Editor.Message.request('cocos-ecs-extension', 'update-behavior-tree'); - return Boolean(result); - } catch (error) { - console.error('请求更新AI系统失败:', error); - return false; - } -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/nodeUtils.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/nodeUtils.ts deleted file mode 100644 index 6b80d219..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/behavior-tree/utils/nodeUtils.ts +++ /dev/null @@ -1,230 +0,0 @@ -import { TreeNode, ValidationResult } from '../types'; -import { NodeTemplate } from '../data/nodeTemplates'; - -/** - * 生成唯一的节点ID - */ -export function generateNodeId(): string { - return 'node_' + Math.random().toString(36).substr(2, 9); -} - -/** - * 根据模板创建节点 - */ -export function createNodeFromTemplate(template: NodeTemplate, x: number = 100, y: number = 100): TreeNode { - const nodeId = generateNodeId(); - - // 深拷贝 properties 以避免引用共享 - let properties: any = {}; - if (template.properties) { - for (const [key, prop] of Object.entries(template.properties)) { - properties[key] = { - name: prop.name, - type: prop.type, - value: prop.value, - description: prop.description, - options: prop.options ? [...prop.options] : undefined, - required: prop.required - }; - } - } - - const node: TreeNode = { - id: nodeId, - type: template.type, - name: template.name, - icon: template.icon, - description: template.description, - x: x, - y: y, - children: [], - properties: properties, - canHaveChildren: template.canHaveChildren, - canHaveParent: template.canHaveParent, - maxChildren: template.maxChildren, - minChildren: template.minChildren, - hasError: false - }; - return node; -} - -/** - * 根据ID查找节点 - */ -export function getNodeById(nodes: TreeNode[], id: string): TreeNode | undefined { - return nodes.find(node => node.id === id); -} - -/** - * 获取根节点 - */ -export function getRootNode(nodes: TreeNode[]): TreeNode | undefined { - return nodes.find(node => !node.parent); -} - -/** - * 递归删除节点及其子节点 - */ -export function deleteNodeRecursive( - nodes: TreeNode[], - nodeId: string, - connections: any[], - onConnectionsUpdate: (connections: any[]) => void -): TreeNode[] { - const deleteRecursive = (id: string) => { - const node = getNodeById(nodes, id); - if (!node) return; - - // 递归删除子节点 - node.children.forEach(childId => deleteRecursive(childId)); - - // 从父节点的children中移除 - if (node.parent) { - const parent = getNodeById(nodes, node.parent); - if (parent) { - const index = parent.children.indexOf(id); - if (index > -1) { - parent.children.splice(index, 1); - } - } - } - - // 移除连接 - const updatedConnections = connections.filter(conn => - conn.sourceId !== id && conn.targetId !== id - ); - onConnectionsUpdate(updatedConnections); - - // 从树中移除节点 - const nodeIndex = nodes.findIndex(n => n.id === id); - if (nodeIndex > -1) { - nodes.splice(nodeIndex, 1); - } - }; - - deleteRecursive(nodeId); - return [...nodes]; // 返回新数组以触发响应式更新 -} - -/** - * 验证行为树结构 - */ -export function validateTree(nodes: TreeNode[]): ValidationResult { - if (nodes.length === 0) { - return { isValid: false, message: '行为树为空' }; - } - - // 检查根节点 - const rootNodes = nodes.filter(node => !node.parent); - if (rootNodes.length === 0) { - return { isValid: false, message: '缺少根节点' }; - } - if (rootNodes.length > 1) { - return { isValid: false, message: `发现多个根节点: ${rootNodes.map(n => n.name).join(', ')}` }; - } - - // 验证每个节点的子节点数量限制 - for (const node of nodes) { - const childrenCount = node.children.length; - - // 检查最小子节点数量 - if (node.minChildren !== undefined && childrenCount < node.minChildren) { - return { - isValid: false, - message: `节点 "${node.name}" 需要至少 ${node.minChildren} 个子节点,当前只有 ${childrenCount} 个` - }; - } - - // 检查最大子节点数量 - if (node.maxChildren !== undefined && childrenCount > node.maxChildren) { - return { - isValid: false, - message: `节点 "${node.name}" 最多只能有 ${node.maxChildren} 个子节点,当前有 ${childrenCount} 个` - }; - } - - // 检查装饰器节点的特殊限制 - if (node.type.includes('decorator') || node.type.includes('Decorator')) { - if (childrenCount !== 1) { - return { - isValid: false, - message: `装饰器节点 "${node.name}" 必须有且只能有一个子节点,当前有 ${childrenCount} 个` - }; - } - } - - // 检查叶子节点不能有子节点 - if (!node.canHaveChildren && childrenCount > 0) { - return { - isValid: false, - message: `叶子节点 "${node.name}" 不能有子节点,但当前有 ${childrenCount} 个` - }; - } - } - - // 检查循环引用 - const visited = new Set(); - const recursionStack = new Set(); - - function hasCycle(nodeId: string): boolean { - if (recursionStack.has(nodeId)) return true; - if (visited.has(nodeId)) return false; - - visited.add(nodeId); - recursionStack.add(nodeId); - - const node = getNodeById(nodes, nodeId); - if (node) { - for (const childId of node.children) { - if (hasCycle(childId)) return true; - } - } - - recursionStack.delete(nodeId); - return false; - } - - for (const node of nodes) { - if (hasCycle(node.id)) { - return { isValid: false, message: '检测到循环引用' }; - } - } - - return { isValid: true, message: '行为树结构有效' }; -} - -/** - * 更新节点属性 - */ -export function updateNodeProperty(node: TreeNode, path: string, value: any): void { - if (!node.properties) return; - - const keys = path.split('.'); - let target: any = node.properties; - - for (let i = 0; i < keys.length - 1; i++) { - target = target[keys[i]]; - } - - target[keys[keys.length - 1]] = value; -} - -/** - * 计算节点的边界框 - */ -export function getNodesBounds(nodes: TreeNode[]): { minX: number; minY: number; maxX: number; maxY: number } { - if (nodes.length === 0) { - return { minX: 0, minY: 0, maxX: 0, maxY: 0 }; - } - - let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; - - nodes.forEach(node => { - minX = Math.min(minX, node.x); - minY = Math.min(minY, node.y); - maxX = Math.max(maxX, node.x + 150); // 节点宽度 - maxY = Math.max(maxY, node.y + 100); // 节点高度 - }); - - return { minX, minY, maxX, maxY }; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/debug/index.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/debug/index.ts deleted file mode 100644 index c51a7dd6..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/debug/index.ts +++ /dev/null @@ -1,717 +0,0 @@ -import { readFileSync } from 'fs-extra'; -import { join } from 'path'; -import { createApp, App, defineComponent, ref, reactive, onMounted, onUnmounted } from 'vue'; -import { WebSocketServer, WebSocket } from 'ws'; -import { IncomingMessage } from 'http'; - -const panelDataMap = new WeakMap(); - -/** - * 游戏实例信息 - */ -interface GameInstance { - id: string; - name: string; - connectTime: number; - lastUpdateTime: number; - isActive: boolean; - debugData?: any; - ws?: WebSocket; // 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[]; - engineFrameTime: number; - ecsPercentage: 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; - percentage: number; - }>; - }; - - // 场景信息 - 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(); - private isRunning: boolean = false; - - constructor(port: number = 8080) { - this.port = port; - } - - async start(): Promise { - if (this.isRunning) return true; - - try { - this.wss = new WebSocketServer({ port: this.port }); - - this.wss.on('connection', (ws: WebSocket, req: IncomingMessage) => { - 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: Buffer) => { - 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: 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; - - // 计算系统性能数据,包括ECS占比 - const systemBreakdown = rawData.performance?.systemBreakdown || []; - const systemPerformance = rawData.performance?.systemPerformance || []; - - // 创建系统名称到占比的映射 - const systemPercentageMap = new Map(); - systemBreakdown.forEach((sys: any) => { - systemPercentageMap.set(sys.systemName, sys.percentage || 0); - }); - - 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 || [], - engineFrameTime: rawData.performance?.engineFrameTime || 0, - ecsPercentage: rawData.performance?.ecsPercentage || 0 - }, - - 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) => { - const systemName = sys.name; - const percentage = systemPercentageMap.get(systemName) || 0; - - return { - name: systemName, - 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, - percentage: percentage - }; - }) - }, - - 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: [], - engineFrameTime: 0, - ecsPercentage: 0 - }, - 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 { - if (!globalDebugServer) { - globalDebugServer = new ECSDebugServer(8080); - } - - if (!globalDebugServer.running) { - await globalDebugServer.start(); - } - - return globalDebugServer; -} - -module.exports = Editor.Panel.define({ - listeners: { - show() { }, - hide() { }, - }, - template: `
`, - 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({ ...defaultDebugInfo }); - const gameInstances = ref([]); - const selectedInstanceId = ref(''); - const isAutoRefresh = ref(true); - const refreshInterval = ref(100); - const lastUpdateTime = ref(''); - const showComponentPoolHelp = ref(false); - - 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'; - }; - - // 打开文档链接 - const openDocumentation = (section: string): void => { - const urls: Record = { - 'component-pool': 'https://github.com/esengine/ecs-framework/tree/master/docs/component-design-guide.md#1-对象池优化', - 'performance-optimization': 'https://github.com/esengine/ecs-framework/tree/master/docs/performance-optimization.md' - }; - - const url = urls[section]; - if (!url) return; - - try { - // 在Cocos Creator扩展环境中,直接使用Electron的shell模块 - const { shell } = require('electron'); - shell.openExternal(url); - } catch (error) { - console.error('无法打开链接:', error); - // 如果失败,复制到剪贴板 - copyUrlToClipboard(url); - } - }; - - // 复制链接到剪贴板的辅助函数 - const copyUrlToClipboard = (url: string): void => { - try { - // 尝试使用现代的剪贴板API - if (navigator.clipboard && navigator.clipboard.writeText) { - navigator.clipboard.writeText(url).then(() => { - console.log(`文档链接已复制到剪贴板: ${url}`); - // 如果可能的话,显示用户友好的提示 - if (typeof alert !== 'undefined') { - alert(`文档链接已复制到剪贴板,请在浏览器中粘贴访问:\n${url}`); - } - }).catch(() => { - fallbackCopyText(url); - }); - } else { - fallbackCopyText(url); - } - } catch (error) { - fallbackCopyText(url); - } - }; - - // 备用的复制文本方法 - const fallbackCopyText = (text: string): void => { - try { - // 创建临时的文本区域 - const textArea = document.createElement('textarea'); - textArea.value = text; - textArea.style.position = 'fixed'; - textArea.style.left = '-999999px'; - textArea.style.top = '-999999px'; - document.body.appendChild(textArea); - textArea.focus(); - textArea.select(); - - const successful = document.execCommand('copy'); - document.body.removeChild(textArea); - - if (successful) { - console.log(`文档链接已复制到剪贴板: ${text}`); - if (typeof alert !== 'undefined') { - alert(`文档链接已复制到剪贴板,请在浏览器中粘贴访问:\n${text}`); - } - } else { - console.log(`请手动复制文档链接: ${text}`); - if (typeof alert !== 'undefined') { - alert(`请手动复制文档链接:\n${text}`); - } - } - } catch (error) { - console.log(`请手动访问文档: ${text}`); - if (typeof alert !== 'undefined') { - alert(`请手动访问文档:\n${text}`); - } - } - }; - - // 组件挂载时初始化 - onMounted(async () => { - await initializeServer(); - updateDebugInfo(); - startAutoRefresh(); - }); - - // 组件卸载时清理 - onUnmounted(() => { - stopAutoRefresh(); - }); - - return { - debugInfo, - gameInstances, - selectedInstanceId, - isAutoRefresh, - refreshInterval, - lastUpdateTime, - showComponentPoolHelp, - manualRefresh, - toggleAutoRefresh, - changeRefreshInterval, - onInstanceChanged, - formatUptime, - formatMemory, - getFpsColor, - getMemoryColor, - getECSTimeColor, - getExecutionTimeColor, - openDocumentation - }; - }, - 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; - } - }, -}); \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/default/index.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/default/index.ts deleted file mode 100644 index d2aac148..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/default/index.ts +++ /dev/null @@ -1,528 +0,0 @@ -/* eslint-disable vue/one-component-per-file */ - -import { readFileSync } from 'fs-extra'; -import { join } from 'path'; -import { createApp, App, defineComponent, ref, onMounted } from 'vue'; -import * as fs from 'fs'; -import * as path from 'path'; -import { exec } from 'child_process'; - -const panelDataMap = new WeakMap(); - -/** - * 检测ECS框架安装状态的工具函数 - */ -async function checkEcsFrameworkStatus(projectPath: string) { - const packageJsonPath = path.join(projectPath, 'package.json'); - const nodeModulesPath = path.join(projectPath, 'node_modules', '@esengine', 'ecs-framework'); - - try { - // 检查package.json是否存在 - const packageJsonExists = fs.existsSync(packageJsonPath); - if (!packageJsonExists) { - return { - packageJsonExists: false, - ecsInstalled: false, - ecsVersion: null - }; - } - - // 读取package.json - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); - - // 检查依赖中是否包含ECS框架 - const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies }; - const ecsInDeps = dependencies['@esengine/ecs-framework']; - - // 检查node_modules中是否实际安装了ECS框架 - const ecsActuallyInstalled = fs.existsSync(nodeModulesPath); - - let ecsVersion = null; - if (ecsActuallyInstalled) { - try { - const ecsPackageJson = JSON.parse( - fs.readFileSync(path.join(nodeModulesPath, 'package.json'), 'utf-8') - ); - ecsVersion = ecsPackageJson.version; - } catch (e) { - console.warn('Unable to read ECS framework version:', e); - } - } - - return { - packageJsonExists: true, - ecsInstalled: ecsActuallyInstalled && !!ecsInDeps, - ecsVersion, - declaredVersion: ecsInDeps - }; - } catch (error) { - console.error('Error checking ECS framework status:', error); - return { - packageJsonExists: false, - ecsInstalled: false, - ecsVersion: null - }; - } -} - -/** - * 检测ECS模板状态 - */ -function checkEcsTemplateStatus(projectPath: string) { - const ecsDir = path.join(projectPath, 'assets', 'scripts', 'ecs'); - - try { - if (!fs.existsSync(ecsDir)) { - return { - templateExists: false, - existingFiles: [] - }; - } - - // 扫描ECS目录中的文件 - const existingFiles: string[] = []; - function scanDirectory(dirPath: string, relativePath: string = '') { - 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()) { - scanDirectory(fullPath, relativeFilePath); - } else { - existingFiles.push(relativeFilePath); - } - } - } - - scanDirectory(ecsDir); - - return { - templateExists: existingFiles.length > 0, - existingFiles - }; - } catch (error) { - console.error('Error checking ECS template status:', error); - return { - templateExists: false, - existingFiles: [] - }; - } -} - -/** - * 获取ECS框架的最新版本 - */ -function getLatestEcsVersion(): Promise { - return new Promise((resolve) => { - exec('npm view @esengine/ecs-framework version', (error, stdout) => { - if (error) { - console.warn('Failed to get latest version:', error); - resolve(null); - } else { - resolve(stdout.trim()); - } - }); - }); -} - -/** - * 获取Node.js版本 - */ -function getNodeVersion(): Promise { - return new Promise((resolve) => { - exec('node --version', (error, stdout) => { - if (error) { - resolve('未知'); - } else { - resolve(stdout.trim().replace('v', '')); - } - }); - }); -} - -/** - * 比较版本号 - */ -function compareVersions(current: string, latest: string): boolean { - try { - const currentParts = current.split('.').map(Number); - const latestParts = latest.split('.').map(Number); - - for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) { - const currentPart = currentParts[i] || 0; - const latestPart = latestParts[i] || 0; - - if (latestPart > currentPart) { - return true; // 有更新 - } else if (latestPart < currentPart) { - return false; // 当前版本更新 - } - } - return false; // 版本相同 - } catch (error) { - console.warn('Version comparison failed:', error); - return false; - } -} - -/** - * @zh 如果希望兼容 3.3 之前的版本可以使用下方的代码 - * @en You can add the code below if you want compatibility with versions prior to 3.3 - */ -// Editor.Panel.define = Editor.Panel.define || function(options: any) { return options } - -module.exports = Editor.Panel.define({ - listeners: { - show() { console.log('ECS Welcome Panel Show'); }, - hide() { console.log('ECS Welcome Panel Hide'); }, - }, - template: readFileSync(join(__dirname, '../../../static/template/default/index.html'), 'utf-8'), - style: readFileSync(join(__dirname, '../../../static/style/default/index.css'), 'utf-8'), - $: { - app: '#app', - }, - methods: { - /** - * 向主进程发送消息的方法 - */ - sendToMain(message: string, ...args: any[]) { - Editor.Message.send('cocos-ecs-extension', message, ...args); - } - }, - ready() { - if (this.$.app) { - const app = createApp({}); - app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ui-'); - - // ECS欢迎组件 - app.component('EcsWelcome', defineComponent({ - setup() { - // 响应式状态 - const checkingStatus = ref(true); - const ecsInstalled = ref(false); - const ecsVersion = ref(null); - const latestVersion = ref(null); - const hasUpdate = ref(false); - const packageJsonExists = ref(false); - const nodeVersion = ref('检测中...'); - const pluginVersion = ref('1.0.0'); - const lastCheckTime = ref(null); - - // ECS模板状态 - const templateExists = ref(false); - const existingFiles = ref([]); - - // 操作状态 - const installing = ref(false); - const updating = ref(false); - const uninstalling = ref(false); - - // 操作状态显示 - const showOperationStatus = ref(false); - const operationStatusType = ref('loading'); - const operationStatusMessage = ref(''); - const operationStatusDetails = ref(''); - - // 显示操作状态 - const setOperationStatus = (type: string, message: string, details?: string) => { - showOperationStatus.value = true; - operationStatusType.value = type; - operationStatusMessage.value = message; - operationStatusDetails.value = details || ''; - - // 自动隐藏成功和警告消息 - if (type === 'success' || type === 'warning') { - setTimeout(() => { - showOperationStatus.value = false; - }, 5000); - } - }; - - // 获取状态图标 - const getStatusIcon = (type: string) => { - switch (type) { - case 'loading': return '⏳'; - case 'success': return '✅'; - case 'error': - case 'failed': return '❌'; - case 'warning': return '⚠️'; - default: return 'ℹ️'; - } - }; - - // 监听来自主进程的消息 - 暂时注释掉,使用定时刷新 - const setupMessageListeners = () => { - // TODO: 使用正确的消息监听API - console.log('Message listeners setup - using polling instead'); - }; - - // 定时检查状态(用于检测操作完成) - let statusCheckInterval: any = null; - const startStatusPolling = () => { - if (statusCheckInterval) clearInterval(statusCheckInterval); - statusCheckInterval = setInterval(() => { - if (installing.value || updating.value || uninstalling.value) { - checkStatus(); - } - }, 3000); // 每3秒检查一次 - }; - - const stopStatusPolling = () => { - if (statusCheckInterval) { - clearInterval(statusCheckInterval); - statusCheckInterval = null; - } - }; - - // 检测状态的方法 - const checkStatus = async () => { - checkingStatus.value = true; - - try { - // 获取当前项目路径 - const projectPath = Editor.Project.path; - - // 检测ECS框架状态 - const status = await checkEcsFrameworkStatus(projectPath); - const prevInstalled = ecsInstalled.value; - const prevVersion = ecsVersion.value; - - packageJsonExists.value = status.packageJsonExists; - ecsInstalled.value = status.ecsInstalled; - ecsVersion.value = status.ecsVersion; - - // 检测ECS模板状态 - const templateStatus = checkEcsTemplateStatus(projectPath); - templateExists.value = templateStatus.templateExists; - existingFiles.value = templateStatus.existingFiles; - - // 检测操作完成 - if (installing.value) { - if (status.ecsInstalled && !prevInstalled) { - installing.value = false; - setOperationStatus('success', 'ECS Framework 安装成功!'); - stopStatusPolling(); - } else if (!status.ecsInstalled) { - // 可能还在安装中,继续等待 - } - } - - if (updating.value) { - if (status.ecsVersion && status.ecsVersion !== prevVersion) { - updating.value = false; - setOperationStatus('success', `ECS Framework 更新成功到 v${status.ecsVersion}!`); - stopStatusPolling(); - } - } - - if (uninstalling.value) { - if (!status.ecsInstalled && prevInstalled) { - uninstalling.value = false; - setOperationStatus('success', 'ECS Framework 卸载成功!'); - stopStatusPolling(); - } - } - - // 获取Node.js版本 - nodeVersion.value = await getNodeVersion(); - - // 检查更新 - if (ecsInstalled.value && ecsVersion.value) { - await checkForUpdates(); - } - - // 更新检查时间 - lastCheckTime.value = new Date().toLocaleString(); - - } catch (error) { - console.error('Status check failed:', error); - - // 如果检查失败,停止操作状态 - if (installing.value || updating.value || uninstalling.value) { - installing.value = false; - updating.value = false; - uninstalling.value = false; - setOperationStatus('error', '状态检查失败,请手动验证操作结果'); - stopStatusPolling(); - } - } finally { - checkingStatus.value = false; - } - }; - - // 检查更新 - const checkForUpdates = async () => { - if (!ecsInstalled.value || !ecsVersion.value) { - setOperationStatus('warning', '请先安装 ECS Framework'); - return; - } - - try { - setOperationStatus('loading', '正在检查更新...'); - - const latest = await getLatestEcsVersion(); - if (latest) { - latestVersion.value = latest; - const needsUpdate = compareVersions(ecsVersion.value, latest); - hasUpdate.value = needsUpdate; - - if (needsUpdate) { - setOperationStatus('success', `发现新版本:v${latest}(当前:v${ecsVersion.value})`); - } else { - setOperationStatus('success', `已是最新版本:v${ecsVersion.value}`); - } - } else { - setOperationStatus('warning', '无法获取最新版本信息,请检查网络连接'); - } - - // 更新检查时间 - lastCheckTime.value = new Date().toLocaleString(); - - } catch (error) { - console.warn('Failed to check updates:', error); - setOperationStatus('error', '检查更新失败,请检查网络连接'); - } - }; - - // 操作方法 - const installEcsFramework = () => { - if (!packageJsonExists.value || installing.value) return; - - Editor.Dialog.info('安装 ECS Framework', { - detail: '即将安装@esengine/ecs-framework到当前项目...', - buttons: ['确定', '取消'], - default: 0, - }).then((result) => { - if (result.response === 0) { - installing.value = true; - setOperationStatus('loading', '正在安装 ECS Framework...'); - startStatusPolling(); - - // 发送安装命令到主进程 - Editor.Message.send('cocos-ecs-extension', 'install-ecs-framework'); - } - }); - }; - - const updateEcsFramework = () => { - if (!hasUpdate.value || updating.value) return; - - Editor.Dialog.info('更新 ECS Framework', { - detail: `即将更新ECS框架从 v${ecsVersion.value} 到 v${latestVersion.value}`, - buttons: ['确定', '取消'], - default: 0, - }).then((result) => { - if (result.response === 0) { - updating.value = true; - setOperationStatus('loading', `正在更新 ECS Framework 到 v${latestVersion.value}...`); - startStatusPolling(); - - Editor.Message.send('cocos-ecs-extension', 'update-ecs-framework', latestVersion.value); - } - }); - }; - - const uninstallEcsFramework = () => { - if (uninstalling.value) return; - - Editor.Dialog.warn('卸载 ECS Framework', { - detail: '确定要卸载ECS框架吗?这将删除项目中的ECS框架依赖。', - buttons: ['确定卸载', '取消'], - default: 1, - }).then((result) => { - if (result.response === 0) { - uninstalling.value = true; - setOperationStatus('loading', '正在卸载 ECS Framework...'); - startStatusPolling(); - - Editor.Message.send('cocos-ecs-extension', 'uninstall-ecs-framework'); - } - }); - }; - - const openDocumentation = () => { - if (!ecsInstalled.value) return; - Editor.Message.send('cocos-ecs-extension', 'open-documentation'); - }; - - const createEcsTemplate = () => { - if (!ecsInstalled.value || templateExists.value) return; - - Editor.Dialog.info('创建 ECS 模板', { - detail: '即将创建基础的ECS项目结构和启动代码...', - buttons: ['确定', '取消'], - default: 0, - }).then((result) => { - if (result.response === 0) { - Editor.Message.send('cocos-ecs-extension', 'create-ecs-template'); - } - }); - }; - - const openGithub = () => { - Editor.Message.send('cocos-ecs-extension', 'open-github'); - }; - - const joinQQGroup = () => { - Editor.Message.send('cocos-ecs-extension', 'open-qq-group'); - }; - - const openGenerator = () => { - Editor.Message.send('cocos-ecs-extension', 'open-generator'); - }; - - // 组件挂载后检测状态 - onMounted(() => { - setupMessageListeners(); - checkStatus(); - }); - - return { - checkingStatus, - ecsInstalled, - ecsVersion, - latestVersion, - hasUpdate, - packageJsonExists, - nodeVersion, - pluginVersion, - lastCheckTime, - templateExists, - existingFiles, - installing, - updating, - uninstalling, - showOperationStatus, - operationStatusType, - operationStatusMessage, - operationStatusDetails, - getStatusIcon, - installEcsFramework, - updateEcsFramework, - uninstallEcsFramework, - checkForUpdates, - openDocumentation, - createEcsTemplate, - openGithub, - joinQQGroup, - openGenerator - }; - }, - template: readFileSync(join(__dirname, '../../../static/template/vue/welcome.html'), 'utf-8'), - })); - - app.mount(this.$.app); - panelDataMap.set(this, app); - } - }, - beforeClose() { }, - close() { - const app = panelDataMap.get(this); - if (app) { - app.unmount(); - } - }, -}); diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/generator/index.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/generator/index.ts deleted file mode 100644 index 6e41d6fb..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/panels/generator/index.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { readFileSync } from 'fs-extra'; -import { join } from 'path'; -import * as path from 'path'; -import { createApp, App, defineComponent, ref, reactive } from 'vue'; -import { CodeGenerator } from '../../CodeGenerator'; - -const panelDataMap = new WeakMap(); - -module.exports = Editor.Panel.define({ - listeners: { - show() { }, - hide() { }, - }, - template: `
`, - style: readFileSync(join(__dirname, '../../../static/style/generator/index.css'), 'utf-8'), - $: { - app: '#app', - }, - ready() { - if (this.$.app) { - const app = createApp(defineComponent({ - setup() { - const featureName = ref(''); - const options = reactive({ - generateComponent: true, - generateSystem: false - }); - - // 组件选项 - const componentOptions = reactive({ - includeComments: true, - addProperties: [] - }); - - // 系统选项 - const systemOptions = reactive({ - systemType: 'EntitySystem' as 'EntitySystem' | 'ProcessingSystem' | 'IntervalSystem' | 'PassiveSystem', - includeComments: true, - requiredComponents: [], - filterByComponent: true - }); - - // 系统类型定义 - const systemTypes = [ - { - value: 'EntitySystem', - name: 'EntitySystem', - icon: '🔄', - description: '批量处理实体,适合需要遍历多个实体的逻辑', - usage: '适用场景:移动系统、渲染系统、物理碰撞系统' - }, - { - value: 'ProcessingSystem', - name: 'ProcessingSystem', - icon: '⚡', - description: '执行全局逻辑,不依赖特定实体', - usage: '适用场景:输入处理、音效管理、场景切换' - }, - { - value: 'IntervalSystem', - name: 'IntervalSystem', - icon: '⏰', - description: '按时间间隔执行,可控制执行频率', - usage: '适用场景:AI决策、状态保存、定时清理' - }, - { - value: 'PassiveSystem', - name: 'PassiveSystem', - icon: '🎯', - description: '被动响应,需要手动调用或事件触发', - usage: '适用场景:技能释放、道具使用、特殊效果' - } - ]; - - const isGenerating = ref(false); - const previewCode = ref(''); - const showPreview = ref(false); - - // 选择系统类型 - const selectSystemType = (type: string) => { - systemOptions.systemType = type as any; - updatePreview(); - }; - - // 生成代码 - const generateCode = async () => { - if (!featureName.value.trim()) { - Editor.Dialog.warn('请输入功能名称', { - detail: '请先输入一个有效的功能名称,例如:Health、Movement、Combat等' - }); - return; - } - - if (!options.generateComponent && !options.generateSystem) { - Editor.Dialog.warn('请选择生成内容', { - detail: '请至少选择一种要生成的代码类型(组件或系统)' - }); - return; - } - - isGenerating.value = true; - - try { - const projectPath = Editor.Project.path; - const ecsDir = path.join(projectPath, 'assets', 'scripts', 'ecs'); - - // 检查ECS目录是否存在 - const fs = require('fs'); - if (!fs.existsSync(ecsDir)) { - Editor.Dialog.warn('ECS目录不存在', { - detail: '请先创建ECS模板后再生成代码。\n\n您可以在欢迎面板中点击"创建ECS模板"来创建基础结构。', - }); - return; - } - - const codeGenerator = new CodeGenerator(); - const generatedFiles: string[] = []; - const baseName = featureName.value.trim(); - - // 生成组件 - if (options.generateComponent) { - const componentDir = path.join(ecsDir, 'components'); - await codeGenerator.generateComponent(baseName, componentDir, componentOptions); - generatedFiles.push(`📦 组件: ${baseName}Component.ts`); - } - - // 生成系统 - if (options.generateSystem) { - const systemDir = path.join(ecsDir, 'systems'); - // 如果选择了组件过滤且生成了组件,自动添加组件过滤 - const requiredComponents = (systemOptions.filterByComponent && options.generateComponent) ? - [`${baseName}Component`] : []; - - const systemOpts = { - ...systemOptions, - requiredComponents - }; - - await codeGenerator.generateSystem( - baseName, - systemDir, - systemOpts - ); - generatedFiles.push(`⚙️ 系统: ${baseName}System.ts`); - } - - // 成功提示 - Editor.Dialog.info('代码生成成功', { - detail: `✅ ${baseName} 功能代码已生成完成!\n\n生成的文件:\n${generatedFiles.join('\n')}\n\n请刷新资源管理器查看新创建的文件。` - }); - - // 清空输入 - featureName.value = ''; - - } catch (error) { - console.error('Failed to generate code:', error); - Editor.Dialog.error('代码生成失败', { - detail: `生成代码时发生错误:\n\n${error}` - }); - } finally { - isGenerating.value = false; - } - }; - - // 预览代码 - const previewGeneration = () => { - if (!featureName.value.trim()) { - showPreview.value = false; - return; - } - - const baseName = featureName.value.trim(); - let preview = `将要生成的文件:\n\n`; - - if (options.generateComponent) { - preview += `📦 组件: ${baseName}Component.ts\n`; - preview += ` - 位置: assets/scripts/ecs/components/\n`; - preview += ` - 基础组件模板\n\n`; - } - - if (options.generateSystem) { - const selectedType = systemTypes.find(t => t.value === systemOptions.systemType); - preview += `⚙️ 系统: ${baseName}System.ts\n`; - preview += ` - 位置: assets/scripts/ecs/systems/\n`; - preview += ` - 类型: ${selectedType?.name || systemOptions.systemType}\n`; - - if (systemOptions.filterByComponent && options.generateComponent) { - preview += ` - 过滤组件: ${baseName}Component\n`; - } else if (systemOptions.filterByComponent) { - preview += ` - 组件过滤: 需要手动配置\n`; - } else { - preview += ` - 组件过滤: 无\n`; - } - preview += `\n`; - } - - previewCode.value = preview; - showPreview.value = true; - }; - - // 监听功能名称变化 - const updatePreview = () => { - if (featureName.value.trim()) { - previewGeneration(); - } else { - showPreview.value = false; - } - }; - - return { - featureName, - options, - componentOptions, - systemOptions, - systemTypes, - isGenerating, - previewCode, - showPreview, - generateCode, - updatePreview, - selectSystemType - }; - }, - template: readFileSync(join(__dirname, '../../../static/template/generator/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); - } - }, -}); \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/scene.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/scene.ts deleted file mode 100644 index ed10cb0e..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/scene.ts +++ /dev/null @@ -1,187 +0,0 @@ -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; - } -}; \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/start-admin.bat b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/start-admin.bat deleted file mode 100644 index cc206ee1..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/start-admin.bat +++ /dev/null @@ -1,50 +0,0 @@ -@echo off -chcp 65001 >nul -title Cocos ECS Extension - 热更新管理后台 - -echo. -echo ====================================== -echo 🚀 Cocos ECS Extension 热更新管理后台 -echo ====================================== -echo. - -:: 检查Node.js是否安装 -node --version >nul 2>&1 -if %errorlevel% neq 0 ( - echo ❌ 错误: 未检测到Node.js,请先安装Node.js - echo 下载地址: https://nodejs.org/ - pause - exit /b 1 -) - -:: 获取Node.js版本 -for /f "tokens=1" %%i in ('node --version') do set NODE_VERSION=%%i -echo ✅ Node.js版本: %NODE_VERSION% - -:: 检查是否首次运行 -if not exist "admin-backend\node_modules" ( - echo. - echo 📦 首次运行,正在安装依赖... - cd admin-backend - call npm install - if %errorlevel% neq 0 ( - echo ❌ 依赖安装失败 - pause - exit /b 1 - ) - cd .. - echo ✅ 依赖安装完成 -) - -:: 启动服务 -echo. -echo 🚀 启动热更新管理后台... -echo 📍 管理界面地址: http://localhost:3001 -echo. -echo 💡 提示: 按 Ctrl+C 可停止服务 -echo. - -cd admin-backend -call npm run dev - -pause \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/base.css b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/base.css deleted file mode 100644 index 1cec7fb0..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/base.css +++ /dev/null @@ -1,261 +0,0 @@ -/* 基础样式和布局 */ - -/* 全局重置和通用样式 */ -* { - box-sizing: border-box; -} - -/* 滚动条样式 */ -::-webkit-scrollbar { - width: 6px; - height: 6px; -} - -::-webkit-scrollbar-track { - background: #1a202c; -} - -::-webkit-scrollbar-thumb { - background: #4a5568; - border-radius: 3px; -} - -::-webkit-scrollbar-thumb:hover { - background: #667eea; -} - -/* 选择文本样式 */ -::selection { - background: rgba(102, 126, 234, 0.3); - color: #ffffff; -} - -#behavior-tree-app { - display: flex; - flex-direction: column; - height: 100vh; - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - background: #1e1e1e; - color: #ffffff; - overflow: hidden; -} - -/* 编辑器容器 */ -.editor-container { - display: flex; - flex: 1; - overflow: hidden; - position: relative; -} - -/* 画布容器 */ -.canvas-container { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; - background: #2d3748; -} - -/* 响应式设计 */ -@media (max-width: 1400px) { - .properties-panel-container { - width: 280px; - } - - .blackboard-sidebar { - width: 260px; - right: 280px; - } - - .blackboard-sidebar.collapsed { - right: 232px; - } -} - -@media (max-width: 1200px) { - .nodes-panel { - width: 240px; - } - - .properties-panel-container { - width: 260px; - } - - .blackboard-sidebar { - width: 240px; - right: 260px; - } - - .blackboard-sidebar.collapsed { - right: 212px; - } -} - -@media (max-width: 900px) { - .editor-container { - flex-direction: column; - } - - .nodes-panel { - width: 100%; - height: 160px; - flex: none; - } - - .properties-panel-container { - width: 100%; - height: 200px; - flex: none; - } - - .blackboard-sidebar { - width: 250px; - max-height: 300px; - top: 380px; - right: 20px; - border-radius: 8px; - } - - .blackboard-sidebar.collapsed { - right: 20px; - } - - .canvas-container { - flex: 1; - min-height: 300px; - } -} - -/* 编辑器容器禁用状态 */ -.editor-container.disabled { - position: relative; - pointer-events: none; - opacity: 0.3; - filter: blur(1px); -} - -/* 安装遮罩层 */ -.install-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(26, 32, 44, 0.95); - backdrop-filter: blur(8px); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; - pointer-events: all; -} - -.overlay-content { - text-align: center; - padding: 40px; - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 20px; - backdrop-filter: blur(20px); - max-width: 400px; - width: 90%; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); -} - -.overlay-icon { - font-size: 60px; - margin-bottom: 20px; - animation: float 3s ease-in-out infinite; -} - -@keyframes float { - 0%, 100% { transform: translateY(0px); } - 50% { transform: translateY(-10px); } -} - -.overlay-content h3 { - font-size: 24px; - font-weight: 600; - margin-bottom: 12px; - color: #ffffff; -} - -.overlay-content p { - font-size: 16px; - color: rgba(255, 255, 255, 0.8); - margin-bottom: 30px; - line-height: 1.5; -} - -.overlay-actions { - margin-bottom: 20px; -} - -.overlay-install-btn { - display: inline-flex; - align-items: center; - gap: 10px; - padding: 14px 28px; - background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%); - border: none; - border-radius: 12px; - color: white; - font-size: 16px; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - position: relative; - overflow: hidden; - min-width: 200px; -} - -.overlay-install-btn:hover:not(:disabled) { - background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%); - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(34, 197, 94, 0.4); -} - -.overlay-install-btn:disabled { - opacity: 0.7; - cursor: not-allowed; - background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); -} - -.overlay-install-btn.installing { - background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); - animation: installing-glow 2s infinite; -} - -@keyframes installing-glow { - 0%, 100% { - box-shadow: 0 0 10px rgba(59, 130, 246, 0.5); - } - 50% { - box-shadow: 0 0 20px rgba(59, 130, 246, 0.8); - } -} - -.loading-spinner { - width: 16px; - height: 16px; - border: 2px solid rgba(255, 255, 255, 0.3); - border-top: 2px solid white; - border-radius: 50%; - animation: spin 1s linear infinite; - margin-left: 8px; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -.overlay-note { - margin-top: 16px; -} - -.overlay-note small { - color: rgba(255, 255, 255, 0.6); - font-size: 13px; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/canvas.css b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/canvas.css deleted file mode 100644 index 970006d2..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/canvas.css +++ /dev/null @@ -1,149 +0,0 @@ -/* 画布区域样式 */ - -.canvas-container { - flex: 1; - display: flex; - flex-direction: column; - background: #1a202c; - overflow: hidden; -} - -.canvas-toolbar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 16px; - background: #2d3748; - border-bottom: 1px solid #4a5568; -} - -.zoom-controls, .canvas-actions { - display: flex; - gap: 8px; - align-items: center; -} - -.zoom-controls button, .canvas-actions button { - padding: 4px 8px; - background: #4a5568; - border: 1px solid #718096; - border-radius: 4px; - color: white; - cursor: pointer; - font-size: 11px; - transition: background 0.3s ease; -} - -.zoom-controls button:hover, .canvas-actions button:hover { - background: #667eea; -} - -.canvas-area { - flex: 1; - position: relative; - overflow: hidden; - background: #1a202c; - background-image: - radial-gradient(circle, #2d3748 1px, transparent 1px); - background-size: 20px 20px; - background-position: 0 0; - cursor: grab; -} - -.behavior-tree-canvas { - position: absolute; - top: 0; - left: 0; - pointer-events: none; -} - -/* 连接线样式 */ -.connection-layer { - position: absolute; - top: 0; - left: 0; - pointer-events: none; - overflow: visible; -} - -.connection-line { - fill: none; - stroke: #667eea; - stroke-width: 2; - pointer-events: all; - cursor: pointer; - transition: all 0.3s ease; -} - -.connection-line:hover { - stroke: #f6ad55; - stroke-width: 3; - filter: drop-shadow(0 0 6px rgba(246, 173, 85, 0.6)); -} - -.connection-active { - stroke: #48bb78; - stroke-dasharray: 8, 4; - animation: flow 2s linear infinite; -} - -@keyframes flow { - 0% { stroke-dashoffset: 0; } - 100% { stroke-dashoffset: 12; } -} - -.connection-temp { - fill: none; - stroke: #f6ad55; - stroke-width: 2; - stroke-dasharray: 5, 5; - animation: dash 1s linear infinite; - opacity: 0.8; - pointer-events: none; -} - -@keyframes dash { - 0% { stroke-dashoffset: 0; } - 100% { stroke-dashoffset: 10; } -} - -/* 节点层 */ -.nodes-layer { - position: absolute; - top: 0; - left: 0; - pointer-events: none; - transform-origin: 0 0; - width: 100%; - height: 100%; -} - -/* 网格背景 */ -.grid-background { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - opacity: 0.3; -} - -/* 画布状态 */ -.canvas-area.connecting { - cursor: crosshair; -} - -.canvas-area.connecting .tree-node { - pointer-events: none; -} - -.canvas-area.connecting .port { - pointer-events: all; - cursor: pointer; -} - -.drag-over { - background: rgba(102, 126, 234, 0.1); - border: 2px dashed #667eea; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/conditions.css b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/conditions.css deleted file mode 100644 index c64f17e7..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/conditions.css +++ /dev/null @@ -1,359 +0,0 @@ -/* 条件拖拽功能样式 */ - -/* 可拖拽条件节点 */ -.node-item.draggable-condition { - border-left: 3px solid #ffd700; - position: relative; -} - -.node-item.draggable-condition .drag-hint { - position: absolute; - right: 8px; - top: 50%; - transform: translateY(-50%); - font-size: 10px; - color: #ffd700; - animation: bounce-hint 2s infinite; -} - -@keyframes bounce-hint { - 0%, 20%, 50%, 80%, 100% { - transform: translateY(-50%); - } - 40% { - transform: translateY(-60%); - } - 60% { - transform: translateY(-40%); - } -} - -/* 条件装饰器 - 基础状态 */ -.conditional-decorator { - width: 200px; - min-width: 200px; - max-width: 350px; /* 增加最大宽度以容纳长的条件名称 */ - min-height: 80px; - transition: all 0.3s ease; - word-wrap: break-word; -} - -/* 条件装饰器 - 有附加条件状态 */ -.conditional-decorator.has-attached-condition { - width: auto; /* 自动调整宽度 */ - min-width: 280px; /* 进一步增加最小宽度,确保较长的条件名称能完整显示 */ - max-width: 400px; /* 增加最大宽度 */ - min-height: 110px; /* 增加基础高度 */ -} - -/* 条件装饰器 - 展开状态 */ -.conditional-decorator.has-attached-condition .condition-properties { - min-height: 30px; /* 为展开内容预留空间 */ -} - -/* 条件装饰器接受状态 */ -.tree-node.can-accept-condition { - border: 2px dashed #ffd700; - animation: pulse-accept 1.5s infinite; -} - -@keyframes pulse-accept { - 0%, 100% { - border-color: #ffd700; - box-shadow: 0 0 5px rgba(255, 215, 0, 0.3); - } - 50% { - border-color: #ffed4e; - box-shadow: 0 0 15px rgba(255, 215, 0, 0.6); - } -} - -.tree-node.condition-hover { - border-color: #ffd700; - background: rgba(255, 215, 0, 0.1); - transform: scale(1.02); -} - -/* 条件吸附区域 */ -.condition-attachment-area { - margin-top: 8px; - padding: 8px; - border-radius: 4px; - min-height: 32px; -} - -.condition-placeholder { - text-align: center; - padding: 6px 8px; - border: 2px dashed #4a5568; - border-radius: 4px; - color: #a0aec0; - font-size: 10px; - transition: all 0.3s ease; - min-height: 16px; - display: flex; - align-items: center; - justify-content: center; -} - -.tree-node.can-accept-condition .condition-placeholder { - border-color: #ffd700; - color: #ffd700; - background: rgba(255, 215, 0, 0.05); - animation: pulse-placeholder 2s infinite; -} - -@keyframes pulse-placeholder { - 0%, 100% { - background: rgba(255, 215, 0, 0.05); - } - 50% { - background: rgba(255, 215, 0, 0.15); - } -} - -.placeholder-text { - font-size: 10px; - font-weight: 500; -} - -/* 附加的条件 */ -.attached-condition { - background: rgba(255, 215, 0, 0.1); - border: 1px solid #ffd700; - border-radius: 4px; - padding: 6px; - position: relative; - width: 100%; - box-sizing: border-box; - min-height: 36px; -} - -/* 条件信息区域 */ -.condition-info { - display: flex; - align-items: center; - gap: 6px; - font-size: 11px; - cursor: pointer; - padding: 4px 6px 4px 6px; - padding-right: 40px; /* 为右侧两个按钮预留空间 */ - border-radius: 3px; - transition: all 0.2s ease; - line-height: 1.3; - width: 100%; - box-sizing: border-box; -} - -.condition-info:hover { - background: rgba(255, 215, 0, 0.2); - transform: scale(1.01); -} - -.condition-info.condition-selected { - background: rgba(255, 215, 0, 0.3); - border: 1px solid #ffd700; - box-shadow: 0 0 0 1px rgba(255, 215, 0, 0.25); -} - -.condition-icon { - font-size: 11px; - flex-shrink: 0; -} - -.condition-text { - flex: 1; - color: #ffd700; - font-weight: 500; - font-size: 11px; - word-wrap: break-word; - word-break: break-word; - line-height: 1.3; - min-width: 0; - max-width: calc(100% - 50px); /* 为图标、编辑提示和按钮预留空间 */ - white-space: nowrap; /* 尽量保持一行显示 */ - overflow: hidden; - text-overflow: ellipsis; /* 如果实在太长则显示省略号 */ -} - -.edit-hint { - font-size: 9px; - color: #a0aec0; - margin-left: auto; - transition: color 0.2s ease; - flex-shrink: 0; -} - -.condition-info:hover .edit-hint { - color: #ffd700; -} - -/* 展开/收缩按钮 */ -.toggle-condition-btn { - position: absolute; - top: 4px; - right: 20px; - width: 14px; - height: 14px; - border: none; - background: #4a5568; - color: #a0aec0; - cursor: pointer; - border-radius: 2px; - font-size: 8px; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; - line-height: 1; - z-index: 2; -} - -.toggle-condition-btn:hover { - background: #2d3748; - color: #ffd700; - transform: scale(1.1); -} - -/* 移除条件按钮 */ -.remove-condition-btn { - position: absolute; - top: -2px; - right: -2px; - background: #e53e3e; - border: none; - color: white; - cursor: pointer; - font-size: 10px; - width: 16px; - height: 16px; - border-radius: 50%; - line-height: 1; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; - opacity: 0.8; - z-index: 3; -} - -.remove-condition-btn:hover { - background: #c53030; - transform: scale(1.1); - opacity: 1; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); -} - -/* 条件属性展开区域 */ -.condition-properties { - margin-top: 6px; - padding-top: 6px; - width: 100%; - box-sizing: border-box; - padding-right: 6px; /* 为按钮预留一点空间 */ -} - -/* 属性分隔线 */ -.properties-divider { - width: calc(100% - 6px); - height: 1px; - background: linear-gradient(90deg, transparent 0%, #ffd700 20%, #ffd700 80%, transparent 100%); - margin-bottom: 6px; - opacity: 0.6; -} - -/* 条件属性项 */ -.condition-property-item { - display: flex; - justify-content: space-between; - align-items: flex-start; - padding: 2px 4px; - margin-bottom: 2px; - font-size: 10px; - line-height: 1.2; - width: 100%; - box-sizing: border-box; -} - -.condition-property-label { - color: #a0aec0; - font-weight: 500; - flex-shrink: 0; - margin-right: 8px; - white-space: nowrap; -} - -.condition-property-value { - color: #ffd700; - font-weight: 400; - text-align: right; - word-wrap: break-word; - word-break: break-word; - min-width: 0; - flex: 1; - white-space: normal; /* 允许值换行 */ -} - -/* 画布状态 */ -.canvas-area.condition-dragging { - background: rgba(255, 215, 0, 0.02); -} - -.canvas-area.condition-dragging .tree-node:not(.can-accept-condition) { - opacity: 0.6; -} - -/* 条件装饰器节点的特殊样式 */ -.tree-node.node-conditional-decorator { - /* 基础高度和宽度 */ - min-height: 80px; - width: 200px; /* 增加基础宽度 */ - transition: all 0.3s ease; -} - -/* 附加了条件的装饰器节点需要更大的高度 */ -.tree-node.node-conditional-decorator.has-attached-condition { - min-height: 130px; /* 增加高度 */ - width: 220px; /* 进一步增加宽度以容纳更多内容 */ -} - -.tree-node.node-conditional-decorator .condition-attachment-area { - border: 1px solid #9f7aea; - background: rgba(159, 122, 234, 0.05); - margin-top: 4px; - min-height: 20px; - transition: all 0.3s ease; -} - -/* 当有条件附加时,增加条件区域的高度 */ -.tree-node.node-conditional-decorator.has-attached-condition .condition-attachment-area { - min-height: 45px; - padding: 10px 12px; - background: rgba(255, 215, 0, 0.08); - border-color: #ffd700; -} - -.tree-node.node-conditional-decorator.node-selected .condition-attachment-area { - border-color: #ffd700; - background: rgba(255, 215, 0, 0.1); -} - -/* 条件附加成功的动画效果 */ -@keyframes condition-attached { - 0% { - transform: scale(1); - background: rgba(255, 215, 0, 0.3); - } - 50% { - transform: scale(1.05); - background: rgba(255, 215, 0, 0.5); - } - 100% { - transform: scale(1); - background: rgba(255, 215, 0, 0.1); - } -} - -.condition-just-attached { - animation: condition-attached 0.6s ease-out; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/modals.css b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/modals.css deleted file mode 100644 index e290fb47..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/modals.css +++ /dev/null @@ -1,610 +0,0 @@ -/* 模态框和状态栏样式 */ - -/* 状态栏 */ -.status-bar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 16px; - background: #2d3748; - border-top: 1px solid #4a5568; - font-size: 12px; - color: #a0aec0; -} - -.status-left, .status-right { - display: flex; - gap: 16px; -} - -.status-valid { - color: #48bb78; -} - -.status-invalid { - color: #e53e3e; -} - -/* 模态框 */ -.modal-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.75); - backdrop-filter: blur(4px); - display: flex; - justify-content: center; - align-items: center; - z-index: 10000; - animation: modalFadeIn 0.2s ease-out; -} - -@keyframes modalFadeIn { - from { - opacity: 0; - backdrop-filter: blur(0px); - } - to { - opacity: 1; - backdrop-filter: blur(4px); - } -} - -.modal-content { - background: #2d3748; - border-radius: 12px; - box-shadow: 0 25px 80px rgba(0, 0, 0, 0.6); - max-width: 95vw; - max-height: 90vh; - min-width: 800px; - width: auto; - display: flex; - flex-direction: column; - border: 1px solid #4a5568; - animation: modalSlideIn 0.3s ease-out; - overflow: hidden; -} - -@keyframes modalSlideIn { - from { - opacity: 0; - transform: translateY(-20px) scale(0.95); - } - to { - opacity: 1; - transform: translateY(0) scale(1); - } -} - -.modal-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 20px 24px; - border-bottom: 1px solid #4a5568; - background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); - position: relative; -} - -.modal-header::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 2px; - background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); -} - -.modal-header h3 { - margin: 0; - color: #ffffff; - font-size: 16px; - font-weight: 600; - letter-spacing: 0.5px; - display: flex; - align-items: center; - gap: 8px; -} - -.modal-header h3::before { - content: '✨'; - font-size: 14px; -} - -.modal-header button { - background: rgba(255, 255, 255, 0.1); - border: none; - color: #cbd5e0; - cursor: pointer; - font-size: 16px; - width: 32px; - height: 32px; - border-radius: 6px; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; -} - -.modal-header button:hover { - background: rgba(255, 255, 255, 0.2); - color: #ffffff; - transform: scale(1.05); -} - -.modal-body { - flex: 1; - overflow-y: auto; - padding: 24px; - background: #2d3748; - min-width: 750px; -} - -/* 导出选项样式增强 */ -.export-options { - margin-bottom: 16px; - padding: 16px; - background: rgba(255, 255, 255, 0.02); - border: 1px solid #4a5568; - border-radius: 8px; -} - -.export-options label { - display: flex; - align-items: flex-start; - gap: 8px; - margin-bottom: 12px; - padding: 12px 16px; - background: rgba(255, 255, 255, 0.02); - border: 1px solid #4a5568; - border-radius: 6px; - cursor: pointer; - transition: all 0.2s ease; - font-size: 13px; - font-weight: 500; - position: relative; -} - -.export-options label:hover { - background: rgba(255, 255, 255, 0.05); - border-color: #667eea; - transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(102, 126, 234, 0.15); -} - -.export-options label:has(input[type="radio"]:checked) { - background: rgba(102, 126, 234, 0.1); - border-color: #667eea; - box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2); -} - -.export-options input[type="radio"] { - appearance: none; - width: 18px; - height: 18px; - border: 2px solid #4a5568; - border-radius: 50%; - background: #1a202c; - cursor: pointer; - position: relative; - transition: all 0.2s ease; - flex-shrink: 0; - margin-top: 2px; -} - -.export-options input[type="radio"]:checked { - border-color: #667eea; - background: #667eea; -} - -.export-options input[type="radio"]:checked::after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 8px; - height: 8px; - background: white; - border-radius: 50%; -} - -.export-options label span { - color: #e2e8f0; - font-weight: 600; - flex: 1; -} - -.export-options label small { - display: block; - margin-top: 4px; - color: #a0aec0; - font-size: 11px; - font-weight: 400; - line-height: 1.3; -} - -/* 禁用选项样式 */ -.export-options label.disabled-option { - opacity: 0.5; - cursor: not-allowed; - background: rgba(255, 255, 255, 0.01); - border-color: #374151; -} - -.export-options label.disabled-option:hover { - background: rgba(255, 255, 255, 0.01); - border-color: #374151; - transform: none; - box-shadow: none; -} - -.export-options label.disabled-option span { - color: #6b7280; -} - -.export-options label.disabled-option input[type="radio"] { - border-color: #374151; - cursor: not-allowed; -} - -.export-options label.disabled-option input[type="radio"]:disabled { - background: #1f2937; -} - -.code-output { - background: #1a202c; - border: 1px solid #4a5568; - border-radius: 4px; - padding: 16px; - height: 400px; - overflow: auto; -} - -.code-output pre { - margin: 0; - font-family: 'Consolas', 'Monaco', monospace; - font-size: 12px; - color: #e2e8f0; - white-space: pre-wrap; -} - -.modal-footer { - display: flex; - gap: 12px; - padding: 20px 24px; - border-top: 1px solid #4a5568; - justify-content: flex-end; - background: #374151; -} - -.modal-footer button { - padding: 12px 20px; - border: none; - border-radius: 8px; - color: white; - cursor: pointer; - font-size: 13px; - font-weight: 600; - transition: all 0.2s ease; - text-transform: uppercase; - letter-spacing: 0.5px; - position: relative; - overflow: hidden; -} - -.modal-footer button::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); - transition: left 0.5s ease; -} - -.modal-footer button:hover::before { - left: 100%; -} - -.modal-footer button:hover { - transform: translateY(-2px); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3); -} - -.modal-footer button:active { - transform: translateY(0); -} - -.modal-footer .copy-btn { - background: linear-gradient(135deg, #3182ce 0%, #2c5282 100%); - border: 1px solid #3182ce; -} - -.modal-footer .copy-btn:hover { - background: linear-gradient(135deg, #2c5282 0%, #2a4365 100%); - border-color: #2c5282; -} - -.modal-footer .save-file-btn { - background: linear-gradient(135deg, #38a169 0%, #2f855a 100%); - border: 1px solid #38a169; -} - -.modal-footer .save-file-btn:hover { - background: linear-gradient(135deg, #2f855a 0%, #276749 100%); - border-color: #2f855a; -} - -.modal-footer .close-btn { - background: linear-gradient(135deg, #718096 0%, #4a5568 100%); - border: 1px solid #718096; -} - -.modal-footer .close-btn:hover { - background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); - border-color: #4a5568; -} - - - -/* Blackboard模态框特定样式 */ -.blackboard-modal { - width: 520px; - max-width: 90vw; -} - -.form-group { - margin-bottom: 20px; - position: relative; -} - -.form-group label { - display: block; - margin-bottom: 8px; - color: #e2e8f0; - font-size: 13px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; - display: flex; - align-items: center; - gap: 6px; -} - -.form-group label::before { - content: ''; - width: 3px; - height: 14px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 2px; -} - -.form-input, -.form-select, -.form-textarea { - width: 100%; - padding: 12px 16px; - background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%); - border: 2px solid #4a5568; - border-radius: 8px; - color: #e2e8f0; - font-size: 13px; - font-family: inherit; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - box-sizing: border-box; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.05); -} - -.form-input:focus, -.form-select:focus, -.form-textarea:focus { - border-color: #667eea; - outline: none; - background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2), 0 4px 12px rgba(102, 126, 234, 0.15); - transform: translateY(-1px); -} - -.form-input:hover, -.form-select:hover, -.form-textarea:hover { - border-color: #667eea; - background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); - transform: translateY(-1px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.1); -} - -/* 美化表单下拉选择框 */ -.form-select { - appearance: none; - background-image: url('data:image/svg+xml;charset=US-ASCII,'); - background-repeat: no-repeat; - background-position: right 16px center; - background-size: 12px; - padding-right: 48px; - cursor: pointer; -} - -.form-select option { - background: #2d3748; - color: #e2e8f0; - padding: 8px 12px; -} - -.form-input::placeholder, -.form-textarea::placeholder { - color: #718096; - font-style: italic; -} - -.constraint-inputs { - display: flex; - flex-direction: column; - gap: 12px; - background: rgba(255, 255, 255, 0.02); - border: 1px solid #4a5568; - border-radius: 8px; - padding: 16px; - margin-top: 8px; -} - -.constraint-row { - display: flex; - align-items: center; - gap: 12px; -} - -.constraint-row label { - min-width: 60px; - font-size: 12px; - color: #a0aec0; - margin: 0; - text-transform: none; - font-weight: 500; -} - -.constraint-row label::before { - display: none; -} - -/* 自定义复选框样式 */ -.checkbox-group { - display: flex; - align-items: center; - gap: 10px; - margin: 16px 0; - padding: 12px 16px; - background: rgba(255, 255, 255, 0.02); - border-radius: 8px; - border: 1px solid #4a5568; - cursor: pointer; - transition: all 0.2s ease; -} - -.checkbox-group:hover { - background: rgba(255, 255, 255, 0.05); - border-color: #667eea; -} - -.checkbox-group input[type="checkbox"] { - appearance: none; - width: 18px; - height: 18px; - border: 2px solid #4a5568; - border-radius: 4px; - background: #1a202c; - cursor: pointer; - position: relative; - transition: all 0.2s ease; -} - -.checkbox-group input[type="checkbox"]:checked { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-color: #667eea; -} - -.checkbox-group input[type="checkbox"]:checked::after { - content: '✓'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - color: white; - font-size: 12px; - font-weight: bold; -} - -.checkbox-group label { - margin: 0; - color: #e2e8f0; - font-size: 13px; - font-weight: 500; - cursor: pointer; - text-transform: none; - display: block; -} - -.checkbox-group label::before { - display: none; -} - -.constraint-row label { - min-width: 60px; - margin-bottom: 0; - font-size: 11px; -} - -.constraint-row input { - flex: 1; -} - -.allowed-values textarea { - margin-top: 8px; - resize: vertical; - min-height: 100px; - font-family: 'JetBrains Mono', 'Consolas', 'Monaco', monospace; - font-size: 12px; - line-height: 1.5; - background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%); - border: 2px solid #4a5568; - border-radius: 8px; - color: #e2e8f0; - padding: 12px 16px; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.05); -} - -.allowed-values textarea:focus { - border-color: #667eea; - background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%); - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2), 0 4px 12px rgba(102, 126, 234, 0.15); - transform: translateY(-1px); - outline: none; -} - -.allowed-values textarea:hover { - border-color: #667eea; - background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%); - transform: translateY(-1px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.1); -} - -.allowed-values textarea::placeholder { - color: #718096; - font-style: italic; - opacity: 0.8; -} - -.checkbox-label { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; -} - -.checkbox-label input[type="checkbox"] { - width: auto; -} - -.export-code { - background: #1a202c; - border: 1px solid #4a5568; - border-radius: 4px; - padding: 12px; - font-family: 'Consolas', 'Monaco', monospace; - font-size: 11px; - color: #e2e8f0; - resize: none; - min-height: 400px; - width: 100%; - box-sizing: border-box; - line-height: 1.4; - overflow-y: auto; - white-space: pre; - word-wrap: break-word; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/nodes.css b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/nodes.css deleted file mode 100644 index cd5a6734..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/nodes.css +++ /dev/null @@ -1,393 +0,0 @@ -/* 节点样式 */ - -.tree-node { - position: absolute; - width: 160px; - min-height: 80px; - background: #2d3748; - border: 2px solid #4a5568; - border-radius: 8px; - pointer-events: all; - cursor: pointer; - transition: all 0.3s ease; - box-shadow: 0 4px 12px rgba(0,0,0,0.3); - z-index: 1; - animation: nodeAppear 0.3s ease-out; -} - -.tree-node:hover { - border-color: #667eea; - box-shadow: 0 6px 20px rgba(0,0,0,0.4); - transform: translateY(-2px); -} - -.tree-node.node-selected { - border-color: #f6ad55; - box-shadow: 0 0 20px rgba(246, 173, 85, 0.4); -} - -.tree-node.node-error { - border-color: #e53e3e; - background: rgba(229, 62, 62, 0.1); -} - -.node-header { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - background: #4a5568; - border-radius: 6px 6px 0 0; - border-bottom: 1px solid #718096; -} - -.node-header .node-icon { - font-size: 16px; - flex-shrink: 0; -} - -.node-title { - flex: 1; - font-weight: 600; - font-size: 12px; - color: #e2e8f0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.node-delete { - background: none; - border: none; - color: #e53e3e; - cursor: pointer; - font-size: 16px; - font-weight: bold; - padding: 2px 4px; - border-radius: 3px; - transition: all 0.2s ease; - flex-shrink: 0; -} - -.node-delete:hover { - background: rgba(229, 62, 62, 0.2); - transform: scale(1.1); -} - -.node-body { - padding: 8px 12px; - color: #a0aec0; - font-size: 11px; - line-height: 1.4; - max-width: 136px; - overflow: hidden; -} - -/* 条件装饰器节点的node-body需要更大的宽度 */ -.tree-node.node-conditional-decorator .node-body { - max-width: 176px; /* 基础状态 */ -} - -.tree-node.node-conditional-decorator.has-attached-condition .node-body { - max-width: 196px; /* 附加条件后更宽 */ -} - -.node-description { - margin-bottom: 6px; - color: #cbd5e0; - font-style: italic; -} - -/* 端口样式 */ -.port { - position: absolute; - width: 12px; - height: 12px; - border-radius: 50%; - cursor: pointer; - transform: translateX(-50%); - transition: all 0.3s ease; - z-index: 10; - opacity: 0.8; -} - -.port::before { - content: ''; - position: absolute; - top: -4px; - left: -4px; - right: -4px; - bottom: -4px; - border-radius: 50%; - background: transparent; - transition: all 0.3s ease; -} - -.port:hover { - transform: translateX(-50%) scale(1.3); - opacity: 1; -} - -.port-input { - top: -6px; - left: 50%; - background: #4299e1; - border: 2px solid #2b6cb0; -} - -.port-input:hover { - background: #63b3ed; - box-shadow: 0 0 12px rgba(66, 153, 225, 0.6); -} - -.port-output { - bottom: -6px; - left: 50%; - background: #48bb78; - border: 2px solid #2f855a; -} - -.port-output:hover { - background: #68d391; - box-shadow: 0 0 12px rgba(72, 187, 120, 0.6); -} - -.port.connecting { - animation: pulse-port 1s ease-in-out infinite; - transform: translateX(-50%) scale(1.4); -} - -@keyframes pulse-port { - 0%, 100% { - box-shadow: 0 0 8px rgba(246, 173, 85, 0.4); - } - 50% { - box-shadow: 0 0 20px rgba(246, 173, 85, 0.8); - transform: translateX(-50%) scale(1.6); - } -} - -.port.drag-target { - animation: pulse-target 0.8s ease-in-out infinite; - background: #f6ad55 !important; - border-color: #dd6b20 !important; - transform: translateX(-50%) scale(1.5); -} - -@keyframes pulse-target { - 0%, 100% { - box-shadow: 0 0 12px rgba(246, 173, 85, 0.6); - } - 50% { - box-shadow: 0 0 24px rgba(246, 173, 85, 1); - transform: translateX(-50%) scale(1.7); - } -} - -.port-inner { - position: absolute; - top: 2px; - left: 2px; - right: 2px; - bottom: 2px; - border-radius: 50%; - background: rgba(255, 255, 255, 0.3); - transition: all 0.3s ease; -} - -.port:hover .port-inner { - background: rgba(255, 255, 255, 0.6); -} - -.port.connecting .port-inner { - background: rgba(255, 255, 255, 0.8); -} - -.port.drag-target .port-inner { - background: rgba(255, 255, 255, 0.9); -} - -.children-indicator { - position: absolute; - bottom: 2px; - right: 6px; - background: #667eea; - color: white; - border-radius: 50%; - width: 16px; - height: 16px; - display: flex; - align-items: center; - justify-content: center; - font-size: 9px; - font-weight: bold; - z-index: 5; -} - -/* 节点属性预览 */ -.node-properties-preview { - margin-top: 6px; - padding-top: 6px; - border-top: 1px solid rgba(255, 255, 255, 0.1); - font-size: 10px; -} - -.property-preview-item { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 2px; - padding: 1px 0; -} - -.property-label { - color: #a0aec0; - font-weight: 500; - flex-shrink: 0; - margin-right: 4px; - font-size: 9px; -} - -.property-value { - color: #e2e8f0; - font-weight: 600; - text-align: right; - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 9px; -} - -/* 不同类型属性的颜色 */ -.property-value.property-boolean { - color: #68d391; -} - -.property-value.property-number { - color: #63b3ed; -} - -.property-value.property-select { - color: #f6ad55; -} - -.property-value.property-string { - color: #cbd5e0; -} - -.property-value.property-code { - color: #d69e2e; - font-family: 'Consolas', 'Monaco', monospace; -} - -/* 当节点有属性时稍微增加高度空间 */ -.tree-node:has(.node-properties-preview) { - min-height: 100px; -} - -/* 节点悬停时的端口显示优化 */ -.tree-node:hover .port { - opacity: 1; - transform: translateX(-50%) scale(1.1); -} - -.tree-node .port { - opacity: 0.8; - transition: all 0.2s ease; -} - -/* 拖动状态样式 - 优化硬件加速 */ -.tree-node.dragging { - opacity: 0.9; - transform: translateZ(0) scale(1.02) rotate(1deg); - box-shadow: 0 12px 30px rgba(0,0,0,0.4); - z-index: 1000; - cursor: grabbing; - will-change: transform, opacity; - backface-visibility: hidden; - transform-style: preserve-3d; - transition: none !important; - border-color: #67b7dc; - transform-origin: center center; - perspective: 1000px; -} - -.dragging { - opacity: 0.7; - transform: rotate(5deg); -} - -/* 动画效果 */ -@keyframes nodeAppear { - from { - opacity: 0; - transform: scale(0.8); - } - to { - opacity: 1; - transform: scale(1); - } -} - -/* 节点类型提示 */ -.tree-node[data-original-name]:not([data-original-name=""]):hover::after { - content: "原始: " attr(data-original-name); - position: absolute; - top: -30px; - left: 50%; - transform: translateX(-50%); - background: rgba(0, 0, 0, 0.9); - color: #f7fafc; - padding: 4px 8px; - border-radius: 4px; - font-size: 10px; - white-space: nowrap; - z-index: 1000; - pointer-events: none; - opacity: 0; - animation: tooltipFadeIn 0.3s ease forwards; - border: 1px solid #4a5568; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); -} - -.tree-node.node-selected[data-original-name]:not([data-original-name=""]) .node-header::before { - content: "📝"; - position: absolute; - top: -8px; - right: -8px; - background: #f59e0b; - color: white; - width: 16px; - height: 16px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 8px; - border: 2px solid #2d3748; - z-index: 5; -} - -@keyframes tooltipFadeIn { - from { - opacity: 0; - transform: translateX(-50%) translateY(-5px); - } - to { - opacity: 1; - transform: translateX(-50%) translateY(0); - } -} - -/* 节点标题自定义指示器 */ -.node-title.customized { - color: #f6ad55; - position: relative; -} - -.node-title.customized::after { - content: " ✏️"; - font-size: 10px; - opacity: 0.7; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/panels.css b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/panels.css deleted file mode 100644 index e762dbf9..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/panels.css +++ /dev/null @@ -1,1424 +0,0 @@ -/* 侧边面板样式 */ - -/* 左侧节点面板 */ -.nodes-panel { - width: 280px; - background: #2d3748; - border-right: 1px solid #4a5568; - display: flex; - flex-direction: column; - overflow: hidden; -} - -.panel-header { - padding: 12px 16px; - background: #4a5568; - border-bottom: 1px solid #718096; - display: flex; - justify-content: space-between; - align-items: center; - min-height: 48px; -} - -.panel-header h3 { - margin: 0; - font-size: 13px; - font-weight: 600; - color: #e2e8f0; - flex: 1; - letter-spacing: 0.5px; - white-space: nowrap; - overflow: visible; - text-overflow: ellipsis; - min-width: 0; -} - -.header-actions { - display: flex; - gap: 8px; - flex-wrap: wrap; -} - -.tool-btn.small { - padding: 6px 10px; - background: #1a202c; - border: 1px solid #4a5568; - border-radius: 4px; - color: #e2e8f0; - font-size: 11px; - cursor: pointer; - transition: all 0.2s ease; - display: flex; - align-items: center; - gap: 4px; - white-space: nowrap; -} - -.tool-btn.small:hover { - background: #2d3748; - border-color: #667eea; - transform: translateY(-1px); -} - -.search-input { - width: 100%; - padding: 8px 12px; - background: #1a202c; - border: 1px solid #4a5568; - border-radius: 4px; - color: white; - font-size: 12px; -} - -.search-input::placeholder { - color: #a0aec0; -} - -.node-categories { - flex: 1; - overflow-y: auto; - padding: 8px; -} - -.category { - margin-bottom: 16px; -} - -.category-title { - margin: 0 0 8px 0; - padding: 8px 12px; - background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); - border-radius: 6px; - font-size: 12px; - font-weight: 500; - color: #cbd5e0; - letter-spacing: 0.3px; - border-left: 3px solid #667eea; - position: relative; - overflow: hidden; -} - -.category-title::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent); - transition: left 0.5s ease; -} - -.category-title:hover::before { - left: 100%; -} - -.category-title:hover { - background: linear-gradient(135deg, #667eea 0%, #4a5568 100%); - color: #ffffff; - transform: translateX(2px); -} - -.node-list { - display: flex; - flex-direction: column; - gap: 4px; -} - -.node-item { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - background: #1a202c; - border: 1px solid #4a5568; - border-radius: 4px; - cursor: grab; - transition: all 0.3s ease; - font-size: 12px; - position: relative; -} - -.node-item:hover { - background: #2d3748; - border-color: #667eea; - transform: translateX(4px); -} - -.node-item:active { - cursor: grabbing; -} - -.node-item.root { border-left: 3px solid #f6ad55; } -.node-item.composite { border-left: 3px solid #667eea; } -.node-item.decorator { border-left: 3px solid #9f7aea; } -.node-item.action { border-left: 3px solid #48bb78; } -.node-item.condition { border-left: 3px solid #ed8936; } -.node-item.ecs { border-left: 3px solid #38b2ac; } - -.node-icon { - font-size: 14px; - flex-shrink: 0; -} - -.node-name { - font-weight: 500; - flex: 1; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -/* 右侧属性面板容器 */ -.properties-panel-container { - width: 320px; - background: #2d3748; - border-left: 1px solid #4a5568; - display: flex; - flex-direction: column; - overflow: hidden; -} - - - - -/* 属性面板 */ -.properties-panel { - flex: 1; - background: #2d3748; - display: flex; - flex-direction: column; - overflow: hidden; - width: 100%; -} - -.node-properties { - flex: 1; - overflow-y: auto; - padding: 16px; -} - -.property-section { - margin-bottom: 20px; -} - -.property-section h4 { - margin: 0 0 10px 0; - color: #e2e8f0; - font-size: 12px; - font-weight: 600; - border-bottom: 1px solid #4a5568; - padding-bottom: 4px; - letter-spacing: 0.5px; - text-transform: uppercase; -} - -.property-item { - margin-bottom: 12px; - position: relative; -} - -.property-item label { - display: block; - margin-bottom: 4px; - color: #a0aec0; - font-size: 12px; - font-weight: 500; -} - -.property-input-container { - position: relative; -} - -.property-item input, -.property-item textarea, -.property-item select { - width: 100%; - padding: 8px 12px; - background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%); - border: 1px solid #4a5568; - border-radius: 6px; - color: #e2e8f0; - font-size: 12px; - font-family: inherit; - box-sizing: border-box; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.05); -} - -.property-item input:hover, -.property-item textarea:hover, -.property-item select:hover { - border-color: #667eea; - background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); - transform: translateY(-1px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.1); -} - -.property-item input:focus, -.property-item textarea:focus, -.property-item select:focus { - border-color: #667eea; - outline: none; - background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2), 0 4px 12px rgba(102, 126, 234, 0.15); - transform: translateY(-1px); -} - -/* 美化下拉选择框箭头 */ -.property-item select { - appearance: none; - background-image: url('data:image/svg+xml;charset=US-ASCII,'); - background-repeat: no-repeat; - background-position: right 12px center; - background-size: 10px; - padding-right: 36px; - cursor: pointer; -} - -.property-item select option { - background: #2d3748; - color: #e2e8f0; - padding: 8px 12px; -} - -.property-item .code-input { - font-family: 'Consolas', 'Monaco', monospace; - font-size: 11px; - resize: vertical; - min-height: 80px; -} - -.property-help { - margin-top: 4px; - font-size: 10px; - color: #718096; - line-height: 1.4; -} - -.blackboard-droppable { - border: 2px dashed transparent; - transition: border-color 0.2s ease; -} - -.blackboard-droppable.drag-over { - border-color: #667eea; - background: rgba(102, 126, 234, 0.1); -} - -.blackboard-reference { - display: flex; - align-items: center; - gap: 6px; - margin-top: 4px; - padding: 4px 8px; - background: #4a5568; - border-radius: 3px; - font-size: 11px; -} - -.ref-icon { - color: #667eea; -} - -.ref-text { - color: #e2e8f0; - flex: 1; -} - -.clear-ref { - background: none; - border: none; - color: #ef4444; - cursor: pointer; - padding: 0 4px; - border-radius: 2px; - font-size: 12px; -} - -.clear-ref:hover { - background: #ef4444; - color: white; -} - -.config-preview { - background: #1a202c; - border: 1px solid #4a5568; - border-radius: 4px; - padding: 12px; - margin-top: 8px; - font-family: 'Consolas', 'Monaco', monospace; - font-size: 11px; - color: #e2e8f0; - white-space: pre-wrap; - max-height: 200px; - overflow-y: auto; -} - -.no-selection { - padding: 40px 20px; - text-align: center; - color: #718096; - font-style: italic; -} - -.tree-structure { - margin-top: 20px; -} - -.structure-tree { - font-family: 'Consolas', 'Monaco', monospace; - font-size: 11px; -} - -.empty-tree { - padding: 20px; - text-align: center; - color: #718096; - font-style: italic; - border: 2px dashed #4a5568; - border-radius: 4px; -} - -.tree-node-item { - cursor: pointer; -} - -.tree-node-line { - padding: 2px 4px; - border-radius: 2px; - margin: 1px 0; - display: flex; - align-items: center; - gap: 4px; - transition: background 0.2s ease; -} - -.tree-node-line:hover { - background: rgba(102, 126, 234, 0.1); -} - -.tree-node-icon { - font-size: 10px; - width: 12px; -} - -.tree-node-name { - flex: 1; - color: #e2e8f0; -} - -.tree-node-type { - font-size: 9px; - color: #718096; -} - -/* Blackboard悬浮窗口样式 */ -.blackboard-sidebar { - position: fixed; - top: 160px; - right: 320px; - width: 280px; - max-height: calc(100vh - 180px); - background: #2d3748; - border: 1px solid #4a5568; - border-radius: 8px 0 0 8px; - z-index: 999; - display: flex; - flex-direction: column; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - backdrop-filter: blur(8px); - box-shadow: -4px 0 20px rgba(0, 0, 0, 0.15); -} - -.blackboard-sidebar.collapsed { - width: 48px; - right: 272px; -} - -.blackboard-sidebar.transparent { - opacity: 0.7; - background: rgba(45, 55, 72, 0.85); -} - -.blackboard-sidebar.transparent:not(.collapsed) { - transform: translateX(-20px); -} - -.blackboard-toggle { - position: absolute; - left: -24px; - top: 50%; - transform: translateY(-50%); - width: 24px; - height: 48px; - background: #667eea; - border: none; - border-radius: 8px 0 0 8px; - color: white; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 14px; - transition: all 0.3s ease; - z-index: 1001; - box-shadow: -2px 0 8px rgba(0, 0, 0, 0.2); -} - -.blackboard-toggle:hover { - background: #5a6acf; - transform: translateY(-50%) translateX(-2px); - box-shadow: -4px 0 12px rgba(0, 0, 0, 0.3); -} - -.blackboard-toggle span { - transition: transform 0.3s ease; -} - -.blackboard-sidebar.collapsed .blackboard-toggle span { - transform: rotate(180deg); -} - -.blackboard-content { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; -} - -.blackboard-header { - background: linear-gradient(135deg, #667eea, #764ba2); - color: white; - padding: 12px; - position: relative; - min-height: 48px; - display: flex; - align-items: center; -} - -.blackboard-header::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 2px; - background: linear-gradient(90deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4); -} - -.blackboard-header h3 { - margin: 0; - font-size: 13px; - font-weight: 600; - letter-spacing: 0.5px; -} - -.blackboard-scroll-area { - flex: 1; - overflow-y: auto; - padding: 0 12px 12px 12px; -} - -.blackboard-sidebar .blackboard-actions { - display: flex; - gap: 8px; - margin-bottom: 16px; - flex-wrap: wrap; - padding: 12px; - border-bottom: 1px solid #4a5568; -} - -.blackboard-sidebar .blackboard-actions button { - padding: 6px 12px; - border: none; - border-radius: 4px; - font-size: 11px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s ease; - text-transform: uppercase; - letter-spacing: 0.3px; -} - -.blackboard-sidebar .add-var-btn { - background: #48bb78; - color: white; -} - -.blackboard-sidebar .add-var-btn:hover { - background: #38a169; - transform: translateY(-1px); -} - -.blackboard-sidebar .reset-btn { - background: #ed8936; - color: white; -} - -.blackboard-sidebar .reset-btn:hover { - background: #dd6b20; - transform: translateY(-1px); -} - -.blackboard-sidebar .clear-btn { - background: #f56565; - color: white; -} - -.blackboard-sidebar .clear-btn:hover { - background: #e53e3e; - transform: translateY(-1px); -} - -.blackboard-sidebar .blackboard-groups { - display: flex; - flex-direction: column; - gap: 16px; -} - -.blackboard-sidebar .variable-group { - background: rgba(255, 255, 255, 0.02); - border: 1px solid #4a5568; - border-radius: 6px; - overflow: hidden; -} - -.blackboard-sidebar .group-title { - margin: 0; - padding: 8px 12px; - background: #1a202c; - color: #e2e8f0; - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; - border-bottom: 1px solid #4a5568; -} - -.blackboard-sidebar .variable-list { - padding: 8px; - display: flex; - flex-direction: column; - gap: 6px; -} - -.blackboard-sidebar .variable-item { - padding: 8px; - margin: 4px 0; - background: #4a5568; - border-radius: 4px; - transition: all 0.2s ease; - position: relative; - border-left: 3px solid transparent; - user-select: none; -} - -.blackboard-sidebar .variable-item::before { - content: ''; - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 3px; - border-radius: 2px 0 0 2px; - transition: all 0.2s ease; -} - -.blackboard-sidebar .variable-item:hover::before { - background: #ffd700; -} - -.blackboard-sidebar .variable-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - flex: 1; - min-width: 0; -} - -.blackboard-sidebar .variable-drag-area { - display: flex; - align-items: center; - gap: 8px; - flex: 1; - min-width: 0; - cursor: grab; - padding: 4px 8px; - border-radius: 4px; - transition: all 0.2s ease; - position: relative; -} - -.blackboard-sidebar .variable-drag-area:hover { - background: rgba(102, 126, 234, 0.1); - transform: translateX(-2px); -} - -.blackboard-sidebar .variable-drag-area:active { - cursor: grabbing; -} - -.blackboard-sidebar .variable-drag-area .drag-hint { - opacity: 0; - font-size: 10px; - color: #ffd700; - transition: opacity 0.2s ease; - margin-left: auto; -} - -.blackboard-sidebar .variable-drag-area:hover .drag-hint { - opacity: 1; - animation: pulse-hint 2s infinite; -} - -.blackboard-sidebar .variable-info { - display: flex; - align-items: center; - gap: 6px; - flex: 1; - min-width: 0; -} - -.blackboard-sidebar .variable-name { - font-weight: 600; - color: #f7fafc; - font-size: 13px; - flex-shrink: 0; - letter-spacing: 0.3px; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); -} - -.blackboard-sidebar .value-separator { - color: #64748b; - font-weight: 500; - flex-shrink: 0; - opacity: 0.7; -} - -.blackboard-sidebar .variable-type { - font-size: 8px; - padding: 4px 8px; - border-radius: 12px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 700; - flex-shrink: 0; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.2); - line-height: 1; - border: 1px solid rgba(255, 255, 255, 0.1); -} - -.blackboard-sidebar .variable-item.string::before { - background: #48bb78; -} - -.blackboard-sidebar .variable-item.string .variable-type { - background: #48bb78; - color: white; -} - -.blackboard-sidebar .variable-item.number::before { - background: #3182ce; -} - -.blackboard-sidebar .variable-item.number .variable-type { - background: #3182ce; - color: white; -} - -.blackboard-sidebar .variable-item.boolean::before { - background: #d69e2e; -} - -.blackboard-sidebar .variable-item.boolean .variable-type { - background: #d69e2e; - color: white; -} - -.blackboard-sidebar .variable-item.vector2::before, -.blackboard-sidebar .variable-item.vector3::before { - background: #9f7aea; -} - -.blackboard-sidebar .variable-item.vector2 .variable-type, -.blackboard-sidebar .variable-item.vector3 .variable-type { - background: #9f7aea; - color: white; -} - -.blackboard-sidebar .variable-item.object::before, -.blackboard-sidebar .variable-item.array::before { - background: #ed8936; -} - -.blackboard-sidebar .variable-item.object .variable-type, -.blackboard-sidebar .variable-item.array .variable-type { - background: #ed8936; - color: white; -} - -.blackboard-sidebar .variable-actions { - display: flex; - gap: 4px; - opacity: 0; - transition: all 0.3s ease; - flex-shrink: 0; - transform: translateX(8px); -} - -.blackboard-sidebar .variable-item:hover .variable-actions { - opacity: 1; - transform: translateX(0); -} - -.blackboard-sidebar .variable-actions button { - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.15); - font-size: 10px; - cursor: pointer; - padding: 6px; - border-radius: 6px; - transition: all 0.2s ease; - color: #cbd5e0; - width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; - backdrop-filter: blur(4px); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); -} - -.blackboard-sidebar .edit-btn:hover { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-color: #667eea; - transform: translateY(-1px) scale(1.05); - box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); - color: white; -} - -.blackboard-sidebar .remove-btn:hover { - background: linear-gradient(135deg, #f56565 0%, #e53e3e 100%); - border-color: #f56565; - transform: translateY(-1px) scale(1.05); - box-shadow: 0 4px 12px rgba(245, 101, 101, 0.4); - color: white; -} - -.blackboard-sidebar .value-display { - font-family: 'JetBrains Mono', 'Consolas', 'Monaco', monospace; - font-size: 11px; - color: #10b981; - background: none; - border: none; - padding: 0; - flex: 1; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-weight: 500; -} - -/* 不同类型的值颜色 */ -.blackboard-sidebar .variable-item.string .value-display { - color: #f59e0b; /* 琥珀色 - 字符串 */ -} - -.blackboard-sidebar .variable-item.number .value-display { - color: #3b82f6; /* 蓝色 - 数字 */ -} - -.blackboard-sidebar .variable-item.boolean .value-display { - color: #8b5cf6; /* 紫色 - 布尔值 */ -} - -.blackboard-sidebar .variable-item.vector2 .value-display, -.blackboard-sidebar .variable-item.vector3 .value-display { - color: #ec4899; /* 粉色 - 向量 */ -} - -.blackboard-sidebar .variable-item.object .value-display, -.blackboard-sidebar .variable-item.array .value-display { - color: #10b981; /* 绿色 - 对象/数组 */ -} - -.blackboard-sidebar .variable-constraints { - margin-top: 4px; - font-size: 10px; - color: #718096; - font-style: italic; -} - -/* 黑板变量值区域样式 */ -.blackboard-sidebar .variable-value { - margin-top: 6px; - min-height: 24px; - display: flex; - align-items: center; -} - -/* 美化约束值下拉选择框 */ -.blackboard-sidebar .variable-value select { - width: 100%; - padding: 6px 12px; - background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%); - border: 1px solid #4a5568; - border-radius: 6px; - color: #e2e8f0; - font-family: 'JetBrains Mono', 'Consolas', 'Monaco', monospace; - font-size: 11px; - font-weight: 500; - cursor: pointer; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - outline: none; - appearance: none; - background-image: url('data:image/svg+xml;charset=US-ASCII,'); - background-repeat: no-repeat; - background-position: right 8px center; - background-size: 8px; - padding-right: 28px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.05); -} - -/* 下拉框 hover 状态 */ -.blackboard-sidebar .variable-value select:hover { - border-color: #667eea; - background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.1); - transform: translateY(-1px); -} - -/* 下拉框 focus 状态 */ -.blackboard-sidebar .variable-value select:focus { - border-color: #667eea; - background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2), 0 4px 12px rgba(102, 126, 234, 0.15); - transform: translateY(-1px); -} - -/* 下拉框选项样式 */ -.blackboard-sidebar .variable-value select option { - background: #2d3748; - color: #e2e8f0; - padding: 8px 12px; - border: none; - font-size: 11px; -} - -/* 不同类型变量的下拉框颜色 */ -.blackboard-sidebar .variable-item.string .variable-value select { - border-left: 3px solid #f59e0b; - position: relative; -} - -.blackboard-sidebar .variable-item.string .variable-value select:focus { - border-left-color: #f59e0b; - box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.2), 0 4px 12px rgba(245, 158, 11, 0.15); -} - -/* 约束值下拉框的特殊标识 */ -.blackboard-sidebar .variable-value select::before { - content: '🔒'; - position: absolute; - top: 50%; - left: 8px; - transform: translateY(-50%); - font-size: 8px; - opacity: 0.6; - pointer-events: none; - z-index: 1; -} - -/* 添加约束值提示 */ -.blackboard-sidebar .variable-item:has(.variable-value select) .variable-constraints { - color: #f59e0b; - font-weight: 500; - opacity: 0.9; -} - -.blackboard-sidebar .variable-item:has(.variable-value select) .variable-constraints::before { - content: '🔐 '; - font-size: 10px; - margin-right: 4px; -} - -/* 禁用状态样式 */ -.blackboard-sidebar .variable-value select:disabled { - opacity: 0.6; - cursor: not-allowed; - background: #1a202c; - border-color: #2d3748; -} - -.blackboard-sidebar .variable-value select:disabled:hover { - transform: none; - box-shadow: none; -} - -.blackboard-sidebar .empty-blackboard { - text-align: center; - padding: 24px 16px; - color: #718096; -} - -.blackboard-sidebar .empty-icon { - font-size: 32px; - margin-bottom: 12px; - opacity: 0.5; -} - -.blackboard-sidebar .empty-blackboard p { - margin: 0 0 16px 0; - font-size: 13px; -} - -.blackboard-sidebar .add-first-var { - background: #667eea; - color: white; - border: none; - padding: 8px 16px; - border-radius: 4px; - font-size: 12px; - cursor: pointer; - transition: all 0.2s ease; -} - -.blackboard-sidebar .add-first-var:hover { - background: #5a67d8; - transform: translateY(-1px); -} - -/* 只读变量样式 */ -.blackboard-sidebar .variable-item.readonly { - opacity: 0.8; - background: linear-gradient(135deg, #374151 0%, #1f2937 100%) !important; - border-color: #6b7280; -} - -.blackboard-sidebar .variable-item.readonly::before { - background: #6b7280 !important; -} - -.blackboard-sidebar .variable-item.readonly .variable-name { - color: #9ca3af; -} - -.blackboard-sidebar .variable-item.readonly .variable-type { - background: #6b7280 !important; - color: #e5e7eb !important; -} - -/* 拖拽状态样式 */ -.blackboard-sidebar .variable-item:active { - cursor: grabbing !important; - transform: scale(0.95) rotate(2deg); - opacity: 0.8; - z-index: 1000; -} - -/* 响应式断点调整 */ -@media (max-width: 1400px) { - .blackboard-sidebar .variable-item { - padding: 10px 14px; - gap: 10px; - } - - .blackboard-sidebar .variable-name { - font-size: 12px; - } - - .blackboard-sidebar .value-display { - font-size: 10px; - } - - .blackboard-sidebar .variable-type { - font-size: 7px; - padding: 3px 6px; - } - - .blackboard-sidebar .variable-actions button { - width: 20px; - height: 20px; - font-size: 9px; - } -} - -@media (max-width: 1200px) { - .blackboard-sidebar .variable-item { - padding: 8px 12px; - gap: 8px; - min-height: 38px; - } - - .blackboard-sidebar .variable-header { - margin-left: 16px; - gap: 8px; - } - - .blackboard-sidebar .variable-name { - font-size: 11px; - } - - .blackboard-sidebar .value-display { - font-size: 9px; - } - - .blackboard-sidebar .variable-item::before { - left: 10px; - width: 6px; - height: 6px; - } - - .blackboard-sidebar .variable-actions button { - width: 18px; - height: 18px; - font-size: 8px; - padding: 4px; - } -} - -/* Blackboard 拖拽目标区域样式 */ -.blackboard-drop-zone { - margin-bottom: 8px; - padding: 8px; - border: 2px dashed #4a5568; - border-radius: 6px; - background: rgba(74, 85, 104, 0.1); - transition: all 0.3s ease; - cursor: pointer; - min-height: 40px; - display: flex; - align-items: center; - justify-content: center; - position: relative; -} - -.blackboard-drop-zone:hover { - border-color: #667eea; - background: rgba(102, 126, 234, 0.05); -} - -.blackboard-drop-zone.drag-over { - border-color: #667eea; - background: rgba(102, 126, 234, 0.15); - transform: scale(1.02); - box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2); -} - -.blackboard-drop-zone.has-reference { - border-color: #48bb78; - background: rgba(72, 187, 120, 0.1); - border-style: solid; -} - -.blackboard-drop-zone.has-reference:hover { - background: rgba(72, 187, 120, 0.15); -} - -.drop-placeholder { - display: flex; - align-items: center; - gap: 8px; - color: #a0aec0; - font-size: 12px; - text-align: center; - flex-direction: column; - padding: 4px; -} - -.drop-icon { - font-size: 16px; - opacity: 0.7; -} - -.drop-text { - font-weight: 500; - letter-spacing: 0.5px; -} - -.blackboard-drop-zone .drop-placeholder { - transition: all 0.3s ease; -} - -.blackboard-drop-zone:hover .drop-placeholder { - color: #667eea; -} - -.blackboard-drop-zone:hover .drop-icon { - opacity: 1; - transform: scale(1.1); -} - -.blackboard-drop-zone.drag-over .drop-placeholder { - color: #667eea; - transform: scale(1.05); -} - -.blackboard-drop-zone.drag-over .drop-icon { - animation: bounce-drop 0.6s ease-in-out infinite; -} - -@keyframes bounce-drop { - 0%, 100% { - transform: scale(1.1) translateY(0); - } - 50% { - transform: scale(1.2) translateY(-2px); - } -} - -/* 输入框的Blackboard集成样式 */ -.property-item input.with-blackboard { - border-top: 1px solid #4a5568; - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.property-item input.with-blackboard:focus { - border-top-color: #667eea; -} - -@keyframes pulse-hint { - 0%, 100% { - opacity: 0.7; - transform: translateY(-50%) scale(1); - } - 50% { - opacity: 1; - transform: translateY(-50%) scale(1.05); - } -} - -@keyframes bounce-drop { - 0%, 100% { - transform: scale(1.1) translateY(0); - } - 50% { - transform: scale(1.2) translateY(-2px); - } -} - -.blackboard-usage-hint { - margin: 8px 12px; - padding: 8px 12px; - background: rgba(102, 126, 234, 0.1); - border: 1px solid rgba(102, 126, 234, 0.2); - border-radius: 6px; - font-size: 11px; -} - -.hint-content { - display: flex; - align-items: center; - gap: 6px; - color: #667eea; -} - -.hint-icon { - font-size: 12px; - animation: pulse-hint 3s infinite; -} - -.hint-text { - font-weight: 500; - letter-spacing: 0.3px; -} - -/* 调试信息样式 */ -.property-item.debug-info { - background: rgba(255, 193, 7, 0.1); - border-left: 3px solid #ffc107; - padding: 8px 12px; - margin: 4px 0; - border-radius: 4px; -} - -.debug-value { - font-family: 'Courier New', monospace; - background: rgba(0, 0, 0, 0.1); - padding: 2px 6px; - border-radius: 3px; - font-size: 11px; - color: #e83e8c; - font-weight: bold; -} - -/* 节点类型信息区域样式 */ -.node-type-info { - background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); - border: 1px solid rgba(102, 126, 234, 0.3); - border-radius: 8px; - margin-bottom: 16px; - position: relative; - overflow: hidden; -} - -.node-type-info::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 2px; - background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); -} - -.node-type-info h4 { - display: flex; - align-items: center; - justify-content: space-between; - margin: 0; - padding: 12px 16px 8px 16px; - color: #e2e8f0; - font-size: 14px; - font-weight: 600; - border-bottom: 1px solid rgba(102, 126, 234, 0.2); -} - -.node-type-icon { - margin-right: 8px; - font-size: 16px; -} - -.reset-name-btn { - background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); - border: none; - color: white; - padding: 4px 8px; - border-radius: 4px; - font-size: 10px; - cursor: pointer; - transition: all 0.2s ease; - display: flex; - align-items: center; - gap: 4px; - font-weight: 500; -} - -.reset-name-btn:hover { - background: linear-gradient(135deg, #d97706 0%, #b45309 100%); - transform: translateY(-1px); - box-shadow: 0 4px 8px rgba(217, 119, 6, 0.3); -} - -.node-type-details { - padding: 12px 16px 16px 16px; -} - -.type-info-row { - display: flex; - align-items: center; - margin-bottom: 8px; - font-size: 12px; -} - -.type-info-row:last-child { - margin-bottom: 0; -} - -.info-label { - min-width: 80px; - color: #a0aec0; - font-weight: 500; - flex-shrink: 0; -} - -.info-value { - flex: 1; - color: #e2e8f0; - display: flex; - align-items: center; - gap: 8px; -} - -.original-type { - font-weight: 600; - color: #667eea; -} - -.node-id { - font-family: 'JetBrains Mono', 'Consolas', 'Monaco', monospace; - background: rgba(45, 55, 72, 0.8); - padding: 2px 6px; - border-radius: 3px; - font-size: 10px; - color: #9ca3af; - border: 1px solid #4a5568; -} - -.original-description { - font-style: italic; - color: #cbd5e0; - line-height: 1.4; -} - -.custom-name { - font-weight: 600; - color: #f59e0b; -} - -.type-badge { - font-size: 9px; - padding: 2px 6px; - border-radius: 10px; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 700; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); -} - -.badge-根节点 { - background: linear-gradient(135deg, #f6ad55 0%, #ed8936 100%); - color: white; -} - -.badge-组合 { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; -} - -.badge-装饰器 { - background: linear-gradient(135deg, #9f7aea 0%, #805ad5 100%); - color: white; -} - -.badge-动作 { - background: linear-gradient(135deg, #48bb78 0%, #38a169 100%); - color: white; -} - -.badge-条件 { - background: linear-gradient(135deg, #ed8936 0%, #dd6b20 100%); - color: white; -} - -.badge-ecs { - background: linear-gradient(135deg, #38b2ac 0%, #319795 100%); - color: white; -} - -.badge-unknown { - background: linear-gradient(135deg, #718096 0%, #4a5568 100%); - color: white; -} - -/* 名称输入容器样式 */ -.name-input-container { - position: relative; - display: flex; - align-items: center; -} - -.custom-indicator { - position: absolute; - right: 8px; - top: 50%; - transform: translateY(-50%); - color: #f59e0b; - font-size: 12px; - opacity: 0.8; - pointer-events: none; -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/toolbar.css b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/toolbar.css deleted file mode 100644 index 58058469..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/behavior-tree/toolbar.css +++ /dev/null @@ -1,299 +0,0 @@ -/* 头部工具栏样式 */ -.header-toolbar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px 20px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-bottom: 2px solid #4a5568; - box-shadow: 0 2px 8px rgba(0,0,0,0.3); -} - -.toolbar-left { - display: flex; - align-items: center; - gap: 20px; -} - -.toolbar-left h2 { - margin: 0; - font-size: 16px; - font-weight: 600; - text-shadow: 0 2px 4px rgba(0,0,0,0.3); - letter-spacing: 0.5px; -} - -.current-file { - color: #a0aec0; - font-weight: 400; - font-size: 13px; - margin-left: 8px; -} - -.unsaved-indicator { - color: #ff6b6b; - animation: pulse-unsaved 2s infinite; - margin-left: 8px; -} - -@keyframes pulse-unsaved { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.5; } -} - -.toolbar-buttons { - display: flex; - gap: 8px; -} - -.tool-btn { - display: flex; - align-items: center; - gap: 6px; - padding: 8px 12px; - background: rgba(255,255,255,0.1); - border: 1px solid rgba(255,255,255,0.2); - border-radius: 6px; - color: white; - cursor: pointer; - transition: all 0.3s ease; - font-size: 12px; -} - -.tool-btn:hover:not(:disabled) { - background: rgba(255,255,255,0.2); - transform: translateY(-1px); -} - -.tool-btn.active { - background: rgba(102, 126, 234, 0.3); - border-color: #667eea; - color: #e2e8f0; -} - -/* 布局组样式 */ -.layout-group { - display: flex; - gap: 4px; - background: rgba(255,255,255,0.05); - border: 1px solid rgba(255,255,255,0.1); - border-radius: 8px; - padding: 2px; - align-items: center; -} - -.layout-group button { - padding: 8px 12px; - background: transparent; - border: none; - color: rgba(255,255,255,0.8); - font-size: 12px; - font-weight: 500; - border-radius: 6px; - cursor: pointer; - transition: all 0.2s ease; - white-space: nowrap; - min-width: 70px; -} - -.layout-group button:hover { - background: rgba(255,255,255,0.1); - color: white; - transform: translateY(-1px); -} - -.layout-group button:active { - transform: translateY(0); - background: rgba(255,255,255,0.15); -} - - - -.tool-btn.has-changes { - background: rgba(255, 107, 107, 0.2); - border-color: #ff6b6b; - animation: glow-save 2s infinite; -} - -@keyframes glow-save { - 0%, 100% { - box-shadow: 0 0 5px rgba(255, 107, 107, 0.5); - } - 50% { - box-shadow: 0 0 15px rgba(255, 107, 107, 0.8); - } -} - -/* 安装状态容器 */ -.install-status-container { - position: relative; -} - -.install-status { - display: flex; - align-items: center; - gap: 12px; - padding: 10px 16px; - background: rgba(255,255,255,0.1); - border: 1px solid rgba(255,255,255,0.2); - border-radius: 12px; - min-width: 280px; - backdrop-filter: blur(10px); - transition: all 0.3s ease; -} - -.install-status:hover { - background: rgba(255,255,255,0.15); - border-color: rgba(255,255,255,0.3); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(0,0,0,0.2); -} - -/* 状态信息 */ -.status-info { - display: flex; - align-items: center; - gap: 10px; - flex: 1; -} - -.status-icon { - font-size: 18px; - line-height: 1; -} - -.status-text { - display: flex; - flex-direction: column; - gap: 2px; -} - -.main-text { - font-size: 13px; - font-weight: 500; - color: white; -} - -.version-text { - font-size: 11px; - color: rgba(255,255,255,0.7); -} - -/* 安装按钮 */ -.install-btn { - display: flex; - align-items: center; - gap: 6px; - padding: 8px 16px; - background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%); - border: none; - border-radius: 8px; - color: white; - font-size: 12px; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - position: relative; - overflow: hidden; -} - -.install-btn:hover:not(:disabled) { - background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(34, 197, 94, 0.4); -} - -.install-btn:disabled { - opacity: 0.7; - cursor: not-allowed; - background: linear-gradient(135deg, #6b7280 0%, #4b5563 100%); -} - -.install-btn.installing { - background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); -} - -.btn-icon { - font-size: 14px; - line-height: 1; -} - -.btn-text { - font-size: 12px; -} - -/* 加载点动画 */ -.loading-dots { - display: flex; - gap: 2px; - margin-left: 4px; -} - -.loading-dots span { - width: 3px; - height: 3px; - background: white; - border-radius: 50%; - animation: loading-dots 1.4s infinite ease-in-out; -} - -.loading-dots span:nth-child(1) { animation-delay: -0.32s; } -.loading-dots span:nth-child(2) { animation-delay: -0.16s; } - -@keyframes loading-dots { - 0%, 80%, 100% { - transform: scale(0); - opacity: 0.5; - } - 40% { - transform: scale(1); - opacity: 1; - } -} - -/* 刷新按钮 */ -.refresh-btn { - display: flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - background: rgba(255,255,255,0.1); - border: 1px solid rgba(255,255,255,0.2); - border-radius: 6px; - color: white; - cursor: pointer; - transition: all 0.3s ease; - font-size: 12px; -} - -.refresh-btn:hover { - background: rgba(255,255,255,0.2); - transform: rotate(180deg); -} - -/* 状态颜色 */ -.install-status.installed { - border-color: rgba(34, 197, 94, 0.5); - background: rgba(34, 197, 94, 0.1); -} - -.install-status.not-installed { - border-color: rgba(239, 68, 68, 0.5); - background: rgba(239, 68, 68, 0.1); -} - -.install-status.installing { - border-color: rgba(59, 130, 246, 0.5); - background: rgba(59, 130, 246, 0.1); - animation: installing-pulse 2s infinite; -} - -@keyframes installing-pulse { - 0%, 100% { - box-shadow: 0 0 5px rgba(59, 130, 246, 0.3); - } - 50% { - box-shadow: 0 0 15px rgba(59, 130, 246, 0.6); - } -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/debug/index.css b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/debug/index.css deleted file mode 100644 index d46c6743..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/debug/index.css +++ /dev/null @@ -1,1602 +0,0 @@ -/* ============================================ - ECS Framework 调试面板样式 - ============================================ */ - -/* 全局重置 */ -#app { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - font-size: 12px; - color: var(--color-default-contrast); - background: var(--color-normal-fill); - margin: 0; - padding: 0; - height: 100%; - overflow: hidden; -} - -* { - box-sizing: border-box; -} - -/* 主容器 */ -.debug-container { - display: flex; - flex-direction: column; - height: 100%; - background: var(--color-normal-fill); -} - -/* ============================================ - 头部区域 - ============================================ */ - -.debug-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px 20px; - background: var(--color-normal-fill-emphasis); - border-bottom: 1px solid var(--color-normal-border); - min-height: 80px; -} - -.header-title h1 { - margin: 0; - font-size: 18px; - font-weight: 600; - color: var(--color-focus-contrast); -} - -.header-title p { - margin: 4px 0 0 0; - font-size: 11px; - color: var(--color-normal-contrast-weakest); -} - -.header-controls { - display: flex; - gap: 8px; - align-items: center; -} - -/* 按钮基础样式 */ -ui-button.refresh-btn, -ui-button.auto-refresh-btn, -ui-button.export-btn, -ui-button.reset-btn { - display: flex !important; - align-items: center !important; - gap: 4px !important; - padding: 6px 12px !important; - font-size: 11px !important; - background: var(--color-normal-fill-emphasis) !important; - color: var(--color-normal-contrast) !important; - border: 1px solid var(--color-normal-border) !important; - border-radius: 4px !important; - cursor: pointer !important; - transition: all 0.2s ease !important; - min-height: 28px !important; - box-sizing: border-box !important; -} - -ui-button.refresh-btn:hover, -ui-button.auto-refresh-btn:hover, -ui-button.export-btn:hover, -ui-button.reset-btn:hover { - background: var(--color-normal-fill-weaker) !important; - border-color: var(--color-focus-border) !important; -} - -/* 激活状态的自动刷新按钮 */ -ui-button.auto-refresh-btn.active { - background: var(--color-focus-fill) !important; - color: var(--color-focus-contrast) !important; - border-color: var(--color-focus-border) !important; -} - -ui-button.auto-refresh-btn.active:hover { - background: var(--color-focus-fill-emphasis) !important; -} - -/* 修复按钮内文字和图标的对齐 */ -ui-button ui-icon { - display: inline-flex !important; - align-items: center !important; - width: 12px !important; - height: 12px !important; - margin-right: 2px !important; -} - -/* 兼容原始样式类 */ -.refresh-btn, .auto-refresh-btn { - display: flex; - align-items: center; - gap: 4px; - padding: 6px 12px; - font-size: 11px; - background: var(--color-normal-fill-emphasis); - color: var(--color-normal-contrast); - border: 1px solid var(--color-normal-border); - border-radius: 4px; - cursor: pointer; - transition: all 0.2s ease; -} - -.refresh-btn:hover, .auto-refresh-btn:hover { - background: var(--color-normal-fill-weaker); - border-color: var(--color-focus-border); -} - -.auto-refresh-btn.active { - background: var(--color-focus-fill) !important; - color: var(--color-focus-contrast) !important; - border-color: var(--color-focus-border) !important; -} - -.auto-refresh-btn.active:hover { - background: var(--color-focus-fill-emphasis) !important; -} - -/* ============================================ - 状态栏 - ============================================ */ - -.status-bar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 20px; - background: var(--color-normal-fill-weaker); - border-bottom: 1px solid var(--color-normal-border); - font-size: 11px; -} - -.status-info { - display: flex; - gap: 16px; - align-items: center; -} - -.status-indicator { - font-weight: 600; -} - -.status-indicator.online { - color: var(--color-success-border); -} - -.status-indicator.offline { - color: var(--color-danger-border); -} - -.last-update { - color: var(--color-normal-contrast-weaker); -} - -.status-actions { - display: flex; - gap: 6px; -} - -.export-btn, .reset-btn { - display: flex; - align-items: center; - gap: 4px; - padding: 4px 8px; - font-size: 10px; - background: var(--color-normal-fill-emphasis); - color: var(--color-normal-contrast); - border: 1px solid var(--color-normal-border); - border-radius: 3px; - cursor: pointer; - transition: all 0.2s ease; -} - -.export-btn:hover, .reset-btn:hover { - background: var(--color-normal-fill-weaker); - border-color: var(--color-focus-border); -} - -/* ============================================ - 调试内容区域 - ============================================ */ - -.debug-content { - flex: 1; - overflow-y: auto; - overflow-x: hidden; - padding: 0 20px 20px 20px; - max-height: calc(100vh - 140px); - scroll-behavior: smooth; -} - -.debug-section { - margin-bottom: 20px; - background: var(--color-normal-fill-weaker); - border: 1px solid var(--color-normal-border); - border-radius: 6px; - overflow: hidden; -} - -.section-header { - padding: 12px 16px; - background: var(--color-normal-fill-emphasis); - border-bottom: 1px solid var(--color-normal-border); -} - -.section-header h2 { - margin: 0; - font-size: 14px; - font-weight: 600; - color: var(--color-focus-contrast); -} - -/* ============================================ - 状态网格 - ============================================ */ - -.status-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1px; - background: var(--color-normal-border); -} - -.status-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px 16px; - background: var(--color-normal-fill); -} - -.status-item .label { - font-weight: 500; - color: var(--color-normal-contrast-weaker); -} - -.status-item .value { - font-weight: 600; - color: var(--color-default-contrast); -} - -.status-item .value.online { - color: var(--color-success-border); -} - -.status-item .value.offline { - color: var(--color-danger-border); -} - -/* ============================================ - 实体统计卡片 - ============================================ */ - -.entity-stats { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); - gap: 1px; - background: var(--color-normal-border); - padding: 0; -} - -.stat-card { - background: var(--color-normal-fill); - padding: 16px; - text-align: center; - transition: background-color 0.2s ease; -} - -.stat-card:hover { - background: var(--color-normal-fill-emphasis); -} - -.stat-number { - font-size: 24px; - font-weight: 700; - color: var(--color-focus-border); - margin-bottom: 4px; -} - -.stat-label { - font-size: 10px; - color: var(--color-normal-contrast-weaker); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -/* ============================================ - 系统列表 - ============================================ */ - -.systems-list { - padding: 8px; -} - -.system-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 12px; - margin-bottom: 4px; - background: var(--color-normal-fill); - border: 1px solid var(--color-normal-border); - border-radius: 4px; - transition: all 0.2s ease; -} - -.system-item:hover { - background: var(--color-normal-fill-emphasis); - border-color: var(--color-focus-border); -} - -.system-item:last-child { - margin-bottom: 0; -} - -.system-name { - font-weight: 500; - color: var(--color-default-contrast); -} - -.system-stats { - display: flex; - gap: 12px; - align-items: center; - font-size: 10px; -} - -.entity-count { - color: var(--color-normal-contrast-weaker); -} - -.execution-time { - font-weight: 600; - padding: 2px 6px; - border-radius: 3px; - background: var(--color-normal-fill-emphasis); -} - -.update-order { - color: var(--color-normal-contrast-weakest); - font-family: monospace; -} - -/* ============================================ - 组件统计 - ============================================ */ - -.component-stats { - padding: 16px; -} - -.stat-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 0; - border-bottom: 1px solid var(--color-normal-border); -} - -.stat-item:last-child { - border-bottom: none; -} - -.stat-item .label { - font-weight: 500; - color: var(--color-normal-contrast-weaker); -} - -.stat-item .value { - font-weight: 600; - color: var(--color-focus-border); - font-size: 14px; -} - -/* ============================================ - 性能监控 - ============================================ */ - -.performance-warnings { - padding: 16px; -} - -.warning-item { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - margin-bottom: 8px; - background: var(--color-warn-fill); - border: 1px solid var(--color-warn-border); - border-radius: 4px; - color: var(--color-warn-contrast); - font-size: 11px; -} - -.warning-item:last-child { - margin-bottom: 0; -} - -.warning-icon { - color: var(--color-warn-border); -} - -.no-warnings { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - background: var(--color-success-fill); - border: 1px solid var(--color-success-border); - border-radius: 4px; - color: var(--color-success-contrast); - font-size: 11px; -} - -.success-icon { - color: var(--color-success-border); -} - -/* ============================================ - 性能指示器颜色 - ============================================ */ - -.good { - color: var(--color-success-border) !important; -} - -.warning { - color: var(--color-warn-border) !important; -} - -.critical { - color: var(--color-danger-border) !important; -} - -.normal { - color: var(--color-default-contrast) !important; -} - -/* ============================================ - 调试信息 - ============================================ */ - -.debug-info { - padding: 16px; - line-height: 1.5; -} - -.debug-info p { - margin: 0 0 8px 0; - color: var(--color-default-contrast); -} - -.debug-info ul { - margin: 8px 0 16px 0; - padding-left: 20px; -} - -.debug-info li { - margin-bottom: 4px; - color: var(--color-normal-contrast-weaker); -} - -.debug-info strong { - color: var(--color-focus-contrast); -} - -.debug-info code { - background: var(--color-normal-fill-emphasis); - padding: 2px 4px; - border-radius: 3px; - font-family: monospace; - font-size: 10px; - color: var(--color-focus-border); -} - -/* 运行状态通知 */ -.offline-notice, .online-notice { - display: flex; - gap: 12px; - align-items: flex-start; - padding: 12px; - margin-bottom: 16px; - border-radius: 6px; - background: var(--color-normal-fill-emphasis); - border: 1px solid var(--color-normal-border); -} - -.offline-notice { - background: var(--color-warn-fill); - border-color: var(--color-warn-border); -} - -.online-notice { - background: var(--color-success-fill); - border-color: var(--color-success-border); -} - -.notice-content { - flex: 1; -} - -.notice-content p { - margin-bottom: 4px; -} - -.notice-content p:last-child { - margin-bottom: 0; -} - -.info-icon { - color: var(--color-warn-border); - margin-top: 2px; -} - -/* ============================================ - 底部信息 - ============================================ */ - -.debug-footer { - padding: 8px 20px; - background: var(--color-normal-fill-emphasis); - border-top: 1px solid var(--color-normal-border); -} - -.footer-info { - display: flex; - align-items: center; - gap: 6px; - font-size: 10px; - color: var(--color-normal-contrast-weaker); -} - -/* ============================================ - 滚动条样式 - ============================================ */ - -.debug-content::-webkit-scrollbar { - width: 10px; -} - -.debug-content::-webkit-scrollbar-track { - background: var(--color-normal-fill); - border-radius: 5px; - margin: 2px; -} - -.debug-content::-webkit-scrollbar-thumb { - background: var(--color-focus-border); - border-radius: 5px; - transition: background-color 0.2s ease; - min-height: 30px; -} - -.debug-content::-webkit-scrollbar-thumb:hover { - background: var(--color-default-border); -} - -.debug-content::-webkit-scrollbar-thumb:active { - background: var(--color-focus-border-emphasis); -} - -/* ============================================ - 响应式设计 - ============================================ */ - -@media (max-width: 600px) { - .header-controls { - flex-direction: column; - gap: 4px; - } - - .status-bar { - flex-direction: column; - align-items: flex-start; - gap: 8px; - } - - .status-grid { - grid-template-columns: 1fr; - } - - .entity-stats { - grid-template-columns: repeat(2, 1fr); - } -} - -/* ============================================ - 动画效果 - ============================================ */ - -.debug-section { - animation: fadeInUp 0.3s ease-out; -} - -@keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.stat-number { - transition: color 0.3s ease; -} - -.system-item { - transition: all 0.2s ease; -} - -/* ============================================ - 工具提示 - ============================================ */ - -[title] { - position: relative; -} - -[title]:hover::after { - content: attr(title); - position: absolute; - bottom: 100%; - left: 50%; - transform: translateX(-50%); - background: var(--color-normal-fill-emphasis); - border: 1px solid var(--color-normal-border); - padding: 4px 8px; - border-radius: 4px; - font-size: 10px; - white-space: nowrap; - z-index: 1000; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); -} - -/* ECS调试面板样式 */ -.debug-panel { - padding: 16px; - padding-bottom: 32px; - background: var(--color-normal-fill); - color: var(--color-normal-contrast); - height: 100%; - overflow-y: auto; - overflow-x: hidden; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; -} - -/* 顶部工具栏 */ -.debug-toolbar { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px 16px; - background: var(--color-primary-fill); - border-radius: 8px; - margin-bottom: 16px; - gap: 16px; - flex-wrap: wrap; -} - -.toolbar-section { - display: flex; - align-items: center; - gap: 8px; -} - -.toolbar-section label { - font-size: 13px; - font-weight: 500; - color: var(--color-primary-contrast); -} - -.instance-selector { - padding: 6px 12px; - border: 1px solid var(--color-normal-border); - border-radius: 4px; - background: var(--color-normal-fill); - color: var(--color-normal-contrast); - font-size: 13px; - min-width: 200px; -} - -.instance-count { - font-size: 12px; - color: var(--color-normal-contrast-weaken); - padding: 4px 8px; - background: var(--color-normal-fill-emphasis); - border-radius: 12px; -} - -.btn-refresh { - display: flex; - align-items: center; - gap: 4px; - padding: 6px 12px; - background: var(--color-primary-fill); - border: 1px solid var(--color-primary-border); - border-radius: 4px; - color: var(--color-primary-contrast); - cursor: pointer; - font-size: 13px; - transition: all 0.2s; -} - -.btn-refresh:hover:not(:disabled) { - background: var(--color-primary-fill-emphasis); -} - -.btn-refresh:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.checkbox-label { - display: flex; - align-items: center; - gap: 6px; - font-size: 13px; - cursor: pointer; -} - -.checkbox-label input[type="checkbox"] { - margin: 0; -} - -.last-update { - font-size: 12px; - color: var(--color-normal-contrast-weaken); -} - -/* 调试区域 */ -.debug-section { - background: var(--color-normal-fill-emphasis); - border: 1px solid var(--color-normal-border); - border-radius: 8px; - margin-bottom: 16px; - overflow: hidden; -} - -.section-header { - padding: 12px 16px; - background: var(--color-primary-fill); - border-bottom: 1px solid var(--color-normal-border); -} - -.section-header h2 { - margin: 0; - font-size: 16px; - font-weight: 600; - color: var(--color-primary-contrast); -} - -/* 状态网格 */ -.status-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 16px; - padding: 16px; -} - -.status-item { - display: flex; - flex-direction: column; - gap: 4px; -} - -.status-item .label { - font-size: 12px; - color: var(--color-normal-contrast-weaken); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.status-item .value { - font-size: 14px; - font-weight: 600; - color: var(--color-normal-contrast); -} - -.status-item .value.online { - color: #4CAF50; -} - -.status-item .value.offline { - color: #F44336; -} - -/* 无实例提示 */ -.no-instance { - display: flex; - align-items: center; - gap: 8px; - padding: 24px 16px; - text-align: center; - color: var(--color-normal-contrast-weaken); -} - -.no-instance .info-icon { - font-size: 20px; - color: #2196F3; -} - -/* 性能监控网格 */ -.performance-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 16px; - padding: 16px; -} - -.perf-card { - background: var(--color-normal-fill); - border: 1px solid var(--color-normal-border); - border-radius: 6px; - padding: 16px; - text-align: center; -} - -.perf-title { - font-size: 12px; - color: var(--color-normal-contrast-weaken); - margin-bottom: 8px; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.perf-value { - font-size: 24px; - font-weight: 700; - margin-bottom: 4px; -} - -.perf-value.good { - color: #4CAF50; -} - -.perf-value.warning { - color: #FF9800; -} - -.perf-value.critical { - color: #F44336; -} - -.perf-detail { - font-size: 11px; - color: var(--color-normal-contrast-weaken); -} - -/* 内存分析 */ -.memory-breakdown { - padding: 16px; -} - -.memory-item { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 12px; - padding: 8px 0; -} - -.memory-label { - font-size: 13px; - color: var(--color-normal-contrast); - flex: 1; -} - -.memory-value { - font-size: 13px; - font-weight: 600; - color: var(--color-normal-contrast); - margin-right: 12px; - min-width: 80px; - text-align: right; -} - -.memory-bar { - flex: 2; - height: 6px; - background: var(--color-normal-border); - border-radius: 3px; - overflow: hidden; -} - -.memory-fill { - height: 100%; - background: linear-gradient(90deg, #4CAF50, #FF9800); - transition: width 0.3s ease; -} - -/* 实体统计 */ -.entity-stats { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); - gap: 16px; - padding: 16px; -} - -.stat-card { - background: var(--color-normal-fill); - border: 1px solid var(--color-normal-border); - border-radius: 6px; - padding: 16px; - text-align: center; -} - -.stat-number { - font-size: 28px; - font-weight: 700; - color: var(--color-primary); - margin-bottom: 4px; -} - -.stat-label { - font-size: 12px; - color: var(--color-normal-contrast-weaken); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -/* Archetype 分布 */ -.archetype-section { - margin-top: 16px; - padding: 0 16px 16px; -} - -.archetype-section h3 { - margin: 0 0 12px 0; - font-size: 14px; - font-weight: 600; - color: var(--color-normal-contrast); -} - -.archetype-list { - display: flex; - flex-direction: column; - gap: 8px; -} - -.archetype-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 12px; - background: var(--color-normal-fill); - border: 1px solid var(--color-normal-border); - border-radius: 4px; -} - -.archetype-signature { - font-family: 'Courier New', monospace; - font-size: 12px; - color: var(--color-normal-contrast); -} - -.archetype-stats { - display: flex; - gap: 12px; - font-size: 12px; -} - -.entity-count { - color: #2196F3; - font-weight: 500; -} - -.memory-usage { - color: var(--color-normal-contrast-weaken); -} - -/* 组件分析 */ -.component-overview { - display: flex; - gap: 32px; - padding: 16px; - background: var(--color-normal-fill); - border-bottom: 1px solid var(--color-normal-border); -} - -.overview-item { - display: flex; - flex-direction: column; - gap: 4px; -} - -.overview-item .label { - font-size: 12px; - color: var(--color-normal-contrast-weaken); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.overview-item .value { - font-size: 18px; - font-weight: 700; - color: var(--color-primary); -} - -/* 表格样式 */ -.component-table, -.systems-table { - background: var(--color-normal-fill); -} - -.table-header, -.table-row { - display: grid; - align-items: center; - padding: 12px 16px; - border-bottom: 1px solid var(--color-normal-border); - gap: 16px; -} - -.component-table .table-header, -.component-table .table-row { - grid-template-columns: 2fr 1fr 1.5fr 1fr 1.5fr; -} - -.systems-table .table-header, -.systems-table .table-row { - grid-template-columns: 2fr 1fr 1.5fr 1fr 0.8fr 0.8fr; -} - -.table-header { - background: var(--color-primary-fill); - font-size: 12px; - font-weight: 600; - color: var(--color-primary-contrast); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.table-row { - font-size: 13px; - color: var(--color-normal-contrast); - transition: background-color 0.2s; -} - -.table-row:hover { - background: var(--color-normal-fill-emphasis); -} - -.col-name { - font-weight: 500; -} - -.col-count, -.col-entities { - text-align: right; -} - -.col-memory { - text-align: right; - font-family: 'Courier New', monospace; -} - -.col-pool { - text-align: right; -} - -.col-efficiency { - display: flex; - align-items: center; - gap: 8px; -} - -.efficiency-bar { - flex: 1; - height: 4px; - background: var(--color-normal-border); - border-radius: 2px; - overflow: hidden; -} - -.efficiency-fill { - height: 100%; - background: linear-gradient(90deg, #F44336, #FF9800, #4CAF50); - transition: width 0.3s ease; -} - -.col-time { - text-align: right; -} - -.time-range { - font-size: 11px; - color: var(--color-normal-contrast-weaken); - margin-top: 2px; -} - -.col-order { - text-align: center; -} - -.col-status { - text-align: center; -} - -.enabled { - color: #4CAF50; - font-weight: 500; -} - -.disabled { - color: #F44336; - font-weight: 500; -} - -/* 服务器状态 */ -.server-status { - padding: 32px 16px; - text-align: center; -} - -.status-message { - display: flex; - flex-direction: column; - align-items: center; - gap: 16px; - margin-bottom: 32px; -} - -.server-icon { - font-size: 48px; - color: #2196F3; -} - -.message-content h3 { - margin: 0 0 8px 0; - font-size: 18px; - color: var(--color-normal-contrast); -} - -.message-content p { - margin: 4px 0; - font-size: 14px; - color: var(--color-normal-contrast-weaken); -} - -.connection-help { - background: var(--color-normal-fill); - border: 1px solid var(--color-normal-border); - border-radius: 6px; - padding: 16px; - text-align: left; -} - -.connection-help h4 { - margin: 0 0 12px 0; - font-size: 14px; - color: var(--color-normal-contrast); -} - -.connection-help ul { - margin: 0; - padding-left: 20px; -} - -.connection-help li { - margin-bottom: 6px; - font-size: 13px; - color: var(--color-normal-contrast-weaken); -} - -/* 响应式设计 */ -@media (max-width: 768px) { - .debug-toolbar { - flex-direction: column; - align-items: stretch; - } - - .toolbar-section { - justify-content: space-between; - } - - .performance-grid { - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - } - - .entity-stats { - grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); - } - - .component-table .table-header, - .component-table .table-row { - grid-template-columns: 1fr; - gap: 8px; - } - - .systems-table .table-header, - .systems-table .table-row { - grid-template-columns: 1fr; - gap: 8px; - } - - .table-header { - display: none; - } - - .table-row { - display: flex; - flex-direction: column; - gap: 4px; - } -} - -/* 滚动条样式 */ -.debug-panel::-webkit-scrollbar { - width: 6px; -} - -.debug-panel::-webkit-scrollbar-track { - background: var(--color-normal-fill); -} - -.debug-panel::-webkit-scrollbar-thumb { - background: var(--color-normal-border); - border-radius: 3px; -} - -.debug-panel::-webkit-scrollbar-thumb:hover { - background-color: var(--color-focus-border-emphasis); -} - -/* 百分比条样式 */ -.col-percentage { - width: 120px; - text-align: center; -} - -.percentage-bar { - width: 60px; - height: 8px; - background-color: var(--color-normal-fill); - border-radius: 4px; - overflow: hidden; - margin: 0 auto 4px; -} - -.percentage-fill { - background: linear-gradient(90deg, var(--color-primary), var(--color-primary-emphasis)); - height: 100%; - border-radius: inherit; - transition: width 0.3s ease; -} - -/* 调试面板滚动条样式 */ -.debug-panel::-webkit-scrollbar { - width: 8px; -} - -.debug-panel::-webkit-scrollbar-track { - background: var(--color-normal-fill-weaker); - border-radius: 4px; -} - -.debug-panel::-webkit-scrollbar-thumb { - background: var(--color-normal-border); - border-radius: 4px; -} - -.debug-panel::-webkit-scrollbar-thumb:hover { - background: var(--color-focus-border); -} - -/* ============================================ - 帮助面板和文档链接样式 - ============================================ */ - -.section-help { - margin-left: auto; -} - -.help-btn { - display: flex; - align-items: center; - gap: 6px; - padding: 6px 12px; - background: var(--color-normal-fill); - border: 1px solid var(--color-normal-border); - border-radius: 4px; - color: var(--color-default-contrast); - font-size: 11px; - cursor: pointer; - transition: all 0.2s ease; -} - -.help-btn:hover { - background: var(--color-normal-fill-emphasis); - border-color: var(--color-focus-border); - color: var(--color-focus-contrast); -} - -.help-btn ui-icon { - font-size: 12px; -} - -.help-panel { - margin: 16px 0; - padding: 20px; - background: var(--color-normal-fill); - border: 1px solid var(--color-normal-border); - border-radius: 8px; - animation: fadeInDown 0.3s ease; -} - -@keyframes fadeInDown { - from { - opacity: 0; - transform: translateY(-10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.help-content h4 { - margin: 0 0 12px 0; - color: var(--color-focus-contrast); - font-size: 14px; - font-weight: 600; -} - -.help-content p { - margin: 0 0 16px 0; - color: var(--color-default-contrast); - font-size: 12px; - line-height: 1.5; -} - -.help-steps { - margin: 20px 0; -} - -.help-step { - margin-bottom: 20px; - padding: 16px; - background: var(--color-normal-fill-emphasis); - border-radius: 6px; - border-left: 3px solid var(--color-focus-border); -} - -.help-step:last-child { - margin-bottom: 0; -} - -.help-step h5 { - margin: 0 0 12px 0; - color: var(--color-focus-contrast); - font-size: 12px; - font-weight: 600; -} - -.help-step p { - margin: 0; - color: var(--color-default-contrast); - font-size: 11px; - line-height: 1.4; -} - -.code-example { - background: var(--color-normal-fill); - border: 1px solid var(--color-normal-border); - border-radius: 4px; - padding: 12px; - margin: 8px 0 0 0; - font-family: 'Courier New', monospace; - font-size: 10px; - line-height: 1.4; - color: var(--color-focus-border); - overflow-x: auto; - white-space: pre; -} - -.help-links { - display: flex; - gap: 12px; - margin-top: 16px; - padding-top: 16px; - border-top: 1px solid var(--color-normal-border); -} - -.doc-link { - display: flex; - align-items: center; - gap: 6px; - padding: 8px 12px; - background: var(--color-focus-fill); - border: 1px solid var(--color-focus-border); - border-radius: 4px; - color: var(--color-focus-contrast); - text-decoration: none; - font-size: 11px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s ease; -} - -.doc-link:hover { - background: var(--color-focus-fill-emphasis); - border-color: var(--color-focus-border-emphasis); - transform: translateY(-1px); -} - -.doc-link ui-icon { - font-size: 12px; -} - -.doc-access-note { - display: flex; - align-items: flex-start; - gap: 8px; - margin-top: 12px; - padding: 12px; - background: var(--color-info-fill); - border: 1px solid var(--color-info-border); - border-radius: 4px; - font-size: 10px; - color: var(--color-info-contrast); - line-height: 1.4; -} - -.doc-access-note ui-icon { - font-size: 14px; - color: var(--color-info-border); - margin-top: 1px; - flex-shrink: 0; -} - -/* ============================================ - 组件池相关样式 - ============================================ */ - -.help-tooltip { - display: inline-flex; - align-items: center; - margin-left: 4px; - cursor: help; - color: var(--color-normal-contrast-weaker); -} - -.help-tooltip:hover { - color: var(--color-focus-border); -} - -.help-tooltip ui-icon { - font-size: 10px; -} - -.not-configured { - display: inline-flex; - align-items: center; - margin-left: 4px; - color: var(--color-warn-border); - cursor: help; -} - -.not-configured ui-icon { - font-size: 10px; -} - -.no-pool-hint { - font-size: 10px; - color: var(--color-normal-contrast-weaker); - margin-left: 4px; -} - -.pool-suggestion { - margin-top: 16px; - padding: 16px; - background: linear-gradient(135deg, var(--color-warn-fill) 0%, var(--color-focus-fill) 100%); - border: 1px solid var(--color-warn-border); - border-radius: 8px; - animation: fadeInUp 0.4s ease; -} - -.suggestion-content { - display: flex; - gap: 12px; - align-items: flex-start; -} - -.suggestion-icon { - color: var(--color-warn-border); - font-size: 20px; - margin-top: 2px; -} - -.suggestion-text { - flex: 1; -} - -.suggestion-text h4 { - margin: 0 0 8px 0; - color: var(--color-focus-contrast); - font-size: 13px; - font-weight: 600; -} - -.suggestion-text p { - margin: 0 0 12px 0; - color: var(--color-default-contrast); - font-size: 11px; - line-height: 1.5; -} - -.suggestion-action { - padding: 6px 12px; - background: var(--color-focus-border); - border: none; - border-radius: 4px; - color: white; - font-size: 11px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s ease; -} - -.suggestion-action:hover { - background: var(--color-focus-border-emphasis); - transform: translateY(-1px); -} - -/* ============================================ - 响应式设计增强 - ============================================ */ - -@media (max-width: 768px) { - .help-links { - flex-direction: column; - } - - .doc-link { - justify-content: center; - } - - .suggestion-content { - flex-direction: column; - text-align: center; - } - - .suggestion-icon { - align-self: center; - margin-top: 0; - } -} - -@media (max-width: 480px) { - .help-panel { - padding: 16px; - } - - .help-step { - padding: 12px; - } - - .code-example { - font-size: 9px; - padding: 8px; - } - - .help-btn { - padding: 4px 8px; - font-size: 10px; - } - - .section-header { - flex-direction: column; - align-items: flex-start; - gap: 8px; - } - - .section-help { - margin-left: 0; - } -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/default/index.css b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/default/index.css deleted file mode 100644 index 6f2a6c54..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/default/index.css +++ /dev/null @@ -1,483 +0,0 @@ -#text { - color: var(--color-normal-contrast-weakest); - margin: auto; - width: 180px; -} -.counter { - text-align: center; -} - -.ecs-welcome-panel { - height: 100%; - background: var(--color-panel-bg); - color: var(--color-text-normal); - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - overflow: hidden; -} - -.welcome-container { - padding: 16px; - height: 100%; - display: flex; - flex-direction: column; - overflow-y: auto; - overflow-x: hidden; - max-height: 100vh; - box-sizing: border-box; -} - -.header { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - padding: 16px; - border-radius: 6px; - margin-bottom: 16px; - text-align: center; - flex-shrink: 0; -} - -.header h1 { - margin: 0 0 6px 0; - font-size: 20px; - font-weight: 600; -} - -.header p { - margin: 0; - opacity: 0.9; - font-size: 13px; -} - -.status-section { - background: var(--color-area-bg); - border: 1px solid var(--color-border); - border-radius: 6px; - padding: 14px; - margin-bottom: 14px; - flex-shrink: 0; -} - -.status-title { - font-size: 15px; - font-weight: 600; - margin-bottom: 10px; - color: var(--color-text-normal); -} - -.status-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 8px 0; - border-bottom: 1px solid var(--color-border-soft); - flex-wrap: wrap; - gap: 8px; -} - -.status-item:last-child { - border-bottom: none; -} - -.status-label { - color: var(--color-text-contrast-weakest); - flex: 1; - min-width: 200px; -} - -.status-badge { - padding: 4px 8px; - border-radius: 4px; - font-size: 12px; - font-weight: 500; - white-space: nowrap; -} - -.status-badge.installed { - background: #d4edda; - color: #155724; - border: 1px solid #c3e6cb; -} - -.status-badge.not-installed { - background: #f8d7da; - color: #721c24; - border: 1px solid #f5c6cb; -} - -.status-badge.checking { - background: #fff3cd; - color: #856404; - border: 1px solid #ffeaa7; -} - -.version-info { - font-family: 'Courier New', monospace; - font-size: 11px; - color: var(--color-text-contrast-weak); -} - -.update-hint { - color: #ff6b35; - font-size: 10px; - font-weight: 600; - margin-left: 8px; - display: block; - margin-top: 2px; -} - -.management-buttons { - display: flex; - flex-direction: column; - gap: 8px; - margin-top: 8px; -} - -.management-buttons ui-button { - width: 100%; - min-height: 32px; - font-size: 12px; - display: flex; - align-items: center; - justify-content: center; - text-align: center; - padding: 8px 12px; -} - -.management-buttons ui-button:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.management-buttons ui-button.green { - background-color: #28a745; - color: white; - border: 1px solid #28a745; -} - -.management-buttons ui-button.red { - background-color: #dc3545; - color: white; - border: 1px solid #dc3545; -} - -.management-buttons ui-button.blue { - background-color: #007bff; - color: white; - border: 1px solid #007bff; -} - -.operation-status { - padding: 12px; - border-radius: 6px; - margin-top: 8px; -} - -.operation-status.loading { - background: #fff3cd; - border: 1px solid #ffeaa7; - color: #856404; -} - -.operation-status.success { - background: #d4edda; - border: 1px solid #c3e6cb; - color: #155724; -} - -.operation-status.error, -.operation-status.failed { - background: #f8d7da; - border: 1px solid #f5c6cb; - color: #721c24; -} - -.operation-status.warning { - background: #fff3cd; - border: 1px solid #ffeaa7; - color: #856404; -} - -.status-content { - display: flex; - align-items: center; - gap: 8px; -} - -.status-icon { - font-size: 16px; -} - -.status-text { - flex: 1; - font-weight: 500; -} - -.status-details { - margin-top: 8px; - font-size: 11px; - opacity: 0.8; - white-space: pre-line; -} - -.actions-section { - background: var(--color-area-bg); - border: 1px solid var(--color-border); - border-radius: 6px; - padding: 14px; - margin-bottom: 14px; - flex-shrink: 0; -} - -.action-card { - background: var(--color-neutral-bg); - border: 1px solid var(--color-border); - border-radius: 6px; - padding: 12px; - margin-bottom: 10px; - cursor: pointer; - transition: all 0.2s ease; -} - -.action-card:last-child { - margin-bottom: 0; -} - -.action-card h3 { - margin: 0 0 6px 0; - font-size: 14px; - font-weight: 600; - color: var(--color-text-normal); - display: flex; - align-items: center; - gap: 8px; -} - -.action-card p { - margin: 0; - font-size: 12px; - color: var(--color-text-contrast-weakest); - line-height: 1.4; -} - -.footer { - background: var(--color-area-bg); - border: 1px solid var(--color-border); - border-radius: 6px; - padding: 12px; - margin-top: 8px; - text-align: center; - font-size: 11px; - color: var(--color-text-contrast-weak); - flex-shrink: 0; -} - -.footer a { - color: var(--color-primary); - text-decoration: none; -} - -.footer a:hover { - text-decoration: underline; -} - -.last-check { - margin-top: 4px; - font-size: 10px; - opacity: 0.7; -} - -.loading-spinner { - display: inline-block; - width: 14px; - height: 14px; - border: 2px solid var(--color-border); - border-top: 2px solid var(--color-primary); - border-radius: 50%; - animation: spin 1s linear infinite; - margin-right: 6px; - vertical-align: middle; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -.icon { - margin-right: 8px; - font-size: 16px; -} - -/* 优化滚动条样式 */ -.welcome-container::-webkit-scrollbar { - width: 8px; -} - -.welcome-container::-webkit-scrollbar-track { - background: var(--color-area-bg); - border-radius: 4px; -} - -.welcome-container::-webkit-scrollbar-thumb { - background: var(--color-border-emphasis); - border-radius: 4px; - opacity: 0.5; -} - -.welcome-container::-webkit-scrollbar-thumb:hover { - background: var(--color-border-emphasis-strong); - opacity: 0.8; -} - -/* 状态切换动画 */ -.status-badge { - transition: all 0.3s ease; -} - -.action-card { - transform: translateY(0); - transition: all 0.2s ease; -} - -.action-card:hover:not(.disabled) { - background: var(--color-area-bg-hover); - border-color: var(--color-primary-highlight); - transform: translateY(-1px); -} - -.action-card.disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.action-card.disabled:hover { - background: var(--color-neutral-bg); - border-color: var(--color-border); - transform: none; -} - -/* action-card样式 */ -.action-card.special { - background: linear-gradient(135deg, var(--color-primary-fill) 0%, var(--color-focus-fill) 100%); - border-color: var(--color-primary); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); -} - -.action-card.special h3 { - color: var(--color-primary-contrast); - font-weight: 700; -} - -.action-card.special p { - color: var(--color-primary-contrast); - opacity: 0.9; -} - -.action-card.special:hover { - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-focus-border) 100%); - border-color: var(--color-focus-border); - transform: translateY(-2px); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2); -} - -.action-card.special .icon { - animation: pulse 2s infinite; -} - -@keyframes pulse { - 0% { transform: scale(1); } - 50% { transform: scale(1.1); } - 100% { transform: scale(1); } -} - -/* 响应式布局 */ -@media (max-width: 600px) { - .welcome-container { - padding: 16px; - } - - .header h1 { - font-size: 20px; - } - - .status-item { - flex-direction: column; - align-items: flex-start; - } - - .status-label { - min-width: auto; - margin-bottom: 4px; - } -} - -.management-buttons ui-button { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -/* 现有文件信息样式 */ -.existing-files-info { - margin-top: 12px; - padding: 10px; - background: var(--color-area-bg); - border: 1px solid var(--color-border); - border-radius: 4px; - font-size: 11px; -} - -.files-summary { - font-weight: 600; - color: var(--color-text-normal); - margin-bottom: 8px; - padding-bottom: 6px; - border-bottom: 1px solid var(--color-border); -} - -.files-list, -.files-overflow { - max-height: 120px; - overflow-y: auto; -} - -.file-item { - padding: 2px 0; - color: var(--color-text-contrast-weak); - font-family: var(--font-mono); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.more-files { - padding: 4px 0; - color: var(--color-text-contrast-weakest); - font-style: italic; - border-top: 1px solid var(--color-border); - margin-top: 4px; - padding-top: 6px; -} - -/* 文件列表滚动条样式 */ -.files-list::-webkit-scrollbar, -.files-overflow::-webkit-scrollbar { - width: 6px; -} - -.files-list::-webkit-scrollbar-track, -.files-overflow::-webkit-scrollbar-track { - background: var(--color-area-bg); - border-radius: 3px; -} - -.files-list::-webkit-scrollbar-thumb, -.files-overflow::-webkit-scrollbar-thumb { - background: var(--color-border); - border-radius: 3px; -} - -.files-list::-webkit-scrollbar-thumb:hover, -.files-overflow::-webkit-scrollbar-thumb:hover { - background: var(--color-border-emphasis); -} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/generator/index.css b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/generator/index.css deleted file mode 100644 index 10c9c91e..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/style/generator/index.css +++ /dev/null @@ -1,378 +0,0 @@ -/* ECS代码生成器样式 */ -.ecs-generator { - height: 100%; - background: var(--color-panel-bg); - color: var(--color-text-normal); - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - overflow: hidden; -} - -.generator-container { - padding: 16px; - height: 100%; - display: flex; - flex-direction: column; - overflow-y: auto; - overflow-x: hidden; - max-height: 100vh; - box-sizing: border-box; -} - -/* 头部样式 */ -.generator-header { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - padding: 16px; - border-radius: 6px; - margin-bottom: 16px; - text-align: center; - flex-shrink: 0; -} - -.generator-header h1 { - margin: 0 0 6px 0; - font-size: 20px; - font-weight: 600; -} - -.generator-header p { - margin: 0; - opacity: 0.9; - font-size: 13px; -} - -/* 表单区域 */ -.generator-form { - background: var(--color-area-bg); - border: 1px solid var(--color-border); - border-radius: 6px; - padding: 16px; - margin-bottom: 16px; - flex-shrink: 0; -} - -.form-title { - font-size: 15px; - font-weight: 600; - margin-bottom: 16px; - color: var(--color-text-normal); -} - -/* 输入字段 */ -.input-group { - margin-bottom: 20px; -} - -.input-label { - display: block; - font-size: 14px; - font-weight: 500; - color: var(--color-text-normal); - margin-bottom: 8px; -} - -.feature-input { - width: 100%; - padding: 8px 12px; - font-size: 14px; - border: 1px solid var(--color-border); - border-radius: 4px; - background: var(--color-panel-bg); - color: var(--color-text-normal); - box-sizing: border-box; - transition: border-color 0.2s ease; -} - -.feature-input:focus { - outline: none; - border-color: #667eea; -} - -.input-hint { - margin-top: 6px; - font-size: 12px; - color: var(--color-text-contrast-weakest); - font-style: italic; -} - -/* 选项组 */ -.options-group { - margin-bottom: 20px; -} - -.option-item { - display: flex; - align-items: center; - padding: 8px 0; - margin-bottom: 8px; -} - -.option-item:last-child { - margin-bottom: 0; -} - -/* 复选框样式 */ -.checkbox-wrapper { - display: flex; - align-items: center; - cursor: pointer; - user-select: none; -} - -.checkbox-wrapper input[type="checkbox"] { - margin-right: 8px; - width: 16px; - height: 16px; - cursor: pointer; -} - -.option-title { - font-size: 14px; - font-weight: 500; - color: var(--color-text-normal); -} - -.option-description { - margin-left: 24px; - margin-top: 4px; - font-size: 12px; - color: var(--color-text-contrast-weakest); -} - -/* 系统配置区域 */ -.system-config { - margin-top: 16px; - padding: 16px; - background: var(--color-panel-bg); - border: 1px solid var(--color-border-soft); - border-radius: 6px; -} - -.config-section { - margin-bottom: 20px; -} - -.config-section:last-child { - margin-bottom: 0; -} - -.config-label { - display: block; - font-size: 14px; - font-weight: 600; - color: var(--color-text-normal); - margin-bottom: 12px; -} - -.filter-option { - margin-bottom: 8px; -} - -/* 系统类型卡片 */ -.system-type-cards { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: 12px; -} - -.system-card { - padding: 16px; - border: 2px solid var(--color-border); - border-radius: 8px; - background: var(--color-area-bg); - cursor: pointer; - transition: all 0.2s ease; - position: relative; -} - -.system-card:hover { - border-color: #667eea; - background: var(--color-panel-bg); -} - -.system-card.active { - border-color: #667eea; - background: var(--color-panel-bg); - box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1); -} - -.system-card.active::after { - content: '✓'; - position: absolute; - top: 8px; - right: 12px; - color: #667eea; - font-weight: bold; - font-size: 16px; -} - -.card-header { - display: flex; - align-items: center; - margin-bottom: 8px; -} - -.card-icon { - font-size: 20px; - margin-right: 8px; -} - -.card-title { - font-size: 15px; - font-weight: 600; - color: var(--color-text-normal); -} - -.card-description { - font-size: 13px; - color: var(--color-text-contrast-weak); - margin-bottom: 6px; - line-height: 1.4; -} - -.card-usage { - font-size: 12px; - color: var(--color-text-contrast-weakest); - font-style: italic; - line-height: 1.3; -} - -/* 系统类型选择 */ -.system-type-group { - margin-left: 24px; - margin-top: 8px; -} - -.system-type-select { - padding: 6px 8px; - border: 1px solid var(--color-border); - border-radius: 4px; - background: var(--color-panel-bg); - color: var(--color-text-normal); - font-size: 13px; - cursor: pointer; -} - -/* 预览区域 */ -.preview-section { - background: var(--color-area-bg); - border: 1px solid var(--color-border); - border-radius: 6px; - padding: 16px; - margin-bottom: 16px; - flex: 1; - min-height: 200px; - display: flex; - flex-direction: column; -} - -.preview-title { - font-size: 15px; - font-weight: 600; - margin-bottom: 12px; - color: var(--color-text-normal); -} - -.preview-content { - flex: 1; - background: var(--color-panel-bg); - border: 1px solid var(--color-border-soft); - border-radius: 4px; - padding: 12px; - font-family: 'Courier New', monospace; - font-size: 12px; - color: var(--color-text-contrast-weak); - overflow-y: auto; - white-space: pre-wrap; -} - -/* 操作按钮区域 */ -.action-section { - background: var(--color-area-bg); - border: 1px solid var(--color-border); - border-radius: 6px; - padding: 16px; - flex-shrink: 0; -} - -.generate-btn { - width: 100%; - padding: 12px 16px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - border: none; - border-radius: 6px; - font-size: 14px; - font-weight: 600; - cursor: pointer; - transition: opacity 0.2s ease; - display: flex; - align-items: center; - justify-content: center; - gap: 8px; -} - -.generate-btn:hover:not(:disabled) { - opacity: 0.9; -} - -.generate-btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.btn-icon { - font-size: 16px; -} - -.btn-icon.spinning { - animation: spin 1s linear infinite; -} - -@keyframes spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } -} - -/* 滚动条样式 */ -.generator-container::-webkit-scrollbar, -.preview-content::-webkit-scrollbar { - width: 8px; -} - -.generator-container::-webkit-scrollbar-track, -.preview-content::-webkit-scrollbar-track { - background: var(--color-area-bg); -} - -.generator-container::-webkit-scrollbar-thumb, -.preview-content::-webkit-scrollbar-thumb { - background: var(--color-border); - border-radius: 4px; -} - -.generator-container::-webkit-scrollbar-thumb:hover, -.preview-content::-webkit-scrollbar-thumb:hover { - background: var(--color-border-emphasis); -} - -/* 响应式设计 */ -@media (max-width: 600px) { - .generator-container { - padding: 12px; - } - - .generator-header h1 { - font-size: 18px; - } - - .feature-input { - font-size: 16px; - } -} - - - - - - \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/BehaviorTreeEditor.html b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/BehaviorTreeEditor.html deleted file mode 100644 index a52ba363..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/BehaviorTreeEditor.html +++ /dev/null @@ -1,903 +0,0 @@ - -
-
-

🌳 行为树可视化编辑器 - - {{ currentFileName }} - -

-
- - - - - -
-
-
-
-
-
- {{ isInstalled ? '✅' : (isInstalling ? '⏳' : '❌') }} -
- {{ installStatusText() }} - 版本 {{ version }} -
-
- - -
-
-
-
- -
- -
-
-
🤖
-

需要安装AI系统

-

行为树编辑器需要安装AI系统才能正常使用

-
- -
-
- 📝 安装完成后编辑器将自动可用 -
-
-
- -
-
-

📦 节点库

- -
- -
- -
-

🌳 根节点

-
-
- {{ node.icon }} - {{ node.name }} -
-
-
- - -
-

🔗 复合节点

-
-
- {{ node.icon }} - {{ node.name }} -
-
-
- - -
-

🎭 装饰器

-
-
- {{ node.icon }} - {{ node.name }} -
-
-
- - -
-

⚡ 动作节点

-
-
- {{ node.icon }} - {{ node.name }} -
-
-
- - -
-

❓ 条件节点

-
-
- {{ node.icon }} - {{ node.name }} - 🎯 -
-
-
- - -
-

🎮 ECS节点

-
-
- {{ node.icon }} - {{ node.name }} -
-
-
-
-
- - -
-
-
- - {{ Math.round(zoomLevel * 100) }}% - - -
-
- - - - -
-
- -
- - - - - - - - - - - - -
-
-
- {{ node.icon }} - {{ node.name }} - -
-
-
{{ node.description }}
- - -
-
-
- {{ node.attachedCondition.icon }} - {{ getConditionDisplayText(node) }} - 📝 -
- - -
-
-
- {{ key }}: - {{ value }} -
-
- - - -
-
- 🎯 拖拽条件到此处 -
-
- - -
-
- {{ prop.name }}: - - {{ formatPropertyValue(prop) }} - -
-
-
- - -
-
-
- - -
-
-
- -
- {{ node.children.length }} -
-
-
- - -
-
-
- - -
- -
-
-

⚙️ 属性面板

-
- -
- -
-

- {{ activeNode.icon }} - 节点类型信息 - -

-
-
- 原始类型: - - {{ getOriginalNodeName(activeNode.type) }} - - {{ getNodeCategory(activeNode.type) }} - - -
-
- 节点ID: - {{ activeNode.type }} -
-
- 原始描述: - {{ getNodeTemplate(activeNode.type).description }} -
-
- ⚠️ 自定义名称: - {{ activeNode.name }} -
-
-
- -
-

基本信息

-
- -
- - ✏️ -
-
-
- - -
-
- -
-

节点属性

-
- -
- -
-
- 📋 - 拖拽Blackboard变量到此处 -
-
- 🔗 - {{ prop.value }} - -
-
- - - - - - - -
-

{{ prop.description }}

-
-
- -
-

节点配置

-
{{ activeNode ? JSON.stringify(activeNode, null, 2) : '{}' }}
-
-
- -
-

请选择一个节点查看属性

-
-
-
-
- - -
-
-

🌲 树结构

-
-
- -
-
- - -
- ⚠️ - {{ validationResult().message }} -
- - - - - -
- - - -
-
-

📋 Blackboard

-
- -
- - - -
- - -
-
- 💡 - 拖拽变量名使用 -
-
- -
-
-
-

{{ groupName }}

-
-
-
-
- {{ variable.name }} - {{ getTypeDisplayName(variable.type) }} - 🎯 -
-
- - -
-
- -
- - {{ getDisplayValue(variable) }} -
- -
- {{ formatConstraints(variable.constraints) }} -
-
-
-
-
- -
-
📋
-

还没有定义任何变量

- -
-
-
-
- - - - \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/BlackboardPanel.html b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/BlackboardPanel.html deleted file mode 100644 index 56f7b973..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/BlackboardPanel.html +++ /dev/null @@ -1,194 +0,0 @@ -
-
-

🎯 黑板变量

-
- - -
-
- -
- -
-
- {{ expandedGroups.has(group) ? '📂' : '📁' }} - {{ group }} - ({{ groupedBlackboardVariables()[group]?.length || 0 }}) -
- -
-
- -
-
- {{ variable.name }} - : - {{ getDisplayValue(variable) }} -
- {{ getTypeDisplayName(variable.type) }} -
- -
- - -
-
-
-
- - -
-
📋
-
还没有黑板变量
-
点击"添加变量"来创建第一个变量
-
-
- - -
-
-
-

{{ editingVariable ? '编辑变量' : '添加变量' }}

- -
- -
-
- - -
- -
- - -
- -
- - - - - -
- - -
-
- - - -
- -
- -
- - -
- -
- - -
- -
-
- -
-
- - -
-
- - -
-
- - -
-
- - -
- - -
-
- -
- - -
-
-
- - -
-
-
-

导入/导出黑板变量

- -
- -
-
-
- - -
- -
-

以下是当前黑板变量的JSON数据,可以复制保存:

- - -
- -
-

粘贴黑板变量的JSON数据进行导入:

- -
- -
- -
-
-
-
-
-
\ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/TreeNodeItem.html b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/TreeNodeItem.html deleted file mode 100644 index 493bef85..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/TreeNodeItem.html +++ /dev/null @@ -1,16 +0,0 @@ -
-
- {{ node.icon }} - {{ node.name }} - ({{ node.type }}) -
- -
\ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/index.html b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/index.html deleted file mode 100644 index f9315b48..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/behavior-tree/index.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
\ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/debug/index.html b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/debug/index.html deleted file mode 100644 index 39cf312e..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/debug/index.html +++ /dev/null @@ -1,382 +0,0 @@ -
- -
-
- - - {{ gameInstances.length }} 个实例 -
- -
- - - -
- -
- {{ lastUpdateTime }} -
-
- - -
-
-

📊 实例状态

-
-
-
- 实例名称: - {{ debugInfo.instanceName }} -
-
- 运行状态: - - {{ debugInfo.isRunning ? '运行中' : '已停止' }} - -
-
- 当前场景: - {{ debugInfo.currentScene }} -
-
- 运行时间: - {{ formatUptime(debugInfo.uptime) }} -
-
- -
- - 请选择一个游戏实例查看详细信息 -
-
- - -
-
-

⚡ 性能监控

-
-
-
-
帧率
-
- {{ debugInfo.performance.fps }} FPS -
-
- 引擎帧时间: {{ debugInfo.performance.engineFrameTime?.toFixed(2) || '0.00' }}ms -
-
-
-
ECS执行时间
-
- {{ debugInfo.performance.frameTime.toFixed(3) }}ms -
-
- 占总帧时间: {{ debugInfo.performance.ecsPercentage?.toFixed(1) || '0.0' }}% -
-
-
-
总内存
-
- {{ formatMemory(debugInfo.memory.usedMemory) }} -
-
- / {{ formatMemory(debugInfo.memory.totalMemory) }} -
-
-
-
GC回收
-
{{ debugInfo.memory.gcCollections }}
-
次数
-
-
-
- - -
-
-

💾 内存分析

-
-
-
- 实体内存: - {{ formatMemory(debugInfo.memory.entityMemory) }} -
-
-
-
-
- 组件内存: - {{ formatMemory(debugInfo.memory.componentMemory) }} -
-
-
-
-
- 系统内存: - {{ formatMemory(debugInfo.memory.systemMemory) }} -
-
-
-
-
- 对象池内存: - {{ formatMemory(debugInfo.memory.pooledMemory) }} -
-
-
-
-
-
- - -
-
-

👥 实体统计

-
-
-
-
{{ debugInfo.entities.total }}
-
总实体数
-
-
-
{{ debugInfo.entities.active }}
-
活跃实体
-
-
-
{{ debugInfo.entities.inactive }}
-
非活跃实体
-
-
-
{{ debugInfo.entities.pendingAdd }}
-
待添加
-
-
- - -
-

Archetype 分布

-
-
-
{{ archetype.signature }}
-
- {{ archetype.count }} 实体 - {{ formatMemory(archetype.memory) }} -
-
-
-
-
- - -
-
-

🧩 组件分析

-
- -
-
- - -
-
-

📖 组件对象池使用指南

-

组件对象池用于复用组件实例,减少频繁创建/销毁组件带来的内存分配开销。当前利用率为0表示还未配置对象池。

- -
-
-
1. 注册组件对象池
-
import { ComponentPoolManager } from '@esengine/ecs-framework';
-
-// 为Transform组件注册对象池
-ComponentPoolManager.getInstance().registerPool(
-    'Transform',
-    () => new Transform(),
-    (comp) => comp.reset(), // 可选的重置函数
-    100 // 池大小
-);
-
- -
-
2. 使用对象池获取组件
-
// 从对象池获取组件实例
-const poolManager = ComponentPoolManager.getInstance();
-const transform = poolManager.acquireComponent('Transform');
-
-// 使用完毕后释放回池中
-poolManager.releaseComponent('Transform', transform);
-
- -
-
3. 查看性能改进
-

正确配置后,利用率栏将显示池的使用情况。利用率越高说明对象池被有效使用,可以减少GC压力。

-
-
- - - -
- - 如果无法直接打开文档链接,链接将自动复制到剪贴板,请在浏览器中粘贴访问 -
-
-
- -
-
- 组件类型: - {{ debugInfo.components.totalTypes }} -
-
- 组件实例: - {{ debugInfo.components.totalInstances }} -
-
- -
-
-
组件类型
-
实例数
-
内存占用
-
对象池
-
- 利用率 - - - -
-
-
-
{{ comp.typeName }}
-
{{ comp.instanceCount }}
-
{{ formatMemory(comp.totalMemory) }}
-
- {{ comp.poolSize || '未配置' }} - - - -
-
-
-
-
- {{ comp.poolUtilization.toFixed(1) }}% - - (无对象池) - -
-
-
- - -
-
- -
-

💡 性能优化建议

-

检测到所有组件都未配置对象池。为频繁创建/销毁的组件配置对象池可以显著提升性能,减少垃圾回收压力。

- -
-
-
-
- - -
-
-

⚙️ 系统性能

-
-
-
-
系统名称
-
实体数
-
执行时间
-
ECS占比
-
优先级
-
状态
-
-
-
{{ system.name }}
-
{{ system.entityCount }}
-
- - {{ system.averageExecutionTime.toFixed(3) }}ms - -
- ({{ system.minExecutionTime.toFixed(3) }}-{{ system.maxExecutionTime.toFixed(3) }}ms) -
-
-
-
-
-
- {{ (system.percentage || 0).toFixed(1) }}% -
-
{{ system.updateOrder }}
-
- - {{ system.enabled ? '启用' : '禁用' }} - -
-
-
-
- - -
-
-

🔌 调试服务器

-
-
-
- -
-

等待游戏实例连接...

-

调试服务器正在 ws://localhost:8080 监听连接

-

请确保游戏中的ECS框架已启用调试模式

-
-
- -
-

连接说明:

-
    -
  • 在ECSManager组件中启用调试模式
  • -
  • 运行游戏,框架会自动连接到调试服务器
  • -
  • 支持多个游戏实例同时连接
  • -
  • 可以在上方选择不同的实例进行调试
  • -
-
-
-
-
- \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/default/index.html b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/default/index.html deleted file mode 100644 index 77446564..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/default/index.html +++ /dev/null @@ -1,5 +0,0 @@ -
-
- -
-
\ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/generator/index.html b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/generator/index.html deleted file mode 100644 index 41abd80c..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/generator/index.html +++ /dev/null @@ -1,111 +0,0 @@ -
-
- -
-

🛠️ ECS 代码生成器

-

输入功能名称,快速生成组件和系统代码

-
- - -
-
📝 基础设置
- - -
- - -
- 💡 使用英文名称,首字母大写,例如:Health、Movement、Combat -
-
- - -
- - -
- -
-
- 创建基础组件类,包含reset()方法和基本结构 -
- -
- -
-
- 创建处理组件的系统类,包含完整的生命周期方法 -
- - -
- -
- -
- -
-
- 系统只处理包含指定组件的实体。如果勾选了"生成组件",会自动过滤{{featureName}}Component -
-
- - -
- -
-
-
- {{ type.icon }} - {{ type.name }} -
-
{{ type.description }}
-
{{ type.usage }}
-
-
-
-
-
-
- - -
-
👀 生成预览
-
{{ previewCode }}
-
- - -
- -
-
-
\ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/vue/counter.html b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/vue/counter.html deleted file mode 100644 index eee34498..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/vue/counter.html +++ /dev/null @@ -1,6 +0,0 @@ -
-

{{counter}}

- + - - -
\ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/vue/welcome.html b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/vue/welcome.html deleted file mode 100644 index ffc612d3..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/static/template/vue/welcome.html +++ /dev/null @@ -1,162 +0,0 @@ -
- -
-

🎮 ECS Framework for Cocos Creator

-

高性能实体组件系统框架

-
- - -
-
📋 操作状态
-
-
- - {{ getStatusIcon(operationStatusType) }} - {{ operationStatusMessage }} -
-
- {{ operationStatusDetails }} -
-
-
- - -
-
📦 安装状态检测
- -
- ECS Framework (@esengine/ecs-framework) -
- 检测中... -
-
- ✅ 已安装 v{{ecsVersion}} - 有新版本:v{{latestVersion}} -
-
- ❌ 未安装 -
-
- -
- 项目package.json -
- ✅ 存在 -
-
- ❌ 不存在 -
-
- -
- Node.js环境 -
- ✅ v{{nodeVersion}} -
-
-
- - -
-
🔧 ECS Framework 管理
- -
- - - {{ updating ? '更新中...' : (hasUpdate ? `更新到 v${latestVersion}` : '已是最新版本') }} - - - - - {{ uninstalling ? '卸载中...' : '卸载 ECS Framework' }} - - - - 🔄 检查更新 - -
-
- - -
-
🚀 快速开始
- -
-

- 📥 - {{ ecsInstalled ? '重新安装 ECS Framework' : '安装 ECS Framework' }} -

-

正在安装中,请稍候...

-

请先确保项目有package.json文件

-

重新安装当前版本的ECS框架

-

安装@esengine/ecs-framework到当前项目

-
- -
-

- 📚 - 查看文档 -

-

打开ECS框架完整使用文档和教程

-
- -
-

- 🛠️ - 创建ECS模板 -

-

请先安装 ECS Framework

-

⚠️ ECS模板已存在,避免覆盖现有代码

-

生成基础的ECS项目结构和启动代码

- -
-
- 检测到 {{existingFiles.length}} 个已存在的文件 -
-
-
- 📄 {{file}} -
-
-
-
- 📄 {{file}} -
-
... 还有 {{existingFiles.length - 8}} 个文件
-
-
-
-
- - -
-
🛠️ 代码生成工具
- -
-

- 🚀 - 代码生成器 -

-

一站式代码生成工具,输入功能名称快速生成组件和系统代码

-
- -
- - - -
\ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/test-settings.js b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/test-settings.js deleted file mode 100644 index 65cacde3..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/test-settings.js +++ /dev/null @@ -1,132 +0,0 @@ -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('🧹 已清理测试文件'); \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/tsconfig.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/tsconfig.json deleted file mode 100644 index f6bfc7cf..00000000 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "extends": "./base.tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./source", - "types": [ - "node", - "@cocos/creator-types/editor", - ] - }, - "exclude": [ - "admin-backend/**/*", - "admin-desktop/**/*" - ] -} \ No newline at end of file diff --git a/thirdparty/admin-backend b/thirdparty/admin-backend new file mode 160000 index 00000000..897eb020 --- /dev/null +++ b/thirdparty/admin-backend @@ -0,0 +1 @@ +Subproject commit 897eb0201173a5170f1de034319b76239590ed20