细说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/n/370986.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
KMQKJ的头像KMQKJ
上一篇 2025-04-23 00:48
下一篇 2025-04-23 00:48

发表回复

登录后才能评论