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 19:15