在Unity中我们常常扩展各种编辑窗口,创建自己的自定义编辑器窗口,这些窗口可以自由浮动,也可以作为选项卡停靠,就像 Unity 界面中的原生窗口一样。
在Unity中创建自定义窗口通常是通过UnityEditor的EditorWindow类来实现的。以下是创建一个简单自定义窗口的步骤和示例代码:

添加功能步骤:
1、创建一个继承自EditorWindow的新C#脚本。
2、添加一个静态方法来打开窗口。
3、在此方法中,使用GetWindow<自定义窗口类>()打开窗口,并重写窗口的OnGUI方法来定义窗口界面。
示例代码:
using UnityEngine;
using UnityEditor;
 
public class CustomWindow : EditorWindow
{
    // 添加窗口菜单项
    [MenuItem("MyWindow/Open Custom Window")]
    public static void OpenWindow()
    {
        // 打开窗口
        GetWindow<CustomWindow>("My Custom Window");
    }
 
    private void OnGUI()
    {
        // 在这里定义窗口界面
        GUILayout.Label("Hello, this is my custom window!");
        if (GUILayout.Button("Click Me"))
        {
            // 处理按钮点击事件
            Debug.Log("Button Clicked!");
        }
    }
}
将上述代码保存到项目中的Editor文件夹下的C#脚本文件中(如果没有此文件夹,请创建一个)。然后,您可以通过Unity编辑器顶部菜单的"MyWindow"路径来打开您的自定义窗口。每次运行Unity编辑器时,都会检查Editor文件夹中的脚本,并根据MenuItem属性添加相应的菜单项。
目标: 
1、了解一些属性的使用 
2、创建一个自定义窗口 
最终目标: 
利用学到的东西制作自己的工具(自定义的窗口、Inspector、菜单、插件等等)。
一、EditorWindow编辑器窗口类
1、在 Unity 中,EditorWindow 和 ScriptableWizard 都是用于创建自定义编辑器界面的类。
2、EditorWindow 是用于创建自定义编辑器窗口的类,可以包含自定义的 GUI 元素和逻辑。它通常用于创建工具窗口、面板或者其他自定义的编辑器界面。
3、ScriptableWizard 是用于创建向导式界面的类,可以帮助用户完成特定的任务。通常用于引导用户完成一系列步骤,例如创建新的资产、配置项目设置等。与 EditorWindow 相比,向导更加注重交互性和引导性,常用于流程化的操作步骤。
二、EditorWindow参数总览
| 常用参数 | 描述 | 
|---|
| position | 窗口在屏幕空间中的理想位置。 | 
| titleContent | 用于绘制 EditorWindow 标题的 GUIContent。 | 
| Close | 关闭编辑器窗口。 | 
| Show | 显示 EditorWindow 窗口。 | 
| ShowAsDropDown | 显示包含下拉菜单和样式的窗口。 | 
| ShowAuxWindow | 在辅助窗口中显示编辑器窗口。 | 
| ShowModal | 显示模态编辑器窗口。 | 
| ShowNotification | 显示通知消息。 | 
| ShowPopup | 使用弹出式框架显示编辑器窗口。 | 
| ShowUtility | 将 EditorWindow 显示为浮动实用程序窗口。 | 
| CreateWindow | 创建类型为 T 的 EditorWindow。 | 
| FocusWindowIfItsOpen | 聚焦发现的第一个指定类型的 EditorWindow(如果已打开)。 | 
| GetWindow | 返回当前屏幕上的第一个 windowType 类型的 EditorWindow。 | 
| GetWindowWithRect | 返回当前屏幕上第一个 t 类型的 EditorWindow。 | 
| HasOpenInstances | 检查编辑器窗口是否已打开。 | 
| OnBecameInvisible | 在窗口从容器视图中删除或在 EditorWindow 的选项卡式集合中不再可见后调用。 | 
| OnBecameVisible | 将窗口添加到容器视图后调用。 | 
| OnDestroy | 调用 OnDestroy 以关闭 EditorWindow 窗口。 | 
| OnFocus | 在窗口获得键盘焦点时调用。 | 
| OnGUI | 在此处实现您自己的 Editor GUI。 | 
| OnHierarchyChange | 处理程序,用于在层级视图中的对象或对象组发生更改时发送的消息。 | 
| OnInspectorUpdate | OnInspectorUpdate 以每秒 10 帧的速度调用,以便检视面板有机会进行更新。 | 
| OnLostFocus | 在窗口失去键盘焦点时调用。 | 
| OnProjectChange | 处理程序,用于在项目状态发生更改时发送的消息。 | 
| OnSelectionChange | 每当选择发生更改时调用。 | 
三、EditorWindow 的生命周期
1、打开
OnEnable():当打开界面的时候调用
OnFocus():当该窗口被聚焦(点击该窗口)
2、打开中
OnGUI():当渲染 UI 的时候调用
OnSelectionChange():当选择发生更改时调用,选中的可选项(在 Project 和 Hierarchy 视图中)
OnInspectorUpdate():当属性界面更新时,几乎一直在更
OnHierarchyChange():当场景层次界面发生改变时调用 ");// 在 Hierarchy 界面改变(增加、减少物体)
OnProjectChange():当项目发生更改时调用 ");// 在 Project 视图删除、增加文件
OnValidate():当拖拽式赋值时调用
3、关闭
OnLostFocus():从该窗口离开时调用(点击非窗口外其他地方)
OnDisable():当隐藏的时候调用
OnDestroy():当销毁的时候调用
四、CreateWindow、GetWindow、GetWindowWithRect
1、在 Unity 编辑器中,CreateWindow()、GetWindow() 和 GetWindowWithRect() 是用于处理窗口的函数,但它们的功能和使用场景有所不同。
1、CreateWindow():这个函数通常用于创建一个新的窗口。在 Unity 编辑器中,这可能涉及到创建一个新的 Inspector 窗口、Console 窗口或其他自定义窗口。CreateWindow () 需要指定窗口类型(例如 WindowType.Inspector 或 WindowType.Console)以及其他可能的参数,如窗口标题和大小。
3、GetWindow()::这个函数用于获取一个已经存在的窗口。它通常用于需要引用已打开的窗口的情况,例如,你可能想关闭一个已经打开的 Inspector 窗口。
4、GetWindowWithRect()::这个函数比较特殊,它不仅仅是为了获取一个窗口,而且还要通过一个指定的矩形区域获取。这对于需要在给定屏幕区域中查找和操作窗口非常有用。比如你可能想在指定的屏幕区域查找 Console 窗口。
5、在使用这些函数时,通常需要注意以下几点:
CreateWindow() 用于创建新窗口,而 GetWindow() 和 GetWindowWithRect() 用于获取已存在的窗口。
GetWindow() 和 GetWindowWithRect() 通常用于操作已打开的窗口,例如关闭它们或获取它们的引用。
GetWindowWithRect() 特别适用于在特定屏幕区域查找窗口,这在处理多显示器或多窗口环境时非常有用。
代码示例
using UnityEditor;
using UnityEngine;
public class WindowExample : EditorWindow
{
    private static WindowExample window;//窗体实例
    //显示窗体
    [MenuItem("MyWindow/Second Window")]
    private static void ShowWindow()
    {
        window = EditorWindow.GetWindow<WindowExample>("Window Example");
        window.Show();
    }
    //显示时调用
    private void OnEnable()
    {
        Debug.Log("OnEnable");
    }
    //绘制窗体内容
    private void OnGUI()
    {
        EditorGUILayout.LabelField("Your Second Window", EditorStyles.boldLabel);
    }
    //固定帧数调用
    private void Update()
    {
        Debug.Log("Update");
    }
    //隐藏时调用
    private void OnDisable()
    {
        Debug.Log("OnDisable");
    }
    //销毁时调用
    private void OnDestroy()
    {
        Debug.Log("OnDestroy");
    }
}
五、ScriptableWizard参数总览
Unity为开发者提供了一个简单的快速创建对话框窗体的方式,只需要继承自ScriptableWizard类,我们容易发现ScriptableWizard实际上是继承自EditorWindow,只是做了一层封装,该类窗体一般用于快捷功能的操作,例如统一修改场景中多个对象的位置等信息。
| 常用参数 | 描述 | 
|---|
| createButtonName | 允许您设置向导的 Create 按钮上显示的文本。 | 
| errorString | 允许您设置向导的错误文本。 | 
| helpString | 允许您设置向导的帮助文本。 | 
| isValid | 允许您启用向导 Create 按钮,或者将其禁用,从而使用户无法点击。 | 
| otherButtonName | 允许您设置向导的可选 Other 按钮上显示的文本。忽略此参数将不显示该按钮。 | 
| OnWizardCreate | 当用户单击 Create 按钮时,将调用此函数。 | 
| OnWizardOtherButton | 允许您在用户单击 Others 按钮时提供操作。 | 
| OnWizardUpdate | 在向导打开或者用户在向导中更改内容时,将调用此函数。 | 
六、区别OnWizardCreate、OnWizardOtherButton、OnWizardUpdate
OnWizardCreate:
当向导被创建时调用。
通常用于初始化向导的状态或设置。
OnWizardOtherButton:
当用户在向导界面上点击非标准按钮(如 “下一步”、“上一步” 或自定义按钮)时调用。
你可以根据需要重写此方法以处理非标准按钮的点击事件。
OnWizardUpdate:
在向导的当前步骤被更新时调用。
通常用于根据用户输入或其他条件更新向导的当前步骤内容。
代码样例
using UnityEditor;
using UnityEngine;
public class WindowExample : ScriptableWizard
{
    public float range = 500;
    public Color color = Color.red;
    [MenuItem("GameObject/Create Light Wizard")]
    static void CreateWizard()
    {
        ScriptableWizard.DisplayWizard<WindowExample>("Create Light", "Create", "Apply");
        如果您不想使用辅助按钮,只需将其省略即可:
        //ScriptableWizard.DisplayWizard<WizardCreateLight>("Create Light", "Create");
    }
    //显示在向导窗口里的变量
    public float speedValue = 10;
    public bool canShoot = true;
    //用户单击创建按钮进行调用,固定用法,点击后向导窗口关闭
    private void OnWizardCreate()
    {
        Debug.Log("Create : Change By Value");
        //一般在这里做最终的处理
        //比如在这里可以获取选中的全部object,再利用向导窗口里填写的变量批量改变文件数值
    }
    //用户单击自定义其他按钮时进行调用,固定用法,点击后向导窗口不会关闭
    private void OnWizardOtherButton()//在这里是一个初始化数据的功能
    {
        Debug.Log("Clear");
        speedValue = 0;
        canShoot = false;
        ShowNotification(new GUIContent("数据已经初始化完毕"));
        //该功能用来弹出一个小提示通知信息,几秒后自动消失
    }
    //打开向导或者更改向导里的内容时进行调用,固定用法
    private void OnWizardUpdate()
    {
        Debug.Log("Change");
        //当在向导窗口里一些操作错误或者不规范时,可以通过设置helpString和errorString来进行提示操作人员
        helpString = "文中某某变量填写规范为"XXXX+DDDD+SS"";
        errorString = "文中某某变量填写不规范";
        //编辑器模式下,可以使用EditorPrefs进行数据的存取,用法与游戏运行时PlayerPrefs用法一致
        EditorPrefs.SetFloat("key", speedValue);
    }
    //当在工程里选中操作有变化时进行调用
    private void OnSelectionChange()
    {
        OnWizardUpdate();
    }
}
七、PopupWindow弹出窗口
用于显示从 PopupWindowContent 继承的弹出窗口的类。
弹出窗口没有边距,不可拖动,也无法调节大小。此外,它们还会在失去焦点时自动关闭。它们用于显示短暂的信息或选项。
编辑器中的弹出窗口的一个示例是编辑器“Scene View”工具栏中的“Scene View Effects”选项:

