在上一篇学会了如何利用Attribute来对属性/字段进行拓展后,我们就已经能实现大部分简单功能了。利用Attribute的拓展使用了组件化的思想,相当有利于拓展的复用;但有时候我们需要对个别关键脚本进行高度定制的编辑器拓展,如果靠Attribute实现则可能产生许多注定只能被使用一次的特性,而且编辑起来非常繁琐。这个时候我们就需要学习如何对整个组件的GUI进行编辑。


我们首先创建一个叫做“ETest”的脚本,用作我们拓展的对象。

1
2
3
4
5
6
7
8
9
10
using UnityEngine;

public class ETest : MonoBehaviour
{
[SerializeField] private string str;
[SerializeField] private int i;
[SerializeField] private bool b;
[SerializeField] private double d;
[SerializeField] private float f;
}

在进行拓展前,其在Inspector的GUI是这样的:
6bc24f8ba2d249a6949145e0d9133ad2.png

要对ETest在Inspector整体的GUI进行修改,我们需要创建一个继承自Editor的类。按照习惯我们会将这个类命名为”CustomETest”。这个脚本可以且最好放进Editor文件夹。

1
2
3
4
5
6
7
8
9
10
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(ETest))]
public class CustomETest : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
}
}

按照上面的格式创建脚本后,Inspector中的ETest没有产生任何变化,这是因为我们沿用了父类的OnInspectorGUI方法。删去base.OnInspectorGUI()这一行,Etest在Inspector就会只保留一行类名。
我们在拓展时可以选择保留base.OnInspectorGUI,也可以自己重写这些序列化字段的输入框。
这里使用的两个常用方法:

  • EditorGUILayout.PropertyField:序列化一个属性
  • EditorGUILayout.ObjectField:序列化一个UnityEngine.Object的子类,显示一个可以拖入的框
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    using UnityEditor;
    using UnityEngine;
    [CustomEditor(typeof(ETest))]
    public class CustomETest : Editor
    {
    //我们不需要给字段指定类型,统一用SerializedProperty即可
    private SerializedProperty str;
    private SerializedProperty i;
    private SerializedProperty b;
    private SerializedProperty d;
    private SerializedProperty f;
    private void OnEnable()
    {
    //serilaizedObject是Editor类的变量
    str = serializedObject.FindProperty("str");
    i = serializedObject.FindProperty("i");
    b = serializedObject.FindProperty("b");
    d = serializedObject.FindProperty("d");
    f = serializedObject.FindProperty("f");
    }
    public override void OnInspectorGUI()
    {
    serializedObject.UpdateIfRequiredOrScript(); //让字段被修改时更新在GUI的显示,写在OnInspectorGUI的最前面

    EditorGUILayout.PropertyField(str, new GUIContent("字符串"));
    EditorGUILayout.PropertyField(i, new GUIContent("整型"));
    EditorGUILayout.PropertyField(b, new GUIContent("布尔"));
    EditorGUILayout.PropertyField(d, new GUIContent("双精度浮点"));
    EditorGUILayout.PropertyField(f, new GUIContent("单精度浮点"));

    serializedObject.ApplyModifiedProperties(); //让在GUI修改的内容应用到物体上,写在OnInspectorGUI的最后面
    }
    }
    QQ截图20230814175423.png
    如果想要把一个对象的所有属性序列化出来,而不是一个可拖入整个对象的框,可以用下面的语句:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    SerializedObject so = new SerializedObject(currentBuff);
    SerializedProperty sp = so.GetIterator(); //序列化属性的迭代器

    so.UpdateIfRequiredOrScript();
    while (sp.NextVisible(true))
    {
    EditorGUILayout.PropertyField(sp, true);
    }
    so.ApplyModifiedProperties();

学会如何自己重写序列化字段基本就可以自由发挥了。比如可以根据某些字段取值来自行调整的GUI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public override void OnInspectorGUI()
{
serializedObject.UpdateIfRequiredOrScript(); //让字段被修改时更新在GUI的显示

EditorGUILayout.PropertyField(str, new GUIContent("字符串"));
EditorGUILayout.PropertyField(i, new GUIContent("整型"));
EditorGUILayout.PropertyField(b, new GUIContent("布尔"));
if (b.boolValue)
{
//只有b取true是才会有以下两个输入框
EditorGUILayout.PropertyField(d, new GUIContent("双精度浮点"));
EditorGUILayout.PropertyField(f, new GUIContent("单精度浮点"));
}

if (GUI.changed)serializedObject.ApplyModifiedProperties(); //让在GUI修改的内容应用到物体上
}

把你在概述那节学到的各种GUI类的用法都招呼上去,这和针对字段/属性的拓展没有太大区别
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public override void OnInspectorGUI()
{
serializedObject.UpdateIfRequiredOrScript(); //让字段被修改时更新在GUI的显示

EditorGUILayout.LabelField("字符串", EditorStyles.label);
str.stringValue = EditorGUILayout.TextArea(str.stringValue,
GUILayout.Height(EditorGUIUtility.singleLineHeight * 4)); //为str创建4行的文本输入框

EditorGUILayout.IntSlider(i, 0, 100, new GUIContent("整型")); //用[0,100]的滑动条给i赋值
EditorGUILayout.PropertyField(b, new GUIContent("布尔"));

if (b.boolValue) EditorGUILayout.BeginFadeGroup(0.2f); //如果b==true,将下面的内容设为FadeGroup(只显示20%的区域)

//将下面两个字段的GUI设为水平布局
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(d, new GUIContent("双精度浮点"));
EditorGUILayout.PropertyField(f, new GUIContent("单精度浮点"));
EditorGUILayout.EndHorizontal();

if (b.boolValue) EditorGUILayout.EndFadeGroup();

if (GUI.changed) serializedObject.ApplyModifiedProperties(); //让在GUI修改的内容应用到物体上
}

效果展示:
QQ截图20230814183205.png