Unity UI优化

图片


本文讨论了在Unity中创建的项目的UI元素的优化。 基于官方文档中的信息和个人经验,我试图清楚地解释UI元素的操作原理。 另外,您还将在这里找到实用的技巧,这些技巧将有助于改善项目在用户界面方面的性能。



术语学


UI元素是Unity中与构建用户界面有关的所有元素。 例如,这包括:按钮,文本,图片,下拉菜单等。
画布(canvas) -用户界面的基本元素,是其余元素的容器。
网格 -描述3D模型的一组参数。
四边形是作为四边形的网格。
批处理 -将对象网格合并为一个大网格以更快地渲染。
绘图调用 -从引擎绘图到图形API(例如OpenGL或Direct3D)的命令。
透明队列 -用于渲染透明对象的队列。
Alpha混合 (Alpha blending)-一种用于混合alpha通道上的像素以获得具有透明度的图像的算法。
Atlas是一种资源,它将多种纹理组合在一起。


介绍性


在UI优化中,没有适用于任何情况的通用规则。 一切都归结为在批处理成本和抽签数量之间取得平衡。 可以区分四个主要问题:


  • GPU负载过高(渲染上的负载过大);
  • 重建画布时CPU负载过多;
  • 太多变化的元素导致画布的重建;
  • CPU负载太大,无法生成网格(通常与文本关联)。

Unity UI渲染


Unity用户界面的基本元素是画布。 他负责生成,排序和渲染子接口元素的网格。 所有UI元素必须是任何画布的子元素,否则它们将不会在游戏中显示。
图片
渲染发生在具有Alpha混合的“ 透明”队列中 ,距离摄像机最远从最远到最远)的对象(从后到前)


另外,应该注意的是,UI元素的透明度不会影响性能。 即使元素完全由“不透明”像素组成,仍将使用alpha混合对其进行渲染。


同样重要的是要理解,在渲染时,将处理所有活动元素的所有像素。 这并不取决于它们是否可见,是否被其他物体阻挡甚至完全透明。


重新设计用户界面


重新设计用户界面是一个多步骤的过程,包括构建每个UI元素的网格,并尝试修补这些网格以最大程度地减少绘制调用的次数。


重建分为四个阶段:


  1. 分析元素的结构。
  2. 重建所有活动元素的网格,包括透明度为零的元素。
  3. 重建用于网格划分单元的材料。
  4. 所有元素均根据其顺序绘制。

重建的画布将被缓存并重复使用,直到画布中的元素之一被标记为已修改。
标记了已激活或停用的脏物 ; 谁改变了材料,位置,比例,旋转; 文本组件的文本值已更改; 父母的调动等


在这种情况下,画布重建再次包含至少一个更改的元素。 是的,这仅适用于元素所在的画布。 也就是说,对子画布中元素的更改不会影响父画布。


画布中的元素越多,分析和排序对象的成本就越高。


网格划分


合并网格或批处理可通过减少绘图调用次数来帮助减轻GPU的负担。 在批处理过程中,按深度对网格进行排序并检查是否重叠。 当从远处的元素传递到附近的元素(或从层次结构中的上部元素传递到下部元素)时,具有相同材质或纹理的对象将合并到同一画布内的一个网格中。 为此,它们之间不应存在带有其他材料的物体。 同样,带有其他材料的物品也不应与烘焙物品及其整个容器重叠。 批处理操作是多线程的,其性能取决于处理器中内核的数量而有很大不同。


如果它们具有相同的字体,则文本可能会与其他文本一起颤动。 字体设置和样式相同还是不同都没关系。 如果字体不同,则文本将不会滚动。


还应该记住,文本可以使对象与其整个容器重叠,并且可以轻松地跳过这种重叠。
图片


考虑一个例子。 通过这种方式在层次结构中排列了三个对象A,B和C:
图片
图片
在左侧的图像中,对象A和C将合并,因为 具有相同的材质并且不与对象B相交。在右图中,对象A和C将不会合并,因为 与对象B有一个交点。


通用的UI优化技巧


在开始优化之前,强烈建议您对UI进行概要分析。 这将有助于识别导致性能下降的瓶颈(如果有)。 对于概要分析,有很多工具,既内置于Unity(Unity Profiler),又包含第三方工具。 但是我们不会在本文中讨论UI分析的问题。


