mcpField
B
build-gtk

build-gtk

github.com/unity-technologies开发1.2k1.8k 次安装1 天前更新
代码仓库

关于 build-gtk

面向 Unity Editor 开发者的技能,指导使用 Graph Toolkit 公共 API 构建图编辑器和状态机编辑工具。内容涵盖版本检查、图结构、验证及运行时资产导入流程。

主要功能

  1. 01基于公共 API 构建图工具
  2. 02构建状态机编辑工具
  3. 03配置节点、端口与选项
  4. 04验证图并报告问题
  5. 05通过 ScriptedImporter 编译资产

适用场景

  1. 01为 Unity 项目创建可视化图编辑器
  2. 02构建包含状态与转换的状态机
  3. 03将编辑器图数据导入为运行时资产

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/unity-technologies/skills --skill build-gtk

安装后在 Claude Code 中输入/build-gtk确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
graph-api.md
pitfalls.md
runtime-and-visualization.md
state-machine-api.md

详细介绍

Unity Graph Toolkit(GTK)

基于 Unity 的 Graph Toolkit 模块构建编辑器图工具和状态机工具,仅使用 Unity.GraphToolkit.Editor 命名空间中的公共 API。

重要事项

  • 创建一个还原点,以便在更改失败时回滚。
  • 只做要求的事情。不要更改无关的资源或文件。避免冗长的解释。
  • 在开始工作流或诊断之前,打开并阅读该工作流中指定的参考文件,以及涵盖你打算处理事项的主题索引页面。请在回复中注明你阅读的文件。这是因为关于 Unity 的训练知识可能已经过时、有误,或不适用于当前 Unity 版本,而且 Graph Toolkit 在 6000.4、6000.6 和 6000.7 之间有所变化。

参考资料

按需阅读以下资料。路径相对于此技能文件夹。

  • references/graph-api.md — Graph API 范围:Graph、Node、端口和选项构建器、上下文和块节点、子图、变量、验证、工具栏和上下文菜单。编写任何图工具代码前请阅读。
  • references/state-machine-api.md — State Machine API(Unity 6.7+):StateMachine、State、Condition、转换和规则、自转换、自定义视图。涉及状态、转换或条件时请阅读。
  • references/runtime-and-visualization.md — 使用 ScriptedImporter 将图或状态机编译为运行时资源,以及在图编辑器中显示实时执行情况。用户希望在游戏中使用创作的数据或在运行时进行调试时请阅读。
  • references/pitfalls.md — 能通过编译但仍会出错的问题。交付前以及用户报告编译错误、Add 菜单为空、节点缺失或发生异常时请阅读。

步骤 1:检查 Editor 版本并选择 API

编写代码前,读取 ProjectSettings/ProjectVersion.txt 中的 m_EditorVersion。Graph Toolkit 是一个 Editor 模块,因此无需安装包。com.unity.graphtoolkit 是已弃用的兼容层,不得将其添加到 manifest。

Editor 版本可用功能
早于 6000.4仅有已退役的实验性包(0.x)。其 API 与本技能不同。建议升级。
6000.4 至 6000.5已包含该模块,但不支持端口容量、类型转换、连线和节点可视化、USS 节点样式或元素 ID。
6000.6本技能文档介绍的 Graph API。
6000.7 及更新版本包含以上所有功能,此外还支持 State Machine API、NodeView<T> / StateView<T> / ConditionView<T> 自定义 UI、[GraphMenu] / [BlackboardMenu] 上下文菜单、变更增量 GraphLogger.GraphChanges、端口类型跟随和 Blackboard 类型限制。参考资料中标记为 6.7 的成员。

如果用户要求在低于 6000.7 的 Editor 上构建状态机工具,请明确说明该版本不提供 State Machine API。除非用户提出要求,否则不要用 Graph API 模拟实现。

心智模型

  • Graph Toolkit 仅是一个创作框架。它绘制并持久化图,但不会执行图。 用户编写运行时数据模型和运行它的代码,通常由 ScriptedImporter 提供数据。

  • 每个 Graph 或 StateMachine 子类对应一种资源类型,由传递给 [Graph("ext")] 或 [StateMachine("ext")] 的文件扩展名标识。

  • 通过反射发现类型。与图类位于同一程序集中的节点、状态和条件类会自动列出。其他程序集中的类需通过 [UseWithGraph] 或 [UseWithStateMachine] 显式加入。抽象类不会列出。

  • 所有创作类都带有 [Serializable],并且位于仅限 Editor 的程序集中,可以是 Editor 文件夹,也可以是将平台限制为 Editor 的 asmdef。

  • 只有公共 API 会为用户编译。如果草稿中出现左列中的名称,请停止并改用右列中的名称:

    不要写改为
    GraphModel、GraphObject、GraphTool、Stencil、BaseGraphToolGraph 和 GraphDatabase
    NodeModel、PortModel、WireModelNode、IPort、Wire
    StateModel、TransitionModel、ConditionModelState、ITransition、Condition<T>
    GraphViewEditorWindow、GraphElementsExtensionMethodsCache、ModelView窗口已提供;在 6000.7 上使用 NodeView<T> 实现自定义 UI
    UnityEditor.Experimental.GraphView、graphViewChanged、Port.Create没有等效项;将工具建模为 Graph 和 Node 类
    清单中的 com.unity.graphtoolkit无需替代;该模块已内置

