unity-mcp scripting_ext 工具组指南:在 Unity 编辑器中执行任意 C 代码与批量管理 ScriptableObject
2026/9/15 18:16:31 网站建设 项目流程

unity-mcp scripting_ext 工具组指南:在 Unity 编辑器中执行任意 C# 代码与批量管理 ScriptableObject

【免费下载链接】unity-mcpUnity MCP acts as a bridge between AI assistants and your Unity Editor. Give your LLM tools to manage assets, control scenes, edit scripts, and automate tasks within Unity.项目地址: https://gitcode.com/GitHub_Trending/un/unity-mcp

导读

scripting_ext是 unity-mcp(MCP for Unity)提供的"脚本扩展"工具组,包含两个把 AI 助手能力直接注入 Unity 编辑器的核心工具:execute_codemanage_scriptable_object。前者允许 LLM 在编辑器进程内即时编译并运行任意 C# 方法体(无需创建任何脚本文件),后者则通过 Unity 原生的SerializedObject/SerializedProperty属性路径批量创建、修改 ScriptableObject 资源。读完本文,你将掌握这两个工具的完整参数语义、四种 action 的使用方式、编译器后端(auto/roslyn/codedom)的选择逻辑、patch 结构与对象引用/子资源(Sprite)的赋值技巧,以及它们在服务端、Unity 端与测试中的底层实现原理。

工具组概览:scripting_ext定位

scripting_ext的索引文档 index.md 将本工具组定义为一句话:ScriptableObject management(以及代码执行)。它在工具目录体系中属于"脚本扩展"分组,与coreasset_genprobuilderprofilingtestinguivfx等分组并列(见 tools/index.md 与 scripting_ext/category.json)。

两个工具在manifest.json中均以scripting_ext分组注册(execute_code见 manifest.json,manage_scriptable_object见 manifest.json)。从源码结构看,它们的 Python 端入口分别位于 Server/src/services/tools/execute_code.py 与 Server/src/services/tools/manage_scriptable_object.py,Unity 端处理器分别为 MCPForUnity/Editor/Tools/ExecuteCode.cs 与 MCPForUnity/Editor/Tools/ManageScriptableObject.cs。两个工具都被标记了destructiveHint=True(破坏性提示),意味着 MCP 客户端会要求用户显式确认后才允许调用。

execute_code:在 Unity 编辑器内即时编译并执行 C# 代码

功能定位与执行模型

execute_code的核心价值在于:代码以方法体(method body)形式运行,可直接访问UnityEngineUnityEditor命名空间,通过return把数据回传给 LLM。编译在内存中完成,不创建任何 .cs 脚本文件,也不会污染项目 Assets 目录。

