Unity 如何把UGUI做到极致的优化

在日常工作中,UI的性能和优化往往是最容易被忽略的一点,看似影响不大,但当一个项目达到了瓶颈的时候,在去优化UI,你会发现你的应用貌似像吃了伸腿瞪眼丸一样,以前的各种奇奇怪怪的毛病都好了,其中的收益还是非常大的,所以们比需要重视。

1.优化UI系统能带来什么收益呢?

优化UI系统带来的最明显的收益就是包体内存的骤减,以及整体界面的流畅度。

一个没有提前考虑性能和优化的UI系统带来的问题会有很多,比如:

  • APK包体过大
  • 内存占用过大(致应用闪退)
  • Draw Call过多导致性能问题
  • 界面之间切换不流畅
  • 动画卡顿掉帧
  • 顶点过多造成游戏卡顿
  • 等等

这里列举的只是一部分在很常见的未优化导致的问题。

2.如何进行优化,从哪下手?

相信有些人开始优化的时候会觉得无从下手,或者跟着网上优化一下图片格式,或者删删减减无用资源等,说白了这些跟算不得上是优化,因为他就是基础必备的。
在这里插入图片描述
这三个优化方向我下面会一一介绍需要优化的地方。
只要抓住这三个优化方向,我们的优化就只是时间问题,想一下,像走路一样,只要我们的方向对了,那我们离终点只是时间的问题。
那么想要缩短时间,就要知道有哪些优化点。
找准目标,高效执行。

1.内存优化

1.使用图集
尽量一个界面的元素保持在一个图集中,一些公用的图片放到一张公用的图集中,比如关闭 \底框等,这样做的好处是我们打开一个界面就只需要把对应的图集加载到内存中,如果该界面引用到其他图集中图片,哪怕是一张,对应的整个图集都会加载到内存中,假设该图集占用内存为18m,那我们就相当于白白浪费了18m的内存。IOS平台下图片宽高必须相等而且必须是2的次幂才能进行压缩,而使用图集我们就能很方便的控制图集的宽高,并且图集会自动合批,降低Drawcall。

2.图片图集压缩
目前市面上最流行的压缩格式就是ETC和PVRTC。这一点当然是毋庸置疑的,不过随着时代的进步,各项领域的技术也在进步,ASTC强大的压缩格式,也将渐渐成为主流,它在内存占用上比ETC和PVRTC还要小的情况下,比E和P更清晰,是个很不错的选择。对于非透明的图片,可以选择不带A通道的图片压缩格式,这样图片占的内存会更小。

3.图集错误引用
如果项目中使用到了图集,一定要关注的就是图集的引用关系,在游戏运行时,通过Profile可以看到图集图片的引用个数,如果发现我们的非当前界面的图集因为1个或者2个引用,被加载到内存中,这时我们一定要注意了,不能因为使用1-2个小图片把该而把整图集加载到内存中。

4.资源界面预加载
资源界面预加载同样会导致该界面引用的资源被加载到内存当中,过多的预加载会造成内存的紧张,可以考虑在1G或512M的设备上放弃一些预加载,这样能让我们本来就紧张的内存拥有更多的空间。

5.Http图片加载与下载
不管是下载还是加载,有一点很重要 也是很容易忽略的一点。就是我们的Texture在用完之后一定要记得Destroy掉。不然该Texture仍会持续占用内存。并且我们动态创建Texture时一要选择合理的压缩格式,以及是否开启MinMap,或者进行宽高限定进行二次压缩,否则在图片加载或下载过多的情况下,内存很容易被撑爆。在下载到图片时,我们有必要进行代码二次压缩,这样能很大的降低图片占用的内存。

6.内存泄漏
内存泄露也是项目中比较常见的现象,可以分为Mono和资源两大内存泄漏。
1.Mono内存泄漏可以也就是我们代码造成的内存泄漏,下面列举一般常见的几种泄漏方式。

Mono内存泄漏罪魁祸首:

1.声名无用静态非静态变量。
2.无用的Instance没有置空
3.数据集合或资源列表没有合理的进行清理。
4.使用完成的Texture没有进行Destory
5.集合中储存着无引用的图片资源
等等一些

资源内存泄漏罪魁祸首:

1.内存中加载多份相同的资源
2.无引用的资源或图片还保留在内存中(可以通过Profile查看资源引用情况)
3.场景跳转没有进行合理的内存释放与清理(比如大厅跳游戏,如果不进行释放与清理就会导致大量大厅的资源常驻在内存中)
4.过量的资源或Bundle预加载
等等一些

其实造成内存泄漏的核心是资源加载之后占有了内存,但是在资源不用之后,没有将资源卸载导致内存的无谓占用。

7.AssetBundle的内存问题

1.AssetBundle的加载和释放也是非常重要的问题,如果释放或加载处理不当很容易引起内存问题。
2.并且加密后的Bundle在解密的过程中也会造成一份内存拷贝,要着重留意进行优化和处理。
3.AssetBundle的冗余操作是一定要做的,不然相同的资源都被打进Bundle加载到内存是一件极大的浪费。

上述优化点并不涵盖全部,只是一些常见的,比较容易进行优化的,后需想到的会继续添加。

2.包体优化

1.图片格式压缩

引用 内存优化第1点和第2点
图集和图片的宽高和压缩格式对包体产生着决定性的作用,一定要进行合适的处理,根据图片有无透明通道选择合适的压缩格式,能大大减少包体。尽量合理化的使用图集,不要让图集留下太多的空白像素。

2.无用资源的剔除

项目中没有用到的图片一定要记得及时删除,不要图省事,不然后期资源越多越难处理。当然网上也有很多清理项目资源垃圾的插件,可以精确的识别到该资源有没有被引用,效果也是非常不错的。
Resources文件夹下的无用图片以及资源,一定要删除掉,因为Resources下的文件不管是否有引用都会被打进包体。

3.重复资源的控制和筛选

为了预防资源重复,所以资源最好要从项目开始时就进行控制,放到一个公用的地方,保证其唯一性,否则后期引用一但多了起来,非常让人头疼。

4.Unity打包压缩方法

Compression Method
是非常重要的一个优化点,最好选择LZ4或者LZ4HC如果不进行设置使用Default,以IOS为例,270左右的包,打出来之后你会发现整个包竟然有700多兆。很恐怖。

缩小包体要注意的地方有很多,这里只介绍一些常见的UGUI相关的优化。

3.性能优化

UI系统性能的优化相比于内存和包体优化是比较费劲的,因为这是个精细活,影响性能的问题比较 多,杂,细, 我们想要优化好他的性能,就要先去了解一下都是什么原因导致他的性能下降!

首先,要知道为什么会产生性能问题,那我们就必须去了解一下UGUI的工作原理。UGUI的界面渲染是以Canvas作为一个画布然后CPU根据画布上的元素计算收集该元素的网格顶点数据信息命令GPU去进行绘制,GUP拿到数据之后就开始埋头苦画,最终根据数据把这些画面给呈现出来,这样我们的Canvas上就出现了各种画面,这样一的一个流程也正对应着一个DrawCall。

了解了原理以后,我们就很容易的明白了一个导致性能的问题。

就是某一个Canvas上的元素过多,那么我们的CPU在进行计算的时候就越费时,从而导致整个渲染流程耗时变久,直接导致我们的帧率下降以及画面不流畅。

那么什么时候需要进行计算和绘制呢?

答案就是一个界面被隐藏显示时,或者界面上的元素位置大小有所改变时。
并且物体的Enable、Disabled、SetActive、Scale=0 等都会引起Canvas的重绘。

