第三章自定义检视面板_创建自定义编辑器类_检测与应用修改(本章进度(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);
        }
    }


}
相关推荐
算法_小学生7 分钟前
Hinge Loss(铰链损失函数)详解:SVM 中的关键损失函数
开发语言·人工智能·python·算法·机器学习·支持向量机
YUJIANYUE26 分钟前
纯前端html实现图片坐标与尺寸(XY坐标及宽高)获取
开发语言·前端·javascript
kyle~44 分钟前
C++---cout、cerr、clog
开发语言·c++·算法
thginWalker2 小时前
拓扑排序/
java·开发语言
MediaTea2 小时前
Python 库手册:re 正则表达式模块
开发语言·python·正则表达式
Maybyy2 小时前
javaScript中数组常用的函数方法
开发语言·前端·javascript
alicema11112 小时前
萤石摄像头C++SDK应用实例
开发语言·前端·c++·qt·opencv
爱代码的小黄人2 小时前
MATLAB中绘制系统零极点图(Pole-Zero Map)的几种方法
开发语言·matlab
Despacito0o2 小时前
ESP32S3+VSCode+PlatformIO+FreeRTOS+Arduino多核编程实战:FreeRTOS任务创建+任务调度详解
ide·vscode·编辑器
伍哥的传说2 小时前
Vue3 Anime.js超级炫酷的网页动画库详解
开发语言·前端·javascript·vue.js·vue·ecmascript·vue3