Files
ichni_Creator_Studio/Assets/Scripts/Editor Tools/NodeScript/总览.md

330 lines
9.9 KiB
Markdown
Raw Normal View History

# NodeScript 系统总览
## 文件结构
```
Editor Tools/NodeScript/
├── NodeCore.cs # 核心类型:生命周期、连接器、动态类型
├── NodeManager.cs # 调度中心 + UI 交互
├── NodeObject.cs # MonoBehaviour节点 UI、插槽、选中、状态显示
├── ConnectorSlot.cs # 连接点交互 + 外观刷新
├── NodeUIBuilder.cs # UI 构建Dropdown / FloatField / Toggle / TypeDropdown
├── NodeCompoments/
│ ├── NodeCompoment.cs # 操作 / 控制流节点
│ └── NodeUtility.cs # 工具 / 常量 / 变量节点
├── Node重构.txt # 重构需求文档
├── Node重构大纲.md # 详细重构设计
└── 总览.md # 本文件
```
---
## 一、执行模型:拉取式 + 生命周期
### 1.1 周期调度
```
RunGraph():
1. ComputeLValues() — BFS 算所有节点到 Start 的最短距离 L
2. triggerTable = {全部节点}
3. while triggerTable runtimeTable 非空:
RunCycle()
RunCycle():
1. triggerTable 并入 runtimeTable清空 triggerTable
2. foreach node in runtimeTable:
result = node.Loop()
收集 triggers → triggerTable
if TriggerDownstream → 自动发现下游节点 → triggerTable
if RemoveFromRuntime → 标记移除
3. runtimeTable -= 已完成的节点
```
### 1.2 节点状态
| 状态 | 含义 |
|---|---|
| `Ready` | 等待本周期执行 |
| `Hang` | 前置节点未完成,挂起到下一周期 |
| `Complete` | 已完成,移出 runtimeTable |
### 1.3 LoopResult 返回类型
| 工厂方法 | RemoveFromRuntime | TriggerDownstream | 用途 |
|---|---|---|---|
| `Complete()` | true | true | 节点完成,触发下游 |
| `Hang(preceding)` | false | false | 等待前置节点 |
| `Repeat()` | false | true | 保持活跃+触发下游(循环迭代) |
| `Wait()` | false | false | 空转一周期(循环等下游消费) |
### 1.4 L 值
- BFS 从 `NodeStart`/`NodeEntry` 开始计算L 值显示在节点标题栏 `(L:N)`
- 仅显示,不约束连线
- L=-1 表示孤立节点(无路径可达)
### 1.5 防自循环
连线 A→B 时,从 B 出发沿现有边 DFS看能否回到 A。能回到则拒绝连线。
---
## 二、类型系统
### 2.1 连接器体系
```
IInput IOutput
├── Input<T> (泛型) ├── Output<T> (泛型)
└── InputAny (动态) └── OutputAny (动态)
```
### 2.2 InputAny / OutputAny
| 特性 | 说明 |
|---|---|
| 初始状态 | `DataType = null`,连接点显示**灰色** |
| 连线锁定 | 连线后自动 `LockType(T)`,连接点变为类型对应颜色 |
| 类型传播 | 锁定后 `OnTypePropagated(T)` → 同节点其他未锁定端口跟随 |
| `IsFixedType` | true 时阻止传播覆盖,如 `NodeLerp.t` 固定为 float |
| 可改类型 | `LockType` 允许覆盖(防止 `NodeConst` 切换类型后 DataType 不更新) |
### 2.3 Input<T> 拉取模型
- `Value` 优先读 `Output<T>._value`,回退读 `OutputAny`
- `ConnectAny(IOutput)` 支持 `OutputAny → Input<T>` 桥接(如 `NodeConst → NodeMath.a`
- `GetSourceNode()` 兼容两种源
### 2.4 Output<T> 反向写入
- `_writeBack` 回调:`NodeSet` 通过 targetRef 修改 `NodeVariable` 的内部值
### 2.5 类型兼容表
| from | to | 兼容 |
|---|---|---|
| null | * | ✓(未锁定端口) |
| T | T | ✓ |
| int | float | ✓ |
| float | int | ✓ |
| 其他 | 其他 | ✗ |
### 2.6 类型颜色
| 类型 | 颜色 |
|---|---|
| float | 蓝 |
| int | 青 |
| bool | 橙 |
| string | 紫 |
| Vector2 | 黄 |
| Vector3 | 绿 |
| Color | 粉 |
| GameElement | 深紫 |
| Signal | 白 |
| List\<GameElement\> | 暗紫 |
| null未锁定 | 灰 |
---
## 三、节点目录
### 3.1 入口节点
| 节点 | 输入 | 输出 | 说明 |
|---|---|---|---|
| `NodeStart` | — | `exec(Signal)`, `element(GameElement)` | 图入口,绑定当前选中元素 |
| `NodeEntry` | — | `exec(Signal)` | 纯信号入口 |
### 3.2 运算节点
| 节点 | 输入 | 输出 | 说明 |
|---|---|---|---|
| `NodeMath` | `a(Any)`, `b(Any, fixed)` | `result(Any)` | +-*/ 四则,支持 float/int/V2/V3/Color/string |
| `NodeLerp` | `a(Any)`, `b(Any)`, `t(float,fixed)` | `result(Any)` | 线性插值t 未连默认 0.5 |
| `NodeCompare` | `a(Any)`, `b(Any)` | `result(bool)` | == != > < >= <= |
### 3.3 向量节点
| 节点 | 输入 | 输出 | 说明 |
|---|---|---|---|
| `NodeSplit` | `input(Any)` | `x/y/z/w(float)` | V2 输出 xyV3 输出 xyzColor 输出 rgba |
| `NodeCombine` | `x/y/z/w(float)` | `output(Any)` | UI 选类型后合并为 V2/V3/Color |
| `NodeGetTransform` | `element(GE)` | `pos/rot/scl(V3)` | 读取元素变换 |
| `NodeSetTransform` | `exec(Sig)`, `element(GE)`, `Pos/Rot/Scl(V3)` | — | 设置元素变换 |
### 3.4 数据节点
| 节点 | 输入 | 输出 | 说明 |
|---|---|---|---|
| `NodeConst` | — | `value(Any)` | UI 选类型+填值6 种类型统一 |
| `NodeVariable<T>` | `signal(Sig)`, `set(T)` | `get(T)` | 变量存储;`signal` 连着时等触发才更新 |
| `NodeSet` | `targetRef(Any)`, `value(Any)` | — | 反向写入 targetRef 指向的变量 |
| `NodeSelect` | `cond(Any)`, `true(Any)`, `false(Any)` | `result(Any)` | 二选一cond 未连用 UI Toggle |
### 3.5 控制流节点
| 节点 | 输入 | 输出 | 说明 |
|---|---|---|---|
| `NodeBranch` | `exec(Sig)`, `cond(Any)` | `true/false(Sig)` | cond>0 走 true否则 false |
| `NodeForLoop` | `exec(Sig)`, `count(Any)` | `loopBody(Sig)`, `index(int)`, `completed(Sig)` | 多周期交替输出→Wait→输出→Wait... |
### 3.6 集合节点
| 节点 | 输入 | 输出 | 说明 |
|---|---|---|---|
| `NodeList<T>` | — | `output(List<T>)` | 空列表 |
| `NodeListAdd<T>` | `list(List<T>)`, `item(T)` | `output(List<T>)` | 追加元素 |
| `NodeListGet<T>` | `list(List<T>)`, idx(UI) | `element(T)` | 索引取值 |
| `NodeForEach` | `exec(Sig)`, `list(Any)` | `loopBody(Sig)`, `current(Any)`, `index(int)`, `completed(Sig)` | **非泛型**,连 `List<X>` 自动锁定 current 为 X |
### 3.7 GameElement 操作
| 节点 | 输入 | 输出 | 说明 |
|---|---|---|---|
| `NodeGameElement` | `exec(Sig)`, `Root(GE)`, `Source(GE)` | `newElement(GE)`, `completed(Sig)` | 复制粘贴元素 |
| `NodeChildByIndex` | `parent(GE)`, idx(UI) | `child(GE)` | 子元素按索引 |
| `NodeChildCount` | `parent(GE)` | `count(int)` | 子元素个数 |
| `NodeClone` | `exec(Sig)`, `source(GE)` | `clone(GE)` | Instantiate 克隆 |
### 3.8 调试节点
| 节点 | 输入 | 输出 | 说明 |
|---|---|---|---|
| `NodeDebugLog` | `exec(Sig)`, `value(Any)` | — | 带 Signal 等待的日志 |
| `NodeLog` | `value(Any)` | — | 直接打印日志 |
---
## 四、所有节点统一规范
每个节点的 `Loop()` 遵循:
```csharp
public override LoopResult Loop()
{
// 1. 无输入直接 Complete
// 2. EnsureInputsReady() — 所有已连输入的上游必须 Complete
// 3. 取值 → 计算 → 设输出
// 4. 返回 Complete / Repeat / Wait
}
```
`EnsureInputsReady()``NodeBase` 上定义,自动检查 `GetPrecedingNodes()` 中所有上游的 Status。
---
## 五、UI 特性
### 5.1 节点外观
- 半透明背景 + 标题栏 `Name (L:N)`
- `statusImage`Ready 灰 / Hang 橙黄 / Complete 绿
- 选中节点蓝色高亮
### 5.2 连接线
- `UILineRenderer` 贝塞尔曲线
- 手动距离检测悬停(不依赖 Unity 射线)
- 悬停加粗 +3px选中加粗 ×2
- RectTransform 自动缩放到包围盒
- 拖线时 dragLine 不参与射线
### 5.3 交互
| 操作 | 功能 |
|---|---|
| 左键空白 | 取消所有选中 |
| 左键节点 | 选中节点Shift 多选) |
| 左键拖节点 | 移动节点 |
| 左键拖输出点→输入点 | 连线 |
| 左键线 | 选中线Shift 多选) |
| 中键拖面板 | 平移整个画布 |
| Ctrl+右键 | 右键菜单创建节点 |
| Delete | 删除选中 |
### 5.4 快捷键
| 快捷键 | 功能 |
|---|---|
| `F3` | 新建/销毁 NodeScript 编辑器 |
| `Enter` | 完整运行图 |
| `Shift+Enter` | 单步调试(首次初始化) |
| `Esc` | 退出调试模式 |
| `Ctrl+Enter` | 运行图(保留) |
| `F5` | 拓扑预览(打印分层执行计划) |
| `F1` | 保存 |
| `F2` | 加载 |
| `Ctrl+C/V` | 复制/粘贴节点 |
| `Delete` | 删除选中 |
### 5.5 控制台命令
| 命令 | 说明 |
|---|---|
| `newNode` | 新建/销毁 NodeScript 编辑器 |
| `saveNode` | 保存到默认 `graph.json` |
| `saveNode name` | 另存为 `{name}.json` |
| `loadNode name` | 从 `{name}.json` 加载 |
---
## 六、调试功能
### 6.1 单步调试 (Shift+Enter)
每步打印详细日志:
```
=== Step 3 ===
✓ NodeStart (L:0) → Complete
⏳ NodeMath (L:1) → Hang
▶ NodeBranch (L:2) → Ready
triggers pending: 2, still running: 3
```
`statusImage` 同步变色。
### 6.2 拓扑预览 (F5)
```
═══ Topological Order (BFS layers) ═══
Layer 0 (2 nodes, 3 downstream wires): Start(L:0), Entry(L:0)
Layer 1 (1 nodes, 2 downstream wires): NodeMath(L:1)
...
Unreachable (1 nodes): OrphanConst
Total: 6 nodes, 5 wires, 3 layers
```
---
## 七、NodeManager 关键 API
| 方法 | 说明 |
|---|---|
| `Init(GameElement)` | 绑定元素 + 创建 Start 节点 |
| `RunGraph()` | 完整执行(生命周期循环) |
| `ComputeLValues()` | 重算所有节点的 L 值 + 刷新标题 |
| `SaveToFile(string?)` | 保存图null 用默认路径 |
| `LoadFromFile(string?)` | 加载图 |
| `GetSavePath(string)` | 获取完整保存路径 |
---
## 八、文件存储
| 项目 | 路径 |
|---|---|
| 保存目录 | `Assets/StreamingAssets/NodeScript/` |
| 默认文件 | `graph.json` |
| 自定义文件 | `{name}.json` |
---
## 九、待完成 (Phase 4+)
- [ ] Rect 容器(循环 / 子函数体内嵌区域)
- [ ] 子控制器(循环体内节点独立调度)
- [ ] `NodeSubFunctionDef` / `NodeSubFunctionCall`
- [ ] Manager 扫描注册子函数定义