因为Canvas下的所有元素都是合在一个Mesh中的,所以只要Canvas上的元素有改变或隐藏显示时,那么就需要对其进行一次重新绘制。当然这里并不是简简单单的绘制这么简单,因为绘制需要准备数据,而绘制数据需要CPU通过计算Canvas下所有元素的网格顶点数据,中间涉及到元素合批,层级深度搜寻,等等一些,所以如果我们的所有界面都在一个Canvas下,那么只要我们的Canvas下的元素有一个改变,那么整个Canvas下的所有元素都要重新计算一下网格数据以及Batch进行重新绘制。那么中间消耗的性能也够我们喝一壶了。而且这种性能消耗属于浪费性能。因为我们完全可以避免掉的。

这也是导致游戏性能下降的一个很重要的点。

那么因此也可以理解为什么大家都倡导动静分离了。

下面介绍一下会造成性能下降以及需要注意的点:(包含自己心得以及网上收录)

  1. 进行动静分离拆分Canvas,把频繁移动的放到动态Canvas下,一方面可以降低静态元素的更新频率,另一方面可以减小重建时涉及到的Mesh大小(重建是以Canvas为单位进行的)

  2. 避免频繁的销毁合创建一个界面

  3. 避免频繁的SetActive界面,可以用CanvasGroup进行代替

  4. 尽可能的避免使用Unity自带的OutLine组件,该组件会使得文本的Mesh增加4倍,导致UI重建开销明显增大

  5. 尽可能的避免使用Unity自带的Shadow组件,该组件同样会使得Mesh增加顶点增多。

  6. 谨慎使用Text的Best Fit选项,代价很高,增加额外生成时间。(禁Best Fit)

  7. Mask换Rect Mask 2D或者网上的Mask组件。mask以内和以外把UI分割成两个“世界”,依次计算两个“世界”的drawcall,然后再相加。所有能不用就不用。

  8. 避免Image Text 不需要RayCast Target的组件开启该功能,因为UGUI在手指点击的时候,其实是Cavas上挂的一个Graphic Raycaster组件去遍历整个Cavas下所有UI元素的RayCast Target属性是否允许点击,我们把不需要点击的组件勾掉,会节省不小的性能开销。

  9. 图集一定要规范,不要一个界面用很多图集中的图片, 影响drawcall数量的根本是batch(批处理数),而batch是根本一个一个图集来进行批处理的,如果相邻的两张Image不是一个图集,那么就会造成两个Batch。

  10. 图文交叉,比如 image1 image2 Text 这样的顺序下是两个Drawcall,如果换成 Image1 Text Imgae2 由于Text会打断合批,所以即使两个Image是同一个图集中的图片,也会产生3个Drawall。

  11. 避免Text RichText 属性,当Text组件勾选了Rich Text的时候,其实每次文本显示或赋值时都会先判断一下是否拥有这个属性,然后做正则匹配。如果频繁赋值,也是一笔不小的开销。

  12. 无用的Image以及Sprite为空的Image会打断合批,要谨慎使用。

  13. 如果界面不显示了,该界面的一切周期函数比如Update一定要停掉。

本篇介绍的涵盖一部分,并非全部,后面遇到或者收集到会一一罗列进来。

其实并非所有的优化,都要等到需要优化了,在去优化。如果我们在项目一开始的时候,就去制定一套规范的流程以及框架,并且我们在工作的过程中去遵守这些约定。那么到项目后期出我们就能省下很多宝贵的时间,我们的压力和问题也就比之前要少。

就比入性能优化这块,如果我们单独一条条的去优化,会发现越来越累,因为要修改的地方实在太多,而且也容易滋生其他问题。所以关于性能这块,最好是在项目初期就设计到框架中,一个好的框架,是能为我们前期和后期省下很多时间的。

所以我这里推荐一款UI框架,ZMUIFramework 该框架严谨的考虑了UI系统的各种性能以及内存优化,并制定了合理高效的使用方案以及避免性能问题的方案。该框架的特色 高性能,高自动化,高效率。入门简单使用方便。

3.如何打造一个高性能、效率、自动化UI管理框架?

如何打造一个高性能、效率、自动化UI管理框架https://blog.csdn.net/qq_42461824/article/details/116563318

https://ke.qq.com/course/5545452#term_id=105736835

猜你喜欢

转载自blog.csdn.net/qq_42461824/article/details/116283598