ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用Codex自动解析PSD/Figma,实现微信小游戏节日UI换肤

用Codex自动解析PSD/Figma,实现微信小游戏节日UI换肤 微信小游戏做节日运营时UI 换肤经常是卡在发布路上的最后一环。运营希望在 3 天内上线一套春节、周年庆或暑期活动皮肤但常见流程里美术改图、程序替换资源、测试回归尺寸每一步都可能延后。这篇文章要解决的问题是如何用 Codex 辅助解析 Figma 或 PSD 原文件把图层名称、坐标、宽高继承到微信小游戏的皮肤配置里再通过一套自动脚本生成美术资源和皮肤切换逻辑让节日皮肤从设计稿到小游戏内生效的过程尽量自动化。适合已经接触过微信小游戏、想用 AI 编程工具加速日常开发的开发者阅读。读完可以复现一个最小工作流给定一张设计稿运行脚本后得到皮肤资源目录和 JSON 配置在小游戏里切换皮肤并保持原始排版。1. 先理解微信小游戏换肤的痛点以及 Codex 在其中的位置1.1 为什么运营节日换肤总在赶时间微信小游戏的 UI 换肤本质上不是换一张背景图而是把背景、按钮、标题、弹窗、活动入口、动效切片等一组资源统一替换。节日运营对时效要求很高可能上午确认设计稿晚上就要上线活动页。如果沿用传统方式美术需要逐张导出 PNG程序需要手动填写资源路径和坐标测试还需要在不同机型上反复比对排版。任何一个环节出现命名不一致都可能出现按钮错位、图片黑底、资源找不到等问题。传统流程通常长这样设计在 Figma 或 PSD 中排版。美术手动导出切图。程序把切图放进小游戏资源目录。程序手动维护一张皮肤配置表填写每个资源的位置和尺寸。测试打开小游戏截图和设计稿比对。发现偏移后继续人工调整。这个流程最大的问题不是某个环节慢而是资源信息在设计稿到代码之间发生了“手工搬运”。坐标、尺寸、图层名在搬运过程中丢失或变形。自动化换肤的核心不是写一堆加载图片的代码而是把设计稿里已经存在的图层尺寸、坐标和层级关系自动带入游戏运行时。1.2 Codex 能做哪部分工作不能做哪部分Codex 是 AI 编程助手适合读仓库、生成代码、执行命令、修改文件。它不能直接“看”懂 PSD 的二进制内容也不会替设计师重新画一张符合品牌风格的节日主视觉。但它可以生成和维护一套解析脚本让脚本去调用 psd-tools、Figma API、Pillow 等工具把设计稿解析成小游戏能直接使用的资源包。可以这样理解分工设计师负责设计稿的视觉质量和图层命名。开发者负责把“如何从设计稿导出资源”的规则讲清楚。Codex 负责把规则写成脚本并在脚本报错时快速定位修改。游戏运行时负责根据皮肤配置加载和绘制资源。因此标题里的“AI 一键生成美术资源”更准确的说法是在 Codex 辅助下用程序把 PSD/Figma 中的图层自动化导出为小游戏资源并生成可继承尺寸的配置。如果确实需要生成全新的节日素材可以在此基础上接入图像生成模型但这属于素材补充不属于换肤链路的必备环节。1.3 目标工作流设计源文件到皮肤包的一键链路先明确最终希望达到的效果运营拿到一套设计稿运行一个命令或点击一次脚本就能产出以下内容一个皮肤资源目录里面是 PNG 图片命名规范统一。一个 skin.json包含资源路径、名称、原始坐标、原始尺寸。一份可部署的远端或本地资源微信小游戏运行时按名称加载。这套链路的核心不是某一个 AI 模型而是设计文件、解析脚本、皮肤配置和运行时渲染四层之间的约定。约定越清晰自动化程度越高。Codex 在这里扮演的角色是“快速写出并维护约定对应的脚本”它降低了脚本开发的门槛但不改变工程化设计本身。2. 环境准备工具链和项目结构先跑通2.1 安装与依赖清单在开始写脚本前先把环境补齐。下面表格列出常见组件版本建议以实际安装为准落地前要确认和你本机环境的兼容性。组件版本建议用途Node.js18 或 20 LTS运行小游戏相关工具和 JavaScript 脚本Python3.9 以上运行 PSD 解析和图像处理脚本psd-tools1.9 以上读取 PSD 图层结构并导出图层图片Pillow10.x图像裁剪、缩放、合成requests2.31 以上调用 Figma API 和下载图片Codex CLI当前稳定版生成和维护脚本微信开发者工具稳定版预览、调试、上传小游戏Figma 账号任意可用账号获取 Personal Access Token 并调用 API如果团队没有 Figma可以只用 PSD 源文件psd-tools 足够完成图层解析。如果团队以 Figma 为主优先使用 Figma API因为 Figma 的 JSON 结构更容易被脚本遍历节点中也自带绝对坐标和宽高不需要额外计算。2.2 项目目录结构设计建议把设计源文件和处理脚本分开生成产物也不要混进游戏源码目录。一个常见结构如下mini-game-skin-tool/ ├── design/ │ ├── ui_main.psd │ └── ui_main.figma.json ├── scripts/ │ ├── export_psd.py │ ├── export_figma.py │ ├── build_skin_json.py │ └── common.py ├── dist/ │ └── skins/ │ └── spring/ │ ├── skin_btn_start.png │ ├── skin_banner.png │ ├── metadata.json │ └── skin.json ├── minigame/ │ ├── game.js │ ├── skin-manager.js │ └── config/ │ └── skin.manifest.json └── AGENTS.md设计文件放在design/脚本放在scripts/生成产物放在dist/skins/。小游戏代码放在minigame/只引用dist复制过来的资源或 CDN 地址。这样做的好处是回滚方便重新生成资源不会污染源码目录。2.3 创建 Codex 辅助配置 AGENTS.mdCodex 在修改项目前会先读项目中的说明文件。建议在项目根目录创建AGENTS.md把项目约定写进去。这样后续让 Codex 生成脚本时它更容易理解资源命名规则和输出目录。# 微信小游戏皮肤生成工具 这是一个从 PSD/Figma 导出微信小游戏换肤资源的项目。 - 设计源文件放在 design/ 目录。 - PSD 解析使用 scripts/export_psd.py。 - Figma 解析使用 scripts/export_figma.py。 - 所有输出写到 dist/skins/{皮肤名}/。 - 图层命名规则需要参与换肤的图层必须以 skin_ 开头。 - metadata.json 必须记录每个资源的 name、file、x、y、width、height。 - 生成的 skin.json 必须保留原始坐标系坐标参考设计稿画布。 - 不要改动 minigame/ 下的原始代码除非改动属于皮肤渲染逻辑。AGENTS.md不是必须的但在多文件项目中能明显减少 Codex 理解项目的成本。它相当于把“人脑里的项目约定”写成代码仓库里的规范。3. 核心实现用 Codex 生成 PSD/Figma 资源导出器3.1 从 PSD 原文件导出图层资源并记录尺寸PSD 解析最常见的库是 psd-tools。下面脚本读取一个 PSD 文件遍历所有可见图层筛选名字以skin_开头的图层导出 PNG并生成 metadata.json。import json from pathlib import Path from psd_tools import PSDImage def export_psd_skin(psd_path: Path, output_dir: Path): psd PSDImage.open(psd_path) output_dir.mkdir(parentsTrue, exist_okTrue) metadata [] for layer in psd.descendants(): if layer.is_group() or not layer.visible: continue if not layer.name.startswith(skin_): continue left, top, right, bottom layer.bbox width max(1, right - left) height max(1, bottom - top) image layer.composite() file_name f{layer.name}.png image.save(output_dir / file_name) metadata.append({ name: layer.name, file: file_name, x: left, y: top, width: width, height: height }) with (output_dir / metadata.json).open(w, encodingutf-8) as f: json.dump(metadata, f, ensure_asciiFalse, indent2) if __name__ __main__: export_psd_skin( Path(design/ui_main.psd), Path(dist/skins/spring) )这段代码的意义是“继承图层的尺寸”。layer.bbox返回图层在设计稿画布中的绝对坐标和宽高width和height会写进 metadata后续生成皮肤配置时直接使用。需要注意layer.composite()在部分 psd-tools 版本中的返回尺寸可能与bbox不完全一致。如果发现导出图片尺寸异常可以先打印image.size和(width, height)对比再决定是否裁剪或缩放。3.2 从 Figma 文件导出图层资源并继承坐标如果设计稿在 Figma 中可以直接通过 Figma API 获取文档 JSON再调用图片导出接口。先准备一个环境变量export FIGMA_TOKEN你的 Figma Personal Access Token export FIGMA_FILE_KEY你的 Figma 文件 KeyFile Key 可以从 Figma 文件 URL 中获取例如https://www.figma.com/file/abc123/design中的abc123。下面的脚本会遍历文档节点找出名字以skin_开头的节点导出 PNG 并生成 metadata。import json import os import requests from pathlib import Path FIGMA_TOKEN os.environ[FIGMA_TOKEN] FIGMA_FILE_KEY os.environ[FIGMA_FILE_KEY] OUTPUT_DIR Path(dist/skins/spring) def walk_nodes(node, result): name node.get(name, ) if name.startswith(skin_): result.append(node) for child in node.get(children, []): walk_nodes(child, result) def main(): headers {X-Figma-Token: FIGMA_TOKEN} file_resp requests.get( fhttps://api.figma.com/v1/files/{FIGMA_FILE_KEY}, headersheaders ) file_resp.raise_for_status() file_data file_resp.json() skin_nodes [] walk_nodes(file_data[document], skin_nodes) node_ids [node[id] for node in skin_nodes] img_resp requests.get( fhttps://api.figma.com/v1/images/{FIGMA_FILE_KEY}, headersheaders, params{ids: ,.join(node_ids), format: png, scale: 1} ) img_resp.raise_for_status() image_urls img_resp.json().get(images, {}) OUTPUT_DIR.mkdir(parentsTrue, exist_okTrue) metadata [] for node in skin_nodes: node_id node[id] url image_urls.get(node_id) if not url: continue bbox node.get(absoluteBoundingBox, {}) png_resp requests.get(url) png_resp.raise_for_status() file_name f{node[name]}.png (OUTPUT_DIR / file_name).write_bytes(png_resp.content) metadata.append({ name: node[name], file: file_name, x: bbox.get(x, 0), y: bbox.get(y, 0), width: bbox.get(width, 0), height: bbox.get(height, 0) }) with (OUTPUT_DIR / metadata.json).open(w, encodingutf-8) as f: json.dump(metadata, f, ensure_asciiFalse, indent2) if __name__ __main__: main()Figma API 返回的absoluteBoundingBox就是节点在设计稿中的绝对坐标。脚本中没有对节点类型做过滤真实项目中可以增加node[type] FRAME之类的条件避免把无坐标的组件实例或布尔节点也导出。3.3 让 Codex 生成并改进这套脚本上面两段代码可以自己写也可以交给 Codex 生成。在命令行进入项目根目录启动 Codex 后输入类似下面这样的指令请阅读当前项目结构然后完成 scripts/export_figma.py 脚本。 要求 1. 从环境变量读取 FIGMA_TOKEN 和 FIGMA_FILE_KEY。 2. 拉取 Figma 文件 JSON递归遍历所有节点。 3. 筛选名字以 skin_ 开头的节点。 4. 调用图片导出接口下载 PNG 到 dist/skins/spring/。 5. 生成 metadata.json记录 name、file、x、y、width、height。 6. 增加 HTTP 请求失败重试重试 3 次。 7. 不要修改 minigame/ 下的文件。Codex 写完脚本后先运行一次再打开 metadata.json 检查。这里要强调的是Codex 生成的代码不是“最终答案”。它可能会猜错 Figma API 字段也可能会把输出路径写错。第一次使用时让 Codex 在一个小设计稿上跑通再扩大到全套皮肤资源。4. 微信小游戏侧动态皮肤加载与一键切换4.1 皮肤配置的数据结构资源导出后的 metadata.json 还需要转成小游戏运行时方便读取的配置。推荐生成一个 skin.json结构如下{ id: spring, name: 春节皮肤, version: 20250601, resources: { btn_start: { src: assets/skins/spring/skin_btn_start.png, width: 280, height: 96 } }, layout: { btn_start: { x: 235, y: 800, width: 280, height: 96 } } }这个结构把资源路径和布局信息分开。resources负责描述“图片从哪里来、原始尺寸是多少”layout负责描述“图片应该画在画布的哪个位置、以多大尺寸绘制”。src可以是本地相对路径也可以是 CDN 地址。生成逻辑很简单遍历 metadata把skin_前缀去掉作为资源 ID然后把 file、x、y、width、height 填入对应字段。import json from pathlib import Path def build_skin_json(metadata_path: Path, output_path: Path, skin_id: str, skin_name: str): with metadata_path.open(r, encodingutf-8) as f: metadata json.load(f) resources {} layout {} for item in metadata: resource_id item[name].replace(skin_, , 1) file_name item[file] width item[width] height item[height] resources[resource_id] { src: fassets/skins/spring/{file_name}, width: width, height: height } layout[resource_id] { x: item[x], y: item[y], width: width, height: height } skin_config { id: skin_id, name: skin_name, resources: resources, layout: layout } with output_path.open(w, encodingutf-8) as f: json.dump(skin_config, f, ensure_asciiFalse, indent2) if __name__ __main__: build_skin_json( Path(dist/skins/spring/metadata.json), Path(dist/skins/spring/skin.json), spring, 春节皮肤 )这里的关键是资源 ID。如果设计师在 Figma 或 PSD 中命名混乱比如中文名、带空格的名字生成 JSON 后容易出现路径错误。因此资源命名规范必须在设计阶段就定好。4.2 游戏运行时资源加载在微信小游戏里动态换肤的核心是不要把所有皮肤图片都写死在代码里。可以用一个 SkinManager 负责加载当前皮肤配置、缓存图片、绘制到画布。下面是一个面向 Canvas 2D 的最小实现class SkinManager { constructor() { this.currentSkin null; this.imageCache {}; } loadSkin(skinConfig) { this.currentSkin skinConfig; const keys Object.keys(skinConfig.resources); return Promise.all(keys.map((key) this.loadImage(key))); } loadImage(key) { const resource this.currentSkin.resources[key]; return new Promise((resolve, reject) { const image wx.createImage(); image.onload () { this.imageCache[key] image; resolve(image); }; image.onerror reject; image.src resource.src; }); } draw(ctx, designWidth) { if (!this.currentSkin) return; const scale ctx.canvas.width / designWidth; const layout this.currentSkin.layout; const resources this.currentSkin.resources; Object.keys(layout).forEach((key) { const pos layout[key]; const image this.imageCache[key]; if (!image) return; ctx.drawImage( image, pos.x * scale, pos.y * scale, pos.width * scale, pos.height * scale ); }); } } module.exports SkinManager;designWidth是设计稿宽度比如 750。运行时会根据屏幕实际宽度计算缩放比例保证设计稿中的坐标在不同机型上保持一致。如果游戏使用 Cocos Creator 或 Unity 转换的微信小游戏可以把这个逻辑迁移到对应引擎的 Texture/Sprite 加载层但数据结构可以复用。4.3 节日皮肤切换的落地流程拿到新皮肤后切换流程可以分成四步资源导出运行脚本生成skin_*.png和skin.json。资源上传把皮肤目录上传到 CDN或者打进小游戏分包。配置下发通过后台下发皮肤 ID客户端拉取对应的skin.json。运行时切换调用loadSkin加载完成后重绘界面。在开发阶段可以在本地写死皮肤 ID 来验证。在生产环境建议增加一个skin.manifest.json里面记录每个皮肤 ID 对应的配置版本和资源地址。这样运营只需要改后台配置不需要发新版本小游戏。5. 运行验证从设计稿到游戏内换肤的完整链路5.1 命令行执行步骤假设当前目录是项目根目录先安装 Python 依赖python -m pip install psd-tools pillow requests如果使用 PSD 源文件运行导出脚本python scripts/export_psd.py design/ui_main.psd dist/skins/spring如果使用 Figma则先设置环境变量再运行export FIGMA_TOKENxxx export FIGMA_FILE_KEYxxx python scripts/export_figma.py然后生成皮肤配置python scripts/build_skin_json.py查看生成结果find dist/skins/spring -type f | sort预期产物大致如下dist/skins/spring/skin_banner.png dist/skins/spring/skin_btn_start.png dist/skins/spring/skin_bg.png dist/skins/spring/metadata.json dist/skins/spring/skin.json5.2 验证脚本产物是否合格打开dist/skins/spring/metadata.json逐项核对检查项预期结果说明记录数量等于设计稿里skin_开头的可见图层数量多出来说明过滤规则有问题file 字段对应 PNG 文件确实存在避免资源路径 404width/height与设计稿图层尺寸一致不一致时需要检查图层导出方式x/y与设计稿绝对坐标一致继承坐标依赖这一项图片本身无黑底、无粉红块、无白边检查图层特效和混合模式5.3 在微信开发者工具中验证把dist/skins/spring整个目录复制到微信小游戏项目的资源目录下然后在入口文件中加载皮肤配置并调用 SkinManagerconst SkinManager require(./skin-manager); const springSkin require(./assets/skins/spring/skin.json); const manager new SkinManager(); manager.loadSkin(springSkin).then(() { const ctx canvas.getContext(2d); manager.draw(ctx, 750); });打开微信开发者工具切换到不同机型预览。重点看三个现象背景图是否完整铺满。按钮位置是否与设计稿一致。切换默认皮肤和节日皮肤时老资源是否被正确释放。如果按钮位置整体偏下或偏右优先检查 metadata 中的 x 和 y 是否来自设计稿画布坐标而不是节点本身的局部坐标。6. 常见问题排查资源错位、样式丢失、Codex 脚本报错6.1 PSD 图层导出后出现黑底或缺失现象是导出的 PNG 带着一块黑色背景或者导出后图片是空白的。可能原因有三个图层是带混合模式的组layer.composite()合成的结果和视觉预期不同。图层是文字图层或形状图层psd-tools 对部分图层类型的支持不完整。图层虽然 visible但实际没有像素数据。检查方式是在脚本中打印layer.kind、layer.bbox、layer.composite().size确认图层类型和包围盒是否正常。如果对文字或形状图层导出的结果不满意可以在设计源文件中把这类图层先栅格化或者单独导出为 PNG 后再放入自动流程。对普通像素图层layer.composite()通常已经够用。6.2 Figma 节点 ID 变化和 Token 失效现象是今天还能正常导出明天运行脚本时却提示找不到节点或者图片 URL 返回 null。可能原因是 Figma 文件结构发生了大幅改动节点被移到了别的 Frame导致遍历逻辑匹配不到也可能 Personal Access Token 被撤销或者 Figma 对单个文件的导出数量有限制。检查方式可以分两步先用 curl 验证文件接口能否返回 JSONcurl -H X-Figma-Token: $FIGMA_TOKEN \ https://api.figma.com/v1/files/$FIGMA_FILE_KEY \ -o figma_check.json再打开figma_check.json查看skin_开头的节点是否仍然存在。如果节点还在问题出在脚本的递归条件如果节点不存在说明设计稿结构变了。不要在脚本里硬编码具体节点 ID而是坚持用命名前缀匹配这样文件结构调整后仍能自动兜底。6.3 微信小游戏包体限制和远程资源加载问题现象是资源全部放在本地后微信开发者工具上传时报包体超限或者远程图片加载时显示白屏。微信小游戏对总包体有明确限制全部皮肤资源塞进本地包不是可持续的方案。正确做法是把皮肤资源放到 CDN在客户端通过wx.downloadFile下载到本地缓存或者使用小游戏远程资源能力。配置远程资源时要注意域名白名单。开发阶段可以勾选“不校验合法域名”但上线前必须在小游戏后台配置下载文件域名并且只能使用 HTTPS。资源加载失败时要在image.onerror中记录日志否则运营配置了一个错误 CDN 地址后客户端很难定位。6.4 Codex 生成的代码需要人工确认的关键点Codex 能快速生成脚本但脚本不一定完全符合当前项目和 API 版本。常见问题包括使用了不存在的 Figma API 字段。把输出目录写到了源码目录。layer.composite()的用法与 psd-tools 版本不匹配。生成的 JSON 路径包含反斜杠在微信小游戏里解析出错。每次让 Codex 修改脚本后至少运行一次小样本验证。在 prompt 中明确要求“先增加 dry-run 模式只输出日志不写文件”能降低误改风险。不要把 Codex 当成可以直接接管生产流程的工具把它当作“能快速写出初版工程的结对开发者”更合适。7. 最佳实践与可复用清单7.1 资源规范是自动化的前提自动换肤能不能跑通一半取决于脚本另一半取决于设计稿命名。建议在团队内部定一套可执行规则规则说明图层统一加skin_前缀脚本只处理带前缀的图层避免把装饰线和背景误导出图层名只使用英文、数字、下划线中文和空格会带来路径兼容问题设计稿固定一个画布尺寸推荐 750x1334方便微信小游戏做等比缩放所有可换肤资源放在同一画布下避免跨页面导出后坐标混乱文字图层尽可能转成矢量或位图避免不同环境字体缺失导致样式变化同一资源的默认皮肤和节日皮肤名字保持一致这样客户端逻辑只需要切换资源目录和配置如果设计稿命名已经乱了可以先用脚本打印出所有图层名和设计师一起清理。不要试图在脚本里写一堆模糊匹配规则那会让后期维护成本暴涨。7.2 生产环境换肤发布检查清单每次发节日皮肤前建议按以下清单检查确认设计稿中的skin_图层命名与默认皮肤保持一致。运行脚本生成metadata.json检查记录数量与设计稿图层数量一致。打开 3 张代表图片确认没有黑底、透明通道丢失、尺寸异常。上传资源到 CDN确认 HTTPS 地址可以直接访问。在小游戏后台配置下载文件域名。至少在一台低端 Android 和一台 iOS 上预览检查内存和绘制速度。验证从默认皮肤切换到节日皮肤后再切回默认皮肤是否正常。检查日志系统能记录皮肤加载失败的错误。这套清单并不复杂但它能挡住大多数换肤事故。真正的问题往往不是代码写不出来而是资源在上线前根本没有被验证过。7.3 从工具脚本到自动化管线的扩展方向当前方案已经能把设计稿变成资源包但要支持运营频繁换肤还可以继续扩展在 CI 中增加皮肤生成任务每天定时拉取 Figma 文件比较 metadata 哈希有变化自动提交资源包。增加资源差分机制只上传发生变化的那几张图减少 CDN 刷新时间。把skin.json纳入版本管理皮肤配置和代码一起评审和回滚。在客户端增加皮肤内存缓存上限防止频繁切换导致内存膨胀。把导出脚本接入 Codex遇到 Figma API 字段变化时让 Codex 根据报错日志自动修复代码。这里最值得投入的方向是“让设计稿成为唯一事实来源”。编辑器里改了图层位置脚本重新跑一次游戏内位置就会跟着变不需要程序手工抄坐标。能做到这一点后节假日换肤就从“资源搬运项目”变成了真正的自动化运营能力。Codex 参与这套工作流的价值不在于一键生成一个完整游戏而在于把设计和运行之间那条重复、易错、难维护的路径自动化。如果你正在做微信小游戏的节日运营建议先从一个六到八个图层的界面开始把 PSD 或 Figma 导出脚本跑通再逐步扩大覆盖范围。
返回列表