UGUI优化汇总

Posted by Richbabe on August 6, 2018

前言

相信每个刚刚步入游戏行业的新人第一个接触的便是写UI界面。对于Unity来说,UGUI是每个客户端程序第一个接触学习的东西。在这里我将长期更新一些UGUI的优化方法。

UGUI简介

UGUI是Unity官方推出的UI系统,集成了所见即所得的UI解决方案, 其功能丰富并且使用简单,同时其源代码也是开放的,下载地址:戳我

相比于NGUI,UGUI有以下几个优点

  • 所见即所得的编辑方式,在Scene窗口中即可编辑。
  • 智能的Sprite packer可以将图片按tag自动生成图集而无需人工维护,生成的图集合并方式比较合理,无冗余资源。
  • 渲染顺序与GameObject的Hierarchy顺序相关,靠近根节点显示在底层(先渲染),而靠近叶子节点显示在顶层(后渲染);这样的渲染方式使得调整UI的层级比较方便和直观。
  • RectTranForm及锚点系统更适合于2D平面布局,并且非常方便多分辨率屏幕自适配。

UGUI优化方法汇总

合理的分配图集

合理的分配图集可以降低drawcall(和OpenGL中调用一次glDrawArray函数一个意思)和资源加载速度,具体细节如下:

  • 同一个UI界面的图片尽可能放到一个图集中,这样可以尽可能的降低drawcall
  • 共用的图片放到一个或几个共享的图集中,例如通用的弹框和按钮等;相同功能的图片放到一个图集中, 例如装备图标和英雄头像等;这样可以降低切换界面的加载速度。
  • 不同格式的图片分别放到不同的图集中,例如透明(带Alpha)和不透明(不带Alpha)的图片,这样可以减少图片的存储空间和占用内存。(UGUI的sprite packer会自动处理这种情况)

适当的降低图片的尺寸

有时UI系统的背景可能会使用全屏大小的图片,比如在Iphone上使用1136*640大小的图片;使用这样尺寸的图片代价是很昂贵的,可以和美术同学商量适当的降低图片的精度,使用更低尺寸的图片。在使用低尺寸图片时可以通过九宫格在Scene面板将图片放缩至合适的大小。

使用对象池

使用缓存池来保存ScrollView中的Item,对于移出或移进View外的的元素,不要调用,而是把它们放到缓存池里或从缓存池中取出复用。具体教程可以参考:https://blog.csdn.net/UWA4D/article/details/70054200

将不交互的UI禁用Raycast

除了rebuild过程之外,UGUI的touch处理消耗也可能会成为性能热点。因为UGUI在默认情况下会对所有可见的Graphic组件调用raycast。对于不需要接收touch事件的grahic,一定要禁用raycast。对于unity5以上的可以关闭graphic的Raycast Target而对于unity4.6,可以给不需要接收touch的UI元素加上canvasgroup组件。

避免overdraw

一般来说,造成GPU性能瓶颈主要有两个原因:复杂的vertext或pixel shader计算以及overdraw造成过多的像素填充。在默认情况下UGUI中所有UI元素使用都使用UI/Defaut shader,因此在优化时可优先考虑解决Overdraw问题。Overdraw主要是因为大量UI元素的重叠引起的,查看overdraw比较简单,在scene窗口中选择overdraw模式,场景中越亮的地方表示overdraw越高

为了降低overdraw,可以做如下优化:

  1. 禁用不可见的UI,比如当打开一个系统时如果完全挡住了另外一个系统,则可以将被遮挡住的系统禁用。
  2. 不要使用空的Image,在Unity中,RayCast使用Graphic作为基本元素来检测touch,在笔者参与的项目中,很多同学使用空的image并将alpha设置为0来接收touch事件,这样会产生不必要的overdraw。通过如下类NoDrawingRayCast来接收事件可以避免不必要的overdraw。:
public class NoDrawingRayCast : Graphic 
 { 
     public override void SetMaterialDirty() 
     { 
     
     } 
     
     public override void SetVerticesDirty() 
     { 
     
     } 
     
     protected override void OnFillVBO(List<UIVertex> vbo) 
     { 
         vbo.Clear(); 
     } 
} 

分Canvas

把动的UI分在一个Canvas,不动的UI分在一个Canvas。这是因为UGUI是依赖Canvas做合并的,每帧都要rebuild整个Canvas,因此可以设置只rebuild动的Canvas不rebuild静的Canvas。同时可以通过将不显示的UGUI设置scale为0来代替setActive,频繁setActive开销很大。