文章目录
  1. 1. 前言
  2. 2. MCP for Unity
    1. 2.1. 安装MCP for Unity
    2. 2.2. 启动MCP for Unity Server
    3. 2.3. 配置Codex Client
    4. 2.4. MCP for Unity使用
    5. 2.5. 自定义MCP for Unity工具
  3. 3. AI自动化实战
    1. 3.1. 创建指定字体的TMP字体文件
    2. 3.2. AI自动拆分效果图
      1. 3.2.1. 切图工具制作
      2. 3.2.2. AI自动拆分图片元素
      3. 3.2.3. 制作UI效果图元素拆分Skill
      4. 3.2.4. 使用ui-perfect-splitter Skill
    3. 3.3. AI自动拼UI
      1. 3.3.1. 重用已有UI预制件拼UI
      2. 3.3.2. Skill添加已有预制件重用规则描述
      3. 3.3.3. 添加项目已有预制件目录读取规则
      4. 3.3.4. 制作通用预制件
      5. 3.3.5. AI自动用已有预制件拼UI
    4. 3.4. 逻辑代码自动化
      1. 3.4.1. 模版+组件绑定代码生成
      2. 3.4.2. AI生成逻辑代码
  4. 4. Reference

前言

随着AI越来越强大,纯AI参与制作的流程(AI Vibe Coding,AI自动拼UI,AI自动生图,AI自动生成Psd分层等)越来越多。虽然Unity 6已经推出了官方AI和CLI等AI相关工具,但目前国内使用Unity官方AI有很多门槛限制,所以本章节主要以使用第三方Github的unity-mcp+Codex的方式去搭建AI自动化流程。

本章节主要研究一下几个问题:

  1. AI自动拆分效果图(解决UI图片资源快速生成问题)
  2. AI自动拼UI(节省拼UI时间,提升开发效率)
  3. AI生成UI逻辑代码(节省些基础UI代码逻辑的时间,提升开发效率)

MCP for Unity

Unity MCP是通过使用Model Context Protocol协议让Unity Editor担任一个Server的角色,然后暴露工具,场景管理,脚本编辑,Console数据读取等能力给AI。

MCP for Unity 通过 Model Context Protocol 把 Claude、Cursor、VS Code、本地大模型等 AI 助手接入 Unity 编辑器,让它们直接帮你管理资源、搭场景、写脚本、跑测试,把开发流程里的重复活儿都包了。

从介绍可以看出,要想让CodeX,Claude Code之类的AI工具能访问Unity像人一样去使用Unity工具或者暴露的接口去做事,我们首先需要接入Unity MCP,这样AI工具才能和Unity通信使用对应工具做事。

Unity for MCP会给我们提供一下工具能力(以下说明来源getting-started):

  • 40+ Unity Editor tools exposed over MCP — manage_scene, manage_script, manage_gameobject, manage_material, manage_physics, run_tests, and more.(通过MCP暴露很多Unity编辑器工具支持操作场景,脚本,GameObject,材质,物理,执行测试等)
  • 25+ read-only resources for state introspection — editor_state, gameobject_components, project_info, unity_instances, etc.(能只读部分GameObject组件数据,项目信息等)
  • Auto-configuration for popular MCP clients — Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, Cline, Codex, Qwen, Gemini CLI, Copilot CLI, OpenClaw.(能自动配置适配常规AI工具,比如Codex)
  • Multi-instance support — drive several Unity Editors from a single session via set_active_instance.(单对话能驱动多个Unity编辑器实例)
  • Two transports — HTTP (multi-agent, default) and stdio (single-agent legacy).(支持HTTP和stdio通信方式)

安装MCP for Unity

安装MCP for Unity的要求:

  1. Unity版本Unity 2021.3 LTS or newer
  2. Python版本Python 3.10+ with uv
  3. An MCP client

检查Python版本和UV工具是否满足条件:

CheckPythonAndUVTools

至于Unity我本机使用功能的Unity 6.000.3.19f1,MCP client本机用的Codex。

通过Package Manager->Add package form git URL安装MCP for Unity:

MCP for Unity git URL:”https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#main“

InstallMCPForUnity

启动MCP for Unity Server

MCP for Unity是作为让Unity Editor担任Server的角色,所以安装完成后还需要我们主动启动MCP for Unity。

安装好MCP for Unity如果没自动弹出后续使用引导,可以自行打开MCP for Unity设置界面:

Window->MCP for Unity->Local Setup Window

MCPForUnitySetupWindow

可以看到我们已经满足了MCP for Unity的使用条件,点击Next配置MCP Client:

ConfigureMCPClients

我本人主要用Codex和Copilot,所以自选了上述对应的MCP Client。

点击Configure Selected确认MCP Client配置:

MCPClientConfigurationSuccess

可以看到MCP Client也配置成功了。

然后通过Window->MCP for Unity->Toggle MCP Window就能打开MCP for Unity的详细信息窗口:

MCPForUnityEditorWindow

从面板上看,MCP for Unity的Server已经启动起来了,但还没有激活连接。

除了点击MCP for Unity的Start Server按钮也可以点击Copy复制手动启动的命令在PowerShell里执行:

