Inspector 窗口可对 Unity 编辑器中几乎所有内容(包括游戏对象、Unity 组件、资源、材质)查看和编辑属性和设置,以及查看和编辑编辑器内的设置和首选项。

通常情况是不需要扩展属性面板的,也就是Inspector,但如果需要开发定制的工具和插件,那么就需要扩展属性面板来完成需求。本文会对自定义扩展Inspector进行全面的介绍
一、工具类和概念的介绍
自定义Inspector有两个层面,第一个是扩展已有属性的功能,第二个是按照意图增加新的面板功能。扩展已有的功能,包括替换显示文字,动态控制可见性,增加动画,错误提示,数据的校验和限制等等。增加新的功能就是按照需求定制更为复杂的功能。
Unity的内置工具类提供了必要的实现基础,主要有EditorGUILayout和EditorGUI两大类。两者有重叠的部分,也有特有的部分。总体上来说,EditorGUILayout是带有自动布局的,EditorGUI需要传入Rect来对控件进行定位。另外,这两者和GUILayout与GUI概念非常类似,只不过GUI的主要绘制是在场景中(运行时,游戏中可见),EditorGUI上在Inspector中(仅在编辑器中)。
自定义流程一共分为三步:
第一步,继承Editor父类。
第二步,添加[CustomEditor(typeof(MonoBehaviour))]注解,告诉编辑器这个类是扩展哪个组件的Inspector。
第三步,覆写OnInspectorGUI方法,实现自定义的扩展。
public class CustomEditorTest : MonoBehaviour
{
[Space(10)]
public int intValue;
public bool boolValue;
public Vector2 v2;
public float[] floatArray = new float[] {1.0f, 2.0f, 3.0f};
}
[CanEditMultipleObjects, CustomEditor(typeof(CustomEditorTest))]
public class CustomEditorTestEditor : Editor
{
public override void OnInspectorGUI()
{
// 自定义绘制Inspector
}
}二、一个简单的自定义Inspector面板
对MonoTest类编辑自定义面板
[Serializable]
public class MonoTest: MonoBehaviour
{
public enum EnumValue
{
EnumValue1,
EnumValue2,
EnumValue3,
}
public int intValue;
public bool boolValue;
public EnumValue enumValue;
}
//自定义面板代码
[CustomEditor(typeof(MonoTest), true)]
//[CustomEditor(typeof(CustomClass ), true)]
public class MonoTestEditor : Editor
{
private SerializedProperty m_IntValue;
private SerializedProperty m_BoolValue;
private SerializedProperty m_EnumValue;
private void OnEnable()
{
m_IntValue = serializedObject.FindProperty("intValue");
m_BoolValue = serializedObject.FindProperty("boolValue");
m_EnumValue = serializedObject.FindProperty("enumValue");
}
public override void OnInspectorGUI()
{
//base.OnInspectorGUI();
//serializedObject.Update();
EditorGUILayout.BeginHorizontal();
EditorGUIUtility.labelWidth = 100;
EditorGUILayout.PropertyField(m_IntValue);
EditorGUILayout.PropertyField(m_BoolValue);
EditorGUILayout.PropertyField(m_EnumValue);
EditorGUILayout.EndHorizontal();
//serializedObject.ApplyModifiedProperties();
}
}
对CustomClass类编辑自定义面板
public class CustomClass : MonoBehaviour
{
public List<MonoTest> Datas = new List<MonoTest>();
}
三、Inspector面板的Editor类
Editor类和EditorWindow类都继承自同一个基类:ScriptableObject,他们都可以针对某种脚本类来进行操作
Editor类只能对指定脚本进行扩展,Inspector面板的显示,或变量在Scene视图中的可视化,使用到的类:
GUILayout:
绘制各种2D控件,比如按钮、文本、可折叠列表,以及对它们的分组和排版。这些控件既可以绘制在Editor类里,也可以绘制在EditorWindow里
EditorGUILayout:
与GUILayout类似,但是提供更多的预定义控件
GUIUtility:
绘制自定义的2D控件,比如进度条、对话框、打开文件夹
EditorGUIUtility:
绘制自定义的2D控件,比如进度条、对话框、打开文件夹
四、编辑器开发之CustomEditor
CustomEditor特性,允许我们自定义组件的Inspect检视面板。
public CustomEditor (Type inspectedType);
public CustomEditor (Type inspectedType, bool editorForChildClasses);
param1: inspectedType 检视的类型,即自定义哪个类型的Inspector。
param2: editorForChildClasses 默认为false,为true时表明其子类使用同样的Inspector。
例如,我们创建一个Person组件:
using UnityEngine;
public class Person : MonoBehaviour
{
public string Name;
public int Age;
public float Weight;
}检视面板显示了Person组件中的三个公开字段:

