第四章自定义编辑器窗口_创建and打开and自定义窗口(3/11)

前言

书本里的这章节主要介绍了自定义窗口,涉及到EditorWindow,PopupWindow,GenericMenus和ScriptsWizard类。还介绍了扩展默认窗口

下面是对他们的一些比较介绍

类名 最佳用途 生命周期 交互复杂度
EditorWindow 长期使用的工具面板 持久化(手动关闭) 高(完整 UI)
PopupWindow 临时浮层控件 短暂(自动关闭) 中(轻量控件)
GenericMenu 动态右键/下拉菜单 瞬时(点击后消失) 低(纯文本)
ScriptableWizard 旧版向导流程(已不推荐) 中等(任务驱动) 中(分页表单)
Unity 版本 EditorWindow PopupWindow GenericMenu ScriptableWizard
2017.x ✅ 完整支持 ✅ 完整支持 ✅ 完整支持 ⚠️ 部分支持
2019.x ✅ 完整支持 ✅ 完整支持 ✅ 完整支持 ❌ 已弃用
2020.x+ ✅ 完整支持 ✅ 完整支持 ✅ 完整支持 ❌ 已移除

4.1 如何创建新的编辑器窗口

Unity 的使用是在不同的编辑器窗口中进行的,例如Scene、Game、Project、HierarchyInspector、Console 等,除了这些默认的编辑器窗口,开发者可以创建新的编辑器窗口并定义其中的内容,创建的新的编辑器窗口类需要继承EditorWindow。(这是原文)

4.1.1 打开新创建的编辑器窗口

打开编辑器窗口需要一个菜单路径,例如打开控制台窗口的菜单路径为WindGenera/Console,所以首先要为新创建的编辑器窗口使用Menultem 提供一个窗口入口。调用 EditorWindow 类中的静态方法 GetWindowO可以获取指定类型的窗口实例。

csharp 复制代码
using UnityEngine;
using UnityEditor;

public class MyEditorWindow : EditorWindow
{
    [MenuItem("Example/My Editor Window")]
    public static void ShowWindow()
    {
        // 创建或显示编辑器窗口
        MyEditorWindow window = GetWindow<MyEditorWindow>("窗口标题");
        window.minSize = new Vector2(300, 200); // 设置最小窗口大小
        window.maxSize = new Vector2(1920, 1080); // 设置最大窗口大小
        window.Show();
    }
}

4.1.2 定义编辑器窗口中的GUI内容

回调方法 触发时机 主要作用
OnEnable 窗口创建或重新聚焦时 初始化资源、注册事件、恢复状态
OnDisable 窗口失去焦点或被销毁时 保存配置、反注册事件、释放资源
OnDestroy 窗口关闭并从内存移除时 最终清理、持久化数据
OnGUI 每帧(Repaint 事件) 绘制界面、处理交互逻辑
Update 每帧(与游戏 Update 同步) 实时更新数据、刷新视图
OnSelectionChange 场景或 Project 视图选中对象变化时 同步显示当前选中内容
OnFocus / OnLostFocus 窗口获得 / 失去焦点时 启用/停用输入监听、更新标题栏
OnHierarchyChange 场景层级结构变化时 刷新节点列表、重新构建树形视图
OnProjectChange 资源导入 / 删除 / 移动时 重新加载资源列表、更新引用
csharp 复制代码
using UnityEditor;
using UnityEngine;

/// <summary>
/// 这个脚本演示了如何在编辑器窗口中使用OnGUI方法绘制UI控件  
/// </summary>
public class MyEditorWindow_CustomOnGUI : EditorWindow
{
    [MenuItem("Example/Show Editor Window CustomeOnGUI")]
    public static void ShowWindow()
    {
        // 创建或显示编辑器窗口
        MyEditorWindow_CustomOnGUI window = GetWindow<MyEditorWindow_CustomOnGUI>("使用OnGUI绘制UI控件");
        window.minSize = new Vector2(300, 300); // 设置最小窗口大小
        window.maxSize = new Vector2(1920, 1080); // 设置最大窗口大小
        window.Show();
        
    }
    private void OnGUI()
    {
        GUILayout.Button("按钮");
            
    }

    private void OnFocus()
    {
        Debug.Log("OnFocus");
    }
    private void OnLostFocus()
    {
        Debug.Log("OnLostFocus");
    }
    private void OnHierarchyChange()
    {
        Debug.Log("OnHierarchyChang");
    }
    private void OnInspectorUpdate()
    {
        Debug.Log("OnInspecotrUpdate");
    }
    private void OnProjectChange()
    {
        Debug.Log("OnProjectChang");
    }
    private void OnSelectionChange()
    {
        Debug.Log("OnSelectionChang");
    }
    private void OnValidate()
    {
        Debug.Log("OnValidate");
    }
    private void OnDisable()
    {
        Debug.Log("OnDisable");
    }
    private void OnDestroy()
    {
        Debug.Log("OnDestroy");
    }
}

4.1.3 如何创建弹出窗口

  1. PopupWindow
    PopupWindow 弹出窗口不可以被拖动,也无法调节大小,并且还会在失去焦点时自动关闭。打开弹出窗口需要调用 PopupWindow中的静态方法Show,代码如下:
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class MyEditorWindow_PopupWindow : EditorWindow
{
    [MenuItem("Example/MyEditorWindow_PopupWindow")]
    public static void Open()
    {
        MyEditorWindow_PopupWindow window = GetWindow<MyEditorWindow_PopupWindow>();
        window.titleContent = new GUIContent("窗口标题");
        window.minSize = new Vector2(300f, 300f);
        window.maxSize = new Vector2(1920f, 1080f);
        window.Show();
    }

    private Rect examplePopuWindowRect;
    private void OnGUI()
    {
        // 绘制一个按钮,点击后弹出窗口
        if (GUILayout.Button("打开弹出窗口"))
        {
            PopupWindow.Show(
                new Rect(100, 100, 200, 200),
                new ExamplePopupWindowContent(
                    new Vector2(position.width -6f,100f)));

        }

        if (Event.current.type == EventType.Repaint)
        {
            examplePopuWindowRect = GUILayoutUtility.GetLastRect();
        }

       
    }
}


