一、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<ExampleEditorWindow>("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<ExampleEditorWindow>("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<ExampleEditorWindow>().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的扩展性给编程带来了全新的广阔空间和发展方向,可以用其优秀的自由度、灵活性和多样性不断构建自己的实用工具。