下面是一个自定义弹出窗口的示例,此弹出窗口通过编辑器窗口中的一个按钮显示。该弹出窗口具有三个开关值,并会在失去焦点时自动关闭。此示例以两个脚本的形式提供。第一个脚本定义可通过菜单项打开的编辑器窗口。该编辑器窗口具有一个可显示弹出窗口的按钮。第二个脚本以一个单独的类的形式定义弹出窗口本身的内容。
首先,下面是启动弹出窗口的简单编辑器窗口的代码:
using UnityEngine;
using UnityEditor;
public class EditorWindowWithPopup : EditorWindow
{
    // Add menu item
    [MenuItem("Example/Popup Example")]
    static void Init()
    {
        EditorWindow window = EditorWindow.CreateInstance<EditorWindowWithPopup>();
        window.Show();
    }
    Rect buttonRect;
    void OnGUI()
    {
        {
           GUILayout.Label("Editor window with Popup example", EditorStyles.boldLabel);
           if (GUILayout.Button("Popup Options", GUILayout.Width(200)))
           {
             PopupWindow.Show(buttonRect, new PopupExample());
           }
           if (Event.current.type == EventType.Repaint) 
               buttonRect = GUILayoutUtility.GetLastRect();
        }
    }
}
接下来,下面是弹出窗口本身的代码:
using UnityEngine;
using UnityEditor;
public class PopupExample : PopupWindowContent
{
    bool toggle1 = true;
    bool toggle2 = true;
    bool toggle3 = true;
	
