天天看點

UGUI Panel的顯示和隐藏優化

參考自:https://segmentfault.com/a/1190000012357091

https://zhuanlan.zhihu.com/p/264833204

unity UI如何開啟(顯示)或者關閉(隐藏)Panel界面,相信大家都是知道的,但是如何做最好呢?

可能大家一般開啟/關閉界面的方法就是直接SetActive吧。這樣做通常是可以的,簡答快速地解決需求,但是它又兩個問題:

第一,Disable掉的物體上面的腳本也不運作了,而很多時候我們需要那個腳本運作。這樣就不能用挂在自己身上腳本把自己SetActive(true)或者SetActive(false)。如果把自己的引用給另外一個腳本來對自己SetActive(true)或者SetActive(false),又會造成多餘的耦合,并不符合OOP設計理念。

第二,Disable掉的物體,重新SetActive(true),會造成較大的性能消耗,如果此界面draw call較多,會有明顯的延遲。

不用SetActive(true)/(false)之後,我們還有什麼其他方法解決問題嗎?

以下是大家可能想到的其他方法,它們也各有各的問題:

方法:Scale改為0,0,0,再改為1,1,1;

問題:改回後draw call加倍;大量垃圾回收;

方法:将界面移除Canvas這個父物體;

問題:改回後draw call加倍;大量垃圾回收;而且新增父物體增加額外引用耦合;

方法:放在Camera的某個culling層上;

問題:改回後draw call加倍;大量垃圾回收;隻對screen space-camera有效;

方法:Canvas.enable = false;

問題:改回後延遲嚴重;而且不友善使用;

這些都不好,那你說,什麼方法可行呢?

這個解決方法就是給Panel加一個CanvasGroup,上面提到的問題,在它身上都不會發生;

若要顯示:

GetComponent<CanvasGroup>().alpha = 1;

GetComponent<CanvasGroup>().interactable = true;

GetComponent<CanvasGroup>().blocksRaycasts = true;

若要隐藏:

GetComponent<CanvasGroup>().alpha = 0;

GetComponent<CanvasGroup>().interactable = false;

GetComponent<CanvasGroup>().blocksRaycasts = false;

使用Profiler工具分析CanvasGroup元件

首先建立一個BasePanel腳本,通過CanvasGroup元件來實作UI面闆的顯示(OnEnter)和隐藏(OnExit)。這裡的BasePanel相當于是UIPanel的基類了,這裡我隻是抽出了一部分,等以後有空了把簡易的UI管理類也一起講了。

[RequireComponent(typeof(CanvasGroup))]
public class BasePanel : MonoBehaviour
{
    private CanvasGroup m_CanvasGroup;

    private void Awake()
    {
        m_CanvasGroup = GetComponent<CanvasGroup>();
    }

    public void OnEnter()
    {
        m_CanvasGroup.alpha = 1;
        m_CanvasGroup.blocksRaycasts = true;
    }

    public void OnExit()
    {
        m_CanvasGroup.alpha = 0;
        m_CanvasGroup.blocksRaycasts = false;
    }
}
           

然後就建立一個測試腳本,用于比較CanvasGroup元件和使用SetAcive(true/false)的性能消耗。這邊要引入命名空間UnityEngine.Profiling才可以使用性能檢測,在需要性能測試的函數前後添加Profiler.BeginSample("展示的名字")和Profiler.EndSample()即可。這裡為了明顯展現性能消耗,開放了一個num變量來控制我們循環次數。

public class Main : MonoBehaviour
{
    public BasePanel panel;
    public int num;
    //public GameObject panelGo;

    private void Awake()
    {
        //panel = panelGo.GetComponent<BasePanel>();
    }

    private void Update()
    {
        Profiler.BeginSample("CanvasGroup");
        CanvasGroupFun();
        Profiler.EndSample();

        Profiler.BeginSample("SetActive");
        ActiveFun();
        Profiler.EndSample();
    }

    void CanvasGroupFun()
    {
        for (int i = 0; i < num; i++)
        {
            panel.OnEnter();
            panel.OnExit();
        }
    }

    void ActiveFun()
    {
        for (int i = 0; i < num; i++)
        {
            panel.gameObject.SetActive(true);
            panel.gameObject.SetActive(false);
        }

    }
}
           

将num設為100次結果如圖:

UGUI Panel的顯示和隐藏優化