UI 还原一直是客户端开发里比较耗时的环节:设计给一张 PSD,程序按图层切图、拼节点、调锚点、再手动挂脚本拖引用,一个稍微复杂点的界面就能耗掉小半天。如果能把“PSD 设计稿 → UI 预制体 → 组件绑定 → UI 代码生成”这条链路自动化,开发效率会有非常明显的提升。本文就从 ETYIUI 的角度,完整拆解 PSD 转 UI 自动生成预制体、自动绑定组件、自动生成代码的实现思路与落地步骤。
1. 为什么需要 PSD 转 UI:从手动拼界面到自动生成
1.1 传统 UI 制作流程的效率瓶颈
先还原一下大多数 Unity 项目在没有自动化工具时的 UI 制作流程:
- 设计师在 Photoshop 里完成界面设计稿,输出 PSD。
- 开发根据标注图确认尺寸、间距、字体大小、颜色。
- 开发在 Unity 中手动创建 Canvas、Image、Button、Text 等节点。
- 将 PSD 里切好的图片资源导入 Unity,逐张拖到对应 Image 的 Source Image 上。
- 手动设置 RectTransform 的锚点、偏移、大小。
- 手动编写 UI 脚本,把场景节点拖到 Inspector 引用。
- 反复调整位置、层级,直到和设计稿基本一致。
这个流程存在几个明显问题:
- 重复劳动多。每个界面都要重复“建节点 → 拖图片 → 设锚点”的过程。
- 容易出错。图片拖错、锚点设置不一致、层级顺序不对,都需要重新检查。
- 沟通成本高。设计规范如果没有沉淀成约束,最终效果会有偏差。
- 扩展性差。一旦设计稿调整,改起来非常痛苦。
1.2 ETYIUI 解决什么问题
ETYIUI 是一套面向 Unity 的 UI 自动化生成方案,核心能力概括起来就一句话:
解析 PSD 设计稿,自动生成 UI 预制体,自动绑定 UI 组件,自动生成 C# 代码。
它把“设计师的 PSD 文件”直接作为输入,通过解析图层结构、尺寸、文本内容、图片资源,映射成 Unity 场景中的 UI 节点树,并生成对应的代码骨架,开发只需要在生成的代码上补充业务逻辑即可。
1.3 本文适合的读者
- 项目里 UI 界面多、重复搭建工作量大的 Unity 开发。
- 想了解 PSD 转 UI 工具原理、准备接入或自研类似工具的团队。
- 对编辑器工具链、代码生成、UI 架构感兴趣的进阶开发者。
读完本文,你会理解 PSD 转 UI 的内部流程,掌握从 PSD 规范、工具配置、导出预制体到代码生成的完整操作路径,并知道接入过程中常见的坑怎么处理。
2. 认识 ETYIUI 与 PSD 转 UI 的核心流程
2.1 什么是 ETYIUI
ETYIUI 严格来说不是传统意义上的 UI 框架(不像 UGUI、FGUI 那样是一套运行时组件库),它更偏“UI 生产力工具”:在编辑器阶段完成 PSD 解析、预制体生成和代码生成,运行时仍然使用 UGUI 作为底层渲染和交互方案。
它解决的是 Unity 开发中非常现实的问题:美术资源到 UI 预制体的转化效率。美术同学和开发同学之间的合作方式,从“开发照着设计稿手撸”变成“开发配置好映射规则,工具自动生成初始版本”。
2.2 PSD 转 UI 与传统工作流对比
| 环节 | 传统方式 | ETYIUI 方式 |
|---|---|---|
| 切图 | 手动切图、导出 | 自动读取 PSD 图层并导出 |
| 拼节点 | 手动在场景中创建 | 根据图层树自动生成节点树 |
| 锚点设置 | 手动调整 | 根据图层位置自动计算 |
| 组件挂载 | 手动添加 Image/Text/Button | 根据命名或配置自动映射 |
| 代码绑定 | 拖引用、写字段 | 自动生成引用属性和初始化方法 |
| 设计变更 | 手动同步修改 | 重新解析重新生成 |
从表格可以看出,自动化工具主要替代的是“确定性”工作:给一个图层,我知道它应该是什么组件,应该放在哪个父节点下,应该显示什么图片。这些规则一旦确定,机器执行比人工执行更快更准。
2.3 ETYIUI 核心流程概览
ETYIUI 的完整流程可以拆成六个步骤:
- 设计规范确认:美术按约定的图层命名、分组、切图规则输出 PSD。
- PSD 解析:读取图层树、图层尺寸、位置、文本、图片信息。
- 组件映射:根据图层命名和配置规则,决定该图层生成 Image、Button、Text 还是其他组件。
- 预制体生成:在 Unity 中创建场景节点树,设置 RectTransform,并引用生成的 Sprite、Font 等资源。
- 组件绑定:为需要交互的节点挂上按钮、滑动条、输入框等脚本,并生成 C# 代码字段。
- 业务逻辑扩展:开发在生成代码的对应分部方法或回调区域补充业务。
把这六步理顺后,后面所有配置和代码都是围绕它们展开的。
3. 环境准备与 PSD 文件规范
3.1 环境要求
ETYIUI 在编辑器环境下运行,需要的核心环境如下:
- Unity 版本:建议使用 2021.3 LTS 或更高版本,部分 PSD 解析和 Sprite 处理流程依赖新版 API。低版本也能跑通,但需要留意 API 兼容问题。
- Photoshop:PSD 文件本身建议由 Photoshop 的较新版本输出,方便保留图层数据。
- 字体资源:界面中出现的文本需要 Unity 工程里有对应字体,否则会自动回退到默认字体。
- 图片格式:推荐 PSD 中的图层内容为普通像素图层、智能对象或带图层蒙版的图层,复杂混合模式在解析时可能出现偏差。
这里的版本需要根据你的项目实际情况调整,本文示例以常见环境为例,重点演示配置思路。
3.2 PSD 文件准备规范
PSD 转 UI 的成败,一半取决于工具能力,一半取决于 PSD 文件是否规范。这里给出一个通用规范:
图层分组对应 UI 容器节点。
- 根画板对应 Canvas 下的根节点。
- 组对应 Panel、空节点或者带图片的 Image。
- 普通图层对应 Image、Button、RawImage。
- 文本图层对应 Text(或 TextMeshPro)。
- 命名以
btn_开头的图层优先生成 Button。 - 命名以
txt_开头的图层优先生成 Text。 - 命名以
icon_开头的图层生成 Image。 - 带
9slice或sliced标记的图层自动启用九宫格拉伸。
这是一个比较典型的命名规范示例,实际项目中可以根据团队习惯调整,但最好统一沉淀成文档。
3.3 画板与切图建议
设计阶段建议美术使用“画板”功能来管理不同界面,一个 PSD 文件对应一个或多个界面。每个画板的尺寸就是最终 UI 的参考尺寸,工具会基于这个尺寸计算锚点。
如果 PSD 中有重复使用的图标或按钮背景,建议统一放在一个公共分组下,方便导出后复用 Sprite,避免最终生成大量重复图集资源。
4. 核心原理拆解
4.1 PSD 解析原理
PSD 文件的核心结构包含文件头、颜色模式数据、图层与蒙版信息、图像数据四大部分。工具要读取的主要是“图层与蒙版信息”里的图层树、通道数据、位置信息。
Unity 中解析 PSD 有几种常见方案:
- 使用 Photoshop 导出辅助脚本,把图层信息和图片数据导出为 JSON + PNG 序列。
- 在 Unity 中直接用库解析 PSD 二进制,例如基于 Photoshop 文件格式解析的 C# 实现。
- 外部离线解析后生成中间文件,Unity 再读取中间文件构建预制体。
ETYIUI 在工程中更常见的做法是:先用编辑脚本读取 PSD 图层的矩形区域、图层名、分组关系,再调用 Texture2D 相关 API 把每个图层内容渲染成独立的 Sprite。由于 PSD 内部存储的是原始像素数据,这个过程需要处理坐标翻转、透明通道、变形等问题。
核心思路可以理解为:
解析 PSD 图层树 -> 得到每个图层的名称、位置、尺寸、可见性 -> 裁剪每个图层的像素数据 -> 生成 PNG/Sprite 资源 -> 构建 UI 节点树4.2 组件映射规则
解析出图层后,关键一步是决定“这个图层变成什么 UI 组件”。
最简单的规则是前缀映射:
btn_ -> Button txt_ -> Text img_ / icon_ -> Image input_ -> InputField tgl_ -> Toggle sld_ -> Slider更进阶的规则是支持配置化映射。因为不同团队命名习惯不一样,把映射表抽成 ScriptableObject 或 JSON 配置,可以让美术和程序共同维护,而不需要改代码。
示例映射配置:
{ "btn_": "Button", "txt_": "TextMeshProUGUI", "img_": "Image", "icon_": "Image", "input_": "InputField", "tgl_": "Toggle", "sld_": "Slider" }4.3 预制体生成的本质
生成预制体本质上就是把“解析出的图层节点树”翻译成 Unity 场景对象树。
每个图层对应一个 GameObject:
- 设置 name。
- 设置 RectTransform 的 anchoredPosition、sizeDelta、anchorMin、anchorMax。
- 根据图层类型添加 Image、Text 等组件。
- 设置 sourceImage、font、text 内容。
- 设置兄弟节点顺序,保持和 PSD 图层顺序一致。
图片的保存位置一般放在Assets/Art/UI/Generated/下,预制体放在Assets/Art/UI/Prefabs/下,这是后续代码生成的约定目录。
4.4 自动绑定与代码生成原理
自动绑定的目标是:生成一个 C# 脚本,脚本里的字段能在编辑器模式下自动找到预制体上的组件引用。
有两种常见实现路径:
- 在编辑器脚本中,遍历生成的预制体,按节点名查找对应组件,序列化到 MonoBehaviour 的字段中。
- 在运行时,通过代码反射 + Find 方式查找节点并绑定,减少手动拖引用。
ETYIUI 通常采用编辑器遍历 + 序列化字段的方案,因为它更稳定、可读性更好,也方便开发在 Inspector 中核对引用。
生成出来的脚本结构通常类似:
// 文件路径:Assets/Scripts/UI/Generated/UIMainView.cs public partial class UIMainView : MonoBehaviour { public Button btn_Start; public Button btn_Setting; public TextMeshProUGUI txt_Title; public Image img_Bg; private void Start() { btn_Start.onClick.AddListener(OnClickStart); btn_Setting.onClick.AddListener(OnClickSetting); } partial void OnClickStart(); partial void OnClickSetting(); }这里用partial关键字把生成代码和业务代码分离:工具只负责生成组件字段和点击事件入口,业务逻辑写到另一个同名 partial 类中,避免每次生成覆盖手写代码。
5. 完整实战案例:从 PSD 到可运行 UI 界面
下面通过一个完整的示例演示 ETYIUI 的接入流程。示例假设要生成一个“主菜单界面”,包含背景图、标题文本、两个按钮。
5.1 创建项目结构
先在 Unity 工程中创建如下目录结构:
Assets/ Art/ UI/ PSD/ // 放置 PSD 源文件 Generated/ // 自动生成的图片资源 Prefabs/ // 自动生成的预制体 Scripts/ UI/ Generated/ // 自动生成的 UI 代码 Business/ // 手写的业务代码 Editor/ ETYIUI/ // 编辑器扩展工具脚本项目结构越规范,自动化工具的路径处理越简单。
5.2 准备测试 PSD 文件
在 Photoshop 中新建 1920x1080 画板,创建以下图层:
| 图层名 | 类型 | 内容 |
|---|---|---|
| img_Bg | 普通图层 | 背景图片 |
| txt_Title | 文本图层 | “主菜单”三个字 |
| btn_Start | 分组 | 开始按钮 |
| btn_Start/img_btn_bg | 普通图层 | 按钮背景 |
| btn_Start/txt_btn_text | 文本图层 | “开始游戏” |
| btn_Setting | 分组 | 设置按钮 |
| btn_Setting/img_btn_bg | 普通图层 | 设置按钮背景 |
| btn_Setting/txt_btn_text | 文本图层 | “设置” |
注意btn_Start和btn_Setting用分组表示一个完整按钮,按钮节点生成在分组层上,子节点作为按钮内部元素。
保存为UIMain.psd并放到Assets/Art/UI/PSD/目录下。
5.3 添加编辑器扩展代码
在Assets/Editor/ETYIUI/下创建 PSD 导入监听脚本。这个脚本的作用是:当 PSD 文件导入或者更新时,自动触发解析流程。
// 文件路径:Assets/Editor/ETYIUI/PSDImportListener.cs using System.IO; using UnityEditor; using UnityEngine; public class PSDImportListener : AssetPostprocessor { private static void OnPostprocessAllAssets( string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { foreach (string path in importedAssets) { if (Path.GetExtension(path) == ".psd") { Debug.Log($"[ETYIUI] 检测到 PSD 文件变更:{path}"); // 这里调用后续的解析与生成入口 } } } }该脚本的原理是监听 Unity 资源导入流程,当 PSD 文件发生变更时自动进入生成流程,不需要手动点击导出按钮。
5.4 编写 PSD 解析与预制体生成核心代码
下面是一个简化的生成入口,演示核心流程:从 PSD 读取图层信息、生成 Sprite、创建预制体。
// 文件路径:Assets/Editor/ETYIUI/UIGenerator.cs using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using UnityEngine.UI; public static class UIGenerator { private const string PrefabRoot = "Assets/Art/UI/Prefabs"; private const string SpriteRoot = "Assets/Art/UI/Generated"; [MenuItem("ETYIUI/Generate From PSD")] public static void GenerateFromPSD() { // 选择 PSD 文件 string psdPath = AssetDatabase.GetAssetPath(Selection.activeObject); if (!psdPath.EndsWith(".psd")) { Debug.LogError("[ETYIUI] 请先选中一个 PSD 文件。"); return; } string fileName = Path.GetFileNameWithoutExtension(psdPath); string prefabPath = $"{PrefabRoot}/{fileName}.prefab"; // 1. 创建 UI 根节点 GameObject root = new GameObject(fileName); RectTransform rootRect = root.AddComponent<RectTransform>(); rootRect.sizeDelta = new Vector2(1920, 1080); root.AddComponent<CanvasRenderer>(); // 2. 在这里调用 PSDParser 解析图层树,得到 List<PSDLayerInfo> // 下面简化处理:手动构建两个子节点演示 CreateUINode(root.transform, "img_Bg", "Image", new Vector2(960, 540), new Vector2(1920, 1080)); CreateUINode(root.transform, "txt_Title", "TextMeshProUGUI", new Vector2(960, 900), new Vector2(400, 100)); // 3. 保存预制体 Directory.CreateDirectory(PrefabRoot); PrefabUtility.SaveAsPrefabAsset(root, prefabPath); Object.DestroyImmediate(root); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log($"[ETYIUI] 预制体生成完成:{prefabPath}"); } private static void CreateUINode(Transform parent, string nodeName, string componentType, Vector2 position, Vector2 size) { GameObject go = new GameObject(nodeName); RectTransform rect = go.AddComponent<RectTransform>(); rect.SetParent(parent, false); rect.anchoredPosition = position; rect.sizeDelta = size; switch (componentType) { case "Image": go.AddComponent<Image>(); break; case "Button": go.AddComponent<Button>(); break; case "TextMeshProUGUI": // 简化处理,实际需要引用 TMP 程序集 break; } } }这段代码展示了自动生成预制体的最小骨架。真实项目中,这里会接入 PSD 解析器,把图层信息批量转换成节点,而不是手写固定节点。核心逻辑是一样的:拿到图层数据,创建节点,设置 RectTransform,挂组件,保存预制体。
5.5 自动绑定与代码生成
生成预制体之后,需要生成对应的 C# 代码。这里演示一个代码生成器,扫描预制体上的节点,提取 Button 和 Text 等组件,输出一个 partial 类。
// 文件路径:Assets/Editor/ETYIUI/UICodeGenerator.cs using System.IO; using System.Text; using UnityEditor; using UnityEngine; public static class UICodeGenerator { [MenuItem("ETYIUI/Generate UI Code")] public static void GenerateCode() { string prefabPath = "Assets/Art/UI/Prefabs/UIMain.prefab"; GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath); if (prefab == null) { Debug.LogError("[ETYIUI] 预制体不存在,请先生成预制体。"); return; } StringBuilder sb = new StringBuilder(); sb.AppendLine("using UnityEngine;"); sb.AppendLine("using UnityEngine.UI;"); sb.AppendLine("using TMPro;"); sb.AppendLine(); sb.AppendLine("public partial class UIMainView : MonoBehaviour"); sb.AppendLine("{"); // 遍历所有子节点,按组件类型生成字段 foreach (Transform child in prefab.GetComponentsInChildren<Transform>(true)) { if (child.name.StartsWith("btn_")) { sb.AppendLine($" public Button {child.name};"); } else if (child.name.StartsWith("txt_")) { sb.AppendLine($" public TextMeshProUGUI {child.name};"); } else if (child.name.StartsWith("img_")) { sb.AppendLine($" public Image {child.name};"); } } sb.AppendLine("}"); string savePath = "Assets/Scripts/UI/Generated/UIMainView.cs"; Directory.CreateDirectory(Path.GetDirectoryName(savePath)); File.WriteAllText(savePath, sb.ToString()); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log($"[ETYIUI] 代码生成完成:{savePath}"); } }生成出来的代码是自动化产物,业务逻辑不要写在里面,交给另一个手写的 partial 类维护。这是避免“重新生成覆盖手写代码”的关键设计。
// 文件路径:Assets/Scripts/UI/Business/UIMainView.Business.cs public partial class UIMainView { partial void OnClickStart() { Debug.Log("点击了开始按钮"); } }5.6 运行与验证
- 在 Project 窗口选中
Assets/Art/UI/PSD/UIMain.psd。 - 点击菜单
ETYIUI/Generate From PSD。 - 等待控制台输出“预制体生成完成”。
- 点击菜单
ETYIUI/Generate UI Code。 - 将生成好的
UIMain.prefab拖入场景,运行查看效果。
预期结果是:场景中自动出现背景、标题、按钮节点,层级顺序与 PSD 图层一致,按钮可点击,控制台能输出“点击了开始按钮”。
6. 常见问题与排查思路
在接入 PSD 转 UI 的过程中,最容易遇到的几类问题整理如下:
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| PSD 导入后没有触发生成 | 未接入 AssetPostprocessor 或监听事件未生效 | 检查 Editor 目录脚本是否编译正确,重新导入 PSD |
| 生成的图片位置偏移 | PSD 坐标系与 Unity 坐标系差异 | 统一坐标系换算,PSD 原点一般在左上角,Unity 锚点原点在中心 |
| 图层内容显示为紫色 | Sprite 资源未生成或引用丢失 | 检查 Sprite 保存路径,确认 Texture Type 设置为 Sprite |
| 字体显示不对 | 工程中缺少对应字体资源 | 指定中文字体或 TMP 字体资源,配置默认字体映射 |
| 九宫格拉伸异常 | 未识别 sliced 标记或 Sprite 未设置九宫格 | 在生成 Sprite 时读取 named border 并设置 Sprite Editor 的 border |
| 重新生成后手写代码被覆盖 | 业务代码写在生成类里 | 改用 partial 类把生成代码和业务代码分离 |
| Unity 提示 No valid Unity Editor License | 编辑器许可证未激活 | 检查 Unity License 激活状态,登录账号并激活许可 |
| 预制体生成后层级错乱 | 图层顺序与节点顺序未对应 | 解析 PSD 时需要保留原始图层顺序并反向映射到 UI 层级 |
这里补充一下许可证问题:Unity 编辑器需要通过 Unity Hub 登录激活,如果打开工程时报 “No valid Unity Editor License found”,说明当前机器没有有效的许可证。个人开发可以使用个人版许可证,商业团队注意按公司合规要求选择许可类型。处理思路是打开 Unity Hub,登录账号,在设置里重新激活许可证。
7. 最佳实践与工程建议
7.1 设计规范先行
PSD 转 UI 工具做得再好,如果设计稿不规范,自动生成出来的界面也一定是乱的。所以接入 ETYIUI 的第一步不是写代码,而是和设计师一起定规范:
- 图层命名必须有意义,且带统一前缀。
- 分组层级必须清晰,一个按钮的所有元素放进一个组。
- 需要九宫格的图层要标记
sliced。 - 文本图层必须使用真实文本内容,方便生成 Text 时直接填入字符串。
- 画板尺寸要固定,建议和项目的参考分辨率一致。
7.2 生成代码与业务代码严格分离
这是最容易踩坑的地方。建议把生成代码视为“只读代码”:
- 生成类使用
partial,并放到Scripts/UI/Generated目录。 - 业务代码写在另一个 partial 类,放到
Scripts/UI/Business目录。 - 不要让开发手动修改生成类,所有手改内容都会在下次生成时被覆盖。
- 代码生成的字段命名保持和节点名一致,既方便自动绑定,也方便开发阅读。
7.3 Sprite 管理与图集策略
自动生成界面会产出大量 Sprite,如果没有合理的图集策略,DrawCall 和包体会直线上升:
- 优先使用 Sprite Atlas 将小图打图集。
- 自动化过程中识别重复使用的图标,避免同一张图生成多个副本。
- 大背景图单独处理,不进图集。
- 文本和图标分离处理,方便多语言替换和后续 UI 换皮。
7.4 性能与分辨率适配
生成预制体只是起点,后续还需要处理适配:
- 根据项目目标分辨率设置 Canvas Scaler 的匹配模式。
- 自动生成的 RectTransform 基于设计稿坐标,运行时可能需要配合安全区适配。
- 按钮点击区域不要依赖文字图层的尺寸,Button 组件建议挂在分组根节点上。
- 复杂界面注意 UI 重建开销,不要在每个节点都叠加不必要的 Canvas。
7.5 版本管理与回归测试
工具自动化生成最大的风险是“批量生成后看起来没问题,运行却崩了”。因此建议:
- 生成前后用 Git 记录 diff,方便回滚。
- 自动化生成后做一次批量打开预制体检查。
- UI 回调绑定不依赖字符串,使用生成字段,避免重构时遗漏。
- 建立 UI Smoke 测试脚本,生成后自动把预制体加载到场景进行冒烟验证。
8. 总结与学习路线
到这一篇,我们从“为什么需要 PSD 转 UI”开始,梳理了 ETYIUI 的整体流程,对 PSD 解析、组件映射、预制体生成、组件绑定、代码生成五个关键环节做了拆解,并通过一个主菜单界面示例演示了完整落地过程。文中涉及的关键点包括:PSD 图层规范如何影响生成结果、自动生成预制体的编辑器代码怎么写、如何用 partial 类隔离生成代码和业务代码,以及接入过程中常见的资源、坐标、字体、层级问题时怎么排查。
如果你准备在项目中真正落地这套工具链,下一步可以做三件事:
- 把团队的设计规范文档化,确定图层前缀、分组和画板规则。
- 先挑一个简单界面做 Pilot 试点,跑通“设计稿 → 预制体 → 代码”的完整链路。
- 再逐步扩展组件类型支持,比如 Slider、Toggle、InputField 以及九宫格和富文本。
自动化工具不是银弹,它替代的是重复劳动,并不能替代对 UI 架构的理解。真正决定这套方案上限的,是设计规范和映射规则设计得是否足够好。把规则定清楚,工具才会越用越顺手。