Work / UI Tooling

Psd2UGUI PSD → Unity UGUI 界面转换工具

按规范画的 PSD 一条命令转成 Prefab、控件、九宫格和绑定类,并用像素对比验收;乱稿先由模型整理,程序执行并把关。

Role
个人项目(AI 辅助开发,制作方式见页内说明)
Year
2026
Team
个人项目
Tech
Python · Unity 2022.3 · UGUI / TextMeshPro · Claude Code skill
Status
开发中

全流程实录,约 3 分半,无声。不是录屏:页面操作由脚本在无界面浏览器里完成,模型的部分按工具记下的会话记录回放,Unity 画面是 Play 模式下逐帧录的。这里是压缩到 720p 的版本。

概览

给一张按规范画的 PSD,一条命令得到 Unity 原生 UGUI 的界面资源:Prefab、控件、九宫格切图、给程序用的绑定类,外加一份设计稿与引擎截图的像素对比报告。可以在浏览器里预览和修改,修改直接写回 PSD;不按规范画的稿子,先由模型整理成标准稿。

乱稿 →(模型判断,程序执行)→ 标准 PSD → 检查器 → 切图、界面描述、绑定类 → Unity 生成 Prefab → 离屏截图 → 与设计稿对比 → 报告

Unity 全程 -batchmode,不开任何窗口。只用 Unity 原生 UGUI 和 TextMeshPro。

≤ 0.023%四张示例稿的非文字区域差异(合格线 1%)
33条规范检查规则,21 条错误、12 条警告
517个图层的 Bag 稿:4 个按钮、5 个页签、60 个列表条目
3 轮盲测:全新的子代理整理四张乱稿
制作方式与状态。个人项目,AI 辅助开发(Claude Code),仍在开发中:图集还没做。暂未开源。数字都来自工具写出的测量文件;它只在四张脚本生成的示例稿和由它们打乱出来的四张乱稿上跑通过,没有在设计师手画的稿子上测过。完整说明见 介绍 PDF(15 页)。

AI 只在一个地方

这个工具最重要的设计决定是把模型关在一个很小的范围里。

转换器是确定性的程序,里面没有 AI:语义全部来自图层名里的标记。模型只做一件事——判断乱稿里每个图层是什么,写成一份带理由的决定清单。改 PSD、核对结果都由程序做。

这样分工之后,转换的结果可以复现、可以测量;模型出错的地方被限制在“命名和分组”上,而且每一条都看得见。

输入:图层名就是全部输入

规范只管命名和分组,不要求设计师换工具。

写法 结果
名字@btn Button。组内同名、状态不同的图层(底:normal 底:pressed 底:disabled)合并成一个 Image,其余状态成为 Sprite Swap 的图
名字@tabs / @toggle ToggleGroup + Toggle,:on / :off 图层随状态切换
名字@list / @grid 子组是条目:结构相同、等间距。方向、列数、间距从稿子量出;第一个条目存成 Prefab,其余都是它的实例
图层@9s / @9s=左,上,右,下 / @raw 九宫格:按容差找边距 / 指定边距 / 不切
名字@fx 投影、发光、选中框:不计入节点矩形
名字@anchor=横-纵 指定锚点;不写时按节点在父节点里的位置自动推断
名字#id 给程序用的 id,成为绑定类的强类型字段

文字是活文字:TextMeshProUGUI,字体、字号、颜色、字距、行距取自 PSD。

输出与还原度

参照图是 PSD 内嵌的合成图,也就是 Photoshop 自己的渲染;引擎图是 Unity 按设计分辨率离屏渲染。任一通道差大于 16/255 记为差异像素。

稿子 图层 切图(去重前 → 后) 按钮 / 页签 / 列表 非文字区域差异 整图差异
ConfirmPopup 21 12 → 12 3 / 0 / 0 0.000% 0.203%
Lobby 76 39 → 31 3 / 0 / 1 0.000% 0.516%
Bag 517 340 → 109 4 / 5 / 1 0.023% 0.659%
Quest(模拟手绘质感) 94 52 → 43 6 / 3 / 1 0.000% 0.344%

合格线是非文字区域 ≤ 1%、整图 ≤ 5%。文字区域内的差异来自两个渲染器的抗锯齿不同,不设合格线;点文字的位置偏差在 0.6 px 以内。Bag 稿从读 PSD 到 Unity 截图完成约 10 秒。

除了 Prefab 和切图,每个界面还会生成一个绑定类,带 #id 的对象都是强类型字段:

var view = new BagView(Instantiate(prefab));
view.Use.onClick.AddListener(OnUse);
view.Title.text = "背包";

Bag.psd 转出的 Prefab:517 个图层,Unity 离屏渲染

规范检查器

“标准稿”的定义就是检查器零错误。错误分两类:转出来的画面会和设计稿不一样的(图层样式、混合模式、调整图层、剪贴蒙版……),和写下的标记无法兑现的(未知标记、互相冲突、id 重复……)。有错误时转换不会启动。

每条发现都指到具体图层,预览页里点一条就选中那个图层,名字上的问题可以直接在页面里改。

检查器在一张故意做坏的稿子上:右侧是发现列表,左侧图层列表里有问题的行带下划线

为什么需要它:把这张坏稿强行转换,再逐图层和 Photoshop 的画面比,6 个画面类违规里 5 个明显不同(差异像素 20% – 41%)——但整张图的判定仍然是“合格”,因为违规图层面积小,摊到整图上过不了 1% 的线。只看整图数字拦不住这些问题。

