目标:在不影响显示效果的前提下,显著降低字体 AssetBundle 体积,并明确动态字体在运行时的性能边界与最佳配置。
TODO
- 把静态字体改为动态字体
- 修改后效果保持不变
- 动态字体性能测试
动态添加新字符性能消耗
触发扩容性能消耗
- 不同大小纹理扩容的消耗
测试预加载性能
废弃方案
缩小静态字体纹理(2048 → 512)
- 初期包体缩小有限
- 随字符数量增加,Atlas 数量和包体仍线性增长
- 无法从根本解决字体包体问题
原始 Fonts.bat:4779KB
| Name | Size |
|---|---|
| Mikado-Bold SDF Atlas | 8388748 |
| NEXA-BOLD SDF Atlas | 8388744 |
| LiberationSans SDF Atlas | 1048716 |
静态字体改动态字体
静态字体直接改为动态字体不需要修改效果,效果不变但是因为动态字体生成需要引用原TTF文件(现在两个字体引用的是同一个文件)
Fonts.bat 大小 1633kb
| Name | Size |
|---|---|
| Mikado-Bold SDF Atlas | 140 |
| NEXA-BOLD SDF Atlas | 136 |
| Mikado-Bold | 95282 |
进一步清理 AB 包中无用字体、移出多语言资源后:Fonts.bat 最终可降至 115KB
不同分辨率初始图包体大小
| 初始空白纹理大小 | Atlas大小 | 包体大小 |
|---|---|---|
| 0 | 140+136 | 115kb |
| 128*128 | 228+224 | 126kb |
| 256*256 | 228+224 | 127kb |
| 512*512 | 228+224 | 128kb |
| 1024*1024 | 228+224 | 134kb |
| 2048*2048 | 228+224 | 159kb |
| 4096*4096 | 228+224 | 257kb |
说明: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×8192 | 10000+字符 | 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/RASTER | 3-5 | 基础防粘连 |
| SDF(无特效) | 5-7 | SDF需要更多边缘数据 |
| SDF(有描边) | 10-15 | 为描边效果预留空间 |
| SDFAA(高品质) | 8-12 | 抗锯齿需要更多过渡 |
动态字体性能测试
单字符动态添加(TryAddCharacters)
- 0.5–1.5ms / 字符
- 与 Atlas 尺寸无强相关
- 与字符复杂度(笔画、曲线)相关
Atlas 扩容性能(SetupNewAtlasTexture)
| 初始空白纹理大小 | 动态添加单字符 | Atlas扩容 | 当前配置Atlas数量 |
|---|---|---|---|
| 256*256 | 0.5ms -1.5ms 字符内容不同消耗不同 | 第一次1.38ms 后续0.34ms | 111 |
| 512*512 | 0.5ms -1.5ms 字符内容不同消耗不同 | 第一次2.15ms 后续0.40ms | 19 |
| 1024*1024 | 0.5ms -1.5ms 字符内容不同消耗不同 | 第一次5.15ms 后续0.47ms | 5 |
| 2048*2048 | 0.5ms -1.5ms 字符内容不同消耗不同 | 第一次10.64ms 后续0.5ms | 2 |
第一次扩容卡顿是因为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 在扩容次数、运行时性能和资源占用之间取得最佳平衡;