关于uGUI与2D角色的显示层级

2016年07月29日 16:43 0 点赞 0 评论 更新于 2025-11-21 18:05

接触Unity3D已有一段时间,项目组在UI解决方案的选型上一直采用原生的uGUI,因此我也使用了一段时间的uGUI,并在其使用方面积累了一些经验。

一、uGUI的基本特性

游戏中的UI与其他游戏元素本质上并无太大差异,不同之处在于,UI通常由2D图片组合而成,包含较多透明元素与渐变元素,且一般显示在屏幕的最顶层。

在使用NGUI时,UI的深度是一个指定的数值,数值越大越靠前。在布置复杂界面时,常常会不知道该如何填写深度值,这让人十分困扰。而使用uGUI则可轻松解决这一问题。例如,在场景中若B图片显示在A图片前面,查看Hierarchy视图会发现,由于A在B的上面,所以Unity会优先渲染A,然后再渲染B,从而使得B显示在A的上面。若想让A图显示在B图的上面,只需在Hierarchy视图里将B拖拽到A的上面即可。

如果一个GameObject下面有多个精灵,其渲染顺序遵循以下规则:首先看父节点在Hierarchy视图中的排序,以此决定父节点的渲染先后;然后依次查看子节点在Hierarchy视图中的排序;若还有孙节点,以此类推。在没有ABA叠层的情况下,图集始终只会产生一个drawcall。因此,在布置界面时,精心设计节点顺序可以节省大量的drawcall。

二、渲染绘制顺序示例分析

下面通过具体示例深入分析渲染绘制顺序。假设UIMain和UINext是同级目录,由于UINext在UIMain下面,所以Unity会优先渲染UIMain,进而UINext将显示在屏幕最前面,UISub的原理与此相同。由此可得出精灵从前面到后面的显示排序为:NextB > NextA > SubB > SubA > MainA > MainB。

在布置界面时,我们可以依据这个排序规则来最小化drawCall。但如果需要在运行时在两个图之间插入一个图,该如何实现呢?以下是示例脚本:

using UnityEngine;
using System.Collections;

public class UIMain : MonoBehaviour
{
void Start()
{
GameObject button = GameObject.Instantiate(Resources.Load<GameObject>("button")) as GameObject;
button.transform.parent = transform;
button.transform.localPosition = Vector3.zero;
button.transform.localScale = Vector3.one;

GameObject AObj = transform.Find("A").gameObject;

GameObject BObj = transform.Find("B").gameObject;

button.transform.SetSiblingIndex(AObj.transform.GetSiblingIndex());
}
}

在上述脚本中,transform.SetSiblingIndexGetSiblingIndex 方法用于设置和获取GameObject在兄弟节点中的位置。

三、uGUI性能优化的最佳实践

uGUI的性能涉及多个方面,以下是一些经过整理的最佳实践建议:

  1. 图集设计
    • 重用图集:设计UI时要考虑重用性,像一些边框、按钮等可作为共享资源,放置在1 - 3张大图集中,称为重用图集。
    • 功能图集:将其它非重用UI按照功能模块进行划分,每个模块使用1 - 2张图集,即功能图集。
    • 图集合并:对于一些同时用到功能图集与重用图集的UI,若功能图集剩下的“空位”较多,可考虑将重用图集中用到的元素单独提取出来,合并到功能图集中,使UI仅依赖于功能图集,通过一定的冗余来提升性能。
  2. Drawcall优化
    • Batch处理:具有相同材质和纹理的UI元素可以进行Batch处理,可Batch的UI上下叠放不会影响性能,但不能Batch的UI元素叠放会增加Drawcall开销。
    • 层叠关系检查:要留意UI元素间的层叠关系,建议使用“T”工具查看其矩形大小。因为有些图片虽然透明,但叠放在其他UI上面且无法Batch时,会无故增加许多Drawcall。
    • 组件层叠优化:UI中常见的Image与Text组件,当Text叠放在Image上面(如Button),且Text上又叠放了一个图片时,至少会多产生2个Drawcall,此时可考虑将字体直接印在下面的图片上。
    • 层级调整:某些情况下,可人为增加层级以减少Drawcall。例如,一个Text的层级为0,另一个可Batch的Text叠放在图片A上,层级为1,此时两个Text因层级不同会产生2个Drawcall。若在第一个Text下方放置一个透明的图片(与图片A可Batch),两个Text的层级就会一致,Drawcall可减少一个。
  3. Mask使用注意事项
    • 避免使用Mask:应尽量避免使用Mask,因为Mask的功能有时可通过变通方式实现,比如设计一个边框,将其叠放在最上面,当底下的UI移动时,就会被边框遮住。
    • 评估性能损耗:若必须使用Mask,需评估其带来的性能损耗,并尽量将其降至最低。例如,当Mask内的UI是动态生成的(如List组件),要留意生成的UI之间是否存在重叠现象。

此外,有空可以多到“泰斗社区”学习交流。由于个人能力有限,文中可能存在一些纰漏,欢迎大家指正。