Unity编辑器界面扩展——1、Unity自定义编辑器窗体

  大家好,我是阿赵。这次来分享一下在Unity引擎里面怎样对编辑器的界面进行扩展。
  第一篇先分享一下Unity编辑器怎样自定义窗体。

1、简单的创建一个窗体

在这里插入图片描述

using UnityEditor;
using UnityEngine;
public class TestEditorWindow : EditorWindow
{
    
    
    [MenuItem("Tools/TestWindow")]
    private static void ShowTestWindow()
    {
    
    
        GetWindow<TestEditorWindow>("测试窗体");
    }

    void OnGUI()
    {
    
    
        GUILayout.Label("这是一个测试界面");
    }
}

在这里插入图片描述

在这里插入图片描述

加粗样式注意事项

  1. 编辑器类脚本一定要放到Editor文件夹里面
      这个Editor文件夹不一定在Assets/Editor,只需要父级文件夹是Editor就行,可以放在别的文件里面。
  2. using UnityEditor;
      想使用编辑器的API,必须要引用UnityEditor。
  3. 继承EditorWindow
    由于是想显示一个窗体,所以要继承EditorWindow,这样这个类就会有一个窗体的载体和生命周期。
  4. MenuItem
      这是Unity编辑器添加菜单选项的方法。里面的参数是路径,比如刚才的例子
[MenuItem("Tools/TestWindow")]

  所以菜单会出现在Unity编辑器的顶部菜单栏,先出现一个Tools的选项,点击进去,会看到二级菜单”TestWindow”。
5. GetWindow
  完整的方法名称是EditorWindow.GetWindow(),由于我们这里已经继承了EditorWindow,所以直接用GetWindow也能调用。传入需要获得的窗体的类型,就可以得到一个对应类型的窗体。
  如果窗体已经显示了出来,返回的就是当前显示的窗体对象。如果这个窗体没有被创建过,就会创建一个新的对应类型的窗体,并且返回这个窗体的实例对象。
6. OnGUI
  需要显示的窗体内容,将会写在OnGUI方法里面。

2、 控制窗体标题和大小

  之前通过GetWindow方法,传入了一个标题,其实也可以不传参数,而在后面修改标题,比如这样:

TestEditorWindow win = GetWindow<TestEditorWindow>();
win.titleContent = new GUIContent("测试窗体");

  通过titleContent可以指定窗体的标题。
  默认的窗体,是没有指定大小的,可以随意的拖动缩放大小。
  如果想指定窗体的大小范围,可以:

win.maxSize = new Vector2(600, 600);
win.minSize = new Vector2(400, 400);

  这样窗体就可以最大放大到600x600,最小缩小到400x400
  如果想指定窗体的大小不想让用户缩放,可以这样:

win.maxSize = win.minSize = new Vector2(600, 600);

这时候窗体被固定在600x600,不能缩放。

3、 窗体方法

  EditorWindow一些方法可以使用

1. 打开和关闭窗体

  获得窗体之后,可以通过Show方法显示,通过Close方法关闭。
  如果是通过单例的形式自己存储了窗体的实例,就可以:

win.Show();
win.Close();

  来控制窗体的打开和关闭。或者在界面里面添加关闭按钮,主动调用Close来关闭。

2. 获得焦点

  比如同时打开了2个窗体,然后2个窗体的对象存储在一个控制类里面。这时候想某个窗体强制获得焦点,就可以:

win1.Focus();

3. 强制重绘

  在某些情况下,窗体可以不会主动的重绘,比如失去了焦点之类的情况。这时候可以通过Repaint方法主动的强制重绘窗体。

void OnInspectorUpdate()
{
    
    
    Repaint();
}

4. 显示提示

  ShowNotification方法可以显示一个比较好看的渐变提示,它会在几秒钟后消失。
比如这样:

ShowNotification(new GUIContent("这是一个提示"));

在这里插入图片描述

如果想控制提示的持续时间,可以:

ShowNotification(new GUIContent("这是一个提示"),10);

这里传入了参数10,这个提示会维持10秒钟再消失。
如果想提示不要等10秒,而是立刻消失,可以:

RemoveNotification();

这样提示会立刻消失。

5. 发送和接收事件

  通过SendEvent可以向指定的窗体发送事件,比如:

win.SendEvent(EditorGUIUtility.CommandEvent("Paste"));

  然后在接收的窗体里面接收:

void OnGUI()
    {
    
    
        Event e = Event.current;

        if (e.commandName == "Paste")
            Debug.Log("Paste received");
    }