坏稿强行转换后的逐图层对比
上:Photoshop 的画面。中:强行转换后的 Unity 画面。下:差异。

在浏览器里预览和修改

一个只监听本机的小服务加一个页面。页面按图层拼出画面,标出转换器识别到的控件;点选图层后可以改名字、id、控件类型、状态、九宫格、锚点和位置。

  • 保存就是写回 PSD,没有旁路文件;写入前先校验,不合法的拒绝
  • 每次写入前自动备份,页面上可以按备份恢复
  • 不覆盖别人的修改:文件在页面读取之后被别的程序改过,写入会被拒绝并重新读取
  • “生成到 Unity”触发完整转换,是否合格、数字和三张对比图直接显示在页面里

预览页:左边是图层树和标记,中间是按图层拼出的画面,右边是选中图层的属性

直接改 PSD 是有风险的,所以这件事先单独验证过:改名、移动、归组之后,Photoshop 打开无报错,文字仍是文字图层、智能对象仍是智能对象,重新渲染的画面与预期相差不到 0.1% 的像素。对照实验里,移动时如果不同步文字变换和蒙版矩形,Photoshop 重新渲染后文字会跳回原位 42 px——这个检查抓得到问题。

乱稿规范化:模型判断,程序执行

不按规范画的稿子画面是对的,只是程序读不出含义:默认图层名、没有标记、控件没成组。

  • 模型做判断:这一层是什么(按钮的底、按下态、发光、列表条目……)。它不直接改 PSD,只写一份决定清单:改名、归组、解散组、移动,每条带一句理由。拿不准的列为“待确认”
  • 程序执行并把关:只改名和调整分组,不动像素。会让重叠图层上下颠倒、或改变显隐和不透明度的操作一律拒绝。写完再读回来,逐图层比像素和文字数据,逐像素比合成画面
  • 像素层面的问题不处理:图层样式、混合模式只报告,留给 Photoshop
介绍 PDF 第 13 页:模型看到的图、写下的决定与理由
介绍 PDF 第 13 页:模型看到的图层视图、它写下的决定和给出的理由

怎么测的:盲测

乱稿是从标准稿打乱出来的,答案是已知的。所以判断交给全新的子代理:每张稿子一个,只拿到 skill 说明和乱稿,被禁止读标准稿、测试和报告。交回的决定清单由程序重新执行,再和标准稿逐个控件按矩形比对。

命中 / 有偏差 / 漏判 / 误判 标准稿里有 第一轮 第二轮 第三轮
按钮 16 12 / 4 / 0 / 5 15 / 1 / 0 / 5 15 / 1 / 0 / 5
页签 8 8 / 0 / 0 / 0 8 / 0 / 0 / 0 8 / 0 / 0 / 0
列表 3 3 / 0 / 0 / 0 3 / 0 / 0 / 0 3 / 0 / 0 / 0
@fx 图层 133 133 / 0 / 0 / 1 133 / 0 / 0 / 1 133 / 0 / 0 / 1
@9s 图层 18 6 / 0 / 12 / 8 18 / 0 / 0 / 15 18 / 0 / 0 / 15

规范化之后四张稿子都能转换,非文字区域差异最大 0.85%(来自故意留下的一个图层样式和一个滤色混合),Play 模式检查 7 / 7 通过。模型整理一张稿子用时 39 – 125 秒。

误判的 5 个按钮是导航列表的五个条目——模型把条目本身也标成了按钮,这是读法不同而不是错。多标的 @9s 是进度条和条目底板,对画面没有影响。

这个测试的边界

  • 第一轮之后改了工具的看图方式和 skill;其中“只有一张非灰色的隐藏底图时判为按下态”这一条是看过答案之后加的,第二轮在这一条上不算盲测
  • 三轮用的是同四张稿子
  • 乱稿是从标准稿打乱出来的:像素干净,一个图层一个用途。真实的乱稿会更糟
  • 三轮里程序没有拒绝过任何操作,所以“拒绝后改用别的归组方式”这条路在盲测里没有走到

还验证了什么

  • Play 模式检查:Unity 以 batchmode 进入 Play 模式,一个虚拟指针通过标准输入模块和射线检测去点按钮、切页签、拖列表。四张稿子全部通过
  • 预览服务:34 项接口检查全部通过;另有一段 16 步的页面脚本会话,每步核对 PSD。最后恢复最早的备份,文件与原稿逐字节相同
  • 九宫格:切完按原尺寸拼回,与原图比较,四张稿子最大通道误差不超过 2 / 255

已知限制

  • 没有图集,也还没有进度条、滑条、输入框
  • 示例稿不是人画的:由 HTML 渲染再经 Photoshop 脚本生成,图层结构比真实设计稿规整
  • 还原度的合格线拦不住小面积的错误,见上面的坏稿实验
  • 段落文字换行可能不同:一行放不下时 Photoshop 会挤压标点排进一行,TextMeshPro 会换行
  • 文字只支持单一样式、横排、无旋转;描边和投影没有还原
  • 锚点推断是启发式的,Bag 稿有三处靠手动指定
  • 带渐变或颗粒的图不裁小;带颗粒的中段拉伸后会成横纹
  • 操作都由脚本完成:Unity 里是虚拟指针,页面是无界面浏览器,没有真人点过,也没有真机
  • 只在一台机器上跑过:Unity 2022.3.62f3,Photoshop 2021,Chrome

示例稿里有三张取材自我另一个作品 Driftglass UI Showcase 的界面:那边是把界面在引擎里搭出来,这边是让同样的界面从 PSD 自动生成。