ET UnityBridge 实战指南:用命令行让 AI 操作 Unity Editor 的全链路解析
【免费下载链接】ETUnity3D Client And C# Server Framework项目地址: https://gitcode.com/GitHub_Trending/et/ET
导读
UnityBridge 是 ET 框架中连接「AI / 命令行工具」与「Unity Editor」的本地文件桥接模块:它由一个纯 .NET 命令行程序(ET.UnityBridge.dll)和驻留在 Unity Editor 中的文件宿主组成,让 Agent 无需点击 GUI 即可查询 Unity 编译/PlayMode 状态、操作资源与场景、执行编译热重载、运行 Editor 测试。本文以 SKILL.md 为骨架,结合 CLI 参考、AI 操作参考 及源码实现,完整讲解桥接原理、命令发现、任务路由、deferred 命令等待与错误排查,读完即可在 ET 工程中驱动 AI 安全、省 Token 地操作 Unity。
UnityBridge 是什么:一个纯命令行的 Unity 操作入口
UnityBridge 是 ET 的「Unity 本地文件桥接包」(位于 Packages/cn.etetet.unitybridge),由三部分构成(见 AGENTS.md):
| 目录 | 作用 |
|---|---|
DotNet~ | 纯命令行程序ET.UnityBridge(CLI 入口) |
Scripts/Editor | Unity Editor 侧的文件宿主、命令处理器与分发逻辑 |
Scripts/Model/Share | 桥接命令、错误码、路径与文件存储协议 |
它的核心价值在于:AI 通过一行dotnet命令即可操作 Unity Editor,覆盖资源、场景、选择集、GameObject、Transform、Inspector、Prefab、菜单、截图、GameView、Editor 测试等场景,以及Compile/Refresh/RegenProject/EnterPlay/ExitPlay/Reload等会跨越 Unity 状态变化的命令,并可直接排查桥接返回的Error/Message。
桥接工作原理:文件即协议
UnityBridge 不依赖网络端口,而是以本地目录作为请求/响应的「信箱」,其路径解析与文件协议实现在 UnityBridgeStorage.cs:
- 桥接根目录解析优先级:显式
--root参数 > 环境变量ET_UNITY_BRIDGE_ROOT> 默认Temp/UnityBridge(见UnityBridgePathHelper.ResolveRoot)。 - 目录结构:
requests/(待处理请求)、processing/(正在处理)、responses/(已写回的响应)、deadletter/(解析失败的死信)、state/idempotency/(幂等响应缓存)、state/pending-command.json(deferred 命令持久化状态)。 - 写入方式:
UnityBridgeFileStore.WriteTextAtomic采用「先写.tmp再改名」的原子替换,避免半写文件被对端读到。
Editor 侧宿主UnityBridgeEditorHost(UnityBridgeEditorHost.cs)以[InitializeOnLoad]挂载到EditorApplication.update,每 0.2 秒轮询一次requests/目录,取出请求 → 反序列化为命令 → 交给UnityBridgeEditorDispatcher分发 → 写回响应。因此整个链路是:CLI 写请求文件 → Editor 轮询处理 → CLI 读到响应文件。
何时使用与何时不要加载
SKILL 文档明确划定了适用范围,Agent 需要据此决定是否加载本技能:
应该使用:查询宿主是否在线、Unity 是否在编译、PlayMode 状态、CodeMode、Unity 版本;让 AI 操作 Unity Editor 的各类资源/场景/对象;执行Compile/Refresh/RegenProject/EnterPlay/ExitPlay/Reload等跨状态命令;排查桥接返回的Error/Message。
不要加载:只是普通 C# 编译(应走et-build)、只是读写 Excel 或导出 Luban、只是纯代码结构分析(不需要 Unity 参与)、只是要解释命令协议而不实际操作 Unity(此时应只读 proto 或 handler,不要启动 UnityBridge)。
快速上手:最小流程
1. 确认 CLI 可执行
CLI 入口为:
dotnet ./Bin/ET.UnityBridge.dll- 默认桥接根目录:优先读取环境变量
ET_UNITY_BRIDGE_ROOT;未设置时默认Temp/UnityBridge;也可用--root <路径>显式指定。 - 若
Bin/ET.UnityBridge.dll尚不存在,先用et-build编译确认工具是否生成。构建产物路径由 ET.UnityBridge.csproj 的OutputPath决定(Debug/Release 均输出到仓库根的Bin目录),目标框架为net10.0。
2. 发送第一个 Ping
dotnet ./Bin/ET.UnityBridge.dll '{"_t":"Ping"}'dotnet ./Bin/ET.UnityBridge.dll '{"_t":"Ping"}' --root "Temp/UnityBridge"Ping用来判断 UnityBridge 宿主是否在线、获取响应时间,以及读取IsCompiling/IsPlaying/IsPlayingOrWillChangePlaymode/CodeMode/UnityVersion。对应的处理器实现在 UnityBridgePingHandler.cs,其中IsCompiling/IsPlaying/IsPlayingOrWillChangePlaymode直接取自EditorApplication,UnityVersion取自Application.unityVersion。
3. 需要命令列表时查询 HostState
dotnet ./Bin/ET.UnityBridge.dll '{"_t":"HostState"}'HostState在Ping信息的基础上额外返回AvailableCommands(由 UnityBridgeQueryHostStateHandler.cs 通过UnityBridgeEditorDispatcher.GetAvailableCommandTypes()汇总),因此按任务选择最小命令执行,失败时先看Error/Message,再读对应 handler。
命令发现:不要整包读,先 HostState 再 rg
省 Token 的关键是「按需发现命令」:
- 先用
HostState看当前可用命令; - 需要字段格式时用
rg只查对应 proto 小片段:
rg -n "^message .*Request|^message (Ping|HostState|Compile|Refresh|RegenProject|EnterPlay|ExitPlay|Reload)\b" ./Packages/cn.etetet.unitybridge/Proto- 需要行为细节时再查 handler:
rg -n "class UnityBridge.*Handler|AUnityBridgeDeferredHandler" ./Packages/cn.etetet.unitybridge/Scripts/Editor/Share协议定义集中在 UnityBridge_C_11100.proto(基础命令与对象/资产/场景消息)与同目录的UnityBridge_C_11400.proto(其余命令族)。例如Ping/PingResponse的结构(Error、Message、Time、IsCompiling、IsPlaying、CodeMode、UnityVersion等字段)与HostStateResponse.AvailableCommands、UnityTestRunResponse.Matched/Passed/Failed、AssetFindResponse.TotalFound/Returned等,均可从 proto 中直接确认。
任务路由:先读状态,再执行动作
来自 et-unitybridge-ai-ops.md 的完整任务路由表,是 AI 操作 Unity 的「地图」:
| 目标 | 优先命令族 | 常见前置 |
|---|---|---|
| 状态/连通性 | Ping,HostState,EditorGetStateRequest | 无 |
| 编译/刷新 | Compile,Refresh,RegenProject,AssetRefreshRequest,AssetImportRequest | IsCompiling == false |
| PlayMode/热重载 | EnterPlay,ExitPlay,Reload,EditorPauseRequest | 检查IsPlaying/IsPlayingOrWillChangePlaymode |
| 资源 | AssetSearchRequest,AssetFindRequest,AssetLoadRequest,AssetReadTextRequest,AssetGetPathRequest | 先限定 filter/path/count |
| 场景 | SceneGetHierarchyRequest,SceneGetActiveRequest,SceneLoadRequest,SceneSaveRequest,SceneNewRequest | 写操作前确认当前场景 |
| 选择集 | SelectionGetRequest,SelectionSetRequest,SelectionAddRequest,SelectionRemoveRequest,SelectionClearRequest | 先读当前 selection |
| 对象/Transform | GameObject*Request,Transform*Request | 先Find/GetInfo/Get |
| Inspector | InspectorGet*Request,InspectorSet*Request,InspectorAddComponentRequest,InspectorRemoveComponentRequest | 先读组件和属性名 |
| Prefab | PrefabInstantiateRequest,PrefabSaveRequest,PrefabApplyRequest,PrefabGet*Request,PrefabUnpackRequest | 先确认 asset path / instance |
| 截图/GameView | ScreenshotCaptureRequest,GameView*Request | 先读分辨率 |
| 测试 | UnityTestRunRequest | 用精确正则 |
| 批量 | BatchExecuteRequest | 先单步验证 |
以资源查询为例,先小范围查、不要全项目大结果:
dotnet ./Bin/ET.UnityBridge.dll '{"_t":"AssetFindRequest","Filter":"t:Prefab","MaxResults":10}'AssetFindRequest的Filter(如t:Prefab)、SearchInFolders、MaxResults、Format字段定义见 proto,其路径归一化与返回逻辑可查UnityBridgeAssetFindHandler.cs。场景对象操作则先读层级:
dotnet ./Bin/ET.UnityBridge.dll '{"_t":"SceneGetHierarchyRequest","Depth":2,"IncludeInactive":false}'写操作后用GameObjectGetInfoRequest或TransformGetRequest验证,不要只相信命令成功。改 Inspector 时先读组件与属性名,再使用 set 命令,不要猜测SerializedProperty路径:
dotnet ./Bin/ET.UnityBridge.dll '{"_t":"InspectorGetComponentsRequest","Path":"<HierarchyPath>"}'常用命令与传输参数
来自 et-unitybridge-cli.md 的常用命令一览:
dotnet ./Bin/ET.UnityBridge.dll '{"_t":"Ping"}' dotnet ./Bin/ET.UnityBridge.dll '{"_t":"HostState"}' dotnet ./Bin/ET.UnityBridge.dll '{"_t":"Compile"}' dotnet ./Bin/ET.UnityBridge.dll '{"_t":"Refresh"}' dotnet ./Bin/ET.UnityBridge.dll '{"_t":"RegenProject"}' dotnet ./Bin/ET.UnityBridge.dll '{"_t":"EnterPlay"}' dotnet ./Bin/ET.UnityBridge.dll '{"_t":"ExitPlay"}' dotnet ./Bin/ET.UnityBridge.dll '{"_t":"Reload"}'支持的传输参数(由 Program.cs 中的TransportOptions解析):
| 参数 | 说明 | 默认值 |
|---|---|---|
--root <路径> | 显式指定桥接根目录 | 环境变量ET_UNITY_BRIDGE_ROOT,否则Temp/UnityBridge |
--waitMs <毫秒> | CLI 等待最终响应的最大时长 | 15000(DefaultWaitMs) |
--timeoutMs <毫秒> | 请求在 Editor 侧的超时(<=0时按命令类型取默认) | 0,由宿主按命令回退 |
--idempotencyKey <字符串> | 幂等键,重复请求命中缓存直接返回上次响应 | 未提供时自动生成 GUID |
其中--timeoutMs的「按命令回退」逻辑在 UnityBridgeEditorHost.cs 的ResolveTimeoutMs中:Compile为 180000ms,Refresh/RegenProject/EnterPlay/ExitPlay为 60000ms,其余命令为 10000ms。CLI 侧还内置了 deferred 自动延长机制:未显式指定--waitMs时,若检测到pending-command.json中存在本请求的 RpcId,等待期限会自动延长至DefaultDeferredWaitMs = 185000,确保Compile等长耗时命令能拿到最终响应。
带完整参数的示例:
dotnet ./Bin/ET.UnityBridge.dll '{"_t":"HostState"}' --root "Temp/UnityBridge" --waitMs 15000 --timeoutMs 10000 --idempotencyKey "host-state-check"deferred 命令:必须等到最终响应
Compile、Refresh、RegenProject、EnterPlay、ExitPlay、AssetImportRequest、AssetRefreshRequest等是deferred(延迟)命令——它们会先返回「请求已接收」,真正的工作在 Unity 主循环中分帧完成。因此必须等待最终响应,不要看到请求已接收就结束。
其底层机制在 AUnityBridgeDeferredHandler.cs:deferred handler 在首次执行时抛出UnityBridgeDeferredStartedException并返回 deferred 响应;Editor 侧通过UnityBridgeDeferredRuntime.TryPump在EditorApplication.update中持续恢复执行(Run(command, UnityBridgeDeferredContext.CreateResume(startedAt))),直到产出最终响应或抛出UnityBridgeCommandStateException(此时返回对应错误码)。以Refresh为例,UnityBridgeRefreshHandler.cs 会等条件满足后执行AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate)再deferred.Started<RefreshResponse>()。
推荐的执行顺序:
- 编译前:
HostState -> Compile; - 刷新前:
HostState -> Refresh; - 重建工程文件:
HostState -> RegenProject; - 进播放前:确认
IsCompiling == false且IsPlayingOrWillChangePlaymode == false,再EnterPlay; - 热重载前:确认
IsPlaying == true,再Reload; - 退播放前:
HostState -> ExitPlay。
实时等待的正确姿势:
- 先执行
Ping或HostState; - 若
IsCompiling == true,持续轮询直到编译结束; - 再发
Compile/EnterPlay/Reload/ExitPlay等正式命令; - deferred 命令必须读取最终响应。
响应解读:Error 为 0 才是成功
Error == 0表示成功;非 0 表示失败,结合Message判断原因。CompileResponse额外包含DurationMs(编译耗时)。EnterPlayResponse/ExitPlayResponse额外包含IsPlaying。PingResponse额外包含Time、IsCompiling、IsPlaying、IsPlayingOrWillChangePlaymode、CodeMode、UnityVersion。HostStateResponse额外包含AvailableCommands。
向用户汇报时只总结Error、Message和关键字段,不要把完整 JSON 响应贴给用户。写操作后说明如何验证,必要时直接执行验证命令。
跑 Editor 测试
使用精确正则,避免全量跑:
dotnet ./Bin/ET.UnityBridge.dll '{"_t":"UnityTestRunRequest","Name":"^Unitybridge_DeferredHandlerRunContext_Test$"}'判定:Error == 0、Matched > 0、Failed == 0。其实现见 UnityBridgeUnityTestRunHandler.cs:通过TestDispatcher按正则匹配ET.Core/ET.Loader/ET.Model/ET.ModelView/ET.Editor/ET.Hotfix/ET.HotfixView程序集中的测试用例并逐条执行,最终汇总Matched/Passed/Failed与总耗时。仓库中同名测试(如Unitybridge_DeferredHandlerRunContext_Test.cs)即用于验证 deferred 运行上下文。
常见错误与排查
CLI 与参考文档列出的高频错误及其含义:
wait unity bridge response timeout:Unity 未打开、项目未加载完、桥接根目录不一致,或 Editor 未处理请求。排查顺序:先检查 Unity 是否已打开项目、心跳文件是否存在、桥接根目录是否一致。unity is compiling:Unity 正在编译,暂时不能开始新的延迟命令。先轮询Ping,等IsCompiling == false后重试。unity already in playmode or changing playmode:不能再次执行EnterPlay。unity not in playmode:执行ExitPlay或Reload的前置条件不满足。handler is missing:先HostState确认可用命令,再查 proto 名是否写错。execute menu item failed:菜单路径不存在,或 Unity 当前状态不允许执行。
对应的错误码定义集中在 ErrorCode.cs:Success = 0,并区分Timeout、NotInPlayMode、AlreadyInPlayMode、Compiling、HandlerFail、InvalidCommandLine等,便于程序化处理。
省 Token 操作守则
SKILL 与两份参考文档反复强调的 AI 操作纪律,值得作为长期协作约定:
- 不要完整读取所有 UnityBridge proto、handler 或
AvailableCommands; - 先用
HostState或rg发现命令名,再只打开相关 proto 小片段和对应 handler; - 先执行最小读命令确认目标,再做写操作;批量操作前先验证 1 个样本(
BatchExecuteRequest先单步验证); - 不要把完整 JSON 响应贴给用户,只总结
Error、Message和关键字段; - 优先 UnityBridge 命令,不要用 GUI 点击 Unity Editor,除非命令缺失或用户明确要求;如果 UnityBridge 不可用,说明原因和下一步,不回退到 GUI 点击。
小结
UnityBridge 以「文件即协议」的本地桥接设计,为 ET 工程提供了稳定、可脚本化、对 AI 友好的 Unity 操作入口。掌握「HostState 发现命令 → 最小读命令确认 → deferred 命令等待最终响应 → 按 Error/Message 排查」这条主线,即可让 AI 在零 GUI 依赖的前提下完成从编译热重载到场景搭建、从 Inspector 调参到 Editor 测试的全流程自动化操作。进一步深入可阅读 SKILL.md、CLI 参考、AI 操作参考,以及 Proto 协议 和 Editor 宿主实现。
【免费下载链接】ETUnity3D Client And C# Server Framework项目地址: https://gitcode.com/GitHub_Trending/et/ET
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考