跳转至

动态图集方案说明

为了降低 UGUI 的 Draw Call,常用的素材预先整合在一张大图集上。但随着资源的增加,有些资源很难再统计那些资源一起使用的频率,例如道具图标,这就导致图集有效使用率很低,这也增加了内存的压力。

我们引入动态图集处理这类问题:取消预先整合图集的做法,将使用到的素材动态的组成图集。为了降低运算时的布局运算压力,图集的布局是固定的,同尺寸或者相近尺寸的资源才能进入动态图集。

Warning

一般来说只允许同尺寸使用,相近尺寸可考虑让美术调整设计尺寸。

压缩格式

以下为 Android 实机的压缩格式数据

Type B KB MB
RGBA32 2048x2048 16,882,073.6 16,486.4 16.1
ASTC Block 4*4 2048x2048 4194304.0 4096.0 4.0
ASTC Block 5*5 2048x2048 2726297.6 2662.4 2.6
ASTC Block 6*6 2048x2048 1887436.8 1843.2 1.8
ASTC Block 8*8 2048x2048 1048576.0 1024.0 1.0
ASTC Block 10*10 2048x2048 734003.2 716.8 0.7
ASTC Block 12*12 2048x2048 471142.4 460.1 0.4
ASTC Block 12*12 2048x2048 471142.4 460.1 0.4

综合选用 ASTC 6x6 block,动态图集要求散图和动态图集压缩格式一致。

Warning

  • ASTC NxN 系列格式要求素材的尺寸能被 N 整除,以 ASTC 6x6 为例,长宽 104 的图标无法被 6 整除,需要至少处理成 108。
  • Unity Editor 下,即使切换成 Android 平台,数据也和手机上的有出入。

布局方案

为了避免图集中相连素材的像素污染问题,需要预留图片空间至少 1px 即可。一般来说这种处理,可以直接在图集中控制小图的布局位置即可。但是实际上图集 Texture2D 在创建的时候自带颜色,可能是黑色、灰色或者黄色(总之不透明),就使得我们必须事先设置一遍整个Texture2D 的像素成透明。

图集布局

但对一个 2048 长宽的 Texture2D 逐像素处理贴图是非常耗时的,为了避免第一次创建图集的耗时,我们选中方案B。提前将 1px 的像素间隙加在 UI 的源素材中,加入 Atlas 时就不再添加间隙了,使用时的使用区域内缩 1px 即可。

这里引出另一项需求,需要有工具支持增加图片的透明边界。

素材导入流程

G01动态图集流程

素材导入到 Unity 经过两层处理

  1. 导图工具拓展尺寸
  2. 调整载入格式
\[ FinalySize = OriginSize + 2 * (ExpandSize + ExtrudeSize) \]

以 ASTC 6x6 为例,把一张 104 的资源纳入动态图集,最近能被 6 整除的尺寸是 108。那么就要在四周各加上 2px 的空白像素。

调整项

texture_packer.py
# 不打图集模块,需要和 DynamicAtlasManager.lua 里的值保持一致
# 模块小写:(原始宽, 原始高)
disable_atlas_module = {
    "item": (130, 130),
    "runeicon": (130, 130),
    "peticon": (106, 106),
    "fateicon": (106, 106),
}
DynamicAtlasManager.lua
-- 图集映射动态图集的枚举
local moduleTodynamicKeyMap = {
    item            = NM.DynamicAtlasEnum.ItemIcon,
    runeicon        = NM.DynamicAtlasEnum.RuneIcon,
    peticon         = NM.DynamicAtlasEnum.SkillIcon,
    fateicon        = NM.DynamicAtlasEnum.SkillIcon,
}
TextureImportProcessor.cs
-- 不打图集目录
private HashSet<string> dynamicAtlasSet = new HashSet<string>()
{
    "/item/",
    "/runeicon/",
    "/peticon/",
    "/fateicon/",
};

使用

Unity 新增 DynamicImage.cs 专门用于动态图集,并管理其中的引用。需要显示 Image 的对象,用 DynamicImage 代替 Image。代码上

-- 依旧需要获取 iconModule,iconPath,这是基本的素材信息。
local iconModule, iconPath = ResPathManager.GetPetIcon(self.petCfg.modelId)
-- 专用的加载动态图集接口
local sprite = self:LoadSpriteTPDynamic(iconModule, iconPath)
-- C# DynamicImage 端需要 key 用于管理引用
local key = DynamicAtlasManager.GetDynamicKey(iconModule)
-- DynamicImage 提供的接口设置 sprite。
self.view.imgPetIcon:SetNewSprite(sprite, key)

常见问题

使用了动态图集的素材超出布局边界。

Unity 中看到的就是 100 的素材是 98 的基础上加入 1px 组成的。如果 NativeSize 布局,其实内容的尺寸是不变的。然而在运行模式下,动态图集的素材会先扣掉 1px 边界,就导致了用 100 尺寸显示 98 尺寸的素材,造成放大的效果。

对于这种情况暂不处理,如果影响实在恶劣,就只能抛弃图片自带边界的做法。

图片未正常显示

请确认散图和动态图集格式是否一致