以下是在Unity中优化UI的一些建议:


  1. 关闭不可见的对象。 如果某个元素被不透明元素覆盖,则需要禁用其GameObject或该重叠元素的父级GameObject。 同时,仍将绘制alpha设置为0的界面元素。 对于此类对象,您需要在Canvas Renderer组件中启用“剔除透明网格”或仅禁用不可见对象。
    图片
  2. 禁用不透明界面隐藏的世界对象。 如果界面不能覆盖整个世界,则可以将其保存在“渲染纹理”中,然后关闭世界摄影机。
  3. 最小化要绘制的像素数。 将尽可能多的图像合并为一张。 例如,使按钮成为单个精灵是有意义的,而不是使具有背景,笔触,按钮主体等的单独层成为可能。 这将降低使用此类元素的灵活性,并可能导致资源阻塞,因此必须寻求一种折衷方案。
    图片
  4. 避免使用仅用于组织结构的空元素(请勿将元素用作文档层次结构中“文件夹”的名称)。
  5. 避免穿过无法互相烘烤的物体。 如果可能,最好更改层次结构中的位置,容器的大小或重叠和非结块元素的位置。
  6. 对动态元素使用单独的或嵌套的画布。 因此,您可以最小化排序和重建包含大量元素的画布结构的成本。 嵌套画布更方便,因为 继承父画布的设置。 同时,在更改父画布时,所有嵌套的画布也会被重建。 这种情况很少见,但是会发生(例如,更改屏幕分辨率时)。 请记住,不烘焙来自不同画布或嵌套画布的对象以进行联合渲染。 建议根据更新元素的规律性划分画布。 静态元素需要放置在单独的画布中,然后它们只会被绘制一次。 如果存在不断变化的元素,则最好将它们合并在另一个画布中,因为 它们仍然会导致彼此重建。 更改对象也可以按照刷新率分为几个画布。 例如,将每帧更新的元素放在一个画布中,而较少更新的元素放在另一画布中。
  7. 禁用Pixel Perfect将大大提高生产率。 对于具有大量元素的不断更新的对象(例如,滚动清单)尤其如此。
    图片
    帆布
    图片
    嵌套画布
  8. 如果需要禁用画布,请不要禁用包含画布的对象(通过SetActive函数)。 下次打开该画布时,这些元素被标记为已更改,并且所有元素都将被重建。 最好先关闭画布组件本身,然后整个结构和烘焙的数据都不会改变,并且下次打开画布组件时,它们只会开始绘制。
  9. 对于在“ 渲染模式”参数中选择了“ 屏幕空间-相机世界空间”的画布,请始终安装相机。 如果不安装它,则每个框架中的用户界面系统都将通过Object.FindObjectWithTag搜索它以找到Camera.main ,这将影响性能。
  10. 为了不创建多个相同的纹理,可以创建一个灰色阴影,然后通过选择所需的颜色通过“图像”组件对其进行“绘制”。
  11. 仅在需要输入事件的项目上保留“ 射线广播目标”标志,然后删除其余的项目。 默认情况下,在许多元素(图像,文本等)上启用了Raycast目标 。 这会使Raycaster组件的工作复杂化并减慢其工作速度,该组件在Unity UI中处理输入事件。 单击或轻击时,它将遍历元素的整个层次结构,并搜索设置了“ 射线广播目标”标志的所有图形组件然后检查它们是否存在输入事件,并在成功通过检查后将其添加到命中列表中。 之后,按深度对命中列表进行排序,屏幕外的对象将被丢弃。 结果,它给出了最终的命中列表。
    在Unity UI中,许多组件(图像,文本等)都具有“ 射线广播目标”标志。

图片
图像组件上的Raycast Target


图片
TextMeshPro将其隐藏在“ 其他设置”选项卡中。


图片
对于TextMeshPro,您可以在播放器设置-> TextMest Pro->设置->启用Raycast Target中设置默认的Raycast Target设置。


在以下情况下测试元素:


  • 启用射线广播目标
  • 元素本身已打开并激活;
  • 插入点在与RectTransform的交点处。

如果根对象已经具有“射线广播目标”标记,并且该子对象与其几何图形完全重叠,则从子对象中删除“射线广播目标”标志也很有意义。 例如,标准的Unity UI按钮。



图像完全与文本重叠,形状不清 ,在这种情况下,您可以从文本组件中删除“ 射线投射目标 ”。



如果所有画布元素都不等待输入事件,则可以从画布/嵌套画布中删除Graphic Raycaster组件。


处理文本及其优化


Unity界面中的文本由网格组成,其中每个符号均创建自己的四边形。 每次文本值更改时都会重新构建网格。 如果文本组件或其父组件已关闭然后再次打开,也会发生重建。


默认情况下,Unity中的字体被添加为动态字体。 对于舞台上文本组件中使用的每种动态字体,都会创建一个图集。 本地图集中仅包含使用的字符。 例如,如果文本字段包含文本“ New Text”,则为其创建的图集将包含字符“ N”,“ e”,“ w”,“ T”,“ x”和“ t”。 对于每个大小或样式不同的字符,将在地图集中创建一个表示。



