From c99c10fd37c0f5c6f962937f5deecb79fc0f38a4 Mon Sep 17 00:00:00 2001 From: TRADER_FOER Date: Wed, 10 Jun 2026 01:20:02 +0800 Subject: [PATCH] =?UTF-8?q?NodeScript=20Web=20=E8=AE=BE=E8=AE=A1=E8=A7=84?= =?UTF-8?q?=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 Unity 内节点编辑器拆分为独立的 Web 应用 (Vue 3 + Vue Flow), Unity 端精简为执行引擎,通过 localhost:515 WebSocket 通信。 Co-Authored-By: Claude Opus 4.8 --- .../2026-06-10-ichni-nodescript-web-design.md | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-10-ichni-nodescript-web-design.md diff --git a/docs/superpowers/specs/2026-06-10-ichni-nodescript-web-design.md b/docs/superpowers/specs/2026-06-10-ichni-nodescript-web-design.md new file mode 100644 index 00000000..a5129fb7 --- /dev/null +++ b/docs/superpowers/specs/2026-06-10-ichni-nodescript-web-design.md @@ -0,0 +1,247 @@ +# Ichni NodeScript Web — 独立节点编辑器设计 + +## 概述 + +将 Unity 内的 NodeScript 编辑器**整体迁移到 Web**,重新用 Vue 3 + TypeScript 实现。Unity 端精简为只负责执行和执行结果返回,通过 localhost:515 WebSocket 与 Web 前端通信。 + +引用项目: +- [Blueprint (SVG 节点编辑器)](https://github.com/anan1213095357/Blueprint) — 参考节点分类和交互设计 +- [XNode](https://github.com/WPFDevelopersOrg/XNode) — 参考节点编辑器模式 + +## 架构总览 + +``` +┌──────────────────────────────────┐ WebSocket (JSON-RPC) ┌──────────────────────┐ +│ IchniNodeScriptWeb/ │ ◄────────────────────────► │ Unity Editor │ +│ │ ws://localhost:515 │ │ +│ ┌───────────┐ ┌──────────────┐ │ │ ┌────────────────┐ │ +│ │ Vue Flow │ │ NodeEngine │ │ │ │ WS Server 515 │ │ +│ │ 编辑器画布 │ │ (TypeScript)│ │ │ ├────────────────┤ │ +│ ├───────────┤ ├──────────────┤ │ │ │ ExecutionSvc │ │ +│ │ 属性面板 │ │ 纯节点执行 │ │ │ ├────────────────┤ │ +│ ├───────────┤ ├──────────────┤ │ │ │ GameElementSvc │ │ +│ │ 节点面板 │ │ 图编辑逻辑 │ │ │ └────────────────┘ │ +│ ├───────────┤ ├──────────────┤ │ │ │ +│ │ WS 通信 │ │ 保存/加载 │ │ │ 删掉了所有 UI 代码 │ +│ └───────────┘ └──────────────┘ │ │ │ +└──────────────────────────────────┘ └──────────────────────┘ +``` + +## 技术栈 + +| 层 | 技术 | +|---|---| +| **构建** | Vite | +| **框架** | Vue 3 (Composition API) | +| **语言** | TypeScript(严格模式) | +| **状态管理** | Pinia | +| **节点编辑器** | Vue Flow (@vue-flow/core) | +| **WebSocket** | 原生 WebSocket API + JSON-RPC 2.0 | +| **Unity WS** | System.Net.WebSockets (内置,无需三方库) | + +## 系统架构 + +### 核心节点系统 + +后端执行引擎的类型系统(TypeScript): + +- **Node** — 节点基类,含 inputs/outputs 连接器、status、loop() +- **Input/Output** — 连接器:name, dataType, color, value +- **LoopResult** — loop() 返回值:Complete / Hang / Repeat / Wait +- **NodeEngine** — 调度器:triggerTable + runtimeTable,参考现有 NodeManager.RunCycle +- **UnityBridgeNode** — 特殊基类,loop() 时将 inputs 打包发送至 Unity,等待返回后填 outputs + +### 两种节点类型 + +| | 纯节点 (PureNode) | Unity 桥接节点 | +|---|---|---| +| 执行位置 | Web 浏览器内 | Unity 进程 | +| 执行方式 | 同步 JS 运算 | WS 请求 → 等待 → 回调 | +| 等待策略 | 立即 Complete | Hang 直到收到回复 | +| 例子 | NodeMath, NodeBranch, NodeForLoop, NodeConst, NodeSplit | NodeGameElement, NodeSetTransform | + +### Vue Flow 集成 + +| Vue Flow 概念 | 对应实现 | +|---|---| +| Node | 自定义 Vue Flow Node,渲染标题栏 + Handle (输入/输出插槽) + 参数 UI | +| Edge | 连线,onConnect 回调中做类型兼容检查 + 环检测 | +| Handle | Input/Output 端口,颜色根据 dataType 变化 | +| Minimap | Vue Flow 内置小地图 | +| Controls | 缩放/平移控件 | + +## 组件设计 + +| 组件 | 职责 | +|---|---| +| **NodeEditor.vue** | 主画布 (Vue Flow)、工具栏、快捷键 | +| **NodePalette.vue** | 节点创建面板(搜索 + 分类,参考 Blueprint 弹窗) | +| **PropertyPanel.vue** | 侧边属性面板(选中节点后显示参数编辑) | +| **StatusBar.vue** | 底部栏:WS 连接状态、执行状态、节点数 | +| **WebSocketService.ts** | WS 连接管理 + JSON-RPC 请求/响应匹配 | +| **editorStore.ts** | Pinia store:图状态、选中、撤销历史 | + +## 节点分类 + +### 纯 Web 节点(~20 个,TypeScript 实现) + +| 分类 | 节点 | +|---|---| +| 入口 | NodeStart | +| 数学 | NodeMath (+-*/) | +| 插值 | NodeLerp | +| 比较 | NodeCompare (== != > < >= <=) | +| 向量拆分 | NodeSplit | +| 向量合并 | NodeCombine | +| 常量 | NodeConst (6 种类型统一) | +| 分支 | NodeBranch | +| 循环 | NodeForLoop, NodeForEach | +| 数据 | NodeSelect (二选一), NodeSet (赋值) | +| 变量 | NodeVariable, NodeList, NodeListAdd, NodeListGet | +| 调试 | NodeLog | + +### Unity 桥接节点(~5 个) + +| 节点 | 功能 | WS 方法 | +|---|---|---| +| NodeGameElement | 复制/粘贴 GameElement | execute_nodes | +| NodeGetTransform | 读取位置/旋转/缩放 | query_gameelement | +| NodeSetTransform | 写入位置/旋转/缩放 | modify_gameelement | +| NodeChildByIndex | 按索引取子元素 | query_gameelement | +| NodeChildCount | 取子元素数量 | query_gameelement | + +## WebSocket 协议 + +### 传输 +- 协议:纯 WebSocket (`ws://localhost:515`) +- 格式:JSON 文本帧 +- 风格:JSON-RPC 2.0 + +### 消息格式 + +**请求 (Web → Unity):** +```json +{ + "jsonrpc": "2.0", + "id": "req_001", + "method": "execute_nodes", + "params": { ... } +} +``` + +**响应 (Unity → Web):** +```json +{ + "jsonrpc": "2.0", + "id": "req_001", + "result": { ... } +} +``` + +### API 方法 + +| 方法 | 方向 | 说明 | +|---|---|---| +| `execute_nodes` | Web→Unity | 批量执行 Unity 节点,传入 inputs+params,返回 outputs | +| `query_gameelement` | Web→Unity | 查询 GameElement 属性(transform/children 等) | +| `modify_gameelement` | Web→Unity | 修改 GameElement 属性 | +| `get_type_registry` | Web→Unity | 获取 Unity 端注册的桥接节点类型列表 | + +### 连接生命周期 +1. Web 打开页面 → 自动 ws://localhost:515 连接 +2. 连接成功 → 发送 get_type_registry 获取 Unity 节点类型 +3. 连接断开 → Web 显示 "Unity 未连接",禁用运行按钮,可继续编辑 +4. 自动重连(每 3 秒尝试) +5. Unity 退出 → Web 仍可编辑 + +## Unity 端架构 + +### 删除的代码 +- NodeObject.cs (MonoBehaviour 节点 UI) +- ConnectorSlot.cs (连接点交互) +- NodeUIBuilder.cs (UI 控件构建) +- NodeManager 中的 UI 交互:拖线/右键菜单/选中/复制粘贴 +- 文件 Save / Load +- 节点列表管理 (allNodes) + +### 保留的代码 +- NodeCore.cs(核心类型:NodeBase, Input/Output, InputAny/OutputAny, 生命周期) +- NodeCompoment.cs 中部分节点:NodeGameElement, NodeGetTransform, NodeSetTransform, NodeChildByIndex, NodeChildCount +- NodeManager.RunCycle() + RunGraph() — 执行调度 +- ComputeLValues — 内部拓扑排序 +- 类型检查 / 环检测 + +### 新增的代码 +- NodeWebSocketServer.cs — HttpListener + WebSocket 服务端 (port 515) +- JsonRpcHandler.cs — 消息解包和分发 +- NodeExecutionService.cs — 接收图 → 反序列化 → 构建 NodeBase → 执行 → 返回结果 +- GameElementQueryService.cs — 查询/修改 GameElement 属性 + +### 执行流程(Web 发图 → Unity 执行) +1. Web 用户点击"运行" +2. Web 收集所有 Unity 节点的 inputs + params +3. WS 发送 `execute_nodes` { nodes: [...] } +4. Unity 接收 → 反序列化 GraphData → 重建 NodeBase → 设置连接 → RunGraph() +5. 收集每个节点的 outputs → WS 返回结果 +6. Web 填入对应节点 outputs → 继续执行剩余纯节点 + +## 序列化 + +JSON 文件格式,保存在 `IchniNodeScriptWeb/saves/` 下: + +```json +{ + "version": "1.0", + "entryNode": "start", + "nodes": [ + { "id": "n_1", "type": "NodeStart", "position": { "x": -400, "y": 0 }, "params": {} } + ], + "edges": [ + { "from": "n_1", "fromPort": "exec", "to": "n_2", "toPort": "exec" } + ] +} +``` + +每个节点 params 包含节点特定参数(类型选择、数值等),端口连接信息在 edges 数组中。 + +## 项目文件结构 + +``` +IchniNodeScriptWeb/ +├── package.json +├── vite.config.ts +├── tsconfig.json +├── index.html +├── src/ +│ ├── main.ts +│ ├── App.vue +│ ├── components/ +│ │ ├── NodeEditor.vue # Vue Flow 主画布 +│ │ ├── NodePalette.vue # 节点创建面板 +│ │ ├── PropertyPanel.vue # 属性编辑面板 +│ │ └── StatusBar.vue # 底部状态栏 +│ ├── nodes/ +│ │ ├── engine/ +│ │ │ ├── types.ts # 核心类型定义 +│ │ │ ├── NodeEngine.ts # 执行引擎 +│ │ │ └── UnityBridgeNode.ts # 桥接节点基类 +│ │ ├── math/MathNode.ts +│ │ ├── control/BranchNode.ts, ForLoopNode.ts, ForEachNode.ts +│ │ ├── data/ConstNode.ts, VariableNode.ts, SelectNode.ts, SetNode.ts +│ │ ├── vector/SplitNode.ts, CombineNode.ts +│ │ ├── logic/CompareNode.ts, LerpNode.ts +│ │ ├── debug/LogNode.ts +│ │ └── unity/ +│ │ ├── GameElementNode.ts +│ │ ├── GetTransformNode.ts +│ │ ├── SetTransformNode.ts +│ │ ├── ChildByIndexNode.ts +│ │ └── ChildCountNode.ts +│ ├── services/ +│ │ ├── WebSocketService.ts # WS 连接 + JSON-RPC +│ │ └── GraphSerializer.ts # 保存/加载 JSON +│ └── stores/ +│ └── editorStore.ts # Pinia 状态 +├── saves/ # 图的 JSON 文件 +└── public/ +```