关于 unity-editor
unity-editor 是一组用于观察和控制 Unity Editor 的技能,适合检查场景变更、运行时状态和编辑器操作。它提供变更日志与 Play Mode 控制,并避免解析原始场景 YAML。
主要功能
- 01读取场景与文件变更日志
- 02查看编辑器上下文和状态
- 03控制 Play Mode 与逐帧推进
- 04检查 GameObject 运行时状态
- 05选择对象、撤销和重做
- 06执行菜单项并查询标签与图层
适用场景
- 01恢复工作时先检查 Unity 中的持久化变更
- 02运行场景并查看对象组件状态
- 03逐帧推进 Play Mode 以验证行为
- 04查找编辑器菜单命令或当前选择
MCP 相关
README 未说明其与 MCP 的关系。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ besty0728/ unity-skills --skill unity-editor安装后在 Claude Code 中输入/unity-editor确认已加载。
详细介绍
**调用本模块中的任何技能前:**如果你准备根据技能名称或描述猜测参数后调用技能,请停止——先阅读此文件(或通过
GET /skills/recommend?includeSchema=true获取其 schema)。如果你已经从 recommend/schema 获取了参数定义,则可直接进行 dryRun。
触发条件
- 读取持久化场景/文件变更
- 操控 Play 模式
- 检查运行时状态
- 执行菜单项
- 读取持久化场景/文件变更、操控 Play Mode、检查运行时状态、执行菜单项
Unity Editor 技能
观察并操控 Unity Editor,无需解析场景 YAML。
运行模式
- Approval:本模块 Mixed ——
editor_get_changes/editor_get_selection/editor_get_context/editor_get_state/editor_get_tags/editor_get_layers/editor_playmode_inspect标SkillMode.SemiAuto,可直接执行;其余editor_select/editor_undo/editor_redo/editor_playmode_step默认 FullAuto,Approval 模式下需 grant。 - Auto / Bypass:FullAuto 直接执行。
- 含 NeverInSemi 高危 skill:
editor_play/editor_play_capture/editor_stop/editor_pause(标MayEnterPlayMode = true);editor_run_scenario(标MayEnterPlayMode = true、MayTriggerReload = true、RiskLevel = "high");editor_execute_menu(标MayTriggerReload = true——Assets/Refresh、Assets/Reimport All之类的菜单项会触发域重载)。这些在 Approval 和 Auto 下都返MODE_FORBIDDEN,仅 Bypass 或 Allowlist 命中可调;grant 流程对它们不适用,不要尝试。
不要(常见幻觉):
editor_run不存在 → 使用editor_play进入 Play 模式editor_compile/editor_recompile不存在 → 使用debug_force_recompileeditor_save不存在 → 使用菜单路径为"File/Save"的editor_execute_menueditor_execute_menu需要精确的菜单路径——拼写错误会导致静默失败
路由:
- 检查编译情况 → 使用
debug模块的debug_check_compilation - 查看控制台错误 → 使用
debug模块的debug_get_errors - 保存场景 → 使用
scene_save(scene 模块)或菜单路径为"File/Save"的editor_execute_menu - 文件监视报告变更时,或 AI 在用户编辑 Unity 后恢复运行时 → 在读取
.unityYAML 前调用editor_get_changes - 如果
editor_get_changes.dropped=true→ 已超出 500 条目的保留窗口;使用scene_context/scene_diff重建上下文
技能概览
| 技能 | 描述 |
|---|---|
editor_play | 进入 Play 模式 |
editor_play_capture | 观察运行时错误,可选择截图,然后退出 |
editor_stop | 退出 Play 模式 |
editor_pause | 切换暂停状态 |
editor_playmode_step | 将 Play Mode 前进 N 帧(异步任务) |
editor_playmode_inspect | 检查 GameObject 的实时运行时状态(变换、组件字段) |
editor_run_scenario | 将 invoke / read / assert / wait 步骤作为一个异步验证任务运行(参见“运行时验证场景”) |
editor_select | 选择 GameObject |
editor_get_selection | 获取所选对象 |
editor_get_context | 获取完整的编辑器上下文(所选对象、资源、场景) |
editor_get_changes | 根据游标读取持久化场景/文件变更 |
editor_undo | 撤销上一个操作 |
editor_redo | 重做上一个操作 |
editor_get_state | 获取编辑器状态 |
editor_execute_menu | 执行菜单项 |
editor_get_tags | 获取所有标签 |
editor_get_layers | 获取所有层 |
console_set_pause_on_error | 出错时暂停 Play 模式(console 模块) |
技能
editor_play
进入 Play 模式。警告:退出时,Play 模式期间所做的任何未保存场景变更都将丢失。
返回:{success, mode, jobId} —— mode="playing";返回的 jobId 来自 AsyncJobService,调用方可据此轮询 entering_play_mode 的完成状态。
editor_play_capture
进入 Play Mode,观察错误 durationSeconds 秒(默认值为 10,范围为 1–300),可选择捕获 Game View,然后退出。返回一个 Job,其结果包含 healthy、错误汇总、stoppedEarly 和 screenshotPath。
editor_stop
退出播放模式。
返回:{success, mode} — mode="stopped"。
editor_pause
切换暂停状态。
返回:{success, paused} — paused 是新的布尔状态。
editor_playmode_step
使用 EditorApplication.Step 将播放模式向前推进 frames 帧(1–100,默认值为 1)。Unity 会在步进过程中自动进入暂停状态。要求播放模式已处于活动状态——请先调用 editor_play 或 editor_play_capture,否则会返回结构化错误(error + hint + suggestedSkills: ["editor_play", "editor_play_capture"])。同一时间只能有一个步进任务在运行;如果上一个任务仍在运行时再次调用,会返回一条包含活动 jobId 的错误。
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
frames | int | 否 | 1 | 要推进的帧数,限制在 1–100 |
以异步任务运行,而非同步运行:EditorApplication.Step() 只会在之后的 Editor tick 中生效,因此该技能每次只发出一次 Step() 调用,通过观察 Time.frameCount 增加来确认步进已生效,然后才发出下一次调用——未确认就连续调用 Step() 并不可靠。调用本身会立即返回:{success, status: "accepted", jobId, framesRequested}。
使用返回的 jobId 轮询 job_status,直到 status="completed";此时其 details 包含 {framesRequested, framesCompleted, frameCount, isPaused}。不要使用 job_wait——与 playmode/play_capture 一样,此任务的进度依赖 EditorApplication.update tick,而 job_wait 的阻塞循环运行在这些 tick 所在的同一主线程上,因此无法观察进度;它只会白白耗尽整个超时时间。如果退出播放模式,或 Unity 在 10s 内未能推进帧,任务会以 failed_exited_play_mode / failed_step_timeout 失败。
editor_playmode_inspect
检查 GameObject 的实时运行时状态:变换(位置/旋转/缩放)、activeSelf/activeInHierarchy,以及(给出 componentType 时)该组件的公共字段和属性(复用与 component_get_properties 相同的反射辅助程序,而非重新实现)。可在播放模式下使用,包括暂停时;也可在编辑模式下使用,此时返回编辑器时的值——请检查响应中的 isPlaying/isPaused,以了解这些值反映的是哪种状态。
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
name | string | 否* | 对象名称 |
instanceId | int | 否* | 实例 ID(首选) |
path | string | 否* | 对象路径 |
componentType | string | 否 | 组件类型名称;设置后还会返回其公共字段/属性 |
*必须提供一个标识符
返回:{success, gameObject, entityId, instanceId, path, activeSelf, activeInHierarchy, transform: {position, localPosition, rotation, localScale}, isPlaying, isPaused, component}。如果省略 componentType,component 为 null;如果找不到该类型或该组件未附加,则为错误对象;否则为 {gameObject, component, fullTypeName, properties, fields}(形状与 component_get_properties 相同)。
editor_select
选择一个 GameObject。
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
name | string | 否* | 对象名称 |
instanceId | int | 否* | 实例 ID(首选) |
path | string | 否* | 对象路径 |
*必须提供一个标识符
editor_get_selection
获取当前选中的对象。
返回:{count, objects: [{name, instanceId}]}
editor_get_context
获取完整的编辑器上下文,包括选中内容、资源和场景信息。
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
includeComponents | bool | 否 | false | 包含组件列表 |
includeChildren | bool | 否 | false | 包含子对象信息 |
返回值:
selectedGameObjects:层级视图中的对象(instanceId、path、tag、layer)selectedAssets:项目窗口中的资源(GUID、path、type、isFolder)activeScene:当前场景信息(name、path、isDirty)focusedWindow:当前聚焦的编辑器窗口名称isPlaying、isCompiling:编辑器状态
editor_get_changes
读取持久化变更日志 Library/UnitySkills/editor_changes.jsonl。日志记录场景对象/组件/属性摘要,以及跨域重载导入、删除和移动的资源路径。它不会解析或返回原始 .unity YAML。
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
since | long | 否 | 0 | 上次响应的游标;0 表示读取保留的历史记录 |
types | string | 否 | all | 逗号分隔的 scene、file、undo 或 lifecycle |
source | string | 否 | all | editor/manual、rest 或 all |
limit | int | 否 | 100 | 要返回的最新条目数,范围为 1-500 |
返回值: {hasChanges, cursor, oldestSeq, dropped, truncated, changes}。保存 cursor 以供下次调用。dropped=true 表示较早的变更已超出 500 条日志的保留范围,因此需要重建完整的场景上下文。
editor_undo
撤销上一个操作。
editor_redo
重做上一个已撤销的操作。
editor_get_state
获取当前编辑器状态。
返回值:{isPlaying, isPaused, isCompiling, timeSinceStartup, unityVersion, platform}
editor_execute_menu
执行菜单命令。
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
menuPath | string | 是 | 菜单项路径 |
常用菜单路径:
| 菜单路径 | 操作 |
|---|---|
File/Save | 保存当前场景 |
File/Build Settings... | 打开构建设置 |
Edit/Play | 切换播放模式 |
GameObject/Create Empty | 创建空对象 |
Window/General/Console | 打开控制台 |
Assets/Refresh | 刷新资源 |
editor_get_tags
获取所有可用标签。
返回值:{tags: [string]}
editor_get_layers
获取所有可用层。
返回值:{layers: [{index, name}]}
出错时暂停
出错时暂停由控制台模块提供,而非编辑器模块。
使用 console/SKILL.md 中的 console_set_pause_on_error。
使用示例
import unity_skills
# Check editor state before operations
state = unity_skills.call_skill("editor_get_state")
if state['isCompiling']:
print("Wait for compilation to finish")
# On resume or after file-watch notification, inspect semantic changes first.
changes = unity_skills.call_skill("editor_get_changes", since=last_cursor)
last_cursor = changes["cursor"]
# Get full context (useful for understanding current state)
context = unity_skills.call_skill("editor_get_context", includeComponents=True)
for obj in context['selectedGameObjects']:
print(f"Selected: {obj['name']} (ID: {obj['instanceId']})")
# Select and operate on object
unity_skills.call_skill("editor_select", name="Player")
selection = unity_skills.call_skill("editor_get_selection")
# Safe experimentation with undo
unity_skills.call_skill("gameobject_delete", name="TestObject")
unity_skills.call_skill("editor_undo") # Restore if needed
# Execute menu command
unity_skills.call_skill("editor_execute_menu", menuPath="File/Save")
# Step through Play Mode frame-by-frame and assert state changed
import time
# Enter Play Mode with a generous observation window so there's time left to step
capture = unity_skills.call_skill("editor_play_capture", durationSeconds=30)
while True:
capture_status = unity_skills.call_skill("job_status", jobId=capture["jobId"])
if capture_status["currentStage"] not in ("entering_play_mode", "domain_reload_recovery"):
break # Play Mode has been entered; capture_status["currentStage"] == "observing"
time.sleep(0.2)
before = unity_skills.call_skill("editor_playmode_inspect", name="Player")["transform"]["position"]
step = unity_skills.call_skill("editor_playmode_step", frames=3)
while True:
step_status = unity_skills.call_skill("job_status", jobId=step["jobId"]) # not job_wait, see above
if step_status["status"] in ("completed", "failed"):
break
time.sleep(0.1)
after = unity_skills.call_skill("editor_playmode_inspect", name="Player")["transform"]["position"]
assert after != before, "Player did not move after 3 frames"
print(step_status["details"]["frameCount"], step_status["details"]["isPaused"])
最佳实践
- 在执行播放模式操作前检查编辑器状态
- 不要在播放模式下修改场景(变更会丢失)
- 使用撤销功能安全地进行试验
- 恢复后,在读取场景文件前调用
editor_get_changes - 使用
editor_get_context获取用于批量操作的 instanceId - 菜单命令必须与路径完全一致
精确签名
精确的名称、参数、默认值和返回值由 GET /skills/schema 或 unity_skills.get_skill_schema() 定义,而非本文件。
常见错误
完整的传输层错误代码(COMPILING/RATE_LIMIT 等)→ ../../references/protocol-error-codes.md
| 错误 | 触发条件 | 修复方法 |
|---|---|---|
TARGET_NOT_FOUND | 找不到请求的 GameObject 或菜单项(例如,Menu item not found or failed)。 | 使用 gameobject_find / scene_get_hierarchy 验证对象,或检查菜单路径拼写是否完全正确后重试。 |
SKILL_ERROR | 播放模式状态发生冲突,例如 Already in play mode、Not in play mode,或已有活动的逐帧步进任务。 | 使编辑器状态符合技能要求:先进入/退出播放模式,或等待现有的步进任务完成。 |
运行时验证场景
editor_run_scenario 接受 steps(JSON 数组字符串,1–100 个条目)、executionMode(默认值为 play,也可显式指定 edit)和 timeoutSeconds(1–600,默认值为 60)。它会返回一个已持久化的 run_scenario 任务。至少需要一个 assert 或 wait_condition。DryRun 会验证结构和步骤技能元数据,但不会进入 Play Mode 或调用代码;每个实际步骤仍会通过 SkillRouter,并遵循其自身权限。调用步骤目前仅接受 component_invoke_method;其他会改变生命周期的技能和嵌套场景均会被拒绝。
步骤形式:
{"kind":"invoke","skill":"component_invoke_method","args":{...}}:调用一次并等待其任务完成。调用参数中的 executionMode 应与场景一致。{"kind":"read","skill":"component_get_properties","args":{...}}:运行声明为 ReadOnly 的技能,并保留其结果。{"kind":"assert","expected":42,"select":"returnValue","tolerance":0.01}:比较上一步的结果;也可以提供 ReadOnlyskill和args,立即读取。select是相对于响应数据的可选 JSONPath。即使 expected 为 null,路径不存在也会导致失败。数值容差会递归应用于匹配的数组或对象。{"kind":"wait_frames","frames":2}或{"kind":"wait_seconds","seconds":0.5}:等待期间不会阻塞 Editor。帧数等待使用实际的 Play 帧计数器;秒数等待使用墙上时钟时间。暂停的 Play Mode 无法推进帧数等待,Edit Mode 的帧数等待应为零。{"kind":"wait_condition","skill":"component_get_properties","args":{...},"select":"...","expected":true}:重复进行 ReadOnly 观察,直到匹配或场景期限到期。
从 Edit Mode 启动的场景会进入 Play Mode,并在之后退出。从 Play Mode 启动的场景会保持该模式。场景会临时启用 Application.runInBackground,并在成功、失败或取消时恢复其原始值。在进入或退出 Play Mode 时发生域重载,会恢复生命周期清理;已经分派的调用会被中断,不会重放。任意调用都属于高风险操作,不保证支持 Undo 或原子性。
持续轮询任务,直到其进入终止状态。只有在断言成功、没有运行时 Error/Exception/Assert 日志且日志覆盖完整时,resultData.passed 才为 true。steps 包含 actual、expected、tolerance 和 passed;logWindow 和 compilation 会保留证据。运行时错误、日志覆盖缺失、编译待处理、异常、目标缺失和超时都会导致验证失败。取消或超时不保证任意用户代码已停止。在场景外修复源代码,等待编译和重载完成,然后显式提交新的验证任务;绝不要悄悄重放失败的调用。
对于 AI 客户端有限次数的“编译 → 场景 → 修复 → 重跑”循环,请使用运行时验证与有界修复。随附的辅助程序接受调用方提供的修复回调,并将修复次数限制为最多三次、耗时不超过三十分钟。