Files
ichni_Creator_Studio/docs/superpowers/specs/2026-06-10-ichni-nodescript-web-design.md
TRADER_FOER c99c10fd37 NodeScript Web 设计规格
将 Unity 内节点编辑器拆分为独立的 Web 应用 (Vue 3 + Vue Flow),
Unity 端精简为执行引擎,通过 localhost:515 WebSocket 通信。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 01:20:02 +08:00

248 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/
```