1
C:\Users\TonyTang\.local\bin\uvx.exe --from "mcpforunityserver==10.2.0" mcp-for-unity --transport http --http-url http://127.0.0.1:8080 --project-scoped-tools

但貌似启动失败了,好像是8080端口被占用了(应该是因为我们已经启动了一个MCP for Unity的Server占用了8080端口):

RunMCPForUnityFailed

8080端口挺常见的,之前Jenkins搭建好像也是8080端口,为了避免端口冲突,我将MCP for Unity窗口上HTTP连接的端口修改成8081,MCP for Unity Server启动起来后我们点击MCP for Unity的Connect按钮,确认Server启动起来且能连接成功:

MCPForUnityServerConnectSuccess

配置Codex Client

要想Codex能使用MCP for Unity,我们首先要在MCP for Unity的Editor Window选中Codex并点击Configure配置相关配置到Codex的config.toml配置文件。

MCPForUnityCodexConfigContent

为了Codex这个MCP Client能很好的使用MCP for Unity,我们还需要通过MCP for Unity选中Codex作为Client后,点击Install Skills:

InstallCodexMCPForUnitySkills

安装Codex MCP for Unity Skills成功:

InstallCodexMCPForUnitySkillsSuccess

打开Codex的Customize窗口点击Skills可以看到MCP for Unity给我们安装了一个Unity MCP Orchestrator的Skill:

MCPForUnityCodexSkillInstalled

MCP for Unity使用

准备工作完成了,接下来我们测试下通过Codex用自然语言驱动Unity做事。

UsingCodexAndMCPToOperateUnity

CodeCreateRedCubeDialog

CodexCreateRedCubeInUnity

通过始终允许MCP for Unity使用功能一系列的工具后,Codex最终真的创建除了符合我们需求的红色Cube并保存到了我们描述的目录和指定的名字。

走完这一步正面我们已经打通了Codex+MCP for Unity的AI自动化做事的基础流程。

MCP for Unity自带的相关工具在MCP for Unity的Editor Window->Tools下可以看到:

MCPForUnityEditorWindowTools

自定义MCP for Unity工具

实际开发项目过程中,只使用MCP for Unity提供的自带工具还不满足需求,比如我们要想支持打包工具的调用。

  1. 首先我们需要再Editor下定义一个静态class且需要定义McpForUnityTool标签**
  2. 然后定义一个Class作为工具类型,工具类型里定义工具参数成员,工具参数成员必须带上ToolParameter属性标签
  3. 带McpForUnityTool标签的静态class定义静态方法HandleCommand(JObject params)方法进行工具调用处理,需要返回SuccessResponse类表示成功,ErrorReesponse表示出错

实战定义一个BuildToolMcp.cs文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/// <summary>
/// BuildToolMcp.cs
/// 打包工具MCP for Unity
/// </summary>
[McpForUnityTool("打包工具Mcp for Unity")]
public static class BuildToolMcp
{
/// <summary>
/// 打包工具参数
/// </summary>
public class BuildToolParams
{
[ToolParameter("版本号")]
public double VersionCode
{
get;
set;
}

[ToolParameter("资源版本号")]
public int ResourceCode
{
get;
set;
}

[ToolParameter("是否重新构建AB", Required = false)]
public bool RebuildAB
{
get;
set;
}
}

/// <summary>
/// 处理打包工具命令
/// </summary>
/// <param name="parameters"></param>
/// <returns></returns>
public static object HandleCommand(JObject parameters)
{
var buildToolParams = parameters.ToObject<BuildToolParams>();
if(buildToolParams.VersionCode <= 0)
{
return new ErrorResponse($"版本号:{buildToolParams.VersionCode}必须大于0!");
}
else if(buildToolParams.ResourceCode <= 0)
{
return new ErrorResponse($"资源版本号:{buildToolParams.ResourceCode}必须大于0!");
}

DoBuild(buildToolParams);

return new SuccessResponse("Custom tool executed successfully!", new
{
buildToolParams.VersionCode,
buildToolParams.ResourceCode,
buildToolParams.RebuildAB,
});
}

/// <summary>
/// 执行打包操作
/// </summary>
/// <param name="buildToolParams"></param>
private static void DoBuild(BuildToolParams buildToolParams)
{
Debug.Log($"执行Mcp for Unity打包!打包参数: 版本号={buildToolParams.VersionCode}, 资源版本号={buildToolParams.ResourceCode}, 是否重新构建AB={buildToolParams.RebuildAB}");
}
}

工具定义好后,我们需要确认下MCP for Unity是否自动检测添加了该自定义工具,打开MCP for Unity的Editor窗口->Tools->Custom Tools:

CreateCustomToolForMCPForUnity

可以看到MCP for Unity已经自动检测到了我们定义的自定义工具,接下来我们去Codex测试主动使用自定义工具触发打包。

UsingCustomMcpToolInCodex

UsingCustomMcpToolInCodexLog

可以看到我们成功使用Codex自然语言驱动调用了我们给MCP for Unity定义的自定义打包工具,打包参数也和我们描述的一样,日志正确。