    public override Vector2 GetWindowSize()
    {
        return new Vector2(200, 150);
    }
    public override void OnGUI(Rect rect)
    {
        GUILayout.Label("Popup Options Example", EditorStyles.boldLabel);
        toggle1 = EditorGUILayout.Toggle("Toggle 1", toggle1);
        toggle2 = EditorGUILayout.Toggle("Toggle 2", toggle2);
        toggle3 = EditorGUILayout.Toggle("Toggle 3", toggle3);
    }
    public override void OnOpen()
    {
        Debug.Log("Popup opened: " + this);
    }
    public override void OnClose()
    {
        Debug.Log("Popup closed: " + this);
    }
}
以上每段代码应另存为以其类名称命名的单独文件。这些代码都不是行为,因此无需将它们放在游戏对象上。将它们放在项目中后,通过转到新的“Example”菜单并选择“Popup Example”即可试用。然后,单击新编辑器窗口中的按钮可显示弹出选项窗口
八、PopupWindow参数总览
受保护的函数
| OnDisable | 请参阅 ScriptableObject.OnEnable。 | 
| OnEnable | 请参阅 ScriptableObject.OnDisable。 | 
静态函数
| Show | 显示具有给定 PopupWindowContent 的弹出窗口。 | 
继承的成员
静态变量
| focusedWindow | 当前已获得键盘焦点的 EditorWindow。(只读) | 
| mouseOverWindow | 当前在鼠标光标下的 EditorWindow。(只读) | 
变量
| autoRepaintOnSceneChange | 窗口是否会在场景每次发生变化时自动重绘? | 
| maximized | 此窗口是否已最大化? | 
| maxSize | 此窗口的最大大小。 | 
| minSize | 此窗口的最小大小。 | 
| position | 窗口在屏幕空间中的理想位置。 | 
| titleContent | 用于绘制 EditorWindow 标题的 GUIContent。 | 
| wantsMouseEnterLeaveWindow | 检查是否已在此编辑器窗口的 GUI 中收到 MouseEnterWindow 和 MouseLeaveWindow 事件。 | 
| wantsMouseMove | 检查是否已在此编辑器窗口的 GUI 中收到 MouseMove 事件。 | 
| hideFlags | 该对象应该隐藏、随场景一起保存还是由用户修改? | 
| name | 对象的名称。 | 
公共函数
| BeginWindows | 标记所有弹出窗口的开始区域。 | 
| Close | 关闭编辑器窗口。 | 
| EndWindows | 关闭由 EditorWindow.BeginWindows 开始的窗口组。 | 
| Focus | 将键盘焦点移动到另一个 EditorWindow。 | 
| RemoveNotification | 停止显示通知消息。 | 
| Repaint | 重绘窗口。 | 
| SendEvent | 将事件发送到窗口。 | 
| Show | 显示 EditorWindow 窗口。 | 
| ShowAsDropDown | 显示包含下拉菜单和样式的窗口。 | 
| ShowAuxWindow | 在辅助窗口中显示编辑器窗口。 | 
| ShowNotification | 显示通知消息。 | 
| ShowPopup | 使用弹出式框架显示编辑器窗口。 | 
| ShowUtility | 将 EditorWindow 显示为浮动实用程序窗口。 | 
| GetInstanceID | 返回对象的实例 ID。 | 
| ToString | 返回对象的名称。 | 
静态函数
| FocusWindowIfItsOpen | 聚焦发现的第一个指定类型的 EditorWindow(如果已打开)。 | 
| GetWindow | 返回当前屏幕上第一个 t 类型的 EditorWindow。 | 
| GetWindowWithRect | 返回当前屏幕上第一个 t 类型的 EditorWindow。 | 
| Destroy | 移除 GameObject、组件或资源。 | 
| DestroyImmediate | 立即销毁对象 /obj/。强烈建议您改用 Destroy。 | 
| DontDestroyOnLoad | 在加载新的 Scene 时,请勿销毁 Object。 | 
| FindObjectOfType | 返回第一个类型为 type 的已加载的激活对象。 | 
| FindObjectsOfType | 返回所有类型为 type 的已加载的激活对象的列表。 | 
| Instantiate | 克隆 original 对象并返回克隆对象。 | 
| CreateInstance | 创建脚本化对象的实例。 | 
运算符
| bool | 该对象是否存在? | 
| operator != | 比较两个对象是否引用不同的对象。 | 
| operator == | 比较两个对象引用,判断它们是否引用同一个对象。 | 
消息
| Awake | 在新窗口打开时调用。 | 
| OnDestroy | 调用 OnDestroy 以关闭 EditorWindow 窗口。 | 
| OnFocus | 在窗口获得键盘焦点时调用。 | 
| OnGUI | 在此处实现您自己的 Editor GUI。 | 
| OnHierarchyChange | 处理程序,用于在层级视图中的对象或对象组发生更改时发送的消息。 | 
| OnInspectorUpdate | OnInspectorUpdate 以每秒 10 帧的速度调用,以便检视面板有机会进行更新。 | 
| OnLostFocus | 在窗口失去键盘焦点时调用。 | 
| OnProjectChange | 处理程序,用于在项目状态发生更改时发送的消息。 | 
| OnSelectionChange | 每当选择发生更改时调用。 | 
| Update | 在所有可见窗口上每秒调用多次。 | 
| Awake | 当 ScriptableObject 脚本启动时调用此函数。 | 
| OnDestroy | 当脚本化对象将销毁时调用此函数。 |