工作流 A:构建图工具

现在阅读 references/graph-api.md,再开始编写代码;成员名称和构建器链应以该文档为准,而不是凭记忆。第 5 步适用时,也请阅读 references/runtime-and-visualization.md。

  1. Graph 类。 [Graph(AssetExtension)] [Serializable] class MyGraph : Graph,并添加 public const string AssetExtension。添加一个调用 GraphDatabase.PromptInProjectBrowserToCreateNewAsset<MyGraph>() 的 [MenuItem("Assets/Create/...")] 静态方法。需要子图时,在特性中传入 GraphOptions.SupportsSubgraphs。

  2. 节点类。 [Serializable] class MyNode : Node。重写 OnDefinePorts,并确保每条端口构建器链都以 .Build() 结束。重写 OnDefineOptions 添加可在检查器中编辑的设置;当端口依赖这些设置时,在 OnDefinePorts 中使用 GetNodeOptionByName(name).TryGetValue<T>(out var v) 读取它们。对于类似数量的选项,调用 .Delayed(),避免每次按键时都重建端口。使用 [Node("Category/Path", iconPath, title, stylesheet)] 设置位置、图标、默认标题和 USS。

  3. 验证。 重写 OnGraphChanged(GraphLogger logger),并使用 logger.LogError、LogWarning 或 Log 报告问题,同时将有问题的节点或端口作为上下文传入,使标记显示在该对象上。切勿在此回调中修改图;应将修复操作放入附加到消息的 GraphLogAction、菜单项或导入器中。

  4. 按需构建结构。 上下文和块节点、子图、黑板变量、通过 IsConnectionAllowed 进行类型转换、工具栏元素或上下文菜单。详情请参阅 graph-api.md。

  5. 输出。 注册在相同扩展名上的 ScriptedImporter 会将图编译为普通的运行时资源。结构始终如下:

    var graph = GraphDatabase.LoadGraphForImporter<MyGraph>(ctx.assetPath);   // never LoadGraph here
    if (graph == null) return;                                                 // bad path or type: log and stop
    var start = graph.GetNodes().OfType<StartNode>().FirstOrDefault();
    if (start == null) return;                                                 // OnGraphChanged already reported it
    var next = start.GetOutputPortByName("Next").FirstConnectedPort?.GetNode(); // null when unconnected
    var titlePort = next.GetInputPortByName("Title");
    titlePort.TryGetValue<string>(out var title);                              // the value typed on the node
    if (titlePort.IsConnected) { /* resolve titlePort.FirstConnectedPort.GetNode() upstream instead */ }
    ctx.AddObjectToAsset("Runtime", runtime); ctx.SetMainObject(runtime);
    

    运行时资源及其程序集不得引用 Unity.GraphToolkit.Editor;将 Hash128 ID 保持为 Hash128。调试视图和通过代码构建的图请参阅 runtime-and-visualization.md。

  6. 按照下文所述进行验证。

工作流 B:构建状态机工具(Unity 6.7 及更高版本)