6. 窗体里面嵌入窗体

  通过BeginWindows和EndWindows,可以往一个窗体里面嵌入另外一个窗体
比如:

public Rect windowRect = new Rect(100, 100, 200, 200);
    void OnGUI()
    {
    
    
        BeginWindows();
        windowRect = GUILayout.Window(1, windowRect, DoWindow, "Hi There");
        EndWindows();
    }
void DoWindow(int unusedWindowID)
    {
    
    
        GUILayout.Button("Hi");
        GUI.DragWindow();
    }

在这里插入图片描述

7. 其他

  还有其他一些方法,就不一一列举,可以查看Unity的API文档

4、 生命周期

  作为EditorWindow,它有一系列的生命周期,具体可看以下代码:

using UnityEditor;
using UnityEngine;
public class TestEditorWindow : EditorWindow
{
    
    
    [MenuItem("Tools/TestWindow")]
    private static void ShowTestWindow()
    {
    
    
        GetWindow<TestEditorWindow>("测试窗体");
    }

    void OnGUI()
    {
    
    
        GUILayout.Label("这是一个测试界面");
    }
    /// <summary>
    /// 当对象被创建时调用
    /// </summary>
    private void Awake()
    {
    
    
        Debug.Log("Awake");
    }
    /// <summary>
    /// 当窗体被显示时调用
    /// </summary>
    private void OnEnable()
    {
    
    
        Debug.Log("OnEnable");
    }
    /// <summary>
    /// 当窗体被隐藏时调用
    /// </summary>
    private void OnDisable()
    {
    
    
        Debug.Log("OnDisable");
    }
    /// <summary>
    /// 当窗体被销毁时调用
    /// </summary>
    private void OnDestroy()
    {
    
    
        Debug.Log("OnDestroy");
    }
    /// <summary>
    /// 当窗体获得焦点时调用
    /// </summary>
    private void OnFocus()
    {
    
    
        Debug.Log("OnFocus");
    }
    /// <summary>
    /// 当窗体失去焦点时调用
    /// </summary>
    private void OnLostFocus()
    {
    
    
        Debug.Log("OnLostFocus");
    }
    /// <summary>
    /// 当Hierarchy栏有改变时调用
    /// </summary>
    private void OnHierarchyChange()
    {
    
    
        Debug.Log("OnHierarchyChange");
    }
    /// <summary>
    /// 当项目有改变时调用
    /// </summary>
    private void OnProjectChange()
    {
    
    
        Debug.Log("OnProjectChange");
    }
    /// <summary>
    /// 当选择的对象改变时调用
    /// </summary>
    private void OnSelectionChange()
    {
    
    
        Debug.Log("OnSelectionChange");
    }
    /// <summary>
    /// 当Inspector栏有显示时每帧调用
    /// </summary>
    private void OnInspectorUpdate()
    {
    
    
        Debug.Log("OnInspectorUpdate");
    }
    /// <summary>
    /// 当前窗体在显示的过程中每帧调用
    /// </summary>
    private void Update()
    {
    
    
        Debug.Log("Update");
    }
}

  上面的生命周期方法都有备注,应该都很好理解。可以试试运行,打开和关闭界面,进行一些对应的操作,感受一下这些生命周期的调用规律。
  需要说明一下的是,OnInspectorUpdate和Update。如果电脑的焦点在Unity编辑器里面时,不管焦点是否在当前窗体,这两个Update方法都是会被调用的。如果电脑的焦点已经不在Unity编辑器里面,OnInspectorUpdate方法不会被调用,但Update方法还是会被调用。

5、 可以使用的UI显示组件

  先说结论,Unity的GUI、GUILayout、EditorGUI和EditorGUILayout里面的所有方法,都可以写在EditorWindow的OnGUI里面,方法有很多,可以自己查询一下Unity的API文档:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  其中GUI和GUILayout是常规的UnityEditor界面方法,不仅能用于编辑器类,也用于运行时。不过现在基本上运行时大家都会用UGUI之类可视化的UI编辑了,所以GUI和GUILayout会比较多用于编辑器界面扩展。两者的区别是,GUI是需要自己写UI出现的Rect坐标,而GUILayout是可以使用自动布局的。
  而EditorGUI和EditorGUILayout是只能用于编辑器类里面的,EditorGUI需要指定Rect坐标,而EditorGUILayout可以自动布局。
  GUI系列和EditorGUI系列,有很多重复的方法,不过EditorGUI系列会有更多的功能,这些功能只能在编辑器的界面上显示,比如一些对象的输入、曲线调节、颜色输入、下拉列表之类。