參考自: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次結果如圖:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SZyUTMxUjY3cjMjlDM0UGM2YWO3kDM3YWZ5YjMhRmM08CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)