3.1创建自定义编辑器类
3.1.2检测与应用修改
上一节通过在Inspecotor修改字段时,会直接赋值给字段,但是其他字段没有修改也会被重新赋值一下,对性能有一定影响,所以我们可以通过检测Inspecotor变更时候,才对字段修改。
代码示例
using System.Reflection; // 引入反射命名空间,用于访问私有字段
using UnityEditor;
using UnityEngine;
/// <summary>
/// 使用CustomEditor特性,指定这个编辑器类是为CustomComponent组件服务的
/// </summary>
[CustomEditor(typeof(CustomComponent))]
public class CustomCompoentEditor : Editor
{
private CustomComponent component; // 当前正在编辑的CustomComponent组件实例
// 序列化属性,用于访问和修改组件中的序列化字段
private SerializedProperty stringValueProperty; // 字符串类型字段
private SerializedProperty gameObjectProperty; // GameObject类型字段
private SerializedProperty enumValue; // 枚举类型字段
// 反射字段信息,用于访问私有字段
private FieldInfo boolValueFieldInfo; // 布尔类型私有字段
// 当编辑器启用时调用,用于初始化
private void OnEnable()
{
/*
这里只是为了演示不同的获取方式 一种当字段是public时可以直接访问,或者被[serilaredField]
*/
//工作原理就是从target中获取到当前正在编辑的组件实例,然后去查找指定字段
// 获取当前正在编辑的组件实例
component = (CustomComponent)target;
// 通过序列化对象查找并获取各个序列化属性
stringValueProperty = serializedObject.FindProperty("stringValue"); // 查找字符串字段,这里不是反射,是unituy序列化系统
gameObjectProperty = serializedObject.FindProperty("go"); // 查找GameObject字段
enumValue = serializedObject.FindProperty("exampleEnum"); // 查找枚举字段
// 初始化反射字段
boolValueFieldInfo = component.GetType().GetField(
"boolValue",
BindingFlags.NonPublic | BindingFlags.Instance
);
}
/// <summary>
/// 重写OnInspectorGUI方法,自定义Inspector界面
/// </summary>
public override void OnInspectorGUI()
{
// base.OnInspectorGUI(); // 如果取消注释,会显示默认的Inspector界面
ChangeCheckExample();
}
/// <summary>
/// 变化检测示例
/// </summary>
private void ChangeCheckExample()
{
// 1. 使用 EditorGUI.BeginChangeCheck() 检测整数字段变化
EditorGUI.BeginChangeCheck();
component.intValue = EditorGUILayout.IntField("整数", component.intValue);
// 如果检测到变更,返回为true
if (EditorGUI.EndChangeCheck())
{
Debug.Log("整数发生变更");
}
// 2. 处理字符串字段(使用序列化属性)
// private修饰的字段 通过序列化属性的方式访问和修改其值
string newStringValue = EditorGUILayout.TextField("字符串", stringValueProperty.stringValue);
// 对比是否不一致,不一致则更新
if (newStringValue != stringValueProperty.stringValue)
{
stringValueProperty.stringValue = newStringValue;
Debug.Log("StringValue 发生变化");
}
// 3. 处理布尔字段(使用反射)
// private修饰的字段 通过反射的方式访问和修改其值
boolValueFieldInfo.SetValue(
component,
EditorGUILayout.Toggle(
"Bool Value",
(bool)boolValueFieldInfo.GetValue(component)
)
);
// 4. 绘制GameObject字段(使用序列化属性)
EditorGUILayout.PropertyField(gameObjectProperty);
// 5. 处理枚举字段(使用序列化属性)
enumValue.enumValueIndex = EditorGUILayout.Popup(
"枚举值",
enumValue.enumValueIndex,
enumValue.enumNames
);
// 6. 检测整体GUI变化
if (GUI.changed)
{
Debug.Log("GUI发生了变更");
}
}
}
3.完成了字段获取和修改,我们接下来就是对字段的保存。但是下面的代码实际上不会立刻保存,他只是标记保存,当我们保存场景或者项目的时候,才会保存。具体逻辑和代码如下图
示例代码
using System.Reflection; // 引入反射命名空间,用于访问私有字段
using UnityEditor;
using UnityEngine;
/// <summary>
/// 使用CustomEditor特性,指定这个编辑器类是为CustomComponent组件服务的
/// </summary>
[CustomEditor(typeof(CustomComponent))]
public class CustomCompoentEditor : Editor
{
private CustomComponent component; // 当前正在编辑的CustomComponent组件实例
// 序列化属性,用于访问和修改组件中的序列化字段
private SerializedProperty stringValueProperty; // 字符串类型字段
private SerializedProperty gameObjectProperty; // GameObject类型字段
private SerializedProperty enumValue; // 枚举类型字段
// 反射字段信息,用于访问私有字段
private FieldInfo boolValueFieldInfo; // 布尔类型私有字段
// 当编辑器启用时调用,用于初始化
private void OnEnable()
{
/*
这里只是为了演示不同的获取方式 一种当字段是public时可以直接访问,或者被[serilaredField]
*/
//工作原理就是从target中获取到当前正在编辑的组件实例,然后去查找指定字段
// 获取当前正在编辑的组件实例
component = (CustomComponent)target;
// 通过序列化对象查找并获取各个序列化属性
stringValueProperty = serializedObject.FindProperty("stringValue"); // 查找字符串字段,这里不是反射,是unituy序列化系统
gameObjectProperty = serializedObject.FindProperty("go"); // 查找GameObject字段
enumValue = serializedObject.FindProperty("exampleEnum"); // 查找枚举字段
// 初始化反射字段
boolValueFieldInfo = component.GetType().GetField(
"boolValue",
BindingFlags.NonPublic | BindingFlags.Instance
);
}
/// <summary>
/// 重写OnInspectorGUI方法,自定义Inspector界面
/// </summary>
public override void OnInspectorGUI()
{
// base.OnInspectorGUI(); // 如果取消注释,会显示默认的Inspector界面
ApplyModificationExample();
}
private void ApplyModificationExample()
{
// 公共字段(public)可以直接访问和修改
// 创建一个整数输入框,显示并允许修改组件的 intValue 字段
component.intValue = EditorGUILayout.IntField("Int Value", component.intValue);
// 私有字段(private)通过序列化属性(SerializedProperty)访问和修改
// 创建一个文本输入框,显示并允许修改组件的 stringValue 字段
stringValueProperty.stringValue = EditorGUILayout.TextField(
"String Value", stringValueProperty.stringValue);
// 私有字段(private)通过反射(FieldInfo)访问和修改
// 创建一个布尔开关(Toggle),显示并允许修改组件的 boolValue 字段
boolValueFieldInfo.SetValue(component, EditorGUILayout.Toggle(
"Bool Value", (bool)boolValueFieldInfo.GetValue(component)));
// 使用 PropertyField 绘制 GameObject 类型的字段
// 这种方式更通用,能自动处理多种类型的字段显示
EditorGUILayout.PropertyField(gameObjectProperty);
// 使用 Popup 下拉框显示并允许修改枚举类型的字段
enumValue.enumValueIndex = EditorGUILayout.Popup(
"Enum Value", enumValue.enumValueIndex, enumValue.enumNames);
// 检测是否有任何控件发生了变更
if (GUI.changed)
{
// 应用所有通过 SerializedProperty 修改的字段到组件实例中
// 注意:只保存通过 serializedObject.FindProperty 获得的字段
// 不保存通过反射(FieldInfo)获得的字段
serializedObject.ApplyModifiedProperties();
// 手动标记组件为“脏”(Dirty)
// 因为通过反射(FieldInfo)修改的字段不会被 Unity 自动标记为脏,
//我们为了演示反射,我们自定义类CustomComponent 里的go 是通过反射拿到的,所以这里要手动标记组件为脏
// 所谓“脏”就是告诉 Unity:“这个组件的数据被修改了,保存时需要包含它”
EditorUtility.SetDirty(component);
}
}
}