本文分享Unity中的CustomEditor(自定义编辑器)
Unity对继承了MonoBehavior脚本的类(当然还有其它类)提供了属性面板的展示和修改.
默认情况下会显示类的公开字段(public field), 当然这些字段也会被序列化并存储在挂载的预制上.
有些情况下我们需要自定义展示和修改逻辑, 这时我们需要用到CustomEditor
.
本文将对CustomEditor
做一些基本的介绍, 主要围绕类的可序列化字段展开.
CustomEditor的基本知识
下面我们先给出一个相对完整的代码, 然后一一解释其含义.
// Assets/xxxx/MonoTestEditor.cs
public class MonoTest : MonoBehaviour
{
public enum EnumValue
{
EnumValue1,
EnumValue2,
EnumValue3,
}
public int intValue;
public bool boolValue;
public EnumValue enumValue;
}
// Assets/xxxx/Editor/MonoTestEditor.cs
[CustomEditor(typeof(MonoTest), true)]
public class MonoTestEditor : Editor
{
private MonoTest m_Target;
private SerializedProperty m_IntValue;
private SerializedProperty m_BoolValue;
private SerializedProperty m_EnumValue;
private void OnEnable()
{
_target = target as MonoTest;
m_IntValue = serializedObject.FindProperty("intValue");
m_BoolValue = serializedObject.FindProperty("boolValue");
m_EnumValue = serializedObject.FindProperty("enumValue");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
//......//
serializedObject.ApplyModifiedProperties();
}
}
首先我们需要将自定义的Editor类放在任意的Editor目录下.
然后使用[CustomEditor(typeof(MonoTest), true)]
特性来描述要自定义的是哪个类, 第二个参数代表是否对其子类起效.
然后将类继承父类public class MonoTestEditor : Editor
.
CustomEditor
类有两个关键的属性target
和serializedObject
, 前者代表要自定义的类的实例化对象, 默认是Object
类型, 我们一般在OnEnable里将其转换为对应的类: _target = target as MonoTest;
, serializedObject
属性封装了对应类上所有的序列化字段和一些有用的方法, 每个字段对应一个序列化属性SerializedProperty
, 并在使用之前把两者做绑定: m_IntValue = serializedObject.FindProperty("intValue");
.
然后重写OnInspectorGUI
方法, 这个方法会在对象获得焦点或者对象属性变化或者其他一些情况下调用, base.OnInspectorGUI();
表示按照Editor默认的行为将所有的序列化属性绘制出来. 意思就是如果没有其他代码的情况下, 属性面板和默认的绘制一致. 如果去掉这句话, 属性面板上只会绘制脚本的名字.
然后对serializedObject
进行刷新和应用修改: serializedObject.Update();
, serializedObject.ApplyModifiedProperties();
.
最后我们在两句代码之间自定义自己的展示. 顺便说一下, serializedObject.ApplyModifiedProperties()
的返回值能够表明序列化字段是否有修改.
自定义Editor的核心逻辑
我们自定义Editor主要内容是: 定义序列化字段的外观, 同时在字段发生变化时做一些对应的操作.
所以主要打交道的是serializedObject
和serializedObject
, 当然还有各种构建外观的类, 比如GUI, GUILayout, EditorGUILayout
等. 本文只是对自定义Editor做基本的介绍, 不会涉及到GUI
相关类的详细介绍.
定义序列化字段的外观
在一些情况下, 序列化字段的外观太简单, 我们需要更丰富的展示. 这种情况下, 使用GUI
相关的接口可以达到目的. 下面给出一些例子.
public override void OnInspectorGUI()
{
// base.OnInspectorGUI(); // 将默认的绘制关闭
serializedObject.Update();
// 使用基本的绘制
// EditorGUILayout.PropertyField(m_IntValue, new GUIContent("这是一个整型值的提示"));
// EditorGUILayout.PropertyField(m_BoolValue);
// EditorGUILayout.PropertyField(m_EnumValue);
var content = new GUIContent {text = "整型值", tooltip = "这是一个整型值的提示"};
EditorGUILayout.IntSlider(m_IntValue, 0, 100, content); // 使用一个滑动条来代替基本的绘制, 可以规定上下界, 可以修改相关提示
var curValue = m_BoolValue.boolValue;
m_BoolValue.boolValue = EditorGUILayout.Toggle("布尔值", m_BoolValue.boolValue);
if (m_BoolValue.boolValue != curValue)
Debug.LogError("value changed!" + m_BoolValue.boolValue); // 在值变化后进行一些操作
//m_BoolValue.boolValue = EditorGUILayout.Toggle("布尔值", m_BoolValue.boolValue);
// if (serializedObject.ApplyModifiedProperties())
// Debug.LogError("value changed!" + m_BoolValue.boolValue); // 在值变化后进行一些操作
m_EnumValue.intValue = (int)(MonoTest.EnumValue)EditorGUILayout.EnumPopup("enumValue", (MonoTest.EnumValue)m_EnumValue.intValue); // 使用自定义的枚举值
serializedObject.ApplyModifiedProperties();
}
使用EditorGUILayout.PropertyField(xxxx)
将会按照默认行为绘制该字段, 也可以使用别的控件获取值后给该字段赋值:m_BoolValue.boolValue = EditorGUILayout.Toggle("布尔值", m_BoolValue.boolValue);
, 也可以直接给绑定的m_Target
对象赋值, 但是这种赋值在预制上进行时需要手动保存修改. 所以我们一般还是用SerializedProperty
作为中介来操作序列化对象.
字段发生变化时做一些操作
我们可以有一些手段来检测持久化字段的变化, 并在变化时做一些对应的操作.
第一种方式是在绘制之前存储一份当前值, 然后绘制赋值之后将两个值作对比, 如:
m_BoolValue.boolValue = EditorGUILayout.Toggle("布尔值", m_BoolValue.boolValue);
if (m_BoolValue.boolValue != curValue)
Debug.LogError("value changed!" + m_BoolValue.boolValue); // 在值变化后进行一些操作
第二种方式是对每个字段在绘制之后立马做一次应用serializedObject.ApplyModifiedProperties();
, 如果该值发生变化, 这个方法的函数的返回true
, 如:
m_BoolValue.boolValue = EditorGUILayout.Toggle("布尔值", m_BoolValue.boolValue);
if (serializedObject.ApplyModifiedProperties())
Debug.LogError("value changed!" + m_BoolValue.boolValue); // 在值变化后进行一些操作
禁止某些字段的修改
有些时候我们需要禁止对某些字段进行修改, 这时只需要在绘制字段前后对GUI开关即可, 比如字段只有在非运行模式下可以修改:
if (Application.isPlaying)
{
GUI.enabled = false;
m_BoolValue.boolValue = EditorGUILayout.Toggle("布尔值", m_BoolValue.boolValue);
GUI.enabled = true;
}
else
{
m_BoolValue.boolValue = EditorGUILayout.Toggle("布尔值", m_BoolValue.boolValue);
}
这样, 绘制出来的字段就会在运行模式下显示为灰色, 表示不可编辑.
总结
本文对CustomEditor
做了简单的介绍, 并提到了一些可能出现的问题和小技巧, 希望对大家有所帮助.