第三章自定义检视面板_创建自定义编辑器类_检测与应用修改(本章进度(2/9))
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);// 如果检测到变更,返回为trueif (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);}}}