至此打通了MCP for Unity自定义工具使用功能的流程,未来更多DIY的需求想支持AI使用,我们可以自定义MCP for Unity工具即可。

如果Codex没发现该工具,可以重新启动下MCP for Unity Server和Codex的连接

如果有那种复杂的工具任务,没法单帧同步执行完成的,可以使用RequiresPolling标签属性去定义静态类工具,详情参考:

Adding Custom Tools to MCP for Unity

AI自动化实战

创建指定字体的TMP字体文件

首先考虑到主要支持英文和中文,所以我需要下载Noto Sans Simplified Chinese字体文件作为游戏字体,下载地址:Noto+Sans+SC

下载好后放到项目对应目录:

SaveNotoSansSCVariableFontAsset

下载好后,我直接Codex通过MCP for Unity让AI自动制作一个基于NotoSansSC-Variable.ttf字体的TMP包含常用中文字的TMP字体文件:

CodexCreateNotoSansSCTMPFontAsset

CodexCreateNotoSansSCTMPFontAssetResult

可以看到通过Codex+MCP for Unity,真的制作了符合需求的TMP字体文件。

接下来我们让他基于创建的TMP字体文件,新创建一个有Outline的材质球用于有描边的字体使用情况:

CodexCreateTMPMaterial

CodexCreateTMPMaterialAsset

可以看到成功通过AI创建了基于前面创建的TMP字体文件的Outline材质球。

但我发现即使我选择了Bold+50字号+Outline材质球,TMP文字依然看起来很细:

NotoSansSCVariableSDFTMPPreview

根据AI的说法是我下载的字体是包含Bold的,只是默认字体的wght值很小所以很细,要想支持粗一点的文字显示,我们需要将NotoSansSC-Variable.ttf使用相关工具制作一个wght=700的Bold字体文件:

CreateNonoSansSCBoldFontAsset

有了粗体的字体文件,我们重新走一遍前面的TMP字体Asset和Outline材质球流程:

CreateNonoSansSCBoldRelativeAssets

NonoScansSCBoldTMPResult

可以看到字体在没勾选Bold的情况,只是Outline材质都已经显示的比较粗了。

至此我们成功利用AI帮我制作了TMP字体Asset和字体材质球Asset。

AI自动拆分效果图

一般工作流是我们从美术那里得到效果图(png)资源,美术将切个好的单个元素图片也给我们,让我们利用所有单个图片元素和效果图进行UI窗口效果还原。

而AI自动拆分效果图这一步主要是解决我们得到效果图(无论是美术给的还是AI自动生成的),我们直接利用AI实现自动拆分单个元素图片,当我们把单个元素图片拆分得到后,我们才能进行下一步UI拼接效果图还原。

切图工具制作

根据AI的说法要想实现快速根据效果图自动切割拆分图片元素,且实现切割图片元素宽高是2或者4的倍数,要求切割的图片元素尽量少留空白,同时切割元素空白部分用透明提到,导出单张图片格式统一为png这个功能,我们需要先让AI制作一个能实现上述切割需求的脚本工具。

CodexCreateCropImagesTool

可以看到生成的工具启动入口是crop.ps1这个是PowerShell的脚本程序,为了方便跨平台我后续又让AI优化成纯Python的版本:

OptimizeCodexCreateCropImagesTool

可以看到这下支持上述需求的切割工具纯Python化算是完成了,接下来就是让AI使用这个工具去自动分析效果图切割出我们想要的所有单张元素图了。

AI自动拆分图片元素

这里我在网上下载了一张休闲游戏的UI主界面效果图用来测试:

UITemplate

为了避免切割出文字和带锁的元素图,我故意在描述词里去避免了这一点,因为游戏里锁定的置灰效果一般是通过切换置灰材质去实现的,而文本通常不做在图里而是文本单独添加。

UseImageCropperToolSplitUI

UIPictureSplitResult

SplitPictureInfo

可以看到AI根据我们的需求真的切割出了不带文字,单独切割锁图片,不太灰色元素图,图片宽高为2或4的倍数的所有单张元素图。

但我发现AI切割出来的图片元素取名都是带数字?_abc.png的方式,实际开发中并不希望看到数字开头,所以让AI继续优化一版即可,描述词大概如下:

1
目前切割出来的元素图片名字都是数字开头,能去掉数字开头直接按图片元素含义取对应名字吗?除非有雷同元素可以+数字结尾区分。

后面我发现切割出来的元素图没有保持在参考图里的相对像素大小,所以我有继续调优描述:

1
将附件效果图拆分出现的元素图片,要求切割的元素图片周边不要留太多透明空白区域,拆分的图片要符合宽高是2或者4的倍数,拆分的图片不能带文字,保证拆分后的图片内容的相对像素大小占比相对效果图里的大小不变,顺便再把效果图里的各文字的颜色信息和字号大小给输出一份介绍文档了。

制作过程中Codex需要自己造Python工具去辅助约束校验等,通过提示词允许他执行制造工具,后续工具会成为Skill的工作流的固定工具:

1
如果现有skill不支持,那就先单独写脚本完成我上述任务要求。

制作UI效果图元素拆分Skill

为了方便以后重用这个图片切割元素流程工具,顺势将这个流程用Codex做成Skill方便重用:

CreateUIPerfectSplitterSkill

UIPerfectSplitterSkillUnderCodex

可以看到我成功利用Codex的创建Skill的技能生成了ui-perfect-splitter的UI效果图元素切割Skill,这个Skill的核心目的就是限定根据根据效果图保持图片切割元素像素相对大小的正确切割,同时输出符合2或者4宽高倍数的元素切割图片还有一些不带文字的切割限制设定:

1
2
3
4
5
6
7
8
9
10
---
name: ui-perfect-splitter
description: 将用户上传的原始 UI 效果图拆成无文字透明 PNG,保持元素在原图中的真实像素大小与坐标,紧裁并按 2 或 4 的倍数补边,输出布局清单、回拼验收图及文字颜色字号说明。适用于要求像素比例准确的游戏或应用 UI 切图;不用于普通网格切图或重新设计整个界面
---

# UI 像素保真拆图

以用户上传的原始效果图为唯一像素基准。交付能按坐标回拼的素材,而不是凭视觉近似重新生成一套大小不同的图标

******

内容比较多就只粘贴了Skill的开头部分内容。

使用ui-perfect-splitter Skill

新下载的UI效果图:

UITemplate2

使用Codex拆分指令:

UseUIPerfectSplitterSkill

上面使用网上下的UI效果图资源,没注意图片大小问题,一般来说UI效果图使我们游戏选择的参考分辨率为基准来制作,所以我通过Codex将效果图修改成我要的参考分辨率1080X1920后再重新进行了UI元素切割流程。

最新制作的1080X1920的UI效果图资源:

UITemplate2New

最终拆分图片元素部分效果:

UIPictureSplitFinalResult

接下来将相关拆分出来的图片元素资源放到项目对应目录下:

NewImportUISplitPicutres

后续就是准备让Codex+MCP for Unity利用这些资源和效果图进行自动拼UI了。

AI自动拼UI

通过提示词描述让Codex利用对应效果图和切好的图片拼接UI预制件:

CreateUIPrefabByCodex

UI预制件拼出来了,但我发现很多Image节点都单独创建在一个独立的空节点下然后Anchors值设置很奇怪,同时部分Image设置的Sprite并非切割好的默认大小:

ImageAnchorsAndNewNodeProblem

这里我强制给AI说明Image组件就直接做在排版的父节点上,没必要单独再创建一个节点进行防止Image组件,其次图片大小尽量用原图大小(实在对不上效果图大小用Scale调整匹配),修改后:

AfterOptImageNode

经过一系列调优(比如锚点数字奇怪,统一到0或0.5或者1)自动拼UI,最终UI拼接效果:

FinalAutoCreateUIResult

对一下效果图:

UITemplate2New

可以看到AI自动拼的UI窗口效果已经八九不离十了,只有部分Slider组件或者是一些背景适配和细节字体位置还需要调整。

后续我也将这个拼UI的流程制作成了ui-create-window-prefab的Skill,方便后续重用。

重用已有UI预制件拼UI

但真实开发环境中,我们不仅仅是让AI自己用最原始的组件进行效果图还原,我们还会开发很多通用预制件,很多效果图对应部件都应该使用对应通用预制件制作而非使用原始组件拼接,那么如何让AI知道我们的通用预制件和使用规则进行UI自动拼接了?

AI给的思路是在自动拼UI的Skill里添加已有预制件服用规则描述,让自动拼UI前先去读取AGENTS.md里指定的UI预制件目录表描述信息和规则。UI预制件目录表里会有详细当前项目预制件的信息介绍供AI读取。AI自动UI时就会优先使用符合的已有预制件进行UI制作拼接还原。

Skill添加已有预制件重用规则描述

首先让AI把以下预制件重用规则写入ui-create-window-prefab的Skill里(为了避免不必要的AI自动查找预制件重用匹配过程的token和后续问题,我强制要求了只从UI预制件目录表里去匹配)。

ui-create-window-prefab的Skill相关描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 已有预制件复用

