一、什么是 SerializedObject 与 SerializedProperty
1.1 基本概念
在编辑器扩展开发里,SerializedObject 和 SerializedProperty 是非常实用的工具。SerializedObject 就像是一个容器,它可以把一个对象序列化,让我们方便地在编辑器里对这个对象的属性进行操作。而 SerializedProperty 呢,它代表的是 SerializedObject 里的一个具体属性,通过它我们能直接访问和修改这个属性的值。
1.2 简单示例
下面我们用 Unity 开发来举个例子,Unity 是一款很流行的游戏开发引擎,在它的编辑器扩展中 these tools 非常常用。
using UnityEditor;
using UnityEngine;
// 定义一个简单的类
public class MyClass : MonoBehaviour
{
public int myInt = 10; // 定义一个整型属性
public string myString = "Hello"; // 定义一个字符串属性
}
// 自定义编辑器脚本
[CustomEditor(typeof(MyClass))]
public class MyClassEditor : Editor
{
private SerializedObject serializedObj;
private SerializedProperty myIntProp;
private SerializedProperty myStringProp;
private void OnEnable()
{
// 获取当前编辑对象的 SerializedObject
serializedObj = new SerializedObject(target);
// 获取 myInt 属性对应的 SerializedProperty
myIntProp = serializedObj.FindProperty("myInt");
// 获取 myString 属性对应的 SerializedProperty
myStringProp = serializedObj.FindProperty("myString");
}
public override void OnInspectorGUI()
{
// 更新 SerializedObject 中的数据
serializedObj.Update();
// 绘制 myInt 属性的编辑字段
EditorGUILayout.PropertyField(myIntProp);
// 绘制 myString 属性的编辑字段
EditorGUILayout.PropertyField(myStringProp);
// 将修改保存回原对象
serializedObj.ApplyModifiedProperties();
}
}
在这个例子中,我们创建了一个 MyClass 类,包含两个公共属性 myInt 和 myString。然后创建了一个自定义编辑器脚本 MyClassEditor,在 OnEnable 方法里,我们通过 target 获取了当前编辑对象的 SerializedObject,再通过 FindProperty 方法找到了 myInt 和 myString 对应的 SerializedProperty。在 OnInspectorGUI 方法中,我们先调用 serializedObj.Update() 更新数据,然后使用 EditorGUILayout.PropertyField 绘制属性编辑字段,最后调用 serializedObj.ApplyModifiedProperties() 将修改保存回原对象。
二、SerializedObject 的应用场景
2.1 自定义 Inspector 面板
在 Unity 中,默认的 Inspector 面板展示形式可能不能满足我们的需求,这时候就可以使用 SerializedObject 来自定义 Inspector 面板。比如我们有一个复杂的类,包含很多属性,我们可以按照自己的逻辑来分组展示这些属性。
using UnityEditor;
using UnityEngine;
// 定义一个复杂的类
public class ComplexClass : MonoBehaviour
{
public int value1;
public float value2;
public string value3;
public bool value4;
}
// 自定义编辑器脚本
[CustomEditor(typeof(ComplexClass))]
public class ComplexClassEditor : Editor
{
private SerializedObject serializedObj;
private SerializedProperty value1Prop;
private SerializedProperty value2Prop;
private SerializedProperty value3Prop;
private SerializedProperty value4Prop;
private void OnEnable()
{
serializedObj = new SerializedObject(target);
value1Prop = serializedObj.FindProperty("value1");
value2Prop = serializedObj.FindProperty("value2");
value3Prop = serializedObj.FindProperty("value3");
value4Prop = serializedObj.FindProperty("value4");
}
public override void OnInspectorGUI()
{
serializedObj.Update();
// 分组展示属性
EditorGUILayout.LabelField("数值属性", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(value1Prop);
EditorGUILayout.PropertyField(value2Prop);
EditorGUILayout.LabelField("文本和布尔属性", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(value3Prop);
EditorGUILayout.PropertyField(value4Prop);
serializedObj.ApplyModifiedProperties();
}
}
在这个例子中,我们把属性分成了两类,分别展示,这样就能让 Inspector 面板更加清晰。
2.2 数据验证和处理
在编辑器开发中,我们可能需要对输入的数据进行验证。使用 SerializedObject 可以方便地获取和修改属性值,并进行验证。
using UnityEditor;
using UnityEngine;
public class DataValidationClass : MonoBehaviour
{
public int positiveInt;
}
[CustomEditor(typeof(DataValidationClass))]
public class DataValidationClassEditor : Editor
{
private SerializedObject serializedObj;
private SerializedProperty positiveIntProp;
private void OnEnable()
{
serializedObj = new SerializedObject(target);
positiveIntProp = serializedObj.FindProperty("positiveInt");
}
public override void OnInspectorGUI()
{
serializedObj.Update();
EditorGUILayout.PropertyField(positiveIntProp);
// 数据验证
if (positiveIntProp.intValue < 0)
{
EditorGUILayout.HelpBox("输入的数值必须为正数!", MessageType.Error);
positiveIntProp.intValue = 0;
}
serializedObj.ApplyModifiedProperties();
}
}
这里我们对 positiveInt 属性进行验证,如果输入的值小于 0,就显示错误提示,并将其值设为 0。
三、SerializedProperty 的应用场景
3.1 遍历数组和列表
当我们需要处理数组或列表类型的属性时,SerializedProperty 能帮助我们方便地遍历其中的元素。
using UnityEditor;
using UnityEngine;
public class ArrayClass : MonoBehaviour
{
public int[] myArray;
}
[CustomEditor(typeof(ArrayClass))]
public class ArrayClassEditor : Editor
{
private SerializedObject serializedObj;
private SerializedProperty arrayProp;
private void OnEnable()
{
serializedObj = new SerializedObject(target);
arrayProp = serializedObj.FindProperty("myArray");
}
public override void OnInspectorGUI()
{
serializedObj.Update();
EditorGUILayout.PropertyField(arrayProp);
// 遍历数组元素
if (arrayProp.isArray)
{
for (int i = 0; i < arrayProp.arraySize; i++)
{
SerializedProperty element = arrayProp.GetArrayElementAtIndex(i);
EditorGUILayout.IntField($"元素 {i}", element.intValue);
}
}
serializedObj.ApplyModifiedProperties();
}
}
在这个例子中,我们通过 isArray 判断属性是否为数组,然后使用 GetArrayElementAtIndex 方法获取数组中的每个元素,并进行展示。
3.2 嵌套属性操作
当属性是嵌套结构时,SerializedProperty 可以让我们方便地访问和修改嵌套属性的值。
using UnityEditor;
using UnityEngine;
// 嵌套类
public class InnerClass
{
public int nestedValue;
}
public class NestedClass : MonoBehaviour
{
public InnerClass inner;
}
[CustomEditor(typeof(NestedClass))]
public class NestedClassEditor : Editor
{
private SerializedObject serializedObj;
private SerializedProperty innerProp;
private SerializedProperty nestedValueProp;
private void OnEnable()
{
serializedObj = new SerializedObject(target);
innerProp = serializedObj.FindProperty("inner");
nestedValueProp = innerProp.FindPropertyRelative("nestedValue");
}
public override void OnInspectorGUI()
{
serializedObj.Update();
EditorGUILayout.PropertyField(innerProp);
EditorGUILayout.PropertyField(nestedValueProp);
serializedObj.ApplyModifiedProperties();
}
}
在这个例子中,我们通过 FindPropertyRelative 方法找到了嵌套属性 nestedValue,并展示给用户编辑。
四、技术优缺点
4.1 优点
- 方便数据操作:SerializedObject 和 SerializedProperty 提供了统一的接口来访问和修改对象的属性,不需要手动去获取和设置属性,减少了代码的复杂性。就像在上面的例子中,我们可以轻松地获取和修改属性值,而不需要直接操作对象的字段。
- 自动处理序列化:它们会自动处理对象的序列化和反序列化过程,这意味着我们不用担心数据的保存和加载问题。比如在 Unity 编辑器中,当我们修改属性后,使用
ApplyModifiedProperties方法就能将修改保存,下次打开时数据仍然存在。 - 支持嵌套属性:对于复杂的对象结构,它们可以方便地访问和修改嵌套属性,如上面的嵌套类示例,使用
FindPropertyRelative方法就能轻松访问嵌套属性。
4.2 缺点
- 性能开销:由于涉及到序列化和反序列化操作,会有一定的性能开销。特别是在处理大量属性或者频繁调用
Update和ApplyModifiedProperties方法时,性能可能会受到影响。 - 学习成本:对于初学者来说,理解 SerializedObject 和 SerializedProperty 的工作原理和使用方法可能需要花费一些时间。比如,要正确使用
FindProperty和FindPropertyRelative方法,需要清楚属性的名称和嵌套结构。
五、注意事项
5.1 及时更新和应用修改
在使用 SerializedObject 和 SerializedProperty 时,一定要记得在开始修改属性之前调用 serializedObj.Update() 方法来更新数据,修改完成后调用 serializedObj.ApplyModifiedProperties() 方法将修改保存回原对象。如果不这样做,修改可能不会生效。
5.2 正确处理数组和列表
在处理数组和列表属性时,要注意使用正确的方法,如 isArray 判断属性是否为数组,GetArrayElementAtIndex 获取数组元素,arraySize 获取数组的大小等。同时,对数组大小的修改需要使用 serializedObj.ApplyModifiedProperties() 方法使修改生效。
5.3 避免频繁操作
由于存在性能开销,尽量避免在每一帧都调用 Update 和 ApplyModifiedProperties 方法。可以在需要更新时再调用,比如用户进行了实际的修改操作。
六、文章总结
在编辑器扩展开发中,SerializedObject 和 SerializedProperty 是非常强大的工具。SerializedObject 就像一个数据容器,它能帮助我们方便地管理对象的属性,而 SerializedProperty 则让我们可以直接操作这些属性。它们在自定义 Inspector 面板、数据验证、处理数组和嵌套属性等方面都有很好的应用。不过,我们也要注意它们的性能开销和学习成本,在使用时遵循一些注意事项,如及时更新和应用修改、正确处理数组和列表、避免频繁操作等。通过合理运用 SerializedObject 和 SerializedProperty,我们可以开发出更加高效、灵活的编辑器扩展。
评论
围绕“编辑器扩展开发中,SerializedObject与SerializedProperty你真的理解了吗?”参与讨论