现在阅读 references/state-machine-api.md;此 API 目前还没有手动操作指南,因此该文件和脚本参考是关于其成员名称的唯一准确来源。

  1. 状态机类。 [StateMachine(AssetExtension)] [Serializable] class MySM : StateMachine 并添加一个调用 StateMachineDatabase.PromptInProjectBrowserToCreateNewAsset<MySM>() 的菜单项。
  2. 状态。 [Serializable] class Patrol : State。状态没有端口;状态机拥有转换。在 OnDefineOptions 中声明的选项仅显示在图形检查器中。
  3. 条件。 [Serializable] [Condition("Health")] class HealthCondition : Condition<float>。需要比较运算符时,设置 protected override bool DisplayComparisonDropdown => true。 对于没有值的触发器,直接从 Condition 派生。与组(And/Or)和变量相关的条件已内置;请勿重新实现。
  4. 可选项。 自定义自身转换(SelfTransition + [Transition(...)])、通过 StateView<T> 和 ConditionView<T> 自定义 UI,以及 [StateMachineMenu] / [ConditionMenu] 条目。
  5. 验证。 重写 OnStateMachineChanged(StateMachineLogger logger)。规则与 OnGraphChanged 相同。
  6. 输出。 ScriptedImporter 使用 StateMachineDatabase.LoadStateMachineForImporter<MySM> 加载,然后遍历 GetStates()``、每个状态的 GetOutgoingTransitions()、每个转换的 GetRules(),以及每条规则的 RootCondition树。该树始终包含内置类型和用户类,因此编译器必须先处理IGroupCondition(递归处理并遵循 Operation)和 IVariableCondition(Variable.Name、Comparison、Value),再匹配自定义 Condition` 类型。

通过代码编辑图

菜单项、生成器或工具构建或编辑资源时,请将变更操作括起来: LoadGraph/CreateGraph → UndoBeginRecordGraph("Action") → AddNode、Connect、 port.TrySetValue、CreateVariable → GraphDatabase.SaveGraph(graph) → UndoEndRecordGraph()。 状态机的对应方法是 UndoBeginRecordStateMachine、Connect(fromState, toState)、 SaveStateMachine 和 UndoEndRecordStateMachine。在 OnEnable、OnDisable、 OnGraphChanged 或 OnStateMachineChanged 中执行变更会抛出 InvalidOperationException。

决策

用户想要操作原因
限制端口可接受的连线数量在端口构建器中使用 .WithCapacity(PortCapacity.Single)(或 Multi、None)端口默认允许多条连线,因此只有声明限制后,框架才会强制执行
将 int 输出连接到 float 输入重写 Graph.IsConnectionAllowed(IPort output, IPort input)默认情况下,不同类型的端口无法连接;图只会记录连线,转换由你的导入器完成
创建一个可接受多种类型的端口,例如 Shader Graph 中的端口6000.7+:在构建器上使用 .WithDataTypes(typeof(float), typeof(int), typeof(Vector3))。6000.6:使用无类型端口并实现 IsConnectionAllowed6000.6 中不存在 WithDataTypes
创建没有数据类型的执行流端口使用不带类型的 context.AddInputPort("In");其 DataType 为 Untyped。添加 .WithConnectorUI(PortConnectorUI.Arrowhead) 可呈现流端口外观流端口不携带值;为其指定类型容易导致错误连接
在“添加”菜单中隐藏某种节点类型将其设为 abstract,或将其移到没有 [UseWithGraph] 的其他程序集,或设置 GraphOptions.DisableAutoInclusionOfNodesFromGraphAssembly 并显式选择要加入的节点图所在程序集中的所有具体节点类都会自动列出
在“添加”菜单中对节点分组使用 [Node("Category/Sub")]。类名或标题名称会追加在路径后面特性的第一个参数是文件夹路径,而不是节点标题
了解发生了哪些变化,类似旧版 graphViewChanged6000.7+:使用 OnGraphChanged 和 logger.GraphChanges.ChangedNodes,通过 ChangeKinds 标记获取已添加和已修改的节点。任何版本中要获取已删除的节点,以及在 6000.6 中获取所有变化:将缓存的节点 ID HashSet<Hash128> 与 GetNodes() 的结果进行比较GraphChanges 在 6000.7 中加入,且从不报告已删除的节点,只报告已删除的端口
创建多种子图在主图上使用 GraphOptions.SupportsSubgraphs,并在每个子图 Graph 类上使用 [Subgraph(typeof(MainGraph))]没有该特性时,主图类型会兼作唯一的子图类型
为子图节点添加端口在子图中创建类型为 VariableKind.Input 或 Output 的黑板变量子图节点没有 OnDefinePorts;其端口会映射子图的变量
设置节点图标或 USS 外观使用 [Node(category, iconPath, title, stylesheet)];深色主题图标文件使用 d_ 前缀Editor 会在深色主题下选择 d_ 文件,否则使用备用文件
在运行时显示正在运行的节点及端口中流动的数据使用 GraphVisualization API,参见 runtime-and-visualization.mdGraph Toolkit 没有运行时;游戏必须将状态推送回打开的窗口
创建类似 Animator 的状态机编辑器在 6000.7+ 上使用工作流 B。在 6000.6 上,说明此功能不可用6000.7 之前不存在 State Machine API
在节点中添加额外 UI在 6000.7+ 上使用 NodeView<MyNode>。在 6000.6 上,只能使用 USS、DefaultColor 和 FillAmountNodeView<T> 在 6000.7 中加入

约束

  • 成员名称必须与参考资料中的拼写完全一致:属性使用 PascalCase (FirstConnectedPort、IsConnected),特性使用位置构造函数参数,ID 使用 Hash128。如果参考资料中未列出某个成员,请按下方的 URL 模式查询,不要猜测其名称或大小写,因为猜错的成员会导致编译失败,用户也无法分辨是拼写错误还是缺少功能。
  • 链接到脚本参考或手册,而不是复述其中内容,这样文档更新时答案仍然准确。
  • 不要在运行时程序集里引用 Unity.GraphToolkit.Editor,并将可视化代码放在 #if UNITY_EDITOR 下的运行时程序集中,因为该模块仅适用于 Editor,任何对它的引用都会导致 Player 构建失败。
  • 保留用户现有的工具结构,不要重新设置样式或重组未提及的节点,因为节点和端口名称是导入器及已保存资源依赖的查找键。
  • 不要将 Graph Toolkit 内部实现或私有类型作为变通方案,因为用户代码无法访问它们,而且它们可能随时更改。如果公共 API 无法实现某项功能,请说明这一点,并指向下方的社区讨论列表。

验证

  1. 阅读 references/pitfalls.md,并在向用户展示代码前对照检查。
  2. 项目编译无错误。如果 Unity CLI 可用,请用它进行构建或无头运行 Editor;否则请用户将 Editor 切到前台并反馈 Console 信息。
  3. 通过新建菜单项创建资源,双击打开,从 Add 菜单中添加每一种节点或状态类型,将它们连接起来,保存、关闭并重新打开。Console 中不得出现任何错误或警告。
  4. 如果有导入器,请选中资源,并确认生成的运行时对象是 Inspector 中的主资源。 3b. 如果编写了运行时代码(执行器、调试视图),请添加能够证明其行为的 Debug.Log 行,例如记录进入的状态或执行的节点;进入 Play 模式并查看日志。确认行为后,将这些日志移除或加上条件编译保护。
  5. 重新阅读 references/pitfalls.md,并修复其中指出的所有问题。
  6. 如果某一步失败或 Console 报错,请返回并重读该步骤对应的参考文件和主题索引页面,然后重试;问题通常是遗漏了成员名称或版本限制。

最终报告

向用户提供一份简短清单:创建或修改了哪些文件以及它们的存放位置、如何创建并打开第一个资源、导入器会生成什么,以及代码面向哪个 Editor 版本。列出仍需用户决定或完成的事项,例如更多节点类型、运行时执行器或调试视图;如果没有可用的 Editor,也请说明哪些 Console 错误无法验证。

主题索引

优先使用 WebFetch,而不是 WebSearch;速度更快,也能直达准确页面。只获取所需内容。 获取页面前,先替换 <VERSION>:

  • docs.unity.com/en-us/engine/<VERSION> 和 docs.unity3d.com/<VERSION>/Documentation:项目的 Editor 版本,取自 ProjectSettings/ProjectVersion.txt,例如 6000.6 或 6000.7。
  • docs.unity3d.com/Packages/com.unity.graphtoolkit-samples@<VERSION>:Package Manager 中显示的样例包 版本,例如 0.6。

以下页面均位于 https://docs.unity.com/en-us/engine/<VERSION>/ 下;保留 .md 后缀时会以 Markdown 格式提供:

  • 手册索引:manual/extending-the-editor/gtk-index.md
  • 实现图工具:manual/extending-the-editor/gtk-index/implementing-a-graph-tool.md,其 子页面包括该文件夹下的 implement-a-graph-tool.md、implement-nodes.md、implement-node-options.md、 implement-context-nodes.md、implement-block-nodes.md、type-cast-ports.md、 add-custom-toolbar-actions.md、add-subgraph-support.md、graph-processing.md
  • 图窗口和面板:manual/extending-the-editor/gtk-index/landing-graph-interface.md,其下有 graph-window.md、blackboard.md、graph-inspector.md、minimap.md
  • Script Reference:script-reference/unity/graphtoolkit/editor/<type>.md 和 .../<type>/<member>.md,全部使用小写,并省略泛型元数(Condition<T> 对应 condition.md)。 示例:script-reference/unity/graphtoolkit/editor/graph/ongraphchanged.md。状态机类型从 6000.7 开始提供。旧格式 https://docs.unity3d.com/<VERSION>/Documentation/ScriptReference/Unity.GraphToolkit.Editor.<Type>.html 也可访问,格式为 HTML。
  • 样例:https://docs.unity3d.com/Packages/com.unity.graphtoolkit-samples@<VERSION>/manual/index.html。 在 Package Manager 中按名称安装 com.unity.graphtoolkit-samples,然后导入 Texture Maker (导入器)、Visual Novel Director(自定义运行时和调试视图)或 Dungeon Graph Generator(通过代码构建图)。
  • 社区:https://discussions.unity.com/tag/graph-toolkit

评论

支持 Markdown