Babylon.js Flow Graph Editor Phase 2:用户工作流视角的编辑器能力盘点与实现解析
Babylon.js Flow Graph Editor Phase 2:用户工作流视角的编辑器能力盘点与实现解析
导读
本文基于 Babylon.js 仓库中 Flow Graph Editor 的第二阶段问题与功能清单(ISSUES-Phase-2.md)展开。该文档从“用户真正坐下来搭建流程图的日常体验”出发,梳理了编辑器在可用性(默认场景、本地文件拖拽、glTF 往返)、调试(多执行上下文、变量面板)、交互(右键菜单、端口提示、Toast 通知)等方面的缺口。读完全文,你将了解 Flow Graph Editor 当前已实现的 12 项核心能力、各自对应的源码位置与实现机制,以及仍留待后续处理的 3 个待办项,从而快速判断编辑器是否满足你的工作流,并学会利用默认场景、KHR_interactivity 往返、复合模板、多上下文调试等能力搭建与导出交互式流程图。
说明:Phase 1 的问题跟踪在 ISSUES-Phase-1.md;所有已实现特性的用户操作说明统一维护在 MANUAL.md。
1. Phase 2 的背景与跟踪约定
1.1 与 Phase 1 的分工
Phase 1 清单聚焦编辑器的“基础设施级”缺陷(撤销/重做失效、孤立块序列化丢失、无复制粘贴、无断点、无校验等),而 Phase 2 从用户工作流视角审视:当开发者真正开始为一个场景搭建流程图时,编辑器还缺什么。二者共同构成了编辑器的迭代路线。
1.2 清单条目的维护约定
文档明确了两条协作规则(见 ISSUES-Phase-2.md):
- 每个问题完成后,必须同步更新 MANUAL.md 以反映新功能或修复;
- 若问题是 Bug,需在手册中记录预期行为与复现步骤;
- 条目以
[x](已实现)或[ ](未实现)标记状态。
按此约定,Phase 2 的 15 个条目中 12 项已标记实现,3 项仍未完成。
2. Critical:影响生产使用的三个基础能力
2.1 内置默认场景,打开即可开始接线
问题背景:编辑器打开后若未加载 Playground snippet,预览区一片空白,只能提示 “Load a Playground snippet to preview the scene”。用户必须先去外部创建场景才能开始搭建逻辑。
实现结果:编辑器现在会自动创建一个最小默认场景,包含:
- 一个 ArcRotateCamera(轨道相机,支持鼠标控制);
- 一个 HemisphericLight(半球光);
- 一块 8×8 的地面平面;
- 三个基础图元网格:box(蓝色)、sphere(红色)、cylinder(绿色)。
该场景在加载 Playground snippet 或拖入场景文件后被替换(详见 MANUAL.md)。这意味着用户无需离开编辑器即可立刻体验流程图逻辑,场景中的网格、灯光、相机也都会成为块配置字段里可引用的对象。
2.2 本地文件拖拽加载(.glb / .gltf / .babylon)
问题背景:此前唯一获取场景的方式是粘贴 Playground snippet ID,所有工作流都被迫依赖 snippet 服务器。
实现结果:现在可以把 .glb、.gltf、.babylon 文件直接拖拽到 Scene Preview 面板作为测试场景。对于引用外部 .bin 或纹理文件的 .gltf,需将所有文件一起拖入。加载后的场景会替换当前场景(默认场景或 snippet),并通过 SceneLoader 将网格、灯光、相机等对象抽取进 SceneContext,供流程图中的块引用。
编辑器状态中通过 sceneSource: "default" | "snippet" | "file" | "host" 跟踪当前预览场景的来源(见 globalState.ts),SceneContext 则承载场景对象到流程图块之间的绑定关系。
2.3 glTF / KHR_interactivity 往返:生产级资产管线
问题背景:编辑器无法导入/导出内嵌流程图(KHR_interactivity 扩展)的 glTF 文件。这是流程图的主要生产用例——作者将交互行为随 3D 资产一起发布。此前编辑器只支持自己的 JSON 格式与 Babylon snippet 服务器。
实现结果:现在支持完整的“作者在编辑器 → 导出 glTF → 在任意 Babylon.js 应用中加载”标准资产管线:
- Import glTF:拖入含
KHR_interactivity扩展的.glb/.gltf后,流程图自动载入编辑器;场景同步加载为预览场景,以便块对网格/相机/灯光的引用得以解析。若文件含本编辑器导出的BABYLON_flow_graph自定义扩展,流程图同样会被恢复(见 MANUAL.md)。 - Export glTF:若预览场景已加载且 serializers 包可用,场景与流程图一起导出;否则生成仅含流程图数据的极简
.glb。导出文件使用BABYLON_flow_graph自定义扩展,可重新导入本编辑器(见 MANUAL.md)。 - New behavior:可从默认场景/Playground 场景/无图的
.glb出发,选择 Select to reveal 或 Two-step procedure 两种模板,一键“Create behavior”导出并重载预览为 GLB。Reset 会恢复源KHR_node_visibility、KHR_node_selectability、KHR_node_hoverability值(含指针操作实际改动过的隐式默认值)。
底层解析与导出分别由 glTF 加载器扩展目录下的 interactivityGraphParser.ts 与 interactivityGraphExporter.ts 完成,KHR_interactivity 扩展本身由 KHR_interactivity.ts 注册。
3. High:显著影响体验的五项能力
3.1 “How to Use” 嵌入代码对话框
问题背景:用户保存流程图后不知道该写什么代码去加载运行它。
实现结果:工具栏新增 </> 按钮,打开 How to Use 对话框,提供带“复制到剪贴板”的两种集成方式代码样例(见 MANUAL.md):
- 从 snippet 加载:调用
ParseFromSnippetAsync("<snippetId>", scene),若流程图已保存到 snippet 服务器,对话框会预填当前 snippet ID; - 从 JSON 文件加载:读取保存的
.json文件后调用FlowGraph.ParseFlowGraphAsync(data, { scene })。
核心侧的 flowGraphParser.ts 同时提供了从 snippet 恢复整个协调器的入口 ParseFlowGraphCoordinatorFromSnippetAsync(snippetId, options),支持版本化 ID(如 ABC123#4),内部通过 WebRequest.FetchAsync 拉取 snippet 服务器数据(见 flowGraphParser.ts)。
代码样例包含完整的最小样板(import 语句、async 包装),可直接粘贴进用户项目。对话框组件实现位于 howToUseDialogComponent.tsx,其请求由 GlobalState.onHowToUseRequested 可观察对象驱动(见 globalState.ts)。
3.2 变量面板:全局一览、改名传播、运行时值检查
问题背景:此前只能靠 GetVariable/SetVariable 块“祈祷变量名一致”,拼写错误会静默创建出一个孤立变量。
实现结果:属性面板新增 Variables 区块(未选中任何块时显示),列出图中所有被引用变量,每行显示:
- 变量名;
- 引用计数(如
2G / 1S表示 2 个 GetVariable 块与 1 个 SetVariable 块引用它)。
操作能力(详见 MANUAL.md):
| 操作 | 行为 |
|---|---|
| + Add | 以自动生成的名字创建变量,注册到当前选中的执行上下文,默认值 undefined |
| 重命名(双击) | 传播到所有引用旧名的 Get/SetVariable 块,并同步所有执行上下文中的变量 |
| 删除(✕) | 移除所有引用该变量的 Get/SetVariable 块,并从所有执行上下文中删除 |
面板组件实现位于 variablesPanelComponent.tsx,变量相关工具逻辑在 variableUtils.ts。变量与上下文的联动序列化由 GlobalState 中保存的上下文快照机制保障(见 globalState.ts)。
3.3 右键上下文菜单:画布 / 节点 / 连线 / 帧
问题背景:此前画布、节点、连线上都没有右键菜单(共享 UI 库虽有 ContextMenu 原语),常用操作必须背快捷键。
实现结果:四处右键菜单全部落地(详见 MANUAL.md):
- 画布:Add Block...(Space,打开块搜索)、Paste(Ctrl+V)、Create Sticky Note(Ctrl+M)、Select All(Ctrl+A)、Zoom to Fit、Reorganize(自动布局);
- 节点:Delete(Del)、Duplicate(Ctrl+C/V)、Add/Remove Breakpoint(F9)、Create Smart Group(Ctrl+G)、Disconnect All Ports;
- 连线:Delete Connection(Del);
- 帧:Delete Frame(保留内部块)、Collapse/Expand。
3.4 调色板补齐 5 个矩阵转换块
问题背景:FlowGraphBlockNames 中已有、nodeListComponent.tsx 中也写好了工具提示描述的 5 个矩阵类数据转换块,因未加入 allBlockNames.ts 而永远不出现在调色板中。
实现结果:这 5 个块现已加入 Data_Conversion 分类(见 allBlockNames.ts):
| 块 | 功能 |
|---|---|
TransformCoordinatesSystem |
在 local/world/view/projection 坐标系之间转换坐标 |
CombineMatrix2D |
由分量构建 2D 矩阵 |
CombineMatrix3D |
由分量构建 3D 矩阵 |
ExtractMatrix2D |
从 2D 矩阵提取分量 |
ExtractMatrix3D |
从 3D 矩阵提取分量 |
3.5 复合块模板(预接线块组)
问题背景:KHR_interactivity glTF 扩展把单个交互节点映射为一组必须连同内部连接一起实例化的 Babylon.js 流程图块。例如 pointer/interpolate 需要 4 个块(ValueInterpolation + JsonPointerParser + PlayAnimation + BezierCurveEasing),pointer/get、pointer/set 各需 2 个块,animation/start、animation/stop 各需 3 个块。此前这些多块模式只存在于 glTF 加载器的 declarationMapper.ts 中作为导入时接线逻辑,调色板中无法一键拖出。没有这些复合块入口,用户必须手工复刻规范视为“单节点”的复杂接线,glTF 往返就无法做到“导入的样子导出”。
实现结果:编辑器新增 Templates 分类(见 MANUAL.md),拖入即自动创建全部成员块并完成内部接线:
- Common Patterns:Click → Log、Timer Loop、Toggle Boolean、Branch on Condition、Sequence Chain、Delayed Action、Pointer Interaction;
- Animation Patterns:Lerp Animation(tick 驱动的插值);
- Communication:Custom Event Bridge(Send + Receive 配对);
- glTF Interactivity:Get → Set Property、Get → Set Variable。
模板定义以 ICompositeTemplate 接口描述(blocks<a href="https://link.gitcode.com/i/1abbe34d4a2485da810b6db07bc78972" target="_blank">] + connections[] + 每个块的 config 覆盖与偏移),并可直接用 GetTemplatesByCategory() 按分类组织到调色板中(见 [compositeTemplates.ts)。该结构与 declarationMapper.ts 中 blocks[] + interBlockConnectors[] + typeToTypeMapping 的多块路由模型同构,为未来把 KHR 复合模式做成可拖放模板提供了直接范式。
3.6 多执行上下文支持
问题背景:一个 FlowGraph 可拥有多个 FlowGraphContext,每个上下文是独立执行实例(独立变量状态、连接值、资产绑定),这是同一份逻辑驱动不同实体(如 10 个网格各自带状态的“点击即动”)的核心机制。此前编辑器在 GlobalState 中硬编码 selectedContextIndex = 0,完全不暴露上下文管理。
实现结果:工具栏新增 Ctx 下拉框(详见 MANUAL.md):
| 控件 | 说明 |
|---|---|
| Ctx 下拉框 | 切换当前选中的上下文——调试高亮、断点、Variables 面板均跟随所选上下文 |
| + | 创建新执行上下文(默认名 Context N,独立变量状态,assetsContext 复用当前场景) |
| − | 删除所选上下文(仅剩一个时禁用) |
| ✎ | 重命名所选上下文 |
上下文切换时调试观察器会重新挂载到新上下文:selectedContextIndex 设置器在 debug 模式下重新调用 _attachContextExecutionObserver()(见 globalState.ts),断点谓词 context.breakpointPredicate 也按选中上下文安装(见 globalState.ts)。
上下文的创建/删除/重命名由 createNewContext()、removeContextAt()、renameContext() 实现(见 globalState.ts)。序列化往返由核心层原生支持:flowGraph.serialize() 包含所有上下文,ParseFlowGraphContext 负责恢复;编辑器在保存前还会为每个图快照全部上下文的变量与连接值,避免图停止时丢失运行时状态(见 globalState.ts)。
文档中“为每个上下文配置不同 assetsContext(不同网格集/动画组)”仍标记为 deferred ——当前所有上下文共享场景资产。
4. Medium:三项体验打磨
4.1 端口级悬浮提示
每个端口悬浮时显示:端口名、数据类型(如 Number、Vector3、Boolean)、方向(Input/Output)。信号端口显示 “Signal Input” / “Signal Output”。描述信息可来自块的 _registerInput/_registerOutput 元数据(见 MANUAL.md)。
4.2 工具栏撤销/重做按钮
虽然 Ctrl+Z / Ctrl+Shift+Z 一直可用,但工具栏现在也有 Undo(↩)/ Redo(↪) 图标按钮,并在历史栈为空/到达末尾时自动置灰(见 MANUAL.md)。其底层依赖 Phase 1 中修复的 applyUpdate 反序列化逻辑与 onResetRequiredObservable。
4.3 Toast 通知系统
编辑器操作现在有即时视觉反馈:右下角出现短暂通知,4 秒自动消失,可点击 ✕ 提前关闭(见 MANUAL.md)。触发事件与文案示例:
| 事件 | 严重级 | 消息示例 |
|---|---|---|
| 文件加载成功 | Success | “Flow graph loaded from file” |
| 文件保存 | Success | “Flow graph saved to file” |
| Snippet 保存 | Success | “Graph saved — ID: ... (copied to clipboard)” |
| Snippet 加载成功 | Success | “Flow graph loaded from snippet ...” |
| Snippet 存取失败 | Error | 错误描述 |
| 断开全部端口 | Info | “Disconnected all ports” |
所有 Toast 同时写入底部 Log 面板。GlobalState.onToastNotification 可观察对象负责向 UI 派发通知(见 globalState.ts),组件实现位于 toastComponent.tsx。
5. Low:已实现项与遗留待办
5.1 已实现:全选(Ctrl+A)
画布上可用 Ctrl+A / Cmd+A 一键选中所有节点与帧,不再需要手动拖选框。
5.2 遗留 1:选中节点的对齐/分布
distributeGraph() 通过 dagre 对整张图自动布局,但无法只对选中的节点做对齐(左/右/上/下)与均匀分布(水平/垂直)。清理大图局部区域时仍只能手工摆放。
5.3 遗留 2:Playground snippet 的 TypeScript 支持
预览场景加载器仅以 eval() 执行 JavaScript 代码;Playground 的 TypeScript snippet 不会先被转译,导致静默失败。预期方案是检测 TS snippet 并转译,或给出清晰错误提示。
6. 结语:从问题清单到可验证的实现
Phase 2 清单的价值在于它把“编辑器好不好用”落实成了一个个可验证的具体预期,且多数条目已在源码与手册中得到印证:默认场景与文件拖拽让测试场景零门槛,KHR_interactivity 往返打通了 glTF 资产管线,复合模板把导入时的多块接线模式变成了可拖放的调色板条目,多上下文支持则让同一份逻辑可以驱动不同实体并各自调试。结合 ISSUES-Phase-1.md 的基础设施修复、MANUAL.md 的操作手册与 packages/tools/flowGraphEditor/src/ 下的源码,读者可以对照本文逐项验证,并将编辑器真正接入自己的 Babylon.js 交互资产工作流。