Unity编辑器拓展(二|组件绘制)
在上一篇学会了如何利用Attribute来对属性/字段进行拓展后,我们就已经能实现大部分简单功能了。利用Attribute的拓展使用了组件化的思想,相当有利于拓展的复用;但有时候我们需要对个别关键脚本进行高度定制的编辑器拓展,如果靠Attribute实现则可能产生许多注定只能被使用一次的特性,而且编辑起来非常繁琐。这个时候我们就需要学习如何对整个组件的GUI进行编辑。
我们首先创建一个叫做“ETest”的脚本,用作我们拓展的对象。1
2
3
4
5
6
7
8
9
10using UnityEngine;
public class ETest : MonoBehaviour
{
[private string str; ]
[private int i; ]
[private bool b; ]
[private double d; ]
[private float f; ]
}
在进行拓展前,其在Inspector的GUI是这样的:
要对ETest在Inspector整体的GUI进行修改,我们需要创建一个继承自Editor的类。按照习惯我们会将这个类命名为”CustomETest”。这个脚本可以且最好放进Editor文件夹。1
2
3
4
5
6
7
8
9
10using UnityEditor;
using UnityEngine;
[ ]
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
33using UnityEditor;
using UnityEngine;
[ ]
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的最后面
}
}
如果想要把一个对象的所有属性序列化出来,而不是一个可拖入整个对象的框,可以用下面的语句:1
2
3
4
5
6
7
8
9SerializedObject so = new SerializedObject(currentBuff);
SerializedProperty sp = so.GetIterator(); //序列化属性的迭代器
so.UpdateIfRequiredOrScript();
while (sp.NextVisible(true))
{
EditorGUILayout.PropertyField(sp, true);
}
so.ApplyModifiedProperties();
学会如何自己重写序列化字段基本就可以自由发挥了。比如可以根据某些字段取值来自行调整的GUI1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public 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
23public 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修改的内容应用到物体上
}
效果展示: