资讯详情

用MCP和Claude Code从零搭建Unity游戏:AI辅助开发完整工作流

📅 2026/10/12 14:51:55 | 华诺云谱 👁 阅读
用MCP和Claude Code从零搭建Unity游戏:AI辅助开发完整工作流
1. 开局一个空文件夹Claude Code 和 MCP 到底能干什么先交代一下背景。最近我给自己安排了一个小实验手里只有一台装好了常见开发工具的电脑桌面上新建了一个空文件夹名字就叫 Game01里面什么都没有。目标很明确——在不手动写业务代码的前提下用 Claude Code 配合 MCPModel Context Protocol模型上下文协议把 Unity 工程从零搭起来最后得到一个能打开、能操作、能通关的 3D 收集小游戏。这个实验的动机其实来自日常的挫败感。平时用大模型写代码最典型的问题是答案看着对但脚本就是跑不起来代码块复制过去了工程里一片红色报错。原因很简单模型再聪明它也看不见我硬盘上的文件结构不知道 Unity 编译到哪一步了更不知道运行时场景里到底发生了什么。MCP 就是用来补上这层手和眼的协议——它让 AI 不仅能聊天还能直接读写本地文件、执行命令、查询工具状态。Claude Code 在这条链路里的角色更像一个会写代码的施工队长MCP 则是它伸向外部世界的一组工具接口。配合 Unity 场景的话我可以让 AI 反复做这样一件事写脚本 → 触发编译 → 读错误日志 → 改代码 → 再编译直到编辑器日志里没有报错。整个过程我只负责确认方向、设定验收标准偶尔介入一下它改不动的地方。说句实话空文件夹这个卖点确实抓眼球但真正让我兴奋的是这套工作流沉淀下来之后对原型期开发的效率提升。今天这篇不聊什么宏大架构就把这个实验的完整过程拆开环境怎么配、三个 MCP 工具怎么分工、AI 建工程的核心工作流长什么样、以及哪些坑我替你先踩过了。提示我会假设你了解 Unity 的基本操作——新建项目、拖拽物体、给对象挂脚本。如果你连 C# 和 Unity 都不太熟也不影响你理解核心思路只是代码部分需要多看两眼。2. 环境准备三个 MCP 工具把 AI 变成看得见、动得了手的施工队2.1 Unity 版本选择与工程初始化我先装了 Unity Hub在 Hub 里创建了一个 3D Core 空模板项目选的版本是 Unity 6 LTS 对应的稳定版。没选最新预览版的原因很实际预览版的 C# API 和资源管理系统经常变AI 训练语料里的答案容易过时。LTS 版本虽然老一点但绝大多数社区资料和文档片段都能直接对上这对 AI 生成代码的命中率是决定性的——它写出来的 API 必须跟当前引擎版本一致才行。这里有一个绕开大坑的经验不要让 AI 去创建整个 Unity 工程而是先用 Unity Hub 正常创建一次空工程之后让 AI 只折腾 Assets 和 Packages 这两个目录。为什么因为一个完整的 Unity 工程不只是 Assets 和 ProjectSettings还有庞大的 Library 目录资源导入缓存、Logs 目录和 Temp 目录。Library 里的很多二进制索引是 Unity 自己管理的AI 手写不出来硬拼只会制造一堆不可逆的错误。正确姿势是Hub 创建一个干净工程AI 负责在合法边界内折腾。2.2 MCP 配置三个核心工具的分工与 JSON 写法Claude Code 的 MCP 配置维护在一个 JSON 文件里通常放在项目根目录的.mcp.json。我这次用的是项目级配置好处是换机器、换项目时不会污染其他工程。JSON 结构大致是这样{ mcpServers: { filesystem: { command: npx, args: [-y, modelcontextprotocol/server-filesystem, D:/Game01] }, executor: { command: npx, args: [-y, modelcontextprotocol/server-command, --allowed-dir, D:/Game01] }, unity-bridge: { command: node, args: [D:/Game01/Tools/unity-mcp-server/index.js] } } }我配了三类工具分工如下MCP 服务职责为什么要它filesystem读写工程内的文件、目录、JSONAI 写文件的基本操作原语executor/command执行 shell 命令触发 Unity 批处理编译、读取日志、跑 gitunity-bridge与打开中的 Unity 编辑器通信查询场景物体、触发 Play 模式、取回编辑器日志第三个 unity-bridge 严格来说不是官方标配而是社区里很多人做的桥梁服务。原理是在 Unity 编辑器里装一个本地 Socket 插件PC 端的 MCP 服务把 AI 的意图转成 JSON 请求发给编辑器编辑器执行完再回传结果。我在这个实验里自己写了个小号版本只实现了三个接口查询当前场景的物体列表、触发 Play 模式、读取 Editor.log 末尾 200 行。就这三个接口已经能让 AI 从盲写代码变成看着场景改代码。提示.mcp.json里的路径建议用绝对路径尽量避开带空格的中文目录。MCP 子进程的启动目录问题很折磨人我后面在踩坑部分会专门讲。2.3 为什么是三个工具而不是一个万能工具可能有人觉得把 filesystem、executor、unity-bridge 分开很啰嗦不如搞一个Unity 全功能 MCP一步到位。我一开始也这么想后来发现术业有专攻filesystem 读小文件快、不容易出错executor 适合跑 Unity 批处理和读日志unity-bridge 适合跟运行中的编辑器实时交互。把它们拆开任何一环出问题AI 能明确说出是读写失败还是执行超时而不是糊成一团报一个连接失败。分包还有一个隐藏好处就是权限控制。filesystem 我只给了它 D:/Game01 一个目录executor 也只在工程目录内执行命令这样即使 AI 产生幻觉想把整块磁盘删了它也没那个权限。做原型可以适当放开但底线不能丢。3. 让 AI 自己建工程骨架从目录结构到核心脚本3.1 开工第一件事让 AI 先交一份施工方案环境配好后我在 Claude Code 里发的第一条指令不是做一个游戏而是检查 D:/Game01 工程给出当前 Assets 目录结构然后规划一个小型 3D 收集游戏的完整文件清单。为什么要先要清单因为 AI 如果不知道 Unity 工程里哪些文件是必需、哪些是可选的很容易漏掉 Packages/manifest.json 或 ProjectSettings 里的关键配置。它给出来的清单长这样Assets/ Scenes/Main.unity Scripts/PlayerController.cs Scripts/CameraFollow.cs Scripts/Gem.cs Scripts/GameManager.cs Scripts/Enemy.cs Editor/LevelBuilder.cs Packages/manifest.json ProjectSettings/ProjectVersion.txt这个清单其实暴露了 AI 的一个习惯——它倾向于把可配置数据全写死在 MonoBehaviour 里。对原型来说没问题但如果你要做正经项目需要提前在 prompt 里加约束比如把数值配置抽到 ScriptableObject。这个后面再展开。3.2 PlayerController 的第一版与手感改造这一节是整个实验的重头戏。AI 生成的第一版 PlayerController.cs 长这样using UnityEngine; public class PlayerController : MonoBehaviour { public float moveSpeed 8f; public float jumpForce 5f; private Rigidbody rb; private bool isGrounded true; void Start() { rb GetComponentRigidbody(); } void Update() { float h Input.GetAxisRaw(Horizontal); float v Input.GetAxisRaw(Vertical); Vector3 move new Vector3(h, 0f, v).normalized; rb.velocity new Vector3(move.x * moveSpeed, rb.velocity.y, move.z * moveSpeed); if (Input.GetButtonDown(Jump) isGrounded) { rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse); isGrounded false; } } void OnCollisionEnter(Collision collision) { if (collision.gameObject.CompareTag(Ground)) { isGrounded true; } } }乍一看挺正常但往细了说有三个影响能玩的问题。第一它用Update处理物理移动规范做法是用FixedUpdate不然角色高速移动时刚体穿透和碰撞判断都会出问题。第二isGrounded只在碰撞进入时置 true如果角色从平台边缘走出去会保留在地面上的错误状态导致空中可以无限连跳。第三直接改rb.velocity会跟外力互相覆盖出斜坡时速度会突然乱飙。这些如果只是让 AI 输出一段代码它大概率不会意识到。但我的流程是写完 → 批处理编译 → 通过 unity-bridge 进入 Play 模式跑 3 秒 → 查日志闭环跑下来控制台没报错可我一上手玩就发现手感稀烂。于是我把测试感受写成自然语言反馈给它角色离地后还能再跳一次下坡时速度会突然变快撞到墙有轻微卡顿希望移动时有加速度而非瞬间满速。AI 收到反馈后改成FixedUpdate 射线检测地面 加速度插值的写法手感立刻不一样了。这一步让我深刻体会到MCP 闭环的价值不是让 AI 一次写对而是让 AI 能看到反馈然后改对。3.3 CameraFollow盯住目标但别让玩家头晕第二个脚本是 CameraFollow。这个脚本的坑比想象中多。AI 最初给的是非常流行的直接把摄像机设成角色的子物体但这种方式在 3D 物理场景里会抖动因为刚体更新和摄像机渲染不在同一帧。修正后的版本using UnityEngine; public class CameraFollow : MonoBehaviour { public Transform target; public float smoothTime 0.12f; private Vector3 velocity Vector3.zero; private Vector3 offset; void Start() { offset transform.position - target.position; } void LateUpdate() { Vector3 targetPos target.position offset; transform.position Vector3.SmoothDamp(transform.position, targetPos, ref velocity, smoothTime); } }LateUpdate配合SmoothDamp是标准做法不在物理更新阶段追目标而在所有物体更新完之后的 LateUpdate 里做平滑追赶玩家不会觉得镜头在甩头。还有一个细节offset必须在 Start 里基于初始位置记录而不是硬编码new Vector3(0, 10, -10)。这样在场景里调整过摄像机初始位置后不需要改任何代码。AI 如果不被提醒很容易写出死坐标一挪摄像机就全歪。4. 场景装配不手写 YAML用 Editor 脚本让 AI搭积木4.1 手写 .unity 场景文件的失败尝试到了场景装配这一步我遇到了整个实验里最诡异的坑。AI 第一次尝试直接生成.unity文件内容是 YAML 格式的 GameObject、Transform、MeshRenderer 条目。我让它把文件放到 Assets/Scenes 下然后在 Unity 里打开——编辑器直接报scene file is corrupt连场景画面都不给看。原因其实很经典。Unity 场景文件开头有%YAML 1.1 %TAG !u! tag:unity3d.com,2011:然后每个对象都带m_FileID: FileID: ...这样的引用链。这些 FileID 和 GUID 不是随意生成的它们必须和对应资源的.meta文件里的 GUID 严格一致。AI 可以仿写 YAML 的样子但不可能凭空知道某个材质的 GUID除非它先通过 filesystem 去读工程里所有的.meta文件。就算读了只要有一处引用写错Unity 就会把整个场景标记为损坏。这是典型的文本长得像但语义上不可靠纯手写场景文件这条路我直接判了死刑。4.2 用 Editor 脚本绕过 YAML 深渊既然手写 YAML 不行那就让 AI 写一个Editor/LevelBuilder.cs——一个通过[MenuItem]暴露在菜单里的工具脚本在编辑器里执行时自动把整个场景搭出来。完整流程变成AI 通过 filesystem 写 LevelBuilder.cs手动或让 executor 触发在 Unity 菜单里点一下 Tools/LevelBuilder/Build LevelLevelBuilder 用new GameObject()、AddComponentT()、EditorSceneManager.SaveScene()创建并保存场景所有 GUID 和引用关系由 Unity 编辑器自动管理完全绕开手写 YAML。这个方案的本质是让 AI 站在官方 C# API 之上搭积木而不是在文本流里碰运气。下面是一个简化版using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; public static class LevelBuilder { [MenuItem(Tools/LevelBuilder/Build Level)] public static void Build() { Scene newScene EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single); // 地面 GameObject ground GameObject.CreatePrimitive(PrimitiveType.Cube); ground.name Ground; ground.transform.position Vector3.zero; ground.transform.localScale new Vector3(30f, 1f, 30f); ground.tag Ground; // 玩家 GameObject player GameObject.CreatePrimitive(PrimitiveType.Capsule); player.name Player; player.transform.position new Vector3(0f, 1f, 0f); var rb player.AddComponentRigidbody(); rb.mass 1f; rb.collisionDetectionMode CollisionDetectionMode.Continuous; player.AddComponentPlayerController(); // 主相机 Camera cam Camera.main; if (cam ! null) { var follow cam.gameObject.AddComponentCameraFollow(); follow.target player.transform; cam.transform.position player.transform.position new Vector3(0f, 8f, -10f); } // 一串宝石 for (int i 0; i 8; i) { GameObject gem GameObject.CreatePrimitive(PrimitiveType.Sphere); gem.name Gem_ i; gem.transform.position new Vector3(i * 2f - 7f, 1.5f Mathf.Sin(i) * 0.5f, 0f); gem.AddComponentGem(); } EditorSceneManager.SaveScene(newScene, Assets/Scenes/Main.unity); AssetDatabase.Refresh(); } }注意 Player 部分Rigidbody的collisionDetectionMode我特意改成 Continuous。AI 一开始写的是默认值离散小球快速下落时会直接穿过地面。这个细节如果靠人来翻文档要花一些时间但因为我把能玩放进了验收标准里AI 被逼着考虑这种边界情况才补上了这一行。4.3 编译、批处理、日志三件套场景脚本写完就要验证。这时候不能每次都让 AI 打开 Unity 编辑器去点按钮太慢了。更高效的是 Unity 的命令行批处理模式Unity.exe -batchmode -projectPath D:/Game01 -executeMethod LevelBuilder.Build -quit -logFile D:/Game01/Logs/build_level.logexecutor MCP 负责跑这条命令跑完再用 filesystem 去读Logs/build_level.log的末尾。如果编译失败日志里会有error CS1234:这样的片段AI 读完后会顺着修代码然后再次触发编译。这一步是整个 MCP 工作流的闭环发动机写代码 → 编译 → 读日志 → 修代码循环往复。实际操作中有几个容易翻车的参数细节-executeMethod后面的类名必须是静态类里的静态方法-batchmode下不要调用任何需要弹窗的 API否则编辑器会挂起等待人工确认-quit要写在-executeMethod之后否则方法还没执行完编辑器就退了日志路径最好用绝对路径否则默认落到系统临时目录AI 找不到。5. 从能跑到能玩手感、状态管理、反馈三座大山5.1 手感AI 最容易忽略的隐藏工作量编译通过、场景能打开离能玩还有一大段距离。我第一次跑起来的感觉是角色像在冰面上滑行跳跃软绵绵吃掉宝石完全没有成就感。这些都是能跑和能玩的差距。我整理了一张参数调优表让 AI 按这张表逐项改参数初始值最终值说明moveSpeed86太高导致转弯困难acceleration无12让角色加速而非瞬间满速jumpForce57.5配合加重力后跳跃更干脆gravityScale12.2下落更快落点更可控smoothTime0.120.18摄像机稍微跟手一点调参方法是笨但有效的改一个参数 → 批处理编译 → unity-bridge 触发 Play 模式 1 秒 → 抓帧截图 → 让 AI 看图 → 再改。MCP 可以把 Unity 的 Game 视图截图转成临时 PNG 文件再让 filesystem 把图片路径交给 AI。这一步让 AI 从盲调变成看图说话它看到角色在地面上悬空又落下就会意识到是重力或者地面检测出了问题。能看图这种能力对调游戏手感是质的飞跃。5.2 状态管理GameManager 不能只写一个单例第三个核心脚本是 GameManager负责游戏开始、收集进度、游戏结束三个状态。AI 一开始写成了经典单例using UnityEngine; public class GameManager : MonoBehaviour { public static GameManager Instance { get; private set; } public int totalGems 8; public int collectedGems 0; void Awake() { if (Instance ! null Instance ! this) { Destroy(gameObject); return; } Instance this; } public void AddGem() { collectedGems; if (collectedGems totalGems) { Time.timeScale 0f; } } }单例本身没问题但注意一个隐蔽的坑如果GameManager放在场景里而玩家在胜利时切换到另一个场景这个Instance就没了。更稳的方案是DontDestroyOnLoad或者干脆保持单一场景。原型阶段我选了后者省掉一堆场景切换的引用问题。另外用Time.timeScale 0f暂停游戏虽然方便但它会把所有跟Time.deltaTime相关的逻辑一起冻住UI 的飘字动画也会卡住后来我改成了用状态机的isGameOver变量来控制而不是冻结时间。5.3 反馈密度飘字、音效、粒子AI 不会主动想到一个游戏好不好玩很大程度取决于反馈密度。AI 在这方面的弱点是它不会主动想到你得在 prompt 里按优先级排队飘字 → 音效 → 粒子 → 屏幕震动。我这次加了两个最简单的反馈吃掉宝石时玩家头顶飘出 1 的 UI Text0.5 秒后淡出敌人碰到玩家时玩家闪烁红色并被击退。这两个效果代码都很短但体感立竿见影。AI 第一次写的飘字是用 Canvas 动态创建 Text没有处理 Canvas 的渲染模式和缩放字跑到屏幕外面去了。这一看就是AI 看着截图调才修好的问题。没有截图反馈这种位置类 bug 靠文字描述能来回折腾好几轮。6. 踩坑实录这 7 个坑我替你先踩了6.1 .meta 文件与 GUID 引用断裂这是 AI 操作 Unity 工程最大的坑没有之一。Unity 里的每个资源文件都对应一个隐藏的.meta文件里面记录着 GUID。如果 AI 用 filesystem 复制、移动、删除文件时没同步处理.metaUnity 刷新后要么找不到引用要么一堆组件显示 Missing Script。我遇到的具体场景AI 想重命名 PlayerController.cs用命令把它改成 PlayerControl.cs但没动旁边的 PlayerController.cs.meta。Unity 刷新后场景里所有挂在 PlayerController 上的组件全部变灰。正确修法不是自己改 meta——那个 GUID 冲突了没人能保证全局唯一而是让 AI 通过编辑器侧完成重命名比如用 unity-bridge 调用AssetDatabase.RenameAsset。以后凡是涉及 Assets 目录下文件的改名、移动都走编辑器 API不要直接动文件系统。6.2 手写 .unity 场景 YAML 的伪成功前面说了手写 YAML 导致 corrupt但还有更隐蔽的情况AI 写的 YAML 文本层面能被 Unity 解析但物体层级错乱、引用丢失场景打开后一片空白或者只有一个空节点。这种比直接报错还难查因为你以为成功了运行起来全是空引用异常。我的建议是无论场景复杂还是简单一律用 Editor 脚本生成。不手写 YAML 是底线没有例外。6.3 批处理模式的卡死与会话残留Unity 的-batchmode有时候会卡在 Importing Assets 或 Reloading Scripts 阶段尤其是第一次导入资源时。我遇到一次批处理命令跑了 3 分钟没退出executor MCP 一直挂着最后差点超时断连。两个解决办法一是给 executor MCP 设置命令超时比如 60 秒二是在命令里加硬性护栏比如在批处理前先清理Temp目录。如果上次批处理意外崩溃残留的Temp和日志文件会影响下一次启动干脆先rd /s /q D:/Game01/Temp再启动。6.4 社区 MCP 工具与 Unity 版本的兼容性社区里的 unity-bridge 工具质量参差不齐。我自己写的最小实现只有三个接口工作量不大但有一条必须记住Socket 传输的 JSON 里不要塞二进制数据。截图这种内容先写成临时 PNG 文件再把路径传给 AI让 filesystem 去读文件。直接在 JSON 里 base64 编码会瞬间撑爆上下文窗口模型回答质量立刻下降会话也会变得很迟缓。6.5 主题窗口MCP 不是接得越多越好Claude Code 的上下文窗口是有限的每次 MCP 工具调用返回的内容都会占用上下文。如果给 filesystem 开放整块磁盘且不做目录过滤AI 光读 Library 目录下的缓存文件就能把窗口塞满。我只开放了 Assets、Packages、Logs 三个子目录其余路径全部在配置里加排除规则。另外我要求 AI 在每次读日志时只取末尾 200 行不要一次性读整个文件。这些小习惯能显著延长单次会话的有效时长不然每 10 分钟就得开新会话重新带入项目背景效率非常受伤。6.6 中文路径与编码噩梦.mcp.json里不要用带空格的中文路径这条我用实际教训换来了。Windows 上如果工程放在D:\游戏项目\下Unity 批处理经常因为编码问题找不到脚本文件。既然工作流里大量依赖 shell 命令路径里就尽量不要出现中文和空格。这不是偏见是帮 AI 省掉一次无意义的报错排查。6.7 AI 的自我感觉良好是头号杀手最后一个坑是心态层面的。AI 在连续两轮编译通过后会非常自信地告诉你功能已经完善。但实际上它可能只验证了编译没验证运行逻辑。我培养出的对策是每一个功能都必须有可观测的数据验收。比如游戏胜利不能只看代码逻辑要让 AI 自己描述——如果玩家吃掉 8 个宝石界面上哪一行数字变成 8控制台输出什么。拿不到完整闭环描述就不算做完。7. 这套工作流的边界与可复制性实验做到最后我的 Game01 文件夹里多了一个约 100MB 的 Unity 工程里面是一个能跑的 3D 收集游戏玩家移动跳跃吃宝石有一个来回巡逻的敌人碰到会闪红并击退收集满 8 个宝石后显示胜利界面。整个过程里我手动做的事情只有三件安装 Unity Hub、创建空工程、在编辑器菜单里点了一次 LevelBuilder。但这个结论不能过度夸大。我要诚实地划出几条边界第一美术资源没有一个是我让 AI 直接生成的用的都是 Unity 内置原生几何体和免费基础素材第二玩法设计从一开始就很明确AI 是在我的验收标准下做实现而不是从零发明玩法第三整个工程代码量大概在 400 行左右属于典型小原型这个量级 AI 完全可以驾驭但如果目标是几万行的商业化项目MCP 工作流只能解决局部模块解决不了架构治理。那什么时候这套工作流最划算我自己的判断是Game Jam 原型、新玩法验证、工具链脚本、教学 Demo。这些场景的共同点是需求变化快、规模小、允许丑但可玩。如果你在做大型多人项目MCP 更适合用在让 AI 辅助写单元测试、生成关卡数据、批量处理资源这些局部环节而不是让它接管整个工程。最后分享一个我实验后沉淀下来的习惯每次开工前我会把验收标准写成一页README_ACCEPTANCE.md放到工程根目录里用列表写清楚能运行、能操作、能通关、无报错的具体含义。AI 通过 filesystem 能直接读到这份文件它会自己对照检查而不是等我逐字提醒。这个习惯价值很大相当于给 AI 装了一个踩坑提醒器省下来的沟通成本远超写那份文件用的十分钟。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