用Claude Code与MCP构建Unity潜行游戏:敌人AI状态机实战

发布时间:2026/10/7 10:41:06
用Claude Code与MCP构建Unity潜行游戏:敌人AI状态机实战 最近把 AI 编程工具接到 Unity 开发流程里做潜行类游戏原型时我发现一个很实用的组合Claude Code 负责写代码、改逻辑MCP 协议负责让 AI 真正“看懂”Unity 项目结构再加上一套清晰的敌人行为系统与潜行机制整个开发效率比传统手写脚本的方式高不少。这篇教程就把这套流程完整拆开来讲包含环境配置、MCP 原理、玩家潜行控制器、敌人 AI 状态机、以及 AI 智能代理工作流适合想尝试 AI 辅助 Unity 开发的读者也适合正在做潜行、塔防、敌人巡逻类玩法的开发者参考。1. 背景AI 编程工具如何改变 Unity 开发方式1.1 Claude Code 是什么Claude Code 是 Anthropic 推出的命令行 AI 编程代理工具。它不只停留在“对话补全代码”这一层而是可以运行在终端环境里读取项目目录、分析文件、修改代码、执行命令甚至根据测试结果自动修复问题。在 Unity 项目中Claude Code 能做的事情包括读取Assets/Scripts下的 C# 脚本理解现有系统。按照自然语言指令新增状态机、AI 逻辑、关卡管理等脚本。批量重构命名不规范的代码。解释某个脚本的功能帮助开发者快速上手陌生项目。根据报错信息定位并修复编译错误。它本质上是一个“跑在你电脑上的 AI 工程师”而不是网页里的一次性问答工具。正因为它能读取上下文所以特别适合放进 Unity 这类大型工程中工作。1.2 MCP 解决了什么问题MCPModel Context Protocol模型上下文协议是 Anthropic 提出的开放式协议作用是让 AI 模型与外部工具、数据源之间建立标准化的连接。你可以把 MCP 理解成 AI 世界的“USB 接口”。没有 MCP 时AI 只能看到文本看不到系统的真实状态有了 MCPAI 可以通过标准接口调用工具、读取日志、操作文件、触发外部命令。在 Unity 开发场景中MCP 的意义非常明显Unity 工程包含大量.cs、.unity、.prefab、.asset文件纯文本对话无法感知这些文件之间的关系。MCP 服务器可以把 Unity 项目结构、场景对象列表、控制台日志、脚本内容暴露给 Claude Code。开发者可以在聊天窗口里让 AI 搜索场景中挂载了某个脚本的物体或者让 AI 生成一段读取PlayerPrefs的工具方法AI 会基于真实工程数据输出结果而不是凭空猜测。这里要特别强调一个容易混淆的概念Claude Code 和 MCP 并不是一回事。Claude Code 是 AI 编程工具MCP 是它连接外部世界的数据协议。没有 MCPClaude Code 也可以直接读写项目文件接入了 MCP它才能更方便地感知 Unity 场景结构、资源 GUID、项目设置等深层信息。1.3 潜行机制与行为系统的典型场景本文实战部分会围绕一个潜行小游戏展开这是非常适合学习 AI 辅助开发的题材因为它包含第一人称或第三人称控制。玩家蹲伏、移动、躲避敌人视线。敌人巡逻、警戒、追击、返回巡逻点。视野锥检测障碍物遮挡判断。胜利条件与失败条件管理。这些玩法在现代动作冒险、恐怖、潜行类游戏中都大量出现。掌握了基础状态机后还能扩展到更多敌人类型比如远程敌人、守卫犬、摄像头等。2. 环境准备与版本说明2.1 安装 Claude CodeClaude Code 的安装方式会随版本迭代更新本文不写死某一条命令但给出当前最常见的方式。整体流程是安装 Node.js然后通过 npm 安装 Claude Code 命令行工具最后完成账号认证。# 安装 Node.js 后在终端中执行 npm install -g anthropic-ai/claude-code安装完成后在终端输入claude首次运行会引导你完成认证。认证方式一般是你自己的 Claude 账号授权或者配置 API Key。具体以官方文档为准因为这套认证流程更新比较频繁。如果你在 VS Code 中使用也可以安装对应的扩展在编辑器内打开终端直接运行 Claude Code。对于 Unity 开发场景建议在 Unity 项目根目录打开终端这样 AI 能直接看到Assets、Packages、ProjectSettings等目录。需要提醒的是Claude Code 的版本更新速度很快如果某个命令突然失效优先去官方文档确认最新用法不要在网上复制旧命令硬跑。2.2 准备 Unity 项目本文示例以 Unity 6000.xUnity 6 系列为基础用 URP 模板创建项目。如果你使用的是 Unity 2022 LTS 或 2023 LTS代码基本也能通用因为本案例没有使用太多新版本专属特性。创建项目后建议先规划好目录结构StealthDemo/ ├─ Assets/ │ ├─ Scenes/ # Main.unity │ ├─ Scripts/ │ │ ├─ Player/ # 玩家控制 │ │ ├─ Enemy/ # 敌人AI │ │ └─ Game/ # 游戏管理 │ ├─ Prefabs/ │ └─ Materials/ ├─ Packages/ │ ├─ manifest.json ├─ ProjectSettings/ └─ .claude/这个目录结构不是强制要求但清晰的结构会让 AI 在分析工程时更高效。如果你把十几个脚本全扔在一个 Scripts 文件夹里AI 也能处理只是任务复杂时容易混乱。2.3 配置 MCP 服务器接入 MCP 前先要有一个 MCP 服务器端。社区中有多种 Unity MCP 实现它们的作用大同小异把 Unity 工程的能力通过 MCP 暴露给 Claude Code。一个典型的 Unity MCP 服务器可以提供类似这些工具能力列出项目中的所有.unity场景。解析场景中的 GameObject 列表。读取某个对象的 Transform 信息或挂载脚本。返回 Unity 控制台最近的日志。执行自定义命令比如调用编辑器静态方法。配置 MCP 服务器时不同工具的配置文件位置不一样。Claude Code 可以通过项目级配置声明 MCP 服务器大致格式如下{ mcpServers: { unity-mcp: { command: dotnet, args: [ run, --project, C:/Dev/UnityMCPServer/UnityMCPServer.csproj ], env: { UNITY_PROJECT_PATH: C:/Dev/StealthDemo } } } }这段配置告诉你三个关键信息command是启动 MCP 服务器的可执行程序这里是dotnet。args是启动参数表示运行某个 C# 项目。env是传递给服务器的环境变量一般需要指定 Unity 项目的绝对路径。注意不要直接复制这套配置因为不同 MCP 服务器的启动方式差异很大。重点理解格式然后去你使用的 MCP 项目文档中确认准确命令。3. MCP 核心原理AI 如何“操作”Unity3.1 MCP 的三个核心概念MCP 协议里最重要的一组概念是资源Resources、工具Tools、提示词Prompts。资源是 AI 可以读取的外部信息比如一个文件内容、一张图片、一段日志。在 Unity 场景中资源可以是PlayerController.cs的源码也可以是场景 YAML 文件的一部分。工具是 AI 可以调用的函数。比如“运行 Unity 构建命令”“读取目录下所有预制体名称”“在场景文件中查找 MonoBehaviour”。AI 可以根据对话目标自动决定是否调用工具。提示词是预定义的指令模板。你可以把“生成敌人巡逻状态机”这类高频需求存成模板下次直接复用。理解这层关系后你会更容易看懂 MCP 服务器的 README。它本质上就是在实现我提供哪些资源、哪些工具、哪些提示词给 AI。3.2 Unity MCP 的两种工作模式目前 Unity MCP 的集成方式大致分两类。第一种是“文件感知型”。MCP 服务器不直接操作 Unity 编辑器而是读取项目文件。比如从Assets/Scenes/Main.unity的 YAML 中解析出有哪些 GameObject从.cs文件中提取类名从ProjectVersion.txt读取 Unity 版本。这种方式实现简单稳定适合代码生成和结构分析。第二种是“编辑器联动型”。Unity 编辑器内运行一个本地通信服务通过 WebSocket 或 TCP 与 MCP 服务器通信。这样 AI 能实时获取运行时的场景状态、Console 日志、选中物体信息甚至触发编辑器菜单。能力更强但实现复杂度也更高。实际开发中两种模式往往是互补的。如果你只是让 AI 写脚本、重构代码文件感知型足够如果你想让 AI 回报“当前场景中选中的物体叫什么名字”就需要编辑器联动型。3.3 配置后的使用效果完成 MCP 配置后进入 Claude Code 对话窗口AI 会自动判断哪些场景需要调用 MCP 工具。比如你输入我想给场景里所有挂载 EnemyAI 的敌人滚动的速度请帮我写一段批量修改的编辑器脚本。如果 MCP 服务器提供了场景解析工具AI 会先调用工具查看场景中有哪些敌人再生成对应的批量替换脚本。这就是 MCP 和纯文本补全的差别它确实“看到”了项目内容。4. 实战一玩家潜行控制器4.1 需求拆解潜行游戏的第一步是玩家控制。本案例我们不使用 CharacterController 的标准移动而是重点实现几个潜行要素正常走路和蹲伏两种速度。移动速度影响敌人侦测范围。按下 Ctrl 蹲伏屏幕略下降并且角色高度降低。暂停菜单使用Time.timeScale。先创建脚本目录Assets/Scripts/Player/PlayerController.cs。4.2 完整的玩家控制器代码using UnityEngine; namespace StealthGame.Player { [RequireComponent(typeof(CharacterController))] public class PlayerController : MonoBehaviour { [Header(移动参数)] public float walkSpeed 3f; public float crouchSpeed 1.2f; public float runSpeed 5f; public float turnSpeed 2f; [Header(蹲伏设置)] public float crouchHeight 1f; public float standHeight 2f; public float heightLerpSpeed 10f; private CharacterController controller; private float verticalVelocity; private bool isCrouching; private Camera playerCamera; private void Awake() { controller GetComponentCharacterController(); playerCamera GetComponentInChildrenCamera(); } private void Update() { if (Time.timeScale 0f) return; HandleCrouch(); HandleMove(); } private void HandleCrouch() { isCrouching Input.GetKey(KeyCode.LeftControl); if (isCrouching Time.timeScale 0f) return; float targetHeight isCrouching ? crouchHeight : standHeight; float currentHeight Mathf.Lerp(controller.height, targetHeight, Time.deltaTime * heightLerpSpeed); controller.height currentHeight; if (playerCamera ! null) { float targetCameraY isCrouching ? 0.6f : 1.6f; Vector3 localPos playerCamera.transform.localPosition; localPos.y Mathf.Lerp(localPos.y, targetCameraY, Time.deltaTime * heightLerpSpeed); playerCamera.transform.localPosition localPos; } } private void HandleMove() { float horizontal Input.GetAxisRaw(Horizontal); float vertical Input.GetAxisRaw(Vertical); Vector3 direction new Vector3(horizontal, 0, vertical); direction.Normalize(); direction transform.TransformDirection(direction); float speed isCrouching ? crouchSpeed : walkSpeed; if (!isCrouching Input.GetKey(KeyCode.LeftShift)) { speed runSpeed; } controller.Move(direction * speed * Time.deltaTime); if (direction.magnitude 0.01f) { Quaternion targetRotation Quaternion.LookRotation(direction); transform.rotation Quaternion.Slerp( transform.rotation, targetRotation, Time.deltaTime * turnSpeed); } } public bool IsCrouching() { return isCrouching; } } }这段代码里有几个关键点Time.timeScale 0f的判断保证了游戏暂停时玩家不能移动。这在潜行游戏暂停菜单中很常见但是要注意如果你在 Update 里用了不受Time.deltaTime影响的逻辑暂停时可能会产生意外行为。direction transform.TransformDirection(direction)把输入轴转换到世界空间让 WASD 对应摄像机的前后左右。用Mathf.Lerp平滑改变CharacterController.height避免蹲伏时瞬移穿模。使用CharacterController时改变高度前务必确保角色底部没有卡进地板。4.3 运行验证在场景中创建一个空物体作为玩家挂上CharacterController组件把PlayerController拖到该物体上并让 Main Camera 成为它的子物体。运行时按 WASD 移动按 Ctrl 蹲伏按 LeftShift 加速。如果摄像机和角色没有跟随鼠标旋转说明当前脚本还没有处理鼠标视角。潜行游戏通常需要灵活的视角控制你可以把视角旋转放到组件上也可以用鼠标控制private void HandleLook() { float mouseX Input.GetAxis(Mouse X); Vector3 newRotation transform.eulerAngles; newRotation.y mouseX * turnSpeed; transform.eulerAngles newRotation; }这里注意transform.eulerAngles的 Y 轴累加方式在某些特殊角度会出现万向锁问题但中小型项目完全够用。如果做专业 FPS建议使用Transform.Rotate或者四元数累加。5. 实战二敌人巡逻与追击行为系统5.1 行为状态机设计敌人 AI 的状态机是潜行游戏的核心。链路不能太简单也不能太复杂。我建议第一版只做四个状态Patrol沿巡逻点匀速行走检测玩家是否进入视野。Alert听到噪声或者看到可疑目标短暂停留并转向声源。Chase追击玩家持续更新最后已知位置。Return丢失目标后返回巡逻路线起点。状态之间的切换逻辑Patrol - 看到玩家 - Chase Chase - 丢失目标超过距离 - Alert Alert - 未找到玩家 - Return Return - 回到巡逻路径 - Patrol设计时记住一个原则状态机应该由敌人自己的行为驱动而不是由玩家直接调用。也就是说GameManager不应该直接改EnemyAI.state敌人应该自己维护状态。5.2 敌人 AI 核心代码using UnityEngine; namespace StealthGame.Enemy { public enum EnemyState { Patrol, Alert, Chase, Return } [RequireComponent(typeof(CharacterController))] public class EnemyAI : MonoBehaviour { [Header(巡逻设置)] public Transform[] patrolPoints; public float patrolSpeed 1.8f; [Header(侦测设置)] public float viewRadius 10f; public float viewAngle 70f; public float detectCrouchMultiplier 0.6f; [Header(追击设置)] public float chaseSpeed 3.5f; public float loseTargetDistance 15f; [Header(警戒设置)] public float alertTime 3f; public Transform player; private EnemyState state EnemyState.Patrol; private int currentPointIndex; private CharacterController controller; private Vector3 lastKnownPosition; private float alertTimer; private bool sawPlayer; private void Awake() { controller GetComponentCharacterController(); } private void Update() { switch (state) { case EnemyState.Patrol: UpdatePatrol(); break; case EnemyState.Alert: UpdateAlert(); break; case EnemyState.Chase: UpdateChase(); break; case EnemyState.Return: UpdateReturn(); break; } CheckPlayerVisible(); } private void UpdatePatrol() { if (patrolPoints.Length 0) return; Transform target patrolPoints[currentPointIndex]; MoveTo(target.position, patrolSpeed); if (Vector3.Distance(transform.position, target.position) 0.8f) { currentPointIndex (currentPointIndex 1) % patrolPoints.Length; } } private void UpdateAlert() { MoveTo(transform.position, 0f); alertTimer - Time.deltaTime; if (sawPlayer) { state EnemyState.Chase; } else if (alertTimer 0f) { state EnemyState.Return; } } private void UpdateChase() { if (player null) return; float distance Vector3.Distance(transform.position, player.position); if (distance loseTargetDistance) { lastKnownPosition player.position; MoveTo(lastKnownPosition, chaseSpeed); } else { state EnemyState.Return; alertTimer 2f; } } private void UpdateReturn() { if (patrolPoints.Length 0) return; MoveTo(patrolPoints[currentPointIndex].position, patrolSpeed); if (Vector3.Distance(transform.position, patrolPoints[currentPointIndex].position) 0.8f) { state EnemyState.Patrol; } } private void CheckPlayerVisible() { if (player null) return; Vector3 targetDir player.position - transform.position; float distance targetDir.magnitude; if (distance viewRadius) { sawPlayer false; return; } float angle Vector3.Angle(transform.forward, targetDir.normalized); float maxAngle viewAngle * 0.5f; PlayerController playerController player.GetComponentPlayerController(); if (playerController ! null playerController.IsCrouching()) { maxAngle * detectCrouchMultiplier; } if (angle maxAngle) { sawPlayer false; return; } RaycastHit hit; if (Physics.Raycast(transform.position Vector3.up * 0.8f, targetDir.normalized, out hit, distance)) { if (hit.transform.root player.root) { sawPlayer true; lastKnownPosition player.position; if (state EnemyState.Patrol) { state EnemyState.Alert; alertTimer 2f; } } else { sawPlayer false; } } } private void MoveTo(Vector3 targetPos, float speed) { Vector3 direction targetPos - transform.position; direction.y 0; if (direction.magnitude 0.1f) { return; } Vector3 normalizedDir direction.normalized; controller.Move(normalizedDir * speed * Time.deltaTime); Quaternion targetRotation Quaternion.LookRotation(normalizedDir); transform.rotation Quaternion.Slerp( transform.rotation, targetRotation, Time.deltaTime * 4f); } public EnemyState GetState() { return state; } } }这个版本已经具备一个完整潜行敌人的核心行为。值得注意的几个设计点CheckPlayerVisible放在 Update 最后统一执行而不是某个状态内部。这样 Patrol、Chase、Return 过程中都能持续侦测玩家状态切换及时。detectCrouchMultiplier实现了蹲伏降低被侦测概率。玩家蹲下时敌人视野角度变小潜行玩法才有存在意义。MoveTo方法统一封装了移动和朝向逻辑。四个状态只需要传入目标点和速度状态机代码更清爽。5.3 视野可视化与调试视线检测逻辑写好后调试难度会上升。因为你看不到敌人到底看到了哪里。推荐加一个可视化工具方法使用 Unity 自带的 Gizmosprivate void OnDrawGizmosSelected() { Gizmos.color Color.yellow; Vector3 forward transform.forward; Quaternion leftRot Quaternion.Euler(0, -viewAngle * 0.5f, 0); Quaternion rightRot Quaternion.Euler(0, viewAngle * 0.5f, 0); Vector3 leftDir leftRot * forward; Vector3 rightDir rightRot * forward; Gizmos.DrawLine(transform.position, transform.position leftDir * viewRadius); Gizmos.DrawLine(transform.position, transform.position rightDir * viewRadius); Gizmos.color Color.red; if (sawPlayer) { Gizmos.DrawLine(transform.position, lastKnownPosition); } }把这段方法加到EnemyAI.cs中在 Scene 窗口选中敌人时就能看到视野范围和最后一次看到玩家的位置。这是潜行类游戏开发中很实用的调试方式。5.4 把敌人 AI 接到场景中创建巡逻点时可以手动放置几个空物体也可以让 Claude Code 帮你生成创建的编辑器脚本。比如你需要快速在场景里生成一个环形巡逻路径可以直接让 AI 写using UnityEngine; using UnityEditor; public static class PatrolPathTool { [MenuItem(Tools/生成巡逻路径)] public static void CreatePatrolPath() { GameObject parent new GameObject(PatrolPath); for (int i 0; i 8; i) { GameObject point new GameObject(Point_ i); float angle i * Mathf.PI * 2 / 8f; Vector3 pos new Vector3(Mathf.Cos(angle) * 5f, 0, Mathf.Sin(angle) * 5f); point.transform.position pos; point.transform.SetParent(parent.transform); } Selection.activeObject parent; } }这种编辑器脚本非常适合交给 AI 生成。因为它短小、功能单一、出错后容易检查。6. 实战三AI 智能代理工作流6.1 为什么需要一套清晰的工作流Claude Code 功能很强但不代表输入一句话就能得到完美代码。我接触 AI 辅助开发时踩过最大的坑就是对 AI 提出过于庞大的需求结果生成的代码质量很差。比如这句输入帮我做一个潜行游戏包含敌人AI、玩家控制、道具系统、存档系统、菜单。这个任务对 AI 来说太模糊。它不是不能生成而是生成出来的东西往往没有结构互相之间耦合严重。正确的做法是把工作流拆成多个阶段每个阶段只解决一个明确问题。这套流程可以总结为明确任务边界我要做什么功能依赖哪些系统。让 AI 读取现状先读相关代码了解项目已经有什么。生成方案先不写代码让 AI 给出设计思路。落地编码按文件逐个生成。编译验证出现报错后继续让 AI 修复。人工 Review检查 AI 生成代码的质量和安全边界。6.2 用 Claude Code 执行工作流假设项目已经启动了你需要添加一个“敌人速度与玩家潜行速度联动”的功能。传统做法是打开代码自己改AI 辅助流程可以这样第一步让 AI 了解现状请读取 Assets/Scripts/Enemy/EnemyAI.cs 和 Assets/Scripts/Player/PlayerController.cs 列出敌人移动速度相关字段以及玩家移动速度相关字段。Claude Code 会读取这两个文件然后把字段列表整理出来。第二步让 AI 给方案我想实现玩家蹲伏时敌人巡逻速度降低 20%。请给出两个实现方案 一个是 Event 事件驱动一个是 Update 轮询说明各自的优缺点。AI 会给出方案你可以直接选择。这种方式比直接让它写代码可控得多。第三步落地编码采用 Update 轮询方案在 EnemyAI 中增加 crouchSpeedFactor 字段默认为 1 在 GameManager 中根据玩家状态设置该值。接下来 AI 会改动代码并在终端中尝试编译。Unity 的编译发生在编辑器进入 Play 模式时终端里不一定能直接看到编译错误所以更常见的做法是让 AI 修改代码后你切回 Unity 查看 Console 窗口再把报错贴给 AI。6.3 任务拆分示例下面是一套潜行游戏开发任务的拆分对照复杂任务拆分后的子任务搭建场景生成地面、墙体、灯光、巡逻点玩家控制移动、蹲伏、声音检测敌人 AI巡逻、视野、追击、返回游戏流程胜利检测、失败检测、重开数据配置视野半径、速度权重的 Excel/JSON 配置每个子任务里还要继续拆分“设计、编码、测试、调整”。把这样的流程固化下来AI 才能稳定发挥作用。7. 常见问题与排查思路7.1 MCP 连接失败或工具不生效问题现象常见原因解决思路Claude Code 无法加载 MCP 服务器配置路径错误检查配置中的路径是否真实存在工具能列出但调用时报错MCP 服务器依赖未安装单独启动 MCP 服务器看完整报错日志读取不到场景内容Unity 项目路径未传给服务器检查env中的UNITY_PROJECT_PATH工具超时场景文件过大YAML 解析慢换用文件感知型 MCP 或缩小场景规模排查 MCP 问题时有个固定顺序先确认 MCP 服务器自身能启动再确认 Claude Code 能列出工具最后才确认工具调用是否返回正常数据。跳步排查往往浪费时间。7.2 物体速度获取不到很多人在使用 AI 生成“获取物体速度”相关代码时遇到困惑。Unity 中不同的移动组件速度获取方式完全不同。如果是Rigidbody组件Rigidbody rb GetComponentRigidbody(); Vector3 velocity rb.velocity; float speed rb.velocity.magnitude;如果是CharacterController组件CharacterController controller GetComponentCharacterController(); Vector3 velocity controller.velocity; float speed controller.velocity.magnitude;如果是NavMeshAgentNavMeshAgent agent GetComponentNavMeshAgent(); float speed agent.velocity.magnitude;最容易踩的坑是物体挂载了CharacterController却去获取Rigidbody的velocity得到的永远是Vector3.zero。排查时先确认物体挂的是什么组件。7.3 粒子特效内存过高或卡顿潜行游戏中常需要烟雾、脚印、破坏特效。如果每次触发都创建一个新的ParticleSystem并让Play()之后自动销毁那么频繁触发时会产生大量分配GC 压力大。推荐做法是用对象池复用粒子系统初始化时创建 N 个粒子实例并隐藏。需要播放时从池中取一个设置位置后Play()。播放结束后关掉放回池中。另一个隐藏问题是ParticleSystem的Stop()行为。直接调用Stop()如果不带清理参数粒子和子发射器可能残留particleSystem.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);在潜行游戏里玩家隐藏时放出的烟雾如果反复残留会严重影响帧率。建议把粒子特效的管理交给对象池组件。7.4 AI 生成的代码与 Unity 版本不匹配Claude Code 训练数据覆盖了大量 Unity 历史版本但它不一定知道你当前使用的版本。比如它会生成使用UnityEngine.UI的旧版 API而你在 Unity 6 中使用 UGUI 时可能需要改命名空间或者推荐你改用 UI Toolkit。解决思路是在给 AI 输入任务时注明版本信息。建议每次会话开头都加一句环境说明Unity 版本6000.xUnity 6 渲染管线URP 输入系统旧的 Input Manager这样 AI 生成的代码兼容性会好很多。8. 最佳实践与工程建议8.1 给 AI 清晰的 Prompt 模板AI 写代码的效果很大程度上取决于输入信息的质量。我把自己在项目中常用的 Prompt 模板整理成四个固定段落第一个段落是环境信息包括 Unity 版本、渲染管线、输入系统、目标平台。第二个段落是需求描述用一两句话说清楚要做什么以及验收标准。第三个段落是约束条件说明哪些文件可以改、哪些不能动、是否使用对象池、是否要兼容移动端。第四个段落是输出要求比如“返回完整可用代码不要省略注释”“只输出核心脚本不要修改项目设置”。如果每个任务都按这个模板来写Claude Code 的产出稳定度会明显提高。8.2 版本管理与可回滚性AI 辅助开发最大的风险之一是它修改了多个文件后某些改动并不符合预期。因此版本控制要做得比平时更严格。建议使用 Git 时每个任务一个分支# 开始一个 AI 辅助任务前 git checkout -b feature/enemy-crouch-speed # 让 Claude Code 修改代码 # 检查完成后合并如果 AI 改坏了你可以直接丢弃整个分支而不需要逐行撤销。手动复制脚本文件作为备份也是办法但容易被覆盖远不如 Git 分支可靠。在实际项目中“最小权限”原则也适用不要一次性给 AI 修改整个项目目录的权限。如果工具允许配置白名单就只允许 AI 访问Assets/Scripts下的代码。涉及ProjectSettings、Packages/manifest.json时手动确认更稳妥。8.3 性能与安全边界AI 生成的代码在逻辑上可能正确但性能不一定过关。需要重点检查几个方面是否在Update中频繁GetComponent。AI 很容易生成这种代码数量少时没问题敌人一多就有压力。是否使用Resources.Load加载资源。移动端尤其要避免。是否创建大量临时字符串和 GameObject。建议每次 AI 修改完代码后让 AI 自己检查一遍请检查刚才生成的代码找出所有在 Update 中调用 GetComponent 的地方 并给出缓存优化方案。这个步骤虽然看起来多余但能提前拦截大量性能隐患。安全隐患同样值得注意。Autonomous AI Agent 有时会在终端中执行一些命令你要确保它不会执行删除文件、修改全局配置等危险操作。不要轻易授权 AI 执行任意 shell 命令尤其不要在项目目录下运行重置或清理类脚本。9. 总结与学习路线本文从 Claude Code 和 MCP 的背景开始完整走了一遍 Unity 潜行小游戏的 AI 辅助开发流程。你至少应该掌握这些内容Claude Code 的基本使用方式和它适合放到 Unity 工程中的理由。MCP 的资源、工具、提示词三大概念以及 Unity MCP 服务器的大致工作方式。玩家潜行控制器中蹲伏、速度、暂停逻辑的实现要点。敌人 AI 状态机的四个状态设计视野检测中的角度、距离、遮挡判断。如何用一套可复用的工作流让 AI 生成、修改、修复 Unity 代码。常见问题的排查方向包括 MCP 连接失败、速度获取、粒子内存、版本兼容。下一步值得尝试的是把行为系统升级成 Behavior Tree 或者 GOAP也就是做到“敌人会根据环境状态自主选择动作”比如发现玩家后先吹哨、呼叫同伴、再包抄。这类系统用 AI 辅助开发效果很好但需要更严谨的架构设计。如果你正准备在自己的 Unity 项目里接入 Claude Code 和 MCP建议从小的任务开始做好 Git 分支保护多让 AI 做代码阅读和方案讲解再让它动手修改。遇到报错时不要急着换方案把完整报错信息贴给 AI通常几次迭代就能解决。这篇教程里的代码可以直接复制到项目中跑通流程但你需要根据实际 Unity 版本和项目结构调整命名空间与路径。文中示例脚本的作用是演示“什么是完整的敌人 AI 状态机”和“ MCP 如何辅助开发”而不是提供一个放之四海而皆准的终极框架。掌握了这套思路后你可以让 MCP 服务器读取自己的场景数据也可以让 Claude Code 按你的需求持续生成新的敌人类型和玩法逻辑。