Babylon.js Flow Graph Editor Phase 2:用户工作流视角的编辑器能力盘点与实现解析

原创2026-09-30 14:52:021,416 阅读
文章标签:图形学游戏开发3D渲染

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 交互资产工作流。

登录后查看全文
Babylon.js