Unity3D, OnValidate & Reset 資源檢測

在開發時經常需要進行大量資源管理, 在編寫新程序及進行 Prefab 建構時這問題令開發者非常苦惱.

e.g. 建立一個多層架構的 Prefab, 把所有在 ScrollView 底下的 MyListElement 都放到 Array/List 的動作..

這些大量 Drag & Drop 的動作多做數次會令人發瘋.
這時候 OnValidate 就可以幫助開發者減少這些無謂的 setup 動作.
以 UGUI 的 Button 舉例.

ButtonLabel.cs

using UnityEngine;
using UnityEngine.UI;
public class ButtonLabel : MonoBehaviour
{
	public Text m_Label;
	public Button m_Button;

	private void OnValidate()
	{
		if (m_Label == null)
			m_Label = GetComponentInChildren<Text>(true);

		if (m_Button == null)
			m_Button = GetComponentInChildren<Button>(true);
	}
}

當這個 ButtonLabel 加入到 Button 上時候, 我們只要把其中一個 field 更動一下 { 放入 Text } 那麼 Button + Label 會一起自動找取底下第一順位的 Text 跟 Button.
但這個對於懶散慣了的程序員是不足夠的.
我們希望這個動作在 ButtonLabel 一開始放到 GameObject 身上時就自動執行一次.
在 Component 放到 GameObject 身上時會自動跑的 method 是 Reset();
所以我們修改如下:

public class ButtonLabel : MonoBehaviour
{
	public Text m_Label;
	public Button m_Button;

	private void Reset()
	{
		AutoConfig();
	}

	private void OnValidate()
	{
		AutoConfig();
	}

	private void AutoConfig()
	{
		if (m_Label == null)
			m_Label = GetComponentInChildren<Text>(true);

		if (m_Button == null)
			m_Button = GetComponentInChildren<Button>(true);
	}
}

首先 Reset() 在開發者重設 Component 時會被呼叫,
而 OnValidate() 在 “任何” 更改時也會被呼叫一次, 這包括編譯階段. 這間接導致 AutoConfig 在每次編譯時時也被呼叫.
單單一兩個 OnValidate 沒問太大負擔但想像一下你的專案有數千個 OnValidate 的元件每一個在 編譯時也被執行……..
這個測試會令 CPU 浪費很多非必要的檢測資源.

這個時候就需要用到另一個檢測手段.

UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode

用來得知是否正在編譯, 正在編譯就別進行了, 因為我們假設在 Reset 階段已經檢測過了及設定好了.
就別浪費每次對行 Playmode 測試的編譯資源.

using UnityEngine;
using UnityEngine.UI;

public class ButtonLabel : MonoBehaviour
{
	public Text m_Label;
	public Button m_Button;

	private void Reset()
	{
		AutoConfig();
	}

	private void OnValidate()
	{
#if UNITY_EDITOR
		if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
			return;
#endif
		if (!Application.isPlaying)
			AutoConfig();
	}

	private void AutoConfig()
	{
		if (m_Label == null)
			m_Label = GetComponentInChildren<Text>(true);

		if (m_Button == null)
			m_Button = GetComponentInChildren<Button>(true);
	}
}

因為 EditorApplication 只能在 UnityEditor 下才能取得. 所以程序又變得擁擠了.
當然有很多手法令這些檢測變得統一及簡化, 但各人有不同方式, 這不在本篇討論範圍.

 

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *

*