前言
书本里的这章节主要介绍了自定义窗口,涉及到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 如何创建弹出窗口
- 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("弹出窗口已关闭");
}
}
效果
- 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("弹出窗口已关闭");
}
}
效果