目标:在不影响显示效果的前提下,显著降低字体 AssetBundle 体积,并明确动态字体在运行时的性能边界与最佳配置。

TODO

  • 把静态字体改为动态字体
  • 修改后效果保持不变
  • 动态字体性能测试
    • 动态添加新字符性能消耗

    • 触发扩容性能消耗

      • 不同大小纹理扩容的消耗
    • 测试预加载性能

废弃方案

缩小静态字体纹理(2048 → 512)

  • 初期包体缩小有限
  • 随字符数量增加,Atlas 数量和包体仍线性增长
  • 无法从根本解决字体包体问题

原始 Fonts.bat:4779KB

NameSize
Mikado-Bold SDF Atlas8388748
NEXA-BOLD SDF Atlas8388744
LiberationSans SDF Atlas1048716

静态字体改动态字体

静态字体直接改为动态字体不需要修改效果,效果不变但是因为动态字体生成需要引用原TTF文件(现在两个字体引用的是同一个文件)

Fonts.bat 大小 1633kb

NameSize
Mikado-Bold SDF Atlas140
NEXA-BOLD SDF Atlas136
Mikado-Bold95282

进一步清理 AB 包中无用字体、移出多语言资源后:Fonts.bat 最终可降至 115KB

不同分辨率初始图包体大小

初始空白纹理大小Atlas大小包体大小
0140+136115kb
128*128228+224126kb
256*256228+224127kb
512*512228+224128kb
1024*1024228+224134kb
2048*2048228+224159kb
4096*4096228+224257kb

说明:Atlas 越大,初始包体增加,但不随字符增长线性膨胀

后续可以调整调整Point Size、Padding减少字体纹理

Generation Settings

主要参数:
  - Atlas Resolution: 字体纹理图集尺寸
  - Font Size: 采样字号
  - Padding: 字符间距填充
  - Render Mode: 渲染模式
  - Character Set: 字符集选择

Atlas Resolution

原则:越大能容纳的字符越多,但内存消耗也越大

推荐值:

使用场景推荐分辨率预计可容纳字符内存占用 (RGBA)
基础拉丁字符512×512~200个字符1MB
英文字体+符号1024×1024~500个字符4MB
中文字体(常用)2048×2048~3000汉字16MB
中文字体(完整)4096×4096~6000汉字64MB
多语言大字体8192×819210000+字符256MB

Font Size

关键原则:

  • 不是UI显示的大小,而是生成字体纹理时的采样质量
  • 越大质量越高,但纹理占用更大

推荐值:

最终使用场景推荐采样字号说明
小字号UI (8-16px)36-48小字需要高采样保持清晰
中等字号 (16-32px)54-72平衡质量和性能
大字/标题 (32-64px)90-128需要高采样展示细节
超大字号 (64px+)144-180展示字体设计细节

计算公式:

// 经验公式:采样字号 = 最大使用字号 × 1.5
float samplingSize = maxUIFontSize * 1.5f;

// 例如:UI最大用48px字号
// 采样字号 = 48 × 1.5 = 72

SDF/SDFAA模式:
  - 需要更高的采样字号
  - 推荐: 72-144
  - 原因: SDF需要更多数据计算距离场

Bitmap光栅模式:
  - 可以较低采样
  - 推荐: 36-72
  - 原因: 直接光栅化,不需要额外数据

Padding

作用

  • 防止字符在纹理图集上粘连
  • 给特效(描边、发光)留出空间

推荐值

渲染模式推荐Padding说明
Smooth/RASTER3-5基础防粘连
SDF(无特效)5-7SDF需要更多边缘数据
SDF(有描边)10-15为描边效果预留空间
SDFAA(高品质)8-12抗锯齿需要更多过渡

动态字体性能测试

单字符动态添加(TryAddCharacters)

  • 0.5–1.5ms / 字符
  • 与 Atlas 尺寸无强相关
  • 与字符复杂度(笔画、曲线)相关

Atlas 扩容性能(SetupNewAtlasTexture)

初始空白纹理大小动态添加单字符Atlas扩容当前配置Atlas数量
256*2560.5ms -1.5ms 字符内容不同消耗不同第一次1.38ms 后续0.34ms111
512*5120.5ms -1.5ms 字符内容不同消耗不同第一次2.15ms 后续0.40ms19
1024*10240.5ms -1.5ms 字符内容不同消耗不同第一次5.15ms 后续0.47ms5
2048*20480.5ms -1.5ms 字符内容不同消耗不同第一次10.64ms 后续0.5ms2

第一次扩容卡顿是因为AssetDatabase.AddObjectToAsset_Obj()

这是一个纯 Editor 行为,作用是:

  • 把“新生成的 Atlas Texture”
  • 作为子资源
  • 挂到 .fontasset 下面
  • 并写入磁盘

所以它会触发:

  • ScriptableObject 结构变更
  • Asset 序列化
  • AssetDatabase 刷新
  • Editor 内部索引更新

预加载

预加载接口:fontAsset.TryAddCharacters

2048*2048大小下68.75ms就是一次性完成动态添加所有字符和Atlas创建,如果要使用预加载就得做分帧加载

最终方案

本次字体精简通过将静态字体改为动态字体;

动态字体在 效果完全一致 的前提下,可显著降低字体 AssetBundle 体积,包体大小从Fonts.bat 从 4779KB → 159kb,(减少97%);

经性能验证单字符动态添加性能在所有 Atlas 尺寸下 基本稳定(0.5ms–1.5ms),2048×2048 Atlas 在扩容次数、运行时性能和资源占用之间取得最佳平衡;