接下来自定义该组件的检视面板,首先需要在Editor文件夹中创建一个PersonInspector.cs脚本
引入命名空间UnityEditor后,为该类添加CustomEditor特性,并继承Editor类:
using UnityEditor;
[CustomEditor(typeof(Person))]
public class PersonInspector : Editor {}接下来重写OnInspectorGUI方法来自定义我们所需要的内容
比如在面板上显示一个字符串:
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(Person))]
public class PersonInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUILayout.Label("Editor Extension...");
}
}
再比如在面板上添加一个按钮:
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(Person))]
public class PersonInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUILayout.Label("Editor Extension...");
GUILayout.Button("Button");
}
}
具体如何绘制自定义检视面板,例如添加Button、Toggle、Slider等元素,显示string、int、float、enum等字段,在后续文章中进行介绍。
五、重写OnInspectorGUI方法进行绘制
public override void OnInspectorGUI()
{
// 绘制全部原有属性
base.DrawDefaultInspector()
// 后面可以扩展自己功能
}
六、自定义绘制
有两个重要的内置对象,target和serializedObject。target代表的是CustomEditorTest本身,而serializedObject代表的是当前Inspector的可绘制对象。
public override void OnInspectorGUI()
{
// 更新显示
this.serializedObject.Update();
// 自定义绘制
// 应用属性修改
this.serializedObject.ApplyModifiedProperties();
}
七、绘制已经有的属性
有两个接口用来查找已有的属性,FindProperty和FindPropertyRelative。FindProperty用来查找当前属性名对应的属性对象,FindPropertyRelative查找相对于属性对象的属性。
// 显示intValue属性
EditorGUILayout.PropertyField(this.serializedObject.FindProperty("intValue"));
// 显示boolValue并替换属性标签为GUIContent
EditorGUILayout.PropertyField(this.serializedObject.FindProperty("boolValue"), this.boolValueContent);
var v2Property = this.serializedObject.FindProperty("v2");
// 现实v2属性
EditorGUILayout.PropertyField(v2Property);
// 设置v2属性的x属性值
v2Property.FindPropertyRelative("x").floatValue = 999.0f;
// 分隔符
EditorGUILayout.Separator();EditorGUILayout.PropertyField能够在Inspector上绘制属性控件,如果返回false表示属性控件不可用,或者处在不可见的状态。

八、绘制数组属性
// 查找floatArray属性
var elements = this.serializedObject.FindProperty("floatArray");
// 属性元素可见,控件展开状态
if (EditorGUILayout.PropertyField(elements))
{
// 缩进一级
EditorGUI.indentLevel++;
// 设置元素个数
elements.arraySize = EditorGUILayout.DelayedIntField("Size", elements.arraySize);
// 绘制元素
for (int i = 0, size = elements.arraySize; i < size; i++)
{
// 检索属性数组元素
var element = elements.GetArrayElementAtIndex(i);
EditorGUILayout.PropertyField(element);
}
// 重置缩进
EditorGUI.indentLevel--;
}
// 空格
EditorGUILayout.Space();对elements.arraySize赋值就会自动设置数组容量。DelayedIntField在回车的时