1.动静分离

跟canvas的渲染机制有关,即如果一个ui元素始终是静态的,那么画布则不会重新计算绘制,如果是动态的则不然,所以,这里建议将动态和静态的元素分开。静态的元素从属一个canvas,而需要动态变动的元素从属另一个canvas。


2.消除不必要的射线检测(raycast)

默认情况下,canvas自带Graphic Raycaster组件,这对于交互型的UI是必须的,但如果你的UI元素并不需要交互,那么每次点击都会触发判断,这种情况下,你就可以删除Graphic Raycaster组件以减少不必要的计算处理。同样的,各种其他UI元素,如图片、文本等也有Raycast Target属性,如果不需要射线检测(触屏、点击),也可以一并关掉,减少计算量。


3.避免使用 Camera.main

unity默认创建的相机为主相机(tag 为MainCamera),即 main camera,可以通过unity api : Camera.main来直接获取,本质上,它是每次通过获取tag来取得的:

GameObject.FindGameObjectWithTag(“MainCamera”) Unity 2020+ 已优化 可忽略

所以,建议在awake函数中缓存一次,避免多次查找 (GetComponent和Find等方法是很耗时的操作).

Awake()
{
    _mainCamera = Camera.main;
    // 与↓相同
    _mainCamera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>();
}

回到 UGUI这边,当画布的渲染模式切换为world space时,event camera的值为none,如果不指定,它会默认指向main camera,每帧将会产生7-10次的查找操作.


4.慎用布局组件

布局组件是非常好用的功能组件,可以非常方便的用于元素内容的快速对齐排版。尽管如此,但你要是了解它的底层操作原理,你以后使用就会慎重了。因为每个布局组件会将一个GetComponents调用添加到每个子布局元素的脏读中,从而严重降低了嵌套布局组的性能。

另外:unity scroll rect 的实现,也是一个 Layout Group .


5.合并UI对象

对象被重复销毁和创建的时候,我们会考虑用对象池来优化,但是这样会产生一个新问题:UI元素被重新设置,禁用和合并,这会导致Canvas不必要地变脏。

推荐优化方法:

//隐藏不再使用的内容,然后
_text.gameObject.SetActive(false);

//移动画布
text.transform.SetParent(objectPoolCanvas.transform);

下次使用的时候:
//从ObjectPoolCanvas移至Canvas之后
text.transform.SetParent(canvas.transform);

//显示
_text.GameObject.SetActive(true);


6.如何隐藏画布

通常我们禁用一个物件,使用的 gameObject.SetActive(true),如果我们这样禁用画布canvas的话,会触发画布显示重建,同时还会调用 onDisable 和 onEnable,增加了计算量。

推荐方法:_canvas.enable = false;


7.使用 Tween 库而不是 Animator

有时候,UI会配合动画来做一些效果,但是应该避免空的动画,即使动画值没变,动画却依然会弄脏元素,导致canvas不断重建。所以 UGUI中不使用没有任何变化的空闲动画.

推荐方式:自己写补间动画,或者直接使用三方补间库如 dotween 等.


8.尽量用RectMask2D替代Mask组件

Mask组件用到最多是在ScrollView中,网上也有很多资料介绍Mask组件会产生额外的两个drawcall,而大部分时候Mask都是矩形的,所以其实可以使用RectMask2D组件来替换掉Mask组件,官方也比较推荐RectMask2D组件,但如果你的Mask组件不是矩形的那么就不能替换。


9.检查UI组件的 Position 和 Rotation

之前在检查一个UI合批失效的问题,明明打了图集,但就是不能合批,很是诧异,经过测试发现如果你的Image组件pos.z不为零或者rota.x或rota.y不为零,那么会被当作3DUI从而不参与合批,增加drawcall,而这些非零值有时往往是不需要甚至是,不注意被设置上的,但却影响了这个合批,很是坑人。所以需要用工具检查这种被设置错误的UI,酌情进行重置.

10.滥用不可见组件

之前在Profile手头项目的时候发现红米上一个奇怪的现象:战斗界面维持60fps没问题;进入UI界面之后瞬间掉到45fps,甚至有的复杂界面掉到30fps。但战斗场景的Tris/Verts比UI高不少。

通过工具很方便的就定位到了瓶颈在于FillRate爆了,最后发现新手教学部分用了很多“不可见”的Image作为交互响应的控件;但这些东西虽然画上去没有效果,依然占用了显卡资源,特别是有很多大块的区域…找到问题之后就解决起来很方便:实现一个只在逻辑上响应Raycast但是不参与绘制的组件即可,改完之后帧率瞬间正常。

using UnityEngine;
using System.Collections;

namespace UnityEngine.UI
{
    public class CCBlank : MaskableGraphic
    {
        protected CCBlank()
        {
            useLegacyMeshGeneration = false;
        }

        protected override void OnPopulateMesh(VertexHelper toFill)
        {
            toFill.Clear();
        }
    }
}

这里顺便提一句,显卡资源消耗在没有到瓶颈的时候,大概是随着使用的增加正相关,但是到瓶颈之后很多时候是 “崩盘” 节奏.

作者:admin  创建时间:2023-01-05 16:15
最后编辑:admin  更新时间:2023-01-05 19:15