- 制作前必须读取适用的项目 AGENTS.md,并读取其中明确指定的 UI 预制件目录表;只有目录表存在且可读取时,才考虑复用其中明确列出的预制件。AGENTS.md 未指定目录表、指定文件不存在或无法读取时,跳过已有预制件复用,简要说明原因,直接使用指定素材和基础组件拼接,不为此阻塞制作或要求用户补建目录表
- 按效果图识别功能区域,为复用区域记录“区域描述或标注编号 → 源 Prefab 路径(可附 GUID)→ 实例父节点与名称 → 布局、内容和状态配置”。图像坐标需注明参考分辨率,再换算为 UI 布局;不要将图像比例坐标直接写为任意比例锚点
- 仅在上述目录表列出的资源内匹配用途、样式和状态;本次任务提供的区域映射用于选择表内资源,不扩展候选范围。没有适用条目或目录表为空时,直接使用指定素材与基础组件制作;条目资源不存在或不支持所需配置时,报告该条目问题并对该区域改用指定素材与基础组件,不搜索表外替代 Prefab
- 不扫描公共 UI 目录、素材目录中的其他 Prefab 或同类窗口寻找候选,不依据名称或外观相似引入表外预制件,也不自动创建或补全目录表。仅对表内与当前区域相关的候选检查实际层级、脚本组件、序列化引用、尺寸策略、支持状态、配置入口和运行时依赖;目录表明确关联的示例可按需查看
- 目录表需明确列出可复用资源的路径或 GUID,可补充用途与不适用场景、允许修改的实例属性、绑定入口和示例;不能把一个待扫描目录视为已列出的资源清单。预览图是可选辅助资料,不要求每个 Prefab 都有独立 PNG;缺少预览时可在隔离的临时 UI 环境实例化查看,不把 Project 缩略图当作已导出的预览文件
- 多个候选在功能和视觉上无法区分时,列出具体区域与候选差异并询问,继续完成不依赖该选择的区域;匹配明确时直接执行,不增加逐项确认流程
- 使用支持保留 Prefab 连接的 Unity MCP 接口或 Unity 编辑器 API 实例化,将其作为目标窗口的嵌套 Prefab 保存;不要 Unpack、复制成普通节点或按外观重新搭建来冒充复用,也不为复用自动生成 Variant
- 仅修改用户、项目约定或组件配置入口允许的实例属性,如位置、允许调整的尺寸、文字、图标与状态;未声明的结构性修改不默认允许。保留脚本、动画、内部节点名称、序列化引用和必要事件配置,不 Apply 回源资源
- 复用实例根节点仅在确认没有名称依赖且允许重命名时按窗口节点规范命名;否则保留原名,需要窗口侧布局或命名入口时可使用规范命名的外层容器。内部节点沿用源 Prefab 的命名、锚点和组件实现,不为统一 TMP/TUI 类型或通用样式规则重写其内部结构
- 窗口侧布局使用实例根节点允许覆盖的 RectTransform,或使用外层容器;组件内部尺寸与锚点交由其既有布局体系管理。检查布局驱动和尺寸限制,避免强行覆盖后在布局更新时被改回
- 先检查复用组件的封装与绑定约定:由独立控制组件或独立 Binder 管理的内部节点保留其绑定,窗口绑定对外入口;没有独立封装、项目约定由窗口管理的按钮、Toggle、文本和动态图片,按自动绑定规则加入窗口 Binder。实际生成器会合并的嵌套绑定仍检查最终作用域的名称冲突
- 复用授权涵盖所选预制件已有的 Sprite、字体、材质及其他必要资源引用,不代表可以任意取用其他目录的图片重新设计;不通过新建全局对象、修改窗口注册或资源加载配置来补足未接入的运行时依赖

添加项目已有预制件目录读取规则

然后我去手动给项目的AGENTS.md和PrefabCatalog.md添加规则和预制件说明信息。

AGENTS.md的UI制作相关描述:

1
2
3
4
## UI 制作

- UI 预制件目录表:Docs/UI/PrefabCatalog.md
- 自动拼接 UI 时必须读取并遵循该目录表

PrefabCatalog.md(预制件目录表描述信息文档)(以下只是示例,详细内容根据项目实际预制件而定):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# UI 预制件目录表

## currencyCounter

- 资源:Assets/UI/Common/CurrencyCounter.prefab
- GUID:填写实际资源 GUID
- 用途:顶部金币、钻石等货币数量显示
- 外观:左侧货币图标,中间数值,右侧加号
- 不适用:体力倒计时、商品购买按钮
- 推荐尺寸:240 × 72,单位为设计画布 UI 单位
- 尺寸策略:允许调整宽度;保持高度和图标比例
- 使用示例:WindowMainUI 中的金币栏
- 预览图:Docs/UI/Previews/CurrencyCounter.png

### 配置入口

- tmpAmount:修改显示数量
- imgCurrencyIcon:修改货币图标
- btnAdd:控制加号是否显示

### 允许的实例修改

- 根节点位置、锚点和允许范围内的尺寸
- 数值文字
- 货币图标
- 加号显隐

### 必须保留

- 组件脚本、内部节点名称和序列化引用
- 按钮已有的必要回调
- 内部 ComponentBinder 数据

制作通用预制件

制作以下几个预制件:

  1. 一个带图标和文本的通用按钮预制件(CommonIconBtn.prefab)

  2. 一个带底板,带图标和文本的通用按钮预制件(CommonBgBtn.prefab)

  3. 一个通用的红点预制件(RedDot.prefab)

  4. 一个通用资源栏预制件(CommonResBar.prefab)

CreateCommonPrefab

然后在Docs/UI/PrefabCatalog.md文档里编写预制件说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# UI 预制件目录表

## CommonIconBtn

- 资源:Assets/Resources/prefabs/UI/Common/CommonIconBtn.prefab
- 用途:仅包含一个图标和文本的按钮显示
- 外观:中间图标,底部文本描述
- 不适用:包含图标和文本以外节点或组件的按钮情况
- 推荐尺寸:116 × 120,单位为设计画布 UI 单位
- 尺寸策略:允许修改成适配图标大小的尺寸
- 使用示例:无
- 预览图:无

### 配置入口

- CommonIconBtn:修改图标
- tmpDes:修改文本描述

