Skip to content

自定义 UI 图集 ​

把多个图标放在同一张大图中,再用 JSON 为每个矩形区域分配逻辑图片名,普通控件的 Image 属性就能显示其中一块。AI 助手可以直接生成这些资源配置;不需要先把每块裁成独立图片,也不需要为普通图标改用 Canvas。

这条流程使用现有图集加载能力,不增加运行时 API 或客户端能力版本。它适合静态图标、按钮图片和面板背景。动画选型见序列帧方案,需要每次绘制动态选择源矩形时可用 Canvas.DrawImage 的源区域重载。

文件与路径 ​

text
MyProject/
  ui/
    atlas/
      custom_icons.png
      custom_icons.json
      atlas.json
位置示例含义
清单的 AtlasPathatlas/custom_icons.png大图文件,相对于项目 ui/
清单的 ConfigPathatlas/custom_icons.json区域配置文件,相对于项目 ui/
区域的 RelativePathui/image/icons/sword.png子图片的逻辑名称,包含 ui/
控件的 Imageimage/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();
#endif

SlicedEdges 控制九宫格拉伸,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 纹理容器,因此不能仅凭扩展名或图片文件哈希变化判断损坏。核对发布包中的清单、区域配置和实际纹理格式,并在目标平台检查显示效果。