主题
自定义 UI 图集
把多个图标放在同一张大图中,再用 JSON 为每个矩形区域分配逻辑图片名,普通控件的 Image 属性就能显示其中一块。AI 助手可以直接生成这些资源配置;不需要先把每块裁成独立图片,也不需要为普通图标改用 Canvas。
这条流程使用现有图集加载能力,不增加运行时 API 或客户端能力版本。它适合静态图标、按钮图片和面板背景。动画选型见序列帧方案,需要每次绘制动态选择源矩形时可用 Canvas.DrawImage 的源区域重载。
文件与路径
text
MyProject/
ui/
atlas/
custom_icons.png
custom_icons.json
atlas.json| 位置 | 示例 | 含义 |
|---|---|---|
清单的 AtlasPath | atlas/custom_icons.png | 大图文件,相对于项目 ui/ |
清单的 ConfigPath | atlas/custom_icons.json | 区域配置文件,相对于项目 ui/ |
区域的 RelativePath | ui/image/icons/sword.png | 子图片的逻辑名称,包含 ui/ |
控件的 Image | image/icons/sword.png | 引用子图片,省略 ui/ |
RelativePath 是逻辑名称,项目里不需要存在 ui/image/icons/sword.png 这张散图。
登记大图
ui/atlas/atlas.json 的根是数组:
json
[
{
"AtlasPath": "atlas/custom_icons.png",
"ConfigPath": "atlas/custom_icons.json"
}
]已有清单时追加或更新这一对路径,保留其他图集条目。每张大图对应一个区域配置文件,清单可以登记多张大图。
定义子图片
假设 custom_icons.png 为 256×128,大图中剑图标位于左上角 (128, 64),大小为 32×32。生成 ui/atlas/custom_icons.json:
json
[
{
"RelativePath": "ui/image/icons/sword.png",
"X": 128,
"Y": 64,
"Width": 32,
"Height": 32,
"OriginSize": "32 32",
"Border": "0 0 0 0",
"Scaled": false,
"HasAlpha": true,
"CompressType": 0
}
]| 字段 | 普通矩形图标的设置 |
|---|---|
RelativePath | 唯一的逻辑图片名;建议使用小写和项目专属目录,避免重名 |
X / Y | 大图左上角为原点,向右、向下增加,单位为像素 |
Width / Height | 取图矩形的宽高,单位为像素 |
OriginSize | 图片的逻辑宽高,格式为字符串 "宽 高";Width/Height 是图集中实际存储的像素宽高。整图缩小或九宫格重复区域压缩后,两者可以不同;未处理的普通图标填写相同尺寸 |
Border | 本例使用字符串 "0 0 0 0" |
Scaled | 本例没有预先缩小子图片,填 false |
HasAlpha | 包含透明像素时填 true |
CompressType | 本例普通矩形填 0;不要使用需要另一套切片数据的九宫格压缩值 4 |
增加数组条目即可定义更多图标。AI 生成前应读取大图尺寸并确认:X/Y >= 0、Width/Height > 0、X + Width <= 大图宽度、Y + Height <= 大图高度。不要猜测图标坐标,也不要生成仅有 frames/meta 等字段的其他工具格式。
同一套图集中,逻辑名称按小写匹配,重复名称会保留先加载的条目。使用不重复的名称,避免重复定义导致引用到另一块区域。
逻辑尺寸与存储尺寸
OriginSize 保存图片处理前的逻辑尺寸,Width/Height 描述处理后实际存入图集的矩形。图片尺寸查询使用逻辑尺寸;绘制采样使用实际矩形。控件已经明确设置显示宽高时,仍按控件尺寸绘制,不会仅凭 OriginSize 额外留出透明边或产生偏移。
两者不同的典型情况:
- 整张图片预先缩小: 将完整的
128×128图片缩小为64×64存储,填写Width: 64、Height: 64、OriginSize: "128 128"、Scaled: true。内容布局保持完整,只降低存储分辨率。九宫格的Border仍按逻辑尺寸填写,源图采样边宽会按存储尺寸与逻辑尺寸之比换算;例如逻辑边宽 16 像素在这张缩小图片中对应 8 像素。 - 九宫格重复区域压缩: 打包时可缩短图片中可重复的拉伸区域,保留边缘像素,并用
OriginSize记录处理前尺寸。这种处理不属于整图等比缩小,不能套用Scaled: true的整图比例换算。
因此,OriginSize 与存储矩形不同,本身不能说明图片做过哪种处理,更不能表示支持透明裁边还原。它没有记录内容原先位于画布中的位置。
导入 FGUI 图集:透明裁边与九宫格
FGUI 发布时应关闭裁掉透明边的 trimImage。当前区域格式没有透明裁边的 OffsetX / OffsetY 字段,OriginSize 只保存逻辑尺寸,不会自动把裁后的像素居中或恢复到原图中的位置。把裁后的矩形配上原始尺寸,仍会把该矩形绘制到整个控件区域。
已有裁边图集需要根据 FGUI 导出的原始尺寸和裁边偏移,将像素补回透明画布,再重新打包并生成星火区域 JSON。缺少偏移数据时不能仅凭 OriginSize 推断原位置。FGUI 若启用了旋转打包,也应关闭旋转或在转换时还原方向;当前普通区域格式没有旋转字段。
连续矩形图片的九宫格拉伸直接设置 Border: "左 上 右 下",四个数字是各侧保留的像素宽度,不是右下角坐标。例如,四边各保留 8 像素:
json
{
"RelativePath": "ui/image/custom/window.png",
"X": 16,
"Y": 16,
"Width": 64,
"Height": 64,
"OriginSize": "64 64",
"Border": "8 8 8 8",
"Scaled": false,
"HasAlpha": true,
"CompressType": 0
}将此对象放入区域配置数组,再让普通控件通过 Image 引用。各侧保留宽度应来自原始未裁边图片;FGUI 若提供的是中间拉伸矩形,应换算成左、上、右、下四侧宽度。
Grid-0 到 Grid-8 属于 CompressType: 4 的分块九宫格压缩格式,仅在九块分别存放时使用。完整矩形图片设置 Border 即可,不必拆成九块。上述九宫格和裁边结论按当前加载及绘制实现核对;本页末尾的真实 PIE 验证范围仍为普通图标。
配置普通控件
在数据编辑器或 UI 编辑器中,把目标控件的图片 Image 填为 image/icons/sword.png,宽高按希望显示的大小设置。例如设为 96×96,就会把 32×32 的区域放大显示。
代码创建同样适用。在客户端 UI 创建阶段执行:
csharp
#if CLIENT
using GameUI.Control.Extensions;
using GameUI.Control.Primitive;
var icon = new Panel
{
Image = "image/icons/sword.png"
};
icon.Size(96, 96).AbsolutePosition(30, 30);
icon.AddToRoot();
#endifSlicedEdges 控制九宫格拉伸,ClipContent 控制子控件越界裁剪;选择图集区域由区域 JSON 完成。
黑白边与图块串色
图集子图片在放大、缩小或移动后出现细线,需要先区分相邻图块串色、透明轮廓污染、Alpha 混合不匹配和发布压缩。业界常用留边(Padding)、边缘扩展(Extrude)与透明像素颜色扩散(Alpha Bleed / Color Dilation)配合处理;三者作用不同。
| 现象 | 常见原因 | 处理方向 |
|---|---|---|
| 矩形边缘出现邻近图标的颜色或细线 | 双线性过滤采到了取图矩形外的像素 | 增加图块间距,在矩形外复制边缘像素 |
| 沿透明轮廓出现黑边或白边 | 非预乘图片的透明像素 RGB 被黑色或白色污染,过滤时混入可见轮廓;也可能有烘焙进半透明边缘的底色 | 从无底色原稿导出,在透明区域扩散邻近可见像素的 RGB 并保留原 Alpha;检查半透明边缘是否也带底色 |
| 整圈持续发黑或发亮 | 图片、Shader 和混合模式对预乘 Alpha 的约定不一致 | 核对导出、加载、Shader 与混合约定,避免重复预乘或漏做预乘 |
| 缩小时明显,原尺寸正常 | Mipmap 混合了邻近区域,或保护区不足 | 按实际缩小层级增大保护区;不需要 Mipmap 的屏幕 UI 可在支持的纹理配置中关闭后对比 |
| 编辑器正常,发布后才出现边缘噪点 | 目标纹理压缩或发布转换改变了边缘颜色、Alpha | 对比源图和实际发布纹理,检查目标压缩格式、压缩块边界与保护区 |
打包时的保护区
- Padding 是图块之间的空间,仅留一圈透明黑色不能解决所有黑边。Extrude 是在原始矩形外复制最外侧像素的 RGBA,为过滤提供相同的边界颜色。对透明轮廓,仍需检查原图透明像素的颜色。
- 对无 Mipmap、普通双线性过滤的 UI,可从每侧扩展 2 像素、相邻原始矩形之间至少间隔 4 像素开始;这 4 像素供两侧各扩展 2 像素,不是额外再留 4 像素。图集外边缘也应保留保护区。这是起始建议,需要按实际缩放、过滤和压缩验收。
- JSON 的
X/Y/Width/Height指向原始完整图片矩形,不包含外部扩展像素。例如原始32×32图片放在(10,20),区域仍填X: 10、Y: 20、Width: 32、Height: 32、OriginSize: "32 32";每侧 2 像素的外部保护区占用[8,44) × [18,54)。若打包重新移动了图片,应同步更新X/Y。 - 原始图片本身包含的透明画布仍属于原始矩形。外部保护区不改变逻辑尺寸,也不是九宫格的
Border;不要把它计入OriginSize或用Border修黑边。 - 开启 Mipmap、较强缩小或各向异性过滤时,需要更大的保护区,并检查实际使用的各级 Mipmap。纹理压缩还要考虑目标格式的块大小和对齐;固定 2 像素不能保证所有平台与缩放层级都无串色。
Alpha Bleed 的约定: 对非预乘(Straight Alpha)源图,把邻近可见颜色扩散到全透明像素的 RGB 中,但保持其 Alpha 为 0;不要把透明区涂成不透明,也不要改变轮廓的 Alpha。预乘(Premultiplied Alpha)数据需要满足 RGB 已乘 Alpha 的约定,全透明像素通常为零 RGB;应在适当的导出/预处理阶段处理颜色,再按渲染链路所需格式输出,不能直接把非预乘的扩散结果当作预乘数据。不要仅因出现黑边就对所有图片执行预乘。
整张纹理的 Clamp 只保护图集外边界,不会为每个子图片提供独立 Clamp。Point/Nearest 可用于定位过滤问题,或用于明确需要最近邻放大的像素画;普通平滑 UI 优先修复资源边缘。缩 UV 或随意内缩半像素可能切掉有效像素,不应作为所有图标的默认修复。
验证修复
在浅色和深色背景上分别检查原尺寸、放大和实际最小显示尺寸;需要时加入非整数缩放或位置,观察整条轮廓。先比较同一图标的散图与图集,再对比源图和实际发布纹理,定位是素材、打包还是发布转换引入问题。修改后重载图集配置,并保存截图;最终仍需在目标客户端验收。
过滤、Mipmap 和压缩方式由纹理配置及发布流程决定,当前区域 JSON 示例没有这些设置字段。不要添加自创字段或假设存在控件级开关。按项目采用的过滤方式、压缩格式和目标平台检查边缘显示。
自动打包与发布
把自定义大图和同名区域 JSON 直接放在 ui/atlas/ 下。现有自动打包流程会扫描该目录中同名配对的 .png/.jpg 与 .json 文件,重建 atlas.json 清单,因此应同时保留 custom_icons.png 和 custom_icons.json。
- 使用
custom_icons等项目自定名称,避开自动生成的atlas_1、atlas_2等名称,防止重名覆盖。 - 仅在清单中登记一个放在其他目录、没有同名 JSON 的图片,不属于上述自动扫描保留方式。
- 项目内图集随项目资源打包,无需上传为云资源。移动端发布时图片仍可能转换为 GPU 压缩纹理;本流程通过渲染取图,不需要运行时 CPU 裁图或
Resize。 - 发布后仍需在目标客户端检查图片、清单和区域配置是否齐全,并验收显示效果。
配套编辑器及发布服务提供发布包图集校验 MCP时,可从成功发布的 publish_identity.platform_variants 选择实际变体,调用 project_publish_verify_resources,再按返回的 verification_id 查询 status/wait。工具比较清单和区域 JSON、包哈希、KTX 容器及区域边界;异步等待超时后仍可续查。没有身份的旧发布不回填,也不接受任意 URL 或手填版本。报告中的 pixel_check: "not_checked_decoder_unavailable" 表示没有解码检查像素,仍需实际显示验收。
预览与排查
先写完大图、区域 JSON 和清单,再打开项目启动预览。图集配置会缓存,修改后应重新加载项目,必要时重启编辑器。ImageCache.Invalidate 的普通散图刷新保证不包含图集配置。
验收时同时查看截图和 ui.snapshot:确认控件存在且可见、显示的是指定图标而非整张大图、放大后的形状和区域正确。只有控件矩形或编译成功不能证明取图成功。可把同一大图的多个不同区域分别放入普通控件,并同时显示原始大图作为对照。
图片找不到时,依次核对清单的两个路径、区域的 RelativePath、控件 Image 的前缀,以及清单是否在图集加载前就已存在。区域错误时检查坐标、大小、逻辑名称重复与旧配置缓存。
示例中的药瓶、盾牌、剑各占大图中的一个 32×32 区域,可通过普通 Panel.Image 放大到 96×96 显示,无需另外提供散图。
发布纹理可能保留 .png 文件名但使用 BC7/KTX 等 GPU 纹理容器,因此不能仅凭扩展名或图片文件哈希变化判断损坏。核对发布包中的清单、区域配置和实际纹理格式,并在目标平台检查显示效果。