### 允许的实例修改

- 根节点图标的Sprite和大小
- tmpDes文本描述

### 必须保留

- 组件脚本、内部节点名称和序列化引用
- 按钮已有的必要回调
- 内部 ComponentBinder 数据



## CommonBgBtn

- 资源:Assets/Resources/prefabs/UI/Common/CommonBgBtn.prefab
- 用途:仅包含一个底板,一个图标和文本的按钮显示
- 外观:中间图标,底部文本描述,最里层背景图标
- 不适用:包含背景,图标和文本以外节点或组件的按钮情况
- 推荐尺寸:100 × 124,单位为设计画布 UI 单位
- 尺寸策略:允许修改成适配底板和图标大小的尺寸
- 使用示例:无
- 预览图:无

### 配置入口

- CommonBgBtn:修改底板
- imgIcon:修改图标
- tmpDes:修改文本描述

### 允许的实例修改

- 根节点底板图标的Sprite和大小
- imgIcon图标的Sprite和大小
- tmpDes文本描述

### 必须保留

- 组件脚本、内部节点名称和序列化引用
- 按钮已有的必要回调
- 内部 ComponentBinder 数据



## CommonResBar

- 资源:Assets/Resources/prefabs/UI/Common/CommonResBar.prefab
- 用途:顶部金币、钻石等货币数量显示
- 外观:左侧货币图标,中间数值,右侧加号,背景底板图标
- 不适用:体力倒计时、商品购买按钮
- 推荐尺寸:240 × 56,单位为设计画布 UI 单位
- 尺寸策略:允许调整宽度,保持高度
- 使用示例:无
- 预览图:无

### 配置入口

- imgResIcon:修改货币图标
- imgResAmount:修改货币数量文本
- btnResAdd:控制加号按钮是否显示

### 允许的实例修改

- 允许调整根节点宽度,保持高度
- 货币数量文本
- 货币图标
- 加号显隐

### 必须保留

- 组件脚本、内部节点名称和序列化引用
- 按钮已有的必要回调
- 内部 ComponentBinder 数据



## RedDot

- 资源:Assets/Resources/prefabs/UI/Common/RedDot.prefab
- 用途:各入口的红点显示
- 外观:中间显示红点图标
- 不适用:非红点显示
- 推荐尺寸:36 × 36,单位为设计画布 UI 单位
- 尺寸策略:允许调整宽度和高度保持相同比例
- 使用示例:无
- 预览图:无

### 配置入口

- imgRedDot:修改红点图标

### 允许的实例修改

- 根节点宽高保持比例调整
- 红点图标

### 必须保留

- 组件脚本、内部节点名称和序列化引用
- 内部 ComponentBinder 数据

AI自动用已有预制件拼UI

然后基于已经制作的通用预制件和相关描述文件,我们重新AI自动拼接一次效果图:

UseCommonPrefabToAutoCreatePrefab

可以看到Codex真的实现了按需使用指定规则的通用预制件进行效果图还原,但不知道为什么AI给每一层使用通用预制件的地方都单独定义了一个独立父节点,这一点我让AI将此问题优化写入最新的ui-create-window-prefab的Skill。

然后我们再次重新AI自动拼接一次效果图:

UseCommonPrefabToAutoCreatePrefabSuccess

可以看到AI成功使用符合要求的预制件制作还原了效果图!

但我发现通用预制件虽然AI自动切换了底板,图标和文本,但底板和图标的显示大小确没有修改,所以细节上还并没有正确还原效果图:

AutoCreatePrefabSizeProblem

通过进一步优化ui-create-window-prefab的Skill,再次重新AI自动拼接一次效果图:

AutoCreatePrefabSizeProblemFix

可以看到图标的有效大小问题已经成功解决,可以说目前自动拼出来的预制件已经符合90%可用程度了。

逻辑代码自动化

这一步需要引入窗口和组件绑定相关代码,所以会根据我真实搭建的项目框架实践。

逻辑代码自动化目前分为以下两步:

  1. 模版+组件绑定代码生成
  2. AI生成逻辑代码

模版+组件绑定代码生成

一般来说每个项目为了快速开发,都有配套的代码生成工具,比如窗口代码生成工具,子组件代码生成工具。

我的项目使用了之前我写的ComponentBinder,根据ComponentBinder选择的组件绑定类型,会使用不同的代码模版进行配套的组件绑定partial class生成,详细参见截图:

ComponentBinderInspector

窗口模版文件WindowUITemplate.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/*
* Description: #FileName#.cs
* Author: #Author#
* Create Date: #CreatedDate#
*/

using UnityEngine;
using UnityEngine.UI;
using TH.Modules.UI;
using TMPro;

namespace Game.Modules.UI
{
/// <summary>
/// #ClassName#.cs
/// #ClassName#窗口
/// </summary>
public partial class #ClassName# : BaseWindow
{
public #ClassName#()
{

}

/// <summary>
/// 添加监听
/// </summary>
protected override void AddListeners()
{
base.AddListeners();
}

/// <summary>
/// 添加事件监听
/// </summary>
protected override void AddEvents()
{
base.AddEvents();
}

/// <summary>
/// 窗口显示
/// </summary>
protected override void OnShow()
{
base.OnShow();
}

/// <summary>
/// 窗口销毁
/// </summary>
protected override void OnDestroy()
{
base.OnDestroy();
}
}
}

