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

3.1创建自定义编辑器类

3.1.2检测与应用修改

上一节通过在Inspecotor修改字段时,会直接赋值给字段,但是其他字段没有修改也会被重新赋值一下,对性能有一定影响,所以我们可以通过检测Inspecotor变更时候,才对字段修改。

代码示例

csharp 复制代码
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.完成了字段获取和修改,我们接下来就是对字段的保存。但是下面的代码实际上不会立刻保存,他只是标记保存,当我们保存场景或者项目的时候,才会保存。具体逻辑和代码如下图

示例代码

csharp 复制代码
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);
        }
    }


}
相关推荐
看到我,请让我去学习31 分钟前
Qt— 布局综合项目(Splitter,Stacked,Dock)
开发语言·qt
GUET_一路向前44 分钟前
【C语言防御性编程】if条件常量在前,变量在后
c语言·开发语言·if-else·防御性编程
曳渔1 小时前
UDP/TCP套接字编程简单实战指南
java·开发语言·网络·网络协议·tcp/ip·udp
三千道应用题1 小时前
WPF&C#超市管理系统(6)订单详情、顾客注册、商品销售排行查询和库存提示、LiveChat报表
开发语言·c#·wpf
hqxstudying1 小时前
JAVA项目中邮件发送功能
java·开发语言·python·邮件
咪咪渝粮1 小时前
JavaScript 中constructor 属性的指向异常问题
开发语言·javascript
最初的↘那颗心1 小时前
Java HashMap深度解析:原理、实现与最佳实践
java·开发语言·面试·hashmap·八股文
后台开发者Ethan2 小时前
Python需要了解的一些知识
开发语言·人工智能·python
常利兵3 小时前
Kotlin作用域函数全解:run/with/apply/let/also与this/it的魔法对决
android·开发语言·kotlin
幼稚园的山代王3 小时前
Kotlin-基础语法练习一
android·开发语言·kotlin