原文:https://learn.unity.com/tutorial/optimizing-unity-ui#5c7f8528edbc2a002053b5a3
UIText
Unity的内置文本组件是在UI中显示光栅化文本符号的一种方便的方式。 然而,有许多行为并不为人所知,但经常作为性能热点出现。 在向UI添加文本时,始终要记住文本符号实际上是作为单独的四边形呈现的,每个字符一个。 这些四边形往往在字形周围有大量的空白,这取决于它的形状,并且很容易以这样的方式定位文本,从而无意中破坏了其他UI元素的批处理。
文本网格重建
一个主要问题是UI文本网格的重建。 每当UI文本组件被更改时,文本组件必须重新计算用于显示实际文本的多边形。 如果文本组件或它的任何父游戏对象被禁用或重新启用而不改变文本,这种重新计算也会发生。
这种行为对于任何显示大量文本标签的UI来说都是有问题的,最常见的是排行榜或统计数据屏幕。 隐藏和显示Unity UI的最常见方法是启用/禁用包含UI的游戏对象,带有大量文本组件的UI在显示时通常会导致不希望出现的帧率问题。
动态字体和字体地图集
当完整的可显示字符集非常大,或者在运行之前不知道时,动态字体是一种显示文本的方便方法。 在Unity的实现中,这些字体会根据UI Text组件中遇到的字符在运行时构建一个字形图集。
加载的每个不同的Font对象将维护其自己的纹理图集,即使它与另一种字体属于相同的字体家族。 例如,在一个控件上使用Arial加粗文本,而在另一个控件上使用Arial Bold将产生相同的输出,但Unity将保持两个不同的纹理地图集-一个用于Arial,一个用于Arial Bold。
从性能的角度来看,最重要的是要理解Unity UI的动态字体在字体的纹理图集中为每个不同的大小、风格和字符组合保留一个字形。 也就是说,如果UI包含两个文本组件,都显示字母’ a ‘,那么:
如果两个Text组件共享相同的大小,那么字体图集中将包含一个字形。
如果两个Text组件不共享相同的大小(例如,一个是16点,另一个是24点),那么字体图集将包含两个不同大小的字母“A”的副本。
如果一个Text组件是粗体的,而另一个不是,那么字体图集将包含一个粗体的’ a ‘和一个常规的’ a '。
当带有动态字体的UI Text对象遇到尚未栅格化到字体纹理图集中的字形时,必须重建字体的纹理图集。 如果新符号适合当前图集,则会添加它,并将图集重新上传到图形设备。 但是,如果当前地图集太小,那么系统将尝试重建地图集。 这个过程分两个阶段。
首先,以相同的大小重建图集,只使用活动UI Text组件当前显示的符号。 这包括uitextents的父Canvas是启用的,但是禁用了Canvas Renderers。 如果系统成功地将所有当前正在使用的符号装入一个新的地图集,它将对该地图集进行栅格化,而不继续第二步。
第二,如果当前使用的符号集不能放入与当前地图集相同大小的地图集中,则通过将地图集较短的维度加倍来创建更大的地图集。 例如,512x512地图集扩展为512x1024地图集。
由于上面的算法,一个动态字体的图集只会在创建后增加大小。 考虑到重建纹理地图集的成本,必须在重建期间最小化。 这可以通过两种方式实现。
只要可能,使用非动态字体并为所需的字形集预先配置支持。 对于使用约束良好的字符集(比如只使用拉丁/ASCII字符)的ui来说,这通常很好,而且大小范围很小。
如果必须支持非常大范围的字符,比如整个Unicode集,那么字体必须设置为Dynamic。 为了避免可预测的性能问题,在启动时通过font . requestcharactersintexture使用一组适当的字符来启动字体的字形图集。
请注意,字体图集重建是单独为每个UI文本组件被更改。 当填充大量的Text组件时,收集Text组件内容中的所有惟一字符并填充字体图集可能是有利的。 这将确保字形图集只需要重建一次,而不是每次遇到新的字形时都要重建一次。
还注意到,当一个字体atlas重建被触发,目前任何字符,不包含在一个活跃的用户界面文本组件将不会出现在新地图,即使他们最初添加到阿特拉斯由于调用Font.RequestCharactersInTexture。 要解决这个限制,请订阅字体。 textureRebuilt委托和查询字体。 确保所有需要的字符保持为素数。
字体。 textureRebuilt委托目前没有文档记录。 这是一个单一的统一事件。 参数是其纹理被重建的字体。 此事件的订阅者应遵循以下签名:
public void TextureRebuiltCallback(Font rebuiltFont) { /* ... */ }
专门的字形渲染器
对于那些符号很常见的情况,每个符号之间都有相对固定的位置,所以编写一个自定义组件来显示显示这些符号的精灵会更有优势。 这方面的一个例子可能是分数显示。
对于分数,可显示的字符是从一个众所周知的字形集(数字0-9)中提取的,不跨地点更改,并且彼此之间以固定的距离出现。 将整数分解成数字并显示适当的数字精灵是相当简单的。 与canvas驱动的UI Text组件相比,这种特殊的数字显示系统可以通过更少的分配和更快的计算、动画和显示来构建。
回退字体和内存使用情况
对于必须支持大型字符集的应用程序,很容易在字体导入器的“字体名称”字段中列出大量字体。 如果在主字体中无法找到字形,则“字体名称”字段中列出的任何字体都将用作备用字体。 回退顺序由字体在“字体名称”字段中列出的顺序决定。
然而,为了支持这一行为,Unity将保持“字体名称”字段中列出的所有字体载入内存。 如果字体的字符集非常大,那么回退字体所消耗的内存就会过多。 这在包括象形字体时最常见,如日本汉字或汉字。
最佳适合度和性能
一般来说,UI Text组件的Best Fit设置永远不应该被使用。
“Best Fit”动态调整字体的大小到最大的整数点大小,可以在Text组件的包围框中显示而不溢出,并被固定在可配置的最小/最大点大小。 但是,因为Unity会为每个不同大小的字符在字体图集中呈现一个不同的字形,所以使用Best Fit会很快用许多不同的字形大小淹没字体图集。
在Unity 2017.3中,Best Fit使用的尺寸检测不是最优的。 它在字体图集中为测试的每个大小增量生成字形,这进一步增加了生成字体图集所需的时间。 它还会导致图集溢出,从而导致旧的符号被踢出图集。 由于Best Fit计算需要进行大量的测试,这通常会排除其他Text组件使用的字形,并强制在计算出适当的字体大小后至少重新构建字体图集一次。 这个问题已经在Unity 5.4中得到了修正,Best Fit将不会不必要地扩展字体的纹理图集,但仍然比静态大小的文本慢很多。
频繁地重新构建字体图集将迅速降低运行时性能,并导致内存碎片。 设置为Best Fit的文本组件的数量越大,这个问题就越严重。