Unity 端的实现封装在 ExecuteCode.cs 中,其执行模型为:

  1. 用户代码被包装进一个固定模板(WrapUserCode),生成类似下面的动态类:
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEngine; using UnityEditor; public static class MCPDynamicCode { public static object Execute() { // <-- 你的代码插入在这里 return ...; } }
  1. 动态程序集以"每次编译一个全新的内存程序集"的方式产生。由于 Mono 无法卸载已加载的程序集,重复编译相同代码会导致内存泄漏,因此实现引入了一个以"包装后源码 + 编译器"为 key 的编译缓存(_compiledCache,上限 64 条,见 ExecuteCode.cs);同时InitializeOnLoadMethod会在域重载(domain reload)时清空全部缓存与 Roslyn 反射缓存。

  2. 代码长度上限为 50,000 字符(MaxCodeLength);历史记录最多保留 50 条(MaxHistoryEntries),历史条目中的代码预览截断为 500 字符、结果预览截断为 200 字符。

参数总览

参数类型必填说明
actionLiteral['execute', 'get_history', 'replay', 'clear_history']要执行的动作
codestr \| None待执行的 C# 代码(仅execute使用)。必须是合法方法体,可访问 UnityEngine/UnityEditor,用return返回数据
safety_checksbool是否启用危险模式拦截(File.Delete、Process.Start、死循环等)。注意:不是完整沙箱,高级绕过仍可能。默认true
indexint \| None要重放的历史条目索引(仅replay使用)
limitintget_history返回的历史条数(1–50)。默认 10
compilerLiteral['auto', 'roslyn', 'codedom']编译器后端。auto在安装了 Microsoft.CodeAnalysis 时用 Roslyn,否则回退 CodeDom;roslyn强制 Roslyn(C# 12+);codedom强制旧版 CSharpCodeProvider(仅 C# 6)。默认auto

服务端 execute_code.py 会校验参数的组合关系:execute动作必须携带code(缺失时直接返回Parameter 'code' is required for 'execute' action.),replay动作必须携带index,而get_historylimit会被钳制在 1–50 之间(max(1, min(limit, 50))),随后把参数通过send_with_unity_instance以命令名execute_code转发给 Unity 端。

四种 action 的用法

execute(执行代码)

最基本的用法,让 LLM 读取当前场景信息后回传数据。例如查询场景中 GameObject 数量:

action: execute code: return Object.FindObjectsOfType<GameObject>().Length;

执行结果会以{ "success": true, "message": "...", "data": { "result": ..., "compiler": "roslyn" } }形式返回。返回值序列化遵循 SerializeResult:原始类型(isPrimitive)、stringdecimal直接返回;其余对象尝试用JToken.FromObject转 JSON,失败则回退为ToString()

get_history(查看历史)

列出最近的执行记录,每条包含indexcodePreview(500 字符截断)、successresultPreview(200 字符截断)、elapsedMs(耗时,毫秒)、timestampsafetyChecksEnabledcompilerlimit控制返回条数,历史为空时返回No execution history.

replay(重放历史)

index取回某条历史中的原始code,并以该条记录当时的safety_checkscompiler设置重新走一遍 execute 流程(见 HandleReplay)。这在"上一条代码执行成功但后续修改搞坏了状态"时特别有用,可以快速回滚到可用版本。注意:索引越界或历史为空时会返回错误,合法范围是0 ~ 历史条数-1

clear_history(清空历史)

清空全部历史记录并返回被清除的条数。

safety_checks拦截的黑名单模式

safety_checks=true(默认)时,CheckBlockedPatterns 会做大小写不敏感的字符串包含匹配,命中即拒绝执行。完整黑名单见 ExecuteCode.cs:

System.IO.File.Delete System.IO.Directory.Delete FileUtil.DeleteFileOrDirectory AssetDatabase.DeleteAsset AssetDatabase.MoveAssetToTrash EditorApplication.Exit Process.Start Process.Kill while(true) / while (true) for(;;) / for (;;)

命中时的错误信息形如Blocked pattern detected: 'AssetDatabase.DeleteAsset',并提示"如属有意为之,可用 safety_checks=false 关闭"。请务必理解文档与源码中的双重强调:这是一道"防呆"而不是"防攻击"的关卡——字符串匹配很容易被改写绕过(如File.Dele+te(...)),绝不能在不可信输入场景下把它当作安全边界。两个工具在ToolAnnotations上都标记了destructiveHint,也是同样的提醒。

三种编译器后端:auto / roslyn / codedom

后端语言能力依赖说明
auto(默认)Roslyn 可用则 C# 12+,否则 C# 6Microsoft.CodeAnalysis(可选)自动探测并择优
roslynC# 12+必须安装 Microsoft.CodeAnalysis,否则报错并提示改用 codedom推荐用于现代语法与更清晰的诊断信息
codedom仅 C# 6随 .NET 内置的CSharpCodeProvider兜底方案,处理旧语法

Roslyn 后端通过纯反射调用(RoslynCompiler),对Microsoft.CodeAnalysis没有任何编译期依赖——包未安装时IsAvailable返回 false,auto模式自动回退到 CodeDom,roslyn模式则返回Roslyn (Microsoft.CodeAnalysis) is not available.CSharpParseOptions的语言版本被设为Latest,因此支持最新的 C# 语法特性。

CodeDom 路径有两个值得一提的工程细节(源码注释均有交代):

  • 响应文件(.rsp)规避命令行超长CSharpCodeProvider会把每个ReferencedAssemblies转成csc命令行的字面/r:"..."参数。当项目有 100+ 个 asmdef 时,引用路径会击穿 Windows 的 32 KBCreateProcess参数长度上限(报 "The filename or extension is too long")。实现改为把所有/r:引用写入临时 .rsp 响应文件,命令行只传一个短参数@"..."
  • 重复程序集去重:CSharpCodeProvider 无法解析类型转发(type-forwarding),当netstandard.dllmscorlib/System.Runtime/System.Collections同时加载时,List<T>等类型会出现在多个程序集中导致 "type defined multiple times" 错误。因此 FilterAssemblyPathsForCodeDom 会在存在 netstandard 时剔除这组重复程序集,并按"被引用次数优先、版本次之"的策略对同名程序集去重。

此外,CodeDom 编译先输出到临时 DLL 再Assembly.Load,而非直接内存编译——这是因为 Mono 的 CodeDom 会把 mcs 输出到 stdout 的 BOM 行误判为一条伪造错误,导致明明编译成功却拿不到程序集(详见 CodeDomCompile)。

编译与运行错误处理

  • 编译错误:错误行号会通过WrapperLineOffset(值为 10)减去包装模板占用的行数,映射回用户代码的真实行号,返回形如Line 3: ...的诊断。
  • 运行错误:反射调用抛出的TargetInvocationException会被解包,返回Runtime error: <message>,并附带exceptionTypestackTracecompiler字段,便于 LLM 定位问题。

服务端行为与测试佐证

服务端把 Unity 的原始响应归一化为{success, message, data}三字段结构。单元测试 Server/tests/test_execute_code.py 覆盖了:code正确透传(test_execute_forwards_code_to_unity)、safety_checks默认true与显式false的传递、返回值透传(data.result == 42)、code缺失时报错,以及get_historylimit透传等场景。

典型实战场景

  • 快速验证 API:不确定某个UnityEditorAPI 的写法?直接执行一段代码验证,无需新建脚本、等待编译。
  • 批量只读查询:统计场景/资源数据后return给 LLM 决策。
  • 临时修复:对运行态数据做即时修补(注意对资源的持久化修改推荐走manage_scriptable_object这类专用工具,语义更安全)。

manage_scriptable_object:基于 SerializedProperty 路径的 ScriptableObject 资产管理

功能定位与设计理念

manage_scriptable_object把 ScriptableObject 的"创建 + 修改"合并到一个工具中,且修改走的是 Unity 原生SerializedObject/SerializedProperty路径,而非反射(见 ManageScriptableObject.cs 的注释)。这意味着:支持 Undo、属性变更可被 Inspector 正确识别、不依赖字段名的大小写/命名约定,天然兼容序列化系统的各种规则。

Unity 端在收到命令时首先检查EditorStateCache.GetActualIsCompiling() || EditorApplication.isUpdating,处于编译/刷新状态会返回compiling_or_reloading错误并带hint = "retry",客户端可据此稍后重试(见 ManageScriptableObject.cs)。

参数总览

参数类型必填说明
actionLiteral['create', 'modify']create 或 modify
type_namestr \| NoneScriptableObject 类型的完整命名空间限定名(create 用)
folder_pathstr \| NoneAssets/... 下的目标文件夹(create 用)
asset_namestr \| None资源文件名(不含扩展名,create 用)
overwritebool \| str \| None为 true 时覆盖同路径已存在资源(create 用)
targetdict \| str \| None目标资源引用{guid \| path}(modify 用)
patcheslist \| str \| None补丁列表(或 JSON 字符串)。对象引用用{"ref": {"guid": "..."}}{"value": {"guid": "..."}};Sprite 子资源需在 ref/value 对象中携带spriteName;单精灵贴图可仅凭 guid/path 自动解析
dry_runbool \| str \| None为 true 时只校验不落地(仅 modify)

create 动作:创建 ScriptableObject 资源

create需要type_namefolder_pathasset_name三个参数(缺任一都会报invalid_params),并做以下校验(见 HandleCreate):

  • asset_name不得包含路径分隔符(/\);
  • folder_path会被规范化并确保文件夹存在(不存在则自动创建);
  • type_name会被解析为实际类型,且必须可赋值给ScriptableObject,否则返回type_not_found

create同样可以携带patches,在创建实例后立即应用属性补丁再保存资源,实现"一步建好并填好内容"。此外 action 还兼容了createso/modifyso的别名(经NormalizeAction归一化,见 ManageScriptableObject.cs)。

modify 动作:按属性路径打补丁

modify通过target{guid}{path})定位资源,patches是核心参数。服务端 manage_scriptable_object.py 会先用parse_json_payload容忍 LLM 把复杂对象序列化成 JSON 字符串的情况(target必须是对象、patches必须是数组,否则直接报错),再以typeName/folderPath/assetName/overwrite/target/patches/dryRun的驼峰键转发给 Unity。

patch 结构(每条补丁对象)

字段说明
propertyPath(也兼容property_path/path目标属性路径,支持displayNamenested.field等点分路径
op操作类型,set(默认)或array_resize
value要写入的值;对象引用属性也接受ref键(ref优先于value,向后兼容)

例如把displayName设置为"Hello"的补丁:

[ { "propertyPath": "displayName", "op": "set", "value": "Hello" } ]
友好路径自动归一化

写数组属性时,Unity 内部路径格式是myList.Array.data[0],手工书写繁琐且易错。工具内置了NormalizePropertyPath(见 ManageScriptableObject.cs):myList[5]nested.list[0].field这类中括号写法会自动转换为myList.Array.data[5],而已处于.Array.data[...]格式的路径则原样保留。

数组操作:setarray_resize
  • 批量 set:当属性是数组/列表且value为 JSON 数组时,TrySetValueRecursive 会先把arraySize调整为数组长度,再逐个元素递归赋值,支持部分成功(返回Set 2/3 elements. ...)。
  • array_resize:单独调整数组长度,常用于"先扩容再逐元素赋值"的多步流程,例如:
[ { "propertyPath": "materials.Array.size", "op": "array_resize", "value": 2 } ]

array_resize要求value为非负整数;应用后立即ApplyModifiedProperties()Update(),保证后续补丁能基于新数组解析元素路径(见 ApplyPatches)。

对象引用:ref/value+ guid

当目标属性是ObjectReference类型时,赋值必须携带引用来源。推荐的写法是(详见 ApplySet):

[ { "propertyPath": "someMaterial", "op": "set", "value": { "guid": "3f2a...c9d1" } } ]

或使用旧的ref键(优先于value):{ "propertyPath": "...", "op": "set", "ref": { "guid": "3f2a...c9d1" } }ref/value也会被解析为纯字符串 GUID 的形态。引用解析实现在 ComponentOps.SetObjectReference 中,它需要同时处理 guid、路径、fileID 以及 spriteName 子资源等形态(见 ComponentOps.cs 附近注释)。设置成功会返回Set reference to '<name>'.,传 null 则清除引用。

Sprite 子资源:spriteName 与自动解析

Sprite 通常作为贴图(Texture)导入资源的**子资源(sub-asset)**存在,仅凭 guid/path 无法唯一确定引用哪个 Sprite。为此工具约定:

  • 单精灵贴图:仅凭guid/path即可自动解析;
  • 精灵图集(atlas)或多精灵贴图:必须在ref/value对象里携带spriteName
[ { "propertyPath": "icon", "op": "set", "value": { "guid": "5b1a...e0ff", "spriteName": "coin" } } ]

实现会在目标资源中按名称查找匹配的Sprite子资源,找不到时报Sprite '<name>' not found in atlas '<path>'.,类型不匹配时报对应兼容性错误(见 ComponentOps.cs)。

Generic 复杂对象映射

Generic类型的结构体/类字段(如自定义可序列化类),可直接传 JSON 对象做字段级递归映射(见 TrySetValueRecursive),例如{ "propertyPath": "settings", "value": { "volume": 0.8, "loop": true } }。递归深度上限为 20,防止循环引用导致的栈溢出。

dry_run:安全演练模式

dry_run=true(仅 modify)会进入 ValidatePatches 分支:校验每条 patch 的属性路径是否存在、值类型是否兼容、array_resize的 value 是否为非负整数,但不做任何写入。对AnimationCurveQuaternion还会做格式校验(通过 VectorParsing.ValidateAnimationCurveFormat / ValidateQuaternionFormat)。每个 patch 返回{index, propertyPath, op, ok, message}形式的校验结果。这是 LLM 在不确定属性路径是否正确时最稳妥的探索手段——先 dry_run 验证,再正式执行。

变更落盘流程

成功应用的补丁会按序执行:所有 patch 处理完后统一so.ApplyModifiedProperties()EditorUtility.SetDirty(target)AssetDatabase.SaveAssets()(见 ApplyPatches),确保资源变更立即持久化到磁盘。Unity 处于编译/刷新状态时返回compiling_or_reloading并提示retry

测试佐证

集成测试 Server/tests/integration/test_manage_scriptable_object_tool.py 覆盖了:create 参数的透传、modify 时 patches 的转发(含displayNameset 与materials.Array.sizearray_resize 两种形态)、dry_run参数的转发,以及 dry_run 以 JSON 字符串形式传入时的强制转换(test_manage_scriptable_object_dry_run_string_coercion)等场景,可作为"服务端如何把 LLM 的字符串化参数还原为结构化数据"的参考。

实战组合:一条完整的脚本化工作流

将两个工具串联,可以在一次多轮对话中完成"创建配置资源 → 批量填值 → 校验 → 落地"的完整闭环:

  1. createaction=createtype_name=MyGameConfigfolder_path=Assets/Configsasset_name=Level01
  2. dry_run 预演action=modifytarget={"guid":"..."}dry_run=true,先确认playerSettings.speedlevels.Array.size等路径无误。
  3. modify 落地:携带完整 patches(普通字段 set、数组 array_resize 后再逐元素赋值、对象引用{"ref":{"guid":...}}、精灵图集引用加spriteName)正式执行。
  4. 如需临时逻辑修补,再配合execute_code执行一段只读/验证代码,利用return获取反馈。

安全边界与最佳实践小结

  • execute_code不是沙箱:文档(execute_code.md)与源码注释都明确警告safety_checks只是"拦截已知危险模式",高级绕过是可能的。仅在可信环境下对受信任的 LLM 开启,并善用destructiveHint确认机制。
  • 优先专用工具:涉及资源持久化、序列化属性修改时优先manage_scriptable_object(原生 SerializedObject 路径 + Undo + 落盘),把execute_code留给探索性与一次性验证任务。
  • 善用 dry_run 与 get_history:不确定路径用dry_run探路;执行出错用get_history+replay找回可用版本。
  • 理解编译器差异:需要 C# 12+ 现代语法时确保安装了 Microsoft.CodeAnalysis(Roslyn),安装引导可参考 website/docs/guides/roslyn.md 中关于USE_ROSLYN定义符号与Assets/Plugins/Roslyn/的说明;仓库内另有独立的运行时编译演示组件 CustomTools/RoslynRuntimeCompilation/RoslynRuntimeCompiler.cs 可作参考。

scripting_ext这两个工具共同构成了 unity-mcp 中"让 AI 直接操作编辑器运行时与序列化数据"的底层通道:一个面向瞬时执行,一个面向资源持久化,配合core组的场景/资源管理工具,可以覆盖绝大多数自动化游戏开发工作流。

【免费下载链接】unity-mcpUnity MCP acts as a bridge between AI assistants and your Unity Editor. Give your LLM tools to manage assets, control scenes, edit scripts, and automate tasks within Unity.项目地址: https://gitcode.com/GitHub_Trending/un/unity-mcp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询