細說EditorWindow

一、EditorWindow是什麼?

EditorWindow是Unity引擎中的一個可自定義彈出窗口,可以用來作為拓展編輯器界面的工具。在Window菜單下可以找到自定義的EditorWindow。

相對於Inspector面板,EditorWindow提供了更高自由度的自定義操作。一個EditorWindow可以用來做各種各樣的編輯器任務,包括顯示自定義UI、手動讓用戶輸入信息以及執行多步驟的編輯和操作。

下面是一個快速的EditorWindow例子。


using UnityEngine;
using UnityEditor;

public class ExampleEditorWindow : EditorWindow
{
    string myString = "Hello, World!";

    [MenuItem("Window/Example")]
    public static void ShowWindow()
    {
        EditorWindow.GetWindow("Example");
    }

    private void OnGUI()
    {
        GUILayout.Label("This is an example EditorWindow", EditorStyles.boldLabel);

        myString = EditorGUILayout.TextField("Text Field", myString);

        if (GUILayout.Button("Say Hello"))
        {
            Debug.Log(myString);
        }
    }
}

二、EditorWindow分類

在Unity中,通常情況下,可以根據窗口的打開方式對EditorWindow進行分類。

1. 手動打開窗口

手動打開窗口方式是就是通過Unity Editor菜單中的Window選項進行打開。通過在EditorWindow腳本中添加MenuItem特性,可以將自己構建的EditorWindow添加到Window菜單中。示例代碼中,通過添加MenuItem特性將ExampleEditorWindow添加到Window菜單中。


[MenuItem("Window/Example")]
public static void ShowWindow()
{
    EditorWindow.GetWindow("Example");
}

2. 在特定場合打開窗口

除以上的手動打開窗口之外,還有一些特定場合下會自動打開窗口。例如通過組合鍵打開FirstPassProfiler窗口,或者是添加此類代碼:


[UnityEditor.Callbacks.OnOpenAssetAttribute(1)]
public static bool OpenAsset(int instanceID, int line)
{
    Object obj = EditorUtility.InstanceIDToObject(instanceID);
    if (instanceID != 0 && obj is GameObject)
    {
        var window = EditorWindow.GetWindow(typeof(ExampleEditorWindow));
        window.Show();
        return true;
    }
    return false; // let the system open the asset
}

三、常用EditorWindow API

1. public static T GetWindow(string title, [Type type], [bool utility]);

表示根據類型T或者類型type來查找已經打開的EditorWindow實例,如果沒有找到就創建一個新的並返回。


 ExampleEditorWindow window = (ExampleEditorWindow)EditorWindow.GetWindow(typeof(ExampleEditorWindow));

2. public void Focus();

表示將窗口設置為焦點,在用戶輸入時將不會和Inspector或其他窗口爭奪焦點。


 void OnEnable() {
     EditorWindow.GetWindow(「 」).Focus ();
 }

3. public object GetService(Type serviceType);

獲取EditorWindow所依賴的接口或對象。


 public void OnGUI()
 {
     if (Selection.activeObject)
         assetPath = AssetDatabase.GetAssetPath(Selection.activeObject.GetInstanceID());
     else
        assetPath = "";
     EditorGUI.TextField(new Rect(3, 3, position.width - 6, 20), "Selected Asset:", assetPath);
     IPreviewWrapper preview = (IPreviewWrapper)this.GetService(typeof(IPreviewWrapper));
 }

4. public void ShowUtility();

表示打開一個工具窗口,不會在任何任何面板標籤之間顯示,並且不會在場景視圖上出現背景遮罩。


 [MenuItem("Window/Example")]
 static void Open()
 {
     ExampleWindow window = CreateInstance<ExampleWindow>();
     window.ShowUtility();
 }

5. 處理Event事件

在EditorWindow子類中,可以通過繼承於OnGUI函數的Event類,實現處理一些常用Event事件,例如鍵盤事件和鼠標移動事件。


 void OnGUI()
 {
     if (Event.current.type == EventType.KeyDown)
     {
         Debug.Log("Detected key code: " + Event.current.keyCode);
     }
 }
 

四、常見UI控件使用

EditorWindow中使用一些常見的UI控件方式和在普通的GUI界面中幾乎一樣,主要是利用EditorGUILayout函數、GUILayout函數和GUILayoutOption類作為參數來構建UI。

1. GUILayout.Label(string text, params GUILayoutOption[] options);

表示創建一個標籤。


 void OnGUI()
 {
     GUILayout.Label("這是一個標籤",GUILayout.Width(100));
 }
 

2. GUILayout.Button(Texture image, [style], [options]);

創建一個按鈕。


 void OnGUI()
 {
     if (GUILayout.Button("按鈕"))
         Debug.Log("按鈕被點擊");
 }

3. GUILayout.TextField(string text, [style], [options]);

創建一個可編輯的單行文本控件。


 void OnGUI()
 {
     myString = GUILayout.TextField(myString, 25);
 }
 

4. GUILayout.TextArea(string text, [options]);

創建一個可編輯的多行文本控件。


 void OnGUI()
 {
     myString = EditorGUILayout.TextArea(myString);
 }
 

5. GUILayout.HorizontalSlider(float value, float leftValue, float rightValue, [style], [slider], [thumb], [options]);

創建一個水平的滑動條。


 void OnGUI()
 {
     sliderValue = GUILayout.HorizontalSlider(sliderValue, 0.0f, 10.0f);
 }
 

五、EditorWindow Best Practices

1. 記得手動釋放EditorWindow對象和資源

EditorWindow需要手動釋放對象和資源,雖然在EditorWindow關閉時,資源和內存會被自動清理,但是最好手動釋放防止內存泄漏。


 void OnDestroy(){
     if (textureX && !EditorUtility.IsPersistent(textureX))
         DestroyImmediate(textureX, false);
 }
 

2. 需要並發運行的編寫

由於EditorWindow可能會被並發顯示,因此,對於全局數據或變量,需要加鎖處理,應該注意防止資源競爭和線程安全!


 static void ButtonCallBack() {
     Debug.Log("Button " + TextB);
 }
 private void OnGUI() {
     GUILayout.Label("Enter Text : ", EditorStyles.boldLabel);
     TextA = GUILayout.TextField(TextA);
     TextB = GUILayout.TextField(TextB);
     if(GUILayout.Button("Get Text From button")) {
         showText = TextA;
         EditorApplication.delayCall += ButtonCallBack;
     }
     GUILayout.Label("Show Text : "+showText, EditorStyles.boldLabel);
 }
 

3. 對空引用空引用的判斷

在EditorWindow中,如果需要引用Unity Editor中的一個GameObject,可以在OnDestroy函數中判斷空引用並進行釋放。


 private void OnDestroy()
 {
     if (m_mesh != null)
         GameObject.DestroyImmediate(m_mesh);
 }
 

六、結語

本文從EditorWindow基本介紹,EditorWindow分類,常用API,常見UI控件及其最佳實踐進行了詳細分析。EditorWindow的擴展性給編程帶來了全新的廣闊空間和發展方向,可以用其優秀的自由度、靈活性和多樣性不斷構建自己的實用工具。

原創文章,作者:KMQKJ,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/370986.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
KMQKJ的頭像KMQKJ
上一篇 2025-04-23 00:48
下一篇 2025-04-23 00:48

發表回復

登錄後才能評論