帧工坊 FrameCraft 帧工坊 FrameCraft

UI 图集打包:尺寸不一的散图,自动塞进一张合图并生成引擎能读的数据

做 UI 最烦的不是画图,是收尾:背包里 48×48 的图标、240×64 的按钮、400×48 的标题条尺寸全不一样,如果一张图一次加载,几十个 UI 元素就是几十次读取。图集(Atlas)就是把它们排进一张大图,配一个记录每块坐标的数据文件,引擎按坐标去取——读取次数从几十次变成一次。难点在「尺寸不一样怎么排才不浪费」:这一页用 MaxRects 算法自动装箱,并把结果全部摊开给你看:填充率多少、实际用了多少像素、排成了几页、有没有图塞不下,都是实测数字。排完直接导出 TexturePacker JSON / Cocos plist / Godot tres / CSV,Unity、Cocos、Godot、Phaser 拿去就能读。全程在浏览器本地处理,图片不上传服务器。

批量拖入 · MaxRects 自动装箱 · 填充率实测 · 扩边防串色 · 2 的幂对齐 · 超过 2048 自动分页 · 五种数据格式 · 导出 ZIP

1
把散图全部拖进来
🗂️

把图片拖进来,或点这里选

PNG / JPG / WebP,尺寸各不一样也没关系

本地处理不上传;透明 PNG 最佳(JPG 的白底会一起排进合图)
还没上传图片
2
调排布,看合图和填充率
排布 决定图与图怎么挨在一起
图间距 扩边(extrude) 间距留白,扩边是把边缘像素复制出去;两个一起用才不串色
尺寸 合图最终多大,由这两个决定
最大边长 尺寸规则
输出 合图格式和给引擎看的数据格式
合图格式 数据格式
还没排图 —— 先拖几张图进来,或点「载入示例」
3
导出合图 + 数据文件
ZIP 里是每一页的合图 + 对应的数据文件;多页时按 atlas-0 / atlas-1 编号
等待处理

怎么用

  1. 拖入散图:把要打包的 UI 图全选拖进来。没素材就点「载入示例」——那 8 张是故意做成尺寸各异的(图标 32~96、按钮 240×64、标题条 400×48、面板 320×200),正好是等宽网格合图最吃亏的情况。
  2. 先看填充率:汇总条里的百分比就是「合图里真正被用到的像素 ÷ 整张画布」。低于 60% 说明尺寸差异太大,可以开「允许 90° 旋转」试试——注意旋转只在原方向实在塞不下时才会启用,能省空间但不会把横图硬转成竖图(那样反而更浪费)。
  3. 设间距和扩边:间距 2、扩边 2 是通用搭配。扩边是把每块图的边缘像素向外复制两圈,防止缩小时采样串到邻居颜色——不扩边会出现「UI 边缘莫名多出一条别的颜色」。
  4. 选尺寸规则:默认自动裁到最小,填充率最高。老引擎或老 GPU 要求 2 的幂再选「向上取到 2 的幂」,代价是填充率会掉。
  5. 看要不要分页:图太多塞不进 2048 时,工具会自动分成多页(atlas-0、atlas-1…),上方标签页可切换查看,导出时每页各带一份数据文件。
  6. 选数据格式导出:Unity / Phaser / PixiJS 选 JSON(hash 或 array 看你用的加载器),Cocos 选 plist,Godot 3 选 tres(每个 sprite 一个文件)。点「导出 ZIP」一次拿全。

常见问题

图集到底解决了什么问题?

三件事:① 读取次数——20 个 UI 元素散着放要读 20 次,合成一张只要 1 次;② 显存——每张图单独上传都会按 2 的幂对齐补齐,散图浪费的显存比你想的多;③ 合批——同一个图集里的元素在多数引擎里能合并成一次绘制(draw call),UI 元素多了这一步的差别很明显。代价是:改一张图要重新打一次包,所以图集一般在素材定稿后再打。

填充率多少算好?有实测吗?

有。用页面里的示例 8 张图(32×32 ~ 400×48)实测,间距 2、扩边 0:

设置合图尺寸画布像素填充率页数
实测中…————

一般规律:70% 以上算排得不错,50% 以下通常是尺寸差异太大(比如一张 400 宽和一堆 32 宽混在一起)。这张表是页面自己跑出来的,你拖自己的图进来也会实时重算。

表里有个反直觉的地方值得单独说:勾了 trim,画布明明变小了,填充率反而更低。因为填充率的分子分母同时变小——被裁掉的透明边既不算「占用」也不算「画布」,比例自然回落。所以比较 trim 要看「画布像素」这一列(示例里 140k → 124k,实际省掉 11%),别只盯百分比。同理 2 的幂那档填充率掉得厉害,但那是老设备要求付的固定代价,不是排得差。

「扩边」是什么?不扩会怎样?

合图里两块图是紧挨着的。图片被缩小显示时,GPU 采样会在边缘取到邻居那一格的像素——表现就是按钮边缘冒出一条别的颜色,或者透明区外面渗出一圈色。扩边(extrude)的做法是把每块图的边缘像素向外复制 2 圈,让采样落在自己身上。注意两件事:扩边必须和间距一起用(否则扩出去的像素会盖到邻居上,这一页会自动保证间距不小于扩边);扩边会让 frame 变大 4 个像素,数据文件里已经算进去了,你在引擎里取到的还是原始内容。

「裁掉透明边 trim」为什么默认关?

因为 trim 会改掉图的原始尺寸,而 UI 里大量元素是靠原始尺寸定位的——最典型的就是九宫格(九切)图:你量好的 12px 圆角边界,trim 之后偏移全变了,九切坐标直接失效。trim 对「纯图标、不需要对齐」的图能省不少空间,但面板、按钮、边框这类要拉伸的图千万别开。开了的话数据文件里会用 spriteSourceSize 记录偏移,引擎能还原位置,但九切边界仍然得你自己重算。

该不该勾「2 的幂」?

看目标环境:网页、Unity、Godot、小游戏现在都不要求 2 的幂,默认「自动裁到最小」最省。只有很老的 GPU / OpenGL ES 1.x 才强制要求,或者你要开 mipmap 且引擎要求 POT。勾选后画布会向上取到 512 / 1024 / 2048,填充率会明显下降(比如内容只用到 700×300,也会给到 1024×512)——这是必须付的代价,页面会如实把新数字算给你看。

图太多,塞不进 2048 怎么办?

工具会自动分页:装到装不下就开新的一页,导出时每页一张合图 + 一份数据文件(atlas-0、atlas-1…)。这是自动的,你只要看汇总条里的「页数」。如果某一张图本身就超过最大边长(比如 3000×2000 塞进 2048),它会单独列出来告诉你装不下——这时该去把它缩小,或者把最大边长提到 4096。

五种数据格式怎么选?

格式给谁用说明
JSON(hash)Unity、Starling、多数加载器以文件名为 key,取值最快,TexturePacker 的默认格式
JSON(array)Phaser 3、PixiJSframes 是数组,这两个框架的加载器默认读这种
Cocos plistCocos2d-x / Cocos Creatorformat 3 标准格式,含 offset / sourceSize,trim 也能对上
Godot .tresGodot 3每个 sprite 生成一个 AtlasTexture 资源,拖进项目直接用
CSV自查 / 自己写解析器一行一个 sprite,坐标尺寸全摊平

不管选哪种,合图都是同一张,只是配套的数据文件不同;ZIP 里两个都有。

跟「精灵图」那个工具有什么区别?

那边是等宽网格:序列帧动画每一格一样大,按行列排就行,靠行列号定位。这边是异形尺寸:UI 元素高矮胖瘦都不一样,等宽网格会浪费大量空间(按最大的那张对齐),所以必须用装箱算法,并且必须输出坐标数据(没有数据文件,引擎不知道每块在哪)。简记:动画用精灵图,UI 用图集。

收费吗?图片会上传吗?

免费,无水印、无需注册。装箱和渲染全在你自己的浏览器里跑完,图片不经过任何服务器。

相关工具