窗口组件绑定模版文件WindowUIBinder.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
* Description: #FileName#.cs
* Author: #Author#
* Create Date: #CreatedDate#
*/

using UnityEngine;
using UnityEngine.UI;
using TH.Modules.UI;
using TUI;
using TMPro;

namespace Game.Modules.UI
{
/// <summary>
/// #ClassName#窗口的组件绑定
/// </summary>
public partial class #ClassName# : BaseWindow
{
#MEMBER_DEFINITION_LOOP#
/// <summary> #NodeDes# /// </summary>
private #NodeType# #NodeName#;#MEMBER_DEFINITION_LOOP#

/// <summary>
/// 缓存组件
/// </summary>
protected override void CacheComponents()
{
base.CacheComponents();
#MEMBER_INIT_LOOP#
#NodeName# = #NodeMemberName#.NodeDatas[#NodeIndex#].NodeTarget as #NodeType#;#MEMBER_INIT_LOOP#
}

/// <summary>
/// 释放组件
/// </summary>
protected override void ClearComponents()
{
base.ClearComponents();
#MEMBER_DISPOSE_LOOP#
#NodeName# = null;#MEMBER_DISPOSE_LOOP#
}
}
}

最终窗口代码生成WindowTravelHandbook.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*
* Description: WindowTravelHandbook.cs
* Author: TONYTANG
* Create Date: 2026//10/13
*/

using UnityEngine;
using TH.Modules.UI;
using System.Collections.ObjectModel;

namespace Game.Modules.UI
{
/// <summary>
/// WindowTravelHandbook.cs
/// </summary>
public partial class WindowTravelHandbook : BaseWindow
{

public WindowTravelHandbook()
{

}

/// <summary>
/// 添加监听
/// </summary>
protected override void AddListeners()
{
base.AddListeners();
}

/// <summary>
/// 移除监听
/// </summary>
protected override void RemoveListeners()
{
base.RemoveListeners();
}

/// <summary>
/// 添加事件监听
/// </summary>
protected override void AddEvents()
{
base.AddEvents();
}

/// <summary>
/// 窗口显示
/// </summary>
protected override void OnShow()
{
base.OnShow();
}

/// <summary>
/// 窗口销毁
/// </summary>
protected override void OnDestroy()
{
base.OnDestroy();
}
}
}

最终窗口组件绑定代码生成WindowTravelHandbookBinder.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
* Description: WindowTravelHandbookBinder.cs
* Author: TONYTANG
* Create Date: 2026//10/19
*/

using UnityEngine;
using UnityEngine.UI;
using TH.Modules.UI;
using TUI;
using TMPro;