public class ExamplePopupWindowContent : PopupWindowContent
{
    //窗口尺寸
    private Vector2 windowSize;
    //滚动值
    private Vector2 scroll;
    public ExamplePopupWindowContent(Vector2 windowSize)
    {
        this.windowSize = windowSize;
    }
    public override Vector2 GetWindowSize()
    {
        return new Vector2(200f, 200f);
    }
    public override void OnGUI(Rect rect)
    {
        // 在弹出窗口中绘制内容
        GUILayout.Label("这是一个弹出窗口");
        if (GUILayout.Button("关闭"))
        {
            editorWindow.Close();
        }
    }

    public override void OnOpen()
    {
        base.OnOpen();
        Debug.Log("弹出窗口已打开");
    }

    public override void OnClose()
    {
        base.OnClose();
        Debug.Log("弹出窗口已关闭");
    }   
}

效果

  1. GenericMenu
    使用 GencricMcnu 同样可以创建一个弹出窗口,与 PopupWindow 的区别在于,以在弹出窗口内开启水平或垂直布局,创建各类型的交互控件,还可以灵活地绘制编素、而前者弹出的窗口只是一个下拉菜单,它包含的公共方法。
方法签名 作用描述 参数说明 使用示例
AddItem(GUIContent content, bool on, GenericMenu.MenuFunction func) 添加可点击菜单项 content: 菜单项显示内容 on: 是否显示选中标记 func: 点击回调方法 menu.AddItem(new GUIContent("保存"), false, Save)
AddItem(GUIContent content, bool on, GenericMenu.MenuFunction2 func, object userData) 添加带参数的菜单项 userData: 传递给回调的自定义数据 menu.AddItem(new GUIContent("删除"), false, (data) => Delete(data), obj)
AddDisabledItem(GUIContent content, bool on) 添加禁用菜单项 content: 菜单项显示内容 on: 是否显示选中标记 menu.AddDisabledItem(new GUIContent("需要专业版"))
AddSeparator(string path) 添加菜单分隔符 path: 分隔符位置路径 menu.AddSeparator("编辑/")
ShowAsContext() 在鼠标位置显示菜单 无参数 menu.ShowAsContext()
DropDown(Rect rect) 在指定位置显示菜单 rect: 显示位置的矩形区域 menu.DropDown(buttonRect)
allowDuplicateNames 允许重复菜单项名 布尔值属性 menu.allowDuplicateNames = true
示例代码
csharp 复制代码
using UnityEditor;
using UnityEngine;

public class MyEditorWindow_PopupWindow_GerericMenu : EditorWindow
{
    [MenuItem("Example/MyEditorWindow_PopupWindow_GerericMenu")]
    public static void Open()
    {
        MyEditorWindow_PopupWindow_GerericMenu window = GetWindow<MyEditorWindow_PopupWindow_GerericMenu>();
        window.titleContent = new GUIContent("窗口标题");
        window.minSize = new Vector2(300f, 300f);
        window.maxSize = new Vector2(1920f, 1080f);
        window.Show();
    }

    private Rect examplePopuWindowRect;
    private void OnGUI()
    {
        // 绘制一个按钮,点击后弹出窗口
        if (GUILayout.Button("打开弹出窗口"))
        {
            PopupWindow.Show(
                new Rect(100, 100, 200, 200),
            new ExamplePopupWindowContent(
                    new Vector2(position.width - 6f, 100f)));

        }

        if (Event.current.type == EventType.Repaint)
        {
            examplePopuWindowRect = GUILayoutUtility.GetLastRect();
        }

        if (GUILayout.Button("Button"))
        {
            GenericMenu gm = new GenericMenu();
            //添加菜单项
            gm.AddItem(new GUIContent("菜单项1"), false, () => Debug.Log("菜单项1被点击"));
            //添加分隔符 参数传空字符串表示在一级菜单中添加分隔符
            gm.AddSeparator(string.Empty);
            //添加不可交互菜单项
            gm.AddDisabledItem(new GUIContent("Memu2"));
            //通过'/'可添加子菜单项
            gm.AddItem(new GUIContent("Menu3/SubMenu1"), false,
                () => Debug.Log("Select SubMenu1"));
            //在子菜单中添加分隔符
            gm.AddSeparator("Menu3/");
            gm.AddItem(new GUIContent("Menu3/SubMenu2"), false,
                () => Debug.Log("Select SubMenu2"));
            //显示菜单
            gm.ShowAsContext();
        }
    }
}


public class ExamplePopupWindowContent_1 : PopupWindowContent
{
    //窗口尺寸
    private Vector2 windowSize;
    //滚动值
    private Vector2 scroll;
    public ExamplePopupWindowContent_1(Vector2 windowSize)
    {
        this.windowSize = windowSize;
    }
    public override Vector2 GetWindowSize()
    {
        return new Vector2(200f, 200f);
    }
    public override void OnGUI(Rect rect)
    {
        // 在弹出窗口中绘制内容
        GUILayout.Label("这是一个弹出窗口");
        if (GUILayout.Button("关闭"))
        {
            editorWindow.Close();
        }
    }

    public override void OnOpen()
    {
        base.OnOpen();
        Debug.Log("弹出窗口已打开");
    }

    public override void OnClose()
    {
        base.OnClose();
        Debug.Log("弹出窗口已关闭");
    }
}

效果