第三章自定义检视面板_创建自定义编辑器类_检测与应用修改(本章进度(2/9))

发布于:2025-07-19 ⋅ 阅读:(15) ⋅ 点赞:(0)

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);
        }
    }


}

网站公告

今日签到

点亮在社区的每一天
去签到