支持

常见问题解答

关于构建体积、字体、字形图集与运行时性能的直接解答——提炼自真实开发者的提问。

构建体积与分发

UniText 会让我的构建体积膨胀吗?最终究竟有什么被打进构建?

不会。UniText 不会把逐字体的纹理图集烘焙进您的构建。字形是在运行时被光栅化到一个共享图集(基于 Texture2DArray),并带有逐字形的引用计数和 LRU 淘汰。只有字体数据会随构建一起发布——图集从不会。此外,内置的字体压缩(Zstd,级别 22)可将拉丁文/阿拉伯文的字体数据缩小至多约 ~2.7×(CJK 幅度较小,约 ~1.3×)。

我需要保留并发布 .ttf 文件吗?

不需要。UniTextFont 资源会把字体字节内嵌在自身之内。对源字体的引用仅限编辑器(包裹在 #if UNITY_EDITOR 中)——它在播放器构建中根本不存在,因此不是运行时依赖,也不会通过依赖查找被拉进 AssetBundle。资源一旦创建完成,您就可以删除 .ttf。

我甚至需要把 .ttf 导入到我的项目里吗?

不需要。您可以通过 UniText 工具窗口,从磁盘上任意位置的 .ttf 构建一个 UniTextFont(文件对话框会直接读取字节)。把字体导入项目是可选的。

UniText 对一个空构建究竟增加了多少体积?

• WebGL:空构建 ≈ 6–10 MB(具体取决于 Unity 版本和已安装的包);UniText 会加入它的原生库(HarfBuzz、FreeType)。这些正是 Unity 自家 UI Toolkit Advanced Text Generator 所附带的同一批行业标准库——没有任何冷门东西(UniText 只是自带了它自己的副本)。 • Android:一个包含两种架构的 APK 会同时携带两者——每种架构约 +4 MB。但当您发布 AAB 时,Google Play 会交付针对具体设备的包,因此用户只下载自己的那一种架构(~4 MB),而非两者。

在 CJK 或大字体方面,UniText 与 TextMesh Pro 相比如何?

在 TMP 的静态图集填充(Static)模式下,烘焙一款典型 CJK 字体的完整字形集会让构建膨胀数十到数百 MB(需要若干 4096²/8192² 图集;视字形数量和分辨率而定,预计 ~80–250 MB)。关键在于:UniText 的开销不随字形数量增长——无论您发布拉丁文还是完整 CJK,都是同样的那几 MB。

我可以从服务器或 CDN 动态加载字体吗?

可以——这属于开发者一侧,UniText 不会横加干涉。用您自己的代码(UnityWebRequest)下载字体字节,然后在运行时通过 CreateFontAsset(byte[]) 从中构建一个 UniTextFont。若要把字体裁剪到只保留您需要的字符(子集化),请使用 UniText 的构建期工具。包内没有内置的 CDN 自动加载器——这是刻意为之,属于项目的职责,而非引擎的职责。

字体与系统字体

UniText 能使用操作系统的系统字体吗?它在内存和启动上有多大开销?

可以——SystemFont 在运行时直接从操作系统读取字体文件,因此对构建体积毫无影响(不会烘焙任何东西)。解析是惰性的(首次使用时才进行),因此不会拖慢应用启动。

WebGL 支持系统字体吗?

不支持——而且这是浏览器的限制,并非 UniText 的限制。浏览器沙箱不会暴露操作系统字体。对于 WebGL,请把字体内嵌到一个 UniTextFont 资源中(或者自行加载字节——参见上面的 CDN 问题)。

字形图集 —— SDF Detail 与 Tile Size

“SDF Detail” 与 “Tile Size Offset” 究竟有什么作用?

字形存放在三种尺寸的图块中——64、128、256——尺寸由轮廓复杂度决定(段数多 → 128/256,轮廓简单 → 64)。 • SDF Detail(SDF 细节)是对复杂度估算值的乘数。调高它,一个本就不算简单的字形就更可能被提升到更大的图块。(真正简单的形状仍停留在 64——该乘数只影响阈值以上的字形。) • Tile Size Offset(图块尺寸偏移)是沿尺寸阶梯的硬性跳变(选定 64 + 偏移 1 → 128;范围 −2..+2)。

为了性能,这两者我该优先考虑哪一个?

单独看哪个都不必——两者都只决定最终的图块尺寸,而开销仅由该尺寸本身决定。两组解析到相同图块尺寸的组合,开销完全一样。目标是在仍能给出可接受质量的前提下,取最小的图块尺寸。

运行时性能

UniText 何时光栅化字形?它会每帧拷贝内存吗?

仅当某个字形尚未在图集中时——绝不是每帧。对于 “Hello world”,它会光栅化各不相同的字形 H e l o w r d。第二个 H 什么都不做;一个 a 就只光栅化那个 a。

当大量新文本同时出现时,为什么会有短暂的卡顿?

开销与那一帧中被光栅化的、各不相同且首次出现的字形数量成正比——而与文本的长度无关。在我们的基准测试中,第一帧 ~2000 个各不相同的字形,在一台典型的 Android 设备上约 ≈ 1 秒;同样的场景下,TMP 和 UI Toolkit 要花 10+ 秒。复用已缓存的字形则是免费的。

我在 Profiler 里看不到 UniText 的 job——多线程真的在工作吗?

在热路径上,UniText 几乎不依赖 Unity Job System——这是刻意的设计。字形光栅化(我们自研的 lpSDF / lpMSDF)默认通过计算着色器(compute shader)在 GPU 上运行;Burst job 只是在无法使用计算着色器时的 CPU 回退方案。主要的多线程——塑形、布局、网格生成——运行在 UniText 自有的操作系统线程池上(并非 Unity Jobs,这正是它们不会出现在 Jobs profiler 中的原因)。所以“看不到 UniText 的 job”并不意味着“线程关掉了”:要么工作交给了 GPU,要么图集已经预热,要么文本低于并行阈值(短标签或单个标签会被顺序处理——那样更省)。

MonoBehaviour.Update() 会损害性能吗?需要一个统一的 UniTextManager 吗?

“Update() 很慢”对于发布构建而言是个误区。从原生代码调用魔法方法的开销确实存在,但可以忽略不计(~1000 次调用 ≈ 0.16 ms)——它不是瓶颈。朴素代码中真正的开销是 AoS 内存布局,而非回调本身;UniText 在关键处采用 SoA(Unicode 属性、度量、逐像素缓冲区)。(这也是 ECS 快的原因——归功于 SoA,而非“ECS 魔法”。)况且 UniText 本来就是集中式的——只不过不是以 MonoBehaviour 管理器的形式:Update() 本身几乎是空的,而繁重的工作被汇集到 Canvas 渲染回调上的单次批处理过程中(这也是它实现跨组件并行的原因)。

没有找到您的答案?

来我们的 Discord 提问吧——正是这类问题塑造了本页面,而且我们通常回复很快。

有疑问?: [email protected]