新闻详情 资讯动态

全面了解最新资讯与建站知识,洞察行业趋势。

行业资讯

用Codex解析PSD一键生成Cocos Creator预制体:从手工切图到自动化管线

发布时间:2026/10/15 5:03:31
用Codex解析PSD一键生成Cocos Creator预制体:从手工切图到自动化管线 前阵子接了个界面改版的项目设计师交付的是一整套PSD我这边要在Cocos Creator里把UI预制体搭建出来。以前这活基本靠手打开PSD切图、看坐标、记层级然后回编辑器里一个节点一个节点地建光一个商城主界面就能耗掉大半天。这次我直接改了玩法——让Codex去解析PSD把图层结构转成Cocos预制体。跑通之后一张中等复杂度的UI从PSD到可用的预制体压到了十几分钟以内。这篇就把这套“PSD直接生成预制体”的路线、坑位和可落地的脚本逻辑完整写出来。先说清楚我的整体方案不是让Codex“凭空变出预制体”而是让它替我写一套两段式生成管道第一段用脚本解析PSD产出结构化的中间数据第二段把中间数据按Cocos的预制体格式映射成JSON。真正的核心工作量其实是定清楚PSD里的信息和Cocos预制体里的字段怎么对应。只要这个映射稳定了后面换界面、换项目都能复用。1. 为什么要把PSD直接变成预制体1.1 手工重建UI的笨功夫在哪做过Cocos UI的人应该都有感觉从PSD到预制体的手工流程里有几个固定动作相当折磨人。首先是切图。图层要导出成PNG还得记住资源在哪个目录、叫什么名字。设计师直接把整个画板导出成一张大图的方案也常见但那就更麻烦因为你要按显示的矩形框去裁切遇到有阴影、发光的图层还得留边。其次是坐标换算。PSD的坐标原点在左上角Y轴向下Cocos的坐标原点在画布中心Y轴向上。每放一个节点我都会照着设计稿手动算一遍用设计分辨率除以二减掉图层中心点。算错一次整个UI的排版就飘了。最后是层级关系。PSD里有分组、有嵌套同一层里还经常有几十个子图层。手工在编辑器里还原这个树形结构眼睛盯久了很容易看串行。再加上图层顺序不能乱Cocos里排在后面的子节点会盖在前面这一点跟PSD里“画板上靠后的图层显示在最上”是一个逻辑但手拖节点的时候没人帮你检查。这些重复劳动特别适合自动化。问题在于传统写死脚本的方式灵活性很差不同项目的命名规则、字号、资源组织方式都不一样维护成本高。我现在改用Codex来做就是看中了它能把“自然语言描述的需求”快速变成“能跑、能调、能复用的脚本”。1.2 让Codex生成脚本而不是让Codex直接吐预制体AI直接生成一个完整预制体的诱惑很大但我不建议那么干。预制体本质上是JSON里面对象的__id__引用关系非常碎节点、组件、PrefabInfo之间互相指着让模型一次推理出来稍不留神索引就断。最稳的办法是让Codex来写“生成预制体的脚本”。脚本的逻辑是非常确定的输入一个PSD输出一组Cocos节点和组件。整个过程可以反复跑、反复修还能做校验。比如我要求Codex在脚本里加一个“打印图层树”的调试命令跑完后把结构图拉出来跟PSD的图层面板对照哪里不对一目了然。这比直接让Codex输出一个黑盒预制体、然后打开编辑器发现白屏要可控得多。所以我给这个方案定的调子是AI辅助我搭管道管道负责把PSD变预制体。Codex的真正价值是替我把解析代码、映射代码、错误处理代码写出来并在我提出新的边界情况时快速修改逻辑。后面项目里再接到新PSD我只要跑一遍管道就行。1.3 这个方案解决什么、适合谁这个方案对两种场景特别有用。一种是基础UI界面比如背包、商场、排行、设置面板这类结构清楚、组件类型不复杂的页面。这类界面占了日常开发量的大头用管道生成能省下非常多的时间。另一种是改版迭代设计师隔几天就发一版新PSD我只需要重新生成一次就能看到新版布局而不是在编辑器里一个个挪节点。如果你是独立开发者或者团队里没有专职UI开发这个思路更合适。学会怎么让AI写解析和生成脚本以后再面对“某种格式转某种格式”的需求本质上都能套用。2. 先摸清两边的“户口本”PSD与Cocos预制体格式2.1 PSD里到底能拿到哪些信息PSD格式虽然封闭但Python的psd-tools库已经帮我们啃得很透。一个PSD在我眼里就是一棵图层树每个图层节点能拿到这么几类关键信息信息说明例子图层名PS图层面板里的名字“bg_main”、“btn_buy”图层类型pixel、type、group、shape、smart_object像素层、文本层、分组可见性图层是否显示隐藏层可直接跳过不透明度0到255的透明度值半透明遮罩层边界框left/top/right/bottom单位是像素表示图层在画布上的绝对位置文本信息文字内容、字号、颜色、是否加粗文本层专用合成像素图层渲染后的RGBA图像可导出为PNG切片这里面最有用的是bbox和kind。bbox决定了图层在Cocos里的位置和尺寸kind决定了我要生成哪种组件。文本层还能通过engine_dict把字号和颜色挖出来这样生成出来的Label不用再在编辑器里逐个调。隐藏图层和全透明图层在生成时要直接跳过否则Prefab里会出现一堆看不见但占层级的空节点后面改起来很烦。2.2 Cocos预制体到底长什么样Cocos Creator 3.x的预制体并不是普通人理解里的“一个UI文件”它其实是一个JSON数组里面每种对象都带一个__type__字段对象之间用__id__互相引用。一个最简单的UI预制体最外层是一个cc.Prefab里面跟着一个cc.Node作为根节点根节点下面再挂cc.UITransform这类组件。通常每个节点都会有这几个属性_name节点名建议跟PSD图层名保持一致。_parent、_children父子引用。_lpos、_lrot、_lscale局部坐标、旋转、缩放。_active节点是否激活。_components节点上挂的组件列表。_prefab预制体自己的嵌套信息对应一个PrefabInfo对象。UI相关组件里cc.UITransform携带contentSize和anchorPointcc.Sprite负责显示图片cc.Label负责文本。手动拼预制体的难点就在这里每种组件都有自己的必填字段漏掉一个编辑器可能直接提示“load prefab failed”。这也是为什么我不建议人肉去写JSON而让Codex去写生成JSON的模板。模板只需要维护一份Codex可以按照项目实际的Cocos版本把字段补全。2.3 坐标、锚点、层级顺序的映射规则PSD和Cocos坐标系的差异如果不处理生成出来的UI位置全对不上。PSD画板的左上角是(0,0)往右是X正方向往下是Y正方向。Cocos的坐标原点是画布中心往右是X正方向往上才是Y正方向。假设设计分辨率是750x1334一个图层在PSD里的中心点是(365, 720)那它在Cocos根节点下的位置应该是cc_x 365 - 750 / 2 -10 cc_y 1334 / 2 - 720 -53这组公式是整个映射里的核心写错一个符号UI就整体上下颠倒。锚点我直接统一用(0.5, 0.5)。这样节点位置就代表中心点计算最简单。如果你非要用PSD的左上角对齐每个节点的坐标都得额外做锚点偏移代码写起来很容易乱。层级顺序也需要留意。PSD图层列表从头到尾一般是“从底到顶”Cocos子节点列表的顺序也是越靠后渲染越在上层。所以生成_children数组时按PSD图层顺序原样保留就行不用反转。不透明度我就映射成cc.UIOpacity组件。Cocos的Node本身没有透明度属性透明度是挂在UIOpacity组件上的。如果PSD里的父组有不透明度它的子层会在视觉上继承这个关系我放在生成器里统一递归处理避免每个子层重复叠加。3. 实操让Codex先写PSD解析器3.1 准备环境与项目约定开工之前先把工具链准备好。我的环境里装了这几样pip install psd-tools pillowpsd-tools负责读PSD结构pillow负责处理图像导出切片。Codex走的是命令行方式能够直接在项目目录里执行脚本、读文件、改代码这套流程它做起来很顺。然后我建了一个非常简单的项目约定文档让Codex按约定来写解析器设计分辨率固定读取一个配置默认750x1334。所有图层名保留原样遇到特殊字符时做转义。文本层一律生成cc.Label不转图片。像素层导出PNG到assets/ui/目录。临时分析结果输出到.tmp/psd_ir.json方便检查。这些约定不需要很复杂但必须有。没有约定Codex写出来的东西换个项目就废了。给Codex的第一个任务描述大概是这样的读取design/shop_panel.psd用psd-tools解析整个图层树。输出的中间JSON里要保留图层名、类型、可见性、不透明度、bbox、文本内容、字号、颜色。先打印图层树结构不要急着生成Cocos预制体等我确认后再做下一步。这一步的关键是“先打印别生成”。让Codex先展示它读到了什么我再根据实际PSD检查有没有解析错。3.2 中间IR设计中间IR是整个管道的“交换格式”我把它设计成跟PSD图层树几乎一一对应但字段全部改成Cocos友好的命名。下面是一段真实跑出来的精简例子{ designWidth: 750, designHeight: 1334, root: { name: shop_panel, type: group, visible: true, opacity: 1.0, contentSize: { width: 750, height: 1334 }, children: [ { name: bg, type: pixel, visible: true, opacity: 1.0, ccPos: { x: 0, y: -83 }, contentSize: { width: 720, height: 600 }, texture: bg.png }, { name: title, type: text, visible: true, opacity: 1.0, ccPos: { x: -175, y: 450 }, contentSize: { width: 200, height: 50 }, text: 限时特惠, fontSize: 28, color: #FFAA00, bold: false } ] } }这里ccPos已经换算成了Cocos坐标。为什么中间IR要存换算后的坐标而不存PSD原始坐标因为这样生成器就不需要关心坐标系转换只需要机械映射反过来如果发现某一类UI位置不对我只需要改解析器里的转换公式不用动生成器。中间IR还有一个好处就是可以拿给美术或主程做review。生成Cocos预制体之前先看这份JSON层级对不对、坐标靠不靠谱一眼就能判断。这比打开一个几百KB的prefab文件再慢慢找节点要舒服得多。3.3 让Codex跑起来解析器的核心代码长什么样Codex最终替我写出来的解析器核心逻辑不长但每一行都有用。我这边简化后大概是这样from psd_tools import PSDImage DESIGN_WIDTH 750 DESIGN_HEIGHT 1334 def psd_to_cc_pos(layer, design_width, design_height): left, top, right, bottom layer.bbox w right - left h bottom - top css_x left w / 2 css_y top h / 2 cc_x css_x - design_width / 2 cc_y design_height / 2 - css_y return round(cc_x, 2), round(cc_y, 2), w, h def walk(layer): node { name: layer.name, type: layer.kind, visible: layer.visible, opacity: layer.opacity / 255.0, } left, top, right, bottom layer.bbox if layer.kind group: children [walk(child) for child in layer] children [c for c in children if c is not None] node[children] children else: cc_x, cc_y, w, h psd_to_cc_pos(layer, DESIGN_WIDTH, DESIGN_HEIGHT) node[ccPos] {x: cc_x, y: cc_y} node[contentSize] {width: w, height: h} if layer.kind type: node[text] extract_text(layer) node.update(extract_style(layer)) elif layer.kind in (pixel, shape, smart_object): node[texture] f{layer.name}.png return node提取文本的extract_text和extract_style是让Codex按psd-tools的API去写的我不需要记住所有细节只需要告诉它“从engine_dict里拿文本和字号”。如果解析出来的文本是空字符串就说明这个文本层其实是做特殊效果用的我会直接标记成pixel层导出图片而不是硬生成一个空空如也的Label。跑完解析器后Codex在命令行里打印出完整图层树我看到后能确认每个节点都读到了。这个步骤看起来像是在跑一个普通的Python脚本但其实是整个自动化里最容易出错的地方——如果PSD解析阶段丢了信息后面生成器再聪明也补不回来。4. 从中间IR到预制体生成4.1 节点模板与组件映射生成器的任务是把中间IR翻译成Cocos预制体数组。这个阶段我按图层类型定了一套映射规则IR类型Cocos节点组件说明groupNode UITransform当作纯容器节点pixelNode UITransform Sprite显示切片textNode UITransform Label可编辑文本rootNode UITransform预制体根节点生成器里最重要的就是节点对象模板。我让Codex先写一个build_node函数专门产出Cocos的Node节点JSON字段不能漏。一个标准节点大概长这样{ __type__: cc.Node, _name: bg, _parent: { __id__: 1 }, _children: [], _active: true, _lpos: { __type__: cc.Vec3, x: 0, y: -83, z: 0 }, _lrot: { __type__: cc.Quat, x: 0, y: 0, z: 0, w: 1 }, _lscale: { __type__: cc.Vec3, x: 1, y: 1, z: 1 }, _components: [ { __id__: 2 } ], _prefab: { __id__: 5 } }每个对象的__id__其实是它在预制体JSON数组里的数组下标。生成器需要一边组装对象数组一边记录每个对象的下标回头填引用。这个逻辑用递归函数写很合适先处理根节点再依次处理子节点每处理一个对象就append进数组然后把它的下标存起来。这里踩过的最大坑是组件对象和节点对象之间的引用顺序。如果cc.Node的_components里写了组件id但组件对象还没被append到数组里Cocos打开预制体时会直接报“invalid index”。所以我让Codex在生成完整个JSON后加一道索引自检遍历所有__id__引用确保没有越界也没有指向错误类型。4.2 资源引用怎么不翻车生成预制体时Sprite组件的spriteFrame字段需要指向一个资源UUID。这一步是整条链路里最容易卡住的地方因为新建的PNG切片还没有UUID。我的处理方案是让Codex分两步走第一步解析器把PNG切片写进assets/ui/目录。然后在Cocos Creator里打开一次项目让资源系统扫描并生成对应的.meta文件。Cocos会自动为一个新PNG分配UUID。第二步生成器扫描assets/ui/下所有.meta文件把“文件名→UUID”建一个映射表。生成预制体时从映射表里查出对应图片的UUID填进cc.Sprite组件的spriteFrame字段。.meta文件里的UUID通常长这样{ ver: 1.0.18, uuid: a5fe2f34-1c2a-4b2d-8f8a-3c6b0f2b5a1d }读取它很简单import json, os def load_uuid_map(asset_dir): mapping {} for root, _, files in os.walk(asset_dir): for name in files: if name.endswith(.png.meta): with open(os.path.join(root, name), r, encodingutf-8) as f: meta json.load(f) png_name name[:-5] mapping[png_name] meta[uuid] return mapping如果某个切片的PNG没有生成生成器会直接警告而不是生成一个残缺的Sprite。这样我在Cocos里打开prefab之前就知道少了哪张资源。4.3 生成预制体并导入验证所有映射关系准备好之后生成器的最后一步就是递归把IR树转成对象数组然后保存成.prefab文件。python gen_prefab.py --ir .tmp/shop_panel_ir.json --out assets/ui/shop_panel.prefab跑完这个命令我会打开Cocos Creator看一眼效果。第一次生成的预制体在编辑器里大概率会有几个小问题比如某个按钮位置偏了几像素、某段文本字号不对。这很正常因为我用的是通用映射而PSD里的图层结构千变万化。出现问题时我不会直接在编辑器里手动拖拽修复因为一旦手动改了下次重新生成时改动会丢。正确做法是回到中间IR里看这个图层的数据再调整映射规则。比如我发现所有带“btn_”前缀的图层在Cocos里要额外加一个cc.Button组件那就直接在生成器里加一条规则重新跑一遍生成命令。验证的时候我还会让Codex在脚本里输出一份生成摘要包括节点总数、组件总数、引用的资源数。如果节点数跟PSD图层数对不上立刻就能发现有图层被漏掉了。5. 踩坑记录与排查技巧实录5.1 图层顺序反了遮挡关系全反了第一次跑出预制体我发现背景图把整个商城面板的按钮全都盖住了。检查了半天发现是生成器在组织_children数组时按“自动排序”把第一个图层放到了最上面显示。Cocos子节点的渲染顺序是后面的在上PSD图层顺序是后面的原本就在最上所以我不需要任何反转。只要保证生成器按PSD图层列表的原始顺序写入子节点数组遮挡关系就会正确。后来我干脆在生成器里加了个断言如果图层树里子节点数量超过50个就把第一个、最后一个图层名打印出来方便我对照PSD面板快速检查顺序。5.2 文本层字号对不上、字体丢失PSD里的文本经常带字距、行距、描边、阴影这些效果。动态Label只能还原最基础的字号和颜色样式复杂一点的文本硬生成Label反而会非常丑。我的处理原则是纯文本图层但样式简单生成cc.Label文本带描边、阴影、渐变等效果直接导出成图片当普通Sprite处理。怎么判断“样式简单”我让Codex解析文本的engine_dict把有没有图层效果作为判断条件有就降级为图片导出。字体丢失的问题也很常见设计师用的字体开发环境不一定有。我的方案是做一张字体映射表Codex在生成Label时在表里查找不到就回退到系统默认字体。字号大小可以保留因为字体不是同一个实际渲染宽度会有偏差。后来我跟设计约定正文一律用同一款开源字体这个问题才彻底缓解。5.3 切片边缘和自适应尺寸PSD里一个按钮按钮图层可能会带投影或发丝光直接用bbox裁图四周会带上透明的边缘。Cocos显示的时候透明边虽然看不见但会影响点击区域。我一开始没处理结果按钮看起来很小但点击范围大了一圈。后来我们约定凡是需要做识别区域的图层命名里加“_solid”后缀解析器遇到这种层就按不放大内容区域的方式生成UITransform尺寸保证实际尺寸跟样式一致。对于纯背景图则直接用带透明边的图像尺寸这样效果更圆润。九宫格拉伸也是个副本。按钮通常需要切成三宫格或九宫格但我的自动化脚本没法自动判断。我的做法是约定一个配置表{ btn_buy: { sliced: true, left: 40, right: 40, top: 30, bottom: 30 } }只要PSD里图层名出现在这个表里生成器就给Sprite加上sizeMode和sliced相关字段其余图层一律保持普通模式。5.4 不透明度和混合模式处理PSD的图层不透明度是0到255Cocos的UIOpacity是0到255这个可以直接映射。但组图层的透明度和子图层透明度叠加不能在Cocos里直接把两个UIOpacity相乘因为Node本身没有继承半透明机制。我的做法是如果父组的不透明度不是255就在当前节点上加UIOpacity取值等于“父级不透明度×自身不透明度”。混合模式这块就没办法完全还原了。Cocos的Sprite支持一些常见混合模式但PSD里优秀的混合模式可能有几十种。我的处理是生成一个警告列表解析器发现某图层混合模式不是“normal”就打印出来让我手工确认。在实际项目里99%的界面用的是普通混合模式遇到特殊效果我直接让美术把该层单独导出成透明PNG比在引擎里还原混合效果更省事。6. 从单张UI到批量生产力的几点心得这套流程跑通之后我不光是在项目里用还总结出了几个能直接复用的经验。第一能力边界要划清楚。自动生成预制体最适合的是结构规整的UI面板按钮、文本、背景、列表项这类层次清晰的界面能省下80%的搭建时间。但如果是运营活动那种长图拼贴、特效闪烁的页面还是老实让设计师导序列帧或者做手调吧别为了自动化而自动化。第二约定比技术重要。PSD命名规范、是否保留分组、文本层质量、切图方式这些都会直接决定自动化产物的质量。自从我跑了这套流程我跟设计师沟通的第一件事就是“图层命名能不能规范点”效果比改十版脚本都好。第三差量review很香。手工在Cocos里拖出来的预制体很难看出跟上一版差在哪。但现在我有了中间IR和生成脚本每次改版只跑一遍生成命令拿diff看两个JSON之间哪里变了节点位置、组件字段变化一目了然。这个能力在做UI走查和代码review时特别好用。我在实际项目里的体会是Codex这类工具真正改变的不是“写代码”这一步而是让我更快地把一套笨重但可复现的流程固化下来。以前想到要写几百行解析PSD的脚本头大现在只需要描述清楚规则让Codex先把骨架搭出来我再根据实际PSD一轮轮校准就行。如果你也在Cocos项目里被PSD转UI折磨过不妨按这个思路试一次。先拿一个最简单的登录面板跑通再慢慢把规则补全这东西不只是一次性脚本而是你以后所有UI开发都可以依赖的杠杆。

想做一个「会获客」的企业网站?

留下需求,1 小时内获取专属建站方案与透明报价。

免费咨询方案
↑