namespace Game.Modules.UI
{
/// <summary>
/// WindowTravelHandbook窗口的组件绑定
/// </summary>
public partial class WindowTravelHandbook : BaseWindow
{

/// <summary> 返回图鉴 /// </summary>
private TButton tbtnBackButton;
/// <summary> 图鉴入口文字 /// </summary>
private TextMeshProUGUI tmpBackTitleText;
/// <summary> 伙伴详情标题 /// </summary>
private TextMeshProUGUI tmpPageTitleText;
/// <summary> 收藏状态 /// </summary>
private TImage timgBookmarkStateIcon;
/// <summary> 收藏状态 /// </summary>
private TButton tbtnBookmarkButton;
/// <summary> 伙伴场景图 /// </summary>
private TImage timgPortraitSceneBackground;
/// <summary> 伙伴立绘 /// </summary>
private TImage timgCompanionPortrait;
/// <summary> 伙伴图鉴编号 /// </summary>
private TextMeshProUGUI tmpCompanionNumberText;
/// <summary> 伙伴中文名称 /// </summary>
private TextMeshProUGUI tmpCompanionNameText;
/// <summary> 伙伴所属国旗 /// </summary>
private TImage timgCountryFlagIcon;
/// <summary> 伙伴所属国家 /// </summary>
private TextMeshProUGUI tmpCountryNameText;
/// <summary> 外形特点标题 /// </summary>
private TextMeshProUGUI tmpFeaturesTitleText;
/// <summary> 外形特点一 /// </summary>
private TextMeshProUGUI tmpFeature1Text;
/// <summary> 外形特点二 /// </summary>
private TextMeshProUGUI tmpFeature2Text;
/// <summary> 外形特点描述 /// </summary>
private TextMeshProUGUI tmpFeaturesDescriptionText;
/// <summary> 图鉴介绍标题 /// </summary>
private TextMeshProUGUI tmpIntroductionTitleText;
/// <summary> 图鉴介绍内容 /// </summary>
private TextMeshProUGUI tmpIntroductionDescriptionText;
/// <summary> 旅行知识标题 /// </summary>
private TextMeshProUGUI tmpTravelTipTitleText;
/// <summary> 旅行知识内容 /// </summary>
private TextMeshProUGUI tmpTravelTipDescriptionText;
/// <summary> 背景图图片 /// </summary>
private TImage timgPageBackground;

/// <summary>
/// 缓存组件
/// </summary>
protected override void CacheComponents()
{
base.CacheComponents();

tbtnBackButton = mComponentBinder.NodeDatas[0].NodeTarget as TButton;
tmpBackTitleText = mComponentBinder.NodeDatas[1].NodeTarget as TextMeshProUGUI;
tmpPageTitleText = mComponentBinder.NodeDatas[2].NodeTarget as TextMeshProUGUI;
timgBookmarkStateIcon = mComponentBinder.NodeDatas[3].NodeTarget as TImage;
tbtnBookmarkButton = mComponentBinder.NodeDatas[4].NodeTarget as TButton;
timgPortraitSceneBackground = mComponentBinder.NodeDatas[5].NodeTarget as TImage;
timgCompanionPortrait = mComponentBinder.NodeDatas[6].NodeTarget as TImage;
tmpCompanionNumberText = mComponentBinder.NodeDatas[7].NodeTarget as TextMeshProUGUI;
tmpCompanionNameText = mComponentBinder.NodeDatas[8].NodeTarget as TextMeshProUGUI;
timgCountryFlagIcon = mComponentBinder.NodeDatas[9].NodeTarget as TImage;
tmpCountryNameText = mComponentBinder.NodeDatas[10].NodeTarget as TextMeshProUGUI;
tmpFeaturesTitleText = mComponentBinder.NodeDatas[11].NodeTarget as TextMeshProUGUI;
tmpFeature1Text = mComponentBinder.NodeDatas[12].NodeTarget as TextMeshProUGUI;
tmpFeature2Text = mComponentBinder.NodeDatas[13].NodeTarget as TextMeshProUGUI;
tmpFeaturesDescriptionText = mComponentBinder.NodeDatas[14].NodeTarget as TextMeshProUGUI;
tmpIntroductionTitleText = mComponentBinder.NodeDatas[15].NodeTarget as TextMeshProUGUI;
tmpIntroductionDescriptionText = mComponentBinder.NodeDatas[16].NodeTarget as TextMeshProUGUI;
tmpTravelTipTitleText = mComponentBinder.NodeDatas[17].NodeTarget as TextMeshProUGUI;
tmpTravelTipDescriptionText = mComponentBinder.NodeDatas[18].NodeTarget as TextMeshProUGUI;
timgPageBackground = mComponentBinder.NodeDatas[19].NodeTarget as TImage;
}

/// <summary>
/// 释放组件
/// </summary>
protected override void ClearComponents()
{
base.ClearComponents();

tbtnBackButton = null;
tmpBackTitleText = null;
tmpPageTitleText = null;
timgBookmarkStateIcon = null;
tbtnBookmarkButton = null;
timgPortraitSceneBackground = null;
timgCompanionPortrait = null;
tmpCompanionNumberText = null;
tmpCompanionNameText = null;
timgCountryFlagIcon = null;
tmpCountryNameText = null;
tmpFeaturesTitleText = null;
tmpFeature1Text = null;
tmpFeature2Text = null;
tmpFeaturesDescriptionText = null;
tmpIntroductionTitleText = null;
tmpIntroductionDescriptionText = null;
tmpTravelTipTitleText = null;
tmpTravelTipDescriptionText = null;
timgPageBackground = null;
}
}
}

这样绑定生成后,我们最基本的窗口代码就有了,同时也能通过直接访问生成的对应变量直接访问绑定的组件而不需要写任何一行代码。

上面因为我绑定ComponentBinder的预制件叫WindowTravelHandbook,所以生成的窗口代码也叫WindowTravelHandbook

AI生成逻辑代码

上一步只是完成了代码模版和组件绑定的初步生成,真正的逻辑代码还需要借助AI进行根据需求描述进行代码生成。

这一步需要结合代码生成+实际逻辑需求进行和AI对话生成,TODO。

Reference

MCP for Unity

MCP for Unity Getting Started

文章目录
  1. 1. 前言
  2. 2. MCP for Unity
    1. 2.1. 安装MCP for Unity
    2. 2.2. 启动MCP for Unity Server
    3. 2.3. 配置Codex Client
    4. 2.4. MCP for Unity使用
    5. 2.5. 自定义MCP for Unity工具
  3. 3. AI自动化实战
    1. 3.1. 创建指定字体的TMP字体文件
    2. 3.2. AI自动拆分效果图
      1. 3.2.1. 切图工具制作
      2. 3.2.2. AI自动拆分图片元素
      3. 3.2.3. 制作UI效果图元素拆分Skill
      4. 3.2.4. 使用ui-perfect-splitter Skill
    3. 3.3. AI自动拼UI
      1. 3.3.1. 重用已有UI预制件拼UI
      2. 3.3.2. Skill添加已有预制件重用规则描述
      3. 3.3.3. 添加项目已有预制件目录读取规则
      4. 3.3.4. 制作通用预制件
      5. 3.3.5. AI自动用已有预制件拼UI
    4. 3.4. 逻辑代码自动化
      1. 3.4.1. 模版+组件绑定代码生成
      2. 3.4.2. AI生成逻辑代码
  4. 4. Reference