舞台上的文字



字体图集


如果在程序执行期间文本组件的内容发生变化,并且出现了地图集中没有的字符,则将重建整个地图集。 如果地图集的纹理中有可用空间,则只需在其中添加必要的符号。 同时,不会删除当前未使用的字符。 如果地图集中没有足够的空间容纳新字符,则其大小将加倍,并根据活动文本组件中使用的字符再次填充。


如果为项目设置了严格定义的字符数,例如仅设置了拉丁字母,那么值得使用永久存储在内存中的静态字体。 如果您的项目允许大量字符,则最好使用动态字体。
您还可以通过用sprite替换文本组件来提高性能。 例如,可以使用一个地图集中包含仅一组必要字符的地图集中的精灵来制作游戏中出现的数字(分数)。 在这种情况下,重建画布和字体图集将不会产生任何费用。


在字体设置的“ 字体名称”字段中列出的后备字体的使用会增加使用的内存。 这在象形字体中尤其明显。


不建议使用Best Fit ,因为 此选项导致图集快速溢出并导致其结构调整。
最佳适合将忽略字体大小设置,并尝试使文本适合文本组件的轮廓矩形。


现在简要介绍一下TextMeshPro(TMP) ,它是标准Unity文本组件的流行替代品。 每当文本值更改时,TextMeshPro也会重建其网格。 但是,TMP文本不使用动态字体。 为此,预先生成字体图集,其中包括所有必需的字符。 如果场景中的某些文本没有分配给其组件的字体中的字符,则TMP开始搜索备用字体。 如果没有任何内容,则TMP将尝试在所有下载的字体中找到此字符。


TMP中的Best Fit不会像常规文本组件那样产生问题,因此可以使用。


大量具有不同本地化的字体或较大的字体集会占用大量内存。 因此,最好只预加载特定本地化所需的字体。


World Space建议使用TextMeshPro代替TextMeshProUGUI
TextMeshProUGUI用于画布中。


精灵地图集


子画面图集是一种将多种纹理组合为一种资源的资源。 它们使您可以减少抽奖次数( 抽奖次数)并提高生产率。


Atlas创作。
创建地图集: 资产>创建>雪碧地图集
选择地图集,然后将必要的精灵放入打包对象中
图片
单击“ 打包预览”以预览图集。
图片
请记住,即使在场景中使用了地图集中的一个或多个子画面,地图集也将全部加载。 因此,试图将所有图像嵌入一个巨大的地图集是没有意义的,而这在具有小RAM的设备上可以发挥其明显的作用。 最好分成一些较小的地图集,例如游戏菜单的用户界面地图集和游戏模式的界面地图集。


避免在地图集中留出较大的空白空间,以免占用过多的存储空间。 为此,您可以调整地图集的大小或添加其他图像以尽可能地填充空白区域。
图片
图片
对于不在地图集中的图像,必须选择正确的设置。
对于每种压缩格式,都有一些要求可以有效地工作。 最常见的图像要求:


  • 宽度和高度必须是2的幂的倍数;
  • 宽度和高度必须是4的倍数;
  • 前两段在一起。
    否则,纹理将不会被压缩,并且会产生额外的设备内存成本。

选择压缩格式时,Unity会告诉您是否不满足所选格式的任何要求。
图片


同样不要忘记格式与目标设备不同。 有关格式的更多信息和建议,请参阅官方文档


总结优化方法


  • 禁用不可见和透明的对象
  • 减少项目数量
  • 避免穿过无法互相烘烤的物体
  • 根据刷新率通过画布分配项目
  • 对于具有频繁更新的元素的画布,请禁用Pixel Perfect
  • 在不可点击的项目上禁用Raycast Target
  • 删除画布上所有元素都不可单击的Graphic Raycaster组件
  • 对于普通文本组件,请不要使用“最适合”
  • 对于世界空间中的TextMeshPro,请使用TextMeshPro代替TextMeshProUGUI。 画布中使用的TextMeshProUGUI
  • 如有必要,在画布设置中设置相机
  • 通过禁用画布组件来禁用画布
  • 使用灰度精灵并使图像组件设置着色
  • 使用精灵地图集
  • 对不在地图集中的纹理使用正确的大小和压缩格式

资料来源:


全面的Unity UI优化信息


关于此主题的另一个官方资源以更简洁的形式包含了信息。


阿特拉斯白皮书


有关纹理压缩格式的其他信息

Source: https://habr.com/ru/post/zh-CN470608/


All Articles