23.Inspector窗口拓展基础知识

23.Inspector窗口拓展-基础知识


23.1 知识点

Inspector窗口自定义显示指什么

可以自定义某一个脚本在Inspector窗口的相关显示。

SerializedObject和SerializedProperty的作用

SerializedObject 和 SerializedProperty 主要用于在 Unity 编辑器中操作和修改序列化对象的属性。它们通常在自定义编辑器中使用,以创建更灵活、可定制的属性面板。

我们只需要记住简单的规则:

  • SerializedObject 代表脚本(组件)对象。
  • SerializedProperty 代表脚本(组件)对象中的属性。

SerializedObject官方文档: https://docs.unity.cn/cn/2022.1/ScriptReference/SerializedObject.html

SerializedProperty官方文档: https://docs.unity.cn/cn/2022.1/ScriptReference/SerializedProperty.html

自定义脚本在Inspector窗口中显示的内容

声明测试脚本和测试编辑器脚本

  • 单独为某一个脚本实现一个自定义脚本,并且脚本需要继承Editor。一般该脚本命名为自定义脚本名 + Editor。
  • 声明测试脚本
public class TestInspectorMono : MonoBehaviour
{
    // 攻击力
    public int atk;

    // 防御力
    public float def;

    // 敌对目标对象依附的Gameobject
    public GameObject obj;
}
  • 声明要对测试脚本自定义显示到编辑器脚本
public class TestInspectorMonoEditor : Editor
{
}

编辑器脚本加特性

在编辑器脚本前加上如下特性。
命名空间:UnityEditor。
特性名:CustomEditor(想要自定义脚本类名的Type)。

using UnityEditor;

// 通过这个特性,我们就可以为TestInspectorMono脚本自定义Inspector窗口中的显示了
[CustomEditor(typeof(TestInspectorMono))]
public class TestInspectorMonoEditor : Editor
{
}

编辑器脚本声明测试脚本对应变量并获取

  • 声明对应SerializedProperty序列化属性对象。主要通过它和自定义脚本中的成员进行关联。可以利用继承Editor后的成员serializedObject中的FindProperty(“成员变量名”)方法关联对应成员。
  • 比如:
    • SerializedProperty mySerializedProperty;
    • mySerializedProperty = serializedObject.FindProperty(“自定义脚本中的成员名”);
  • 一般在OnEnable函数中初始化。
using UnityEditor;

// 通过这个特性,我们就可以为TestInspectorMono脚本自定义Inspector窗口中的显示了
[CustomEditor(typeof(TestInspectorMono))]
public class TestInspectorMonoEditor : Editor
{
    private SerializedProperty atk;
    private SerializedProperty def;
    private SerializedProperty obj;

    private void OnEnable()
    {
        // 这样就得到与测试脚本对应的字段 注意传入的字符串自定义脚本中的成员名一致
        atk = serializedObject.FindProperty("atk");
        def = serializedObject.FindProperty("def");
        obj = serializedObject.FindProperty("obj");
    }
}

重写OnInspectorGUI函数

  • 重写OnInspectorGUI函数。该函数控制了Inspector窗口中显示的内容。只需要在其中重写内容便可以自定义窗口。注意:其中的逻辑需要包裹在这两句代码之间:
// 更新序列化对象的表示形式
serializedObject.Update();
// 之间应用属性修改
serializedObject.ApplyModifiedProperties();
  • 未更改编辑器脚本前:

  • 重写OnInspectorGUI并声明自定义显示对象

using UnityEditor;

// 通过这个特性,我们就可以为TestInspectorMono脚本自定义Inspector窗口中的显示了
[CustomEditor(typeof(TestInspectorMono))]
public class TestInspectorMonoEditor : Editor
{
    private bool foldOut;

    public override void OnInspectorGUI()
    {
        //base.OnInspectorGUI();//注释掉父类调用后,Inspector窗口默认显示的atk def obj会消失

        serializedObject.Update();

        foldOut = EditorGUILayout.BeginFoldoutHeaderGroup(foldOut, "基础属性");
        if (foldOut)
        {
            GUILayout.Button("测试自定义Inspector窗口");
            EditorGUILayout.IntSlider(atk, 0, 100, "攻击力");
            def.floatValue = EditorGUILayout.FloatField("防御力", def.floatValue);
            EditorGUILayout.ObjectField(obj, new GUIContent("敌对对象"));
        }
        EditorGUILayout.EndFoldoutHeaderGroup();

        serializedObject.ApplyModifiedProperties();
    }
}
  • 更改编辑器脚本后:

获取脚本对象

  • Editor中的target成员变量 得到的是当前正在被自定义编辑器检视的组件对象
using UnityEditor;

// 通过这个特性,我们就可以为TestInspectorMono脚本自定义Inspector窗口中的显示了
[CustomEditor(typeof(TestInspectorMono))]
public class TestInspectorMonoEditor : Editor
{
    public override void OnInspectorGUI()
    {
        ...
        if (foldOut)
        {
             ...
            if (GUILayout.Button("打印当前target对象"))
            {
                Debug.Log("组件类型" + target.GetType());
                Debug.Log("组件依附的游戏对象名" + target.name);
            }
        }
        ...
    }
}

总结

我们为继承Editor的脚本添加[CustomEditor(typeof(想要自定义Inspector窗口的脚本))]特性,在该脚本中按照一定的规则进行编写,便可为Inspector窗口中的某个脚本自定义窗口布局。


23.2 知识点代码

Lesson23_Inspector窗口拓展_基础知识

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson23_Inspector窗口拓展_基础知识 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 Inspector窗口自定义显示指什么?

        //我可以完全自定义某一个脚本在Inspector窗口的相关显示

        #endregion

        #region 知识点二 SerializedObject和SerializedProperty的作用

        //SerializedObject 和 SerializedProperty
        //主要用于在 Unity 编辑器中操作和修改序列化对象的属性。
        //它们通常在自定义编辑器中使用,以创建更灵活、可定制的属性面板

        //我们只需要记住简单的规则
        //SerializedObject 代表脚本(组件)对象
        //SerializedProperty 代表脚本(组件)对象中的属性

        //SerializedObject官方文档: https://docs.unity.cn/cn/2022.1/ScriptReference/SerializedObject.html
        //SerializedProperty官方文档: https://docs.unity.cn/cn/2022.1/ScriptReference/SerializedProperty.html

        #endregion

        #region 知识点三 自定义脚本在Inspector窗口中显示的内容

        //关键步骤:
        
        //1.单独为某一个脚本实现一个自定义脚本,并且脚本需要继承Editor
        //  一般该脚本命名为 自定义脚本名 + Editor
        
        //2.在该脚本前加上特性
        //  命名空间:UnityEditor
        //  特性名:CustomEditor(想要自定义脚本类名的Type)
        
        //3.声明对应SerializedProperty序列化属性 对象
        //  主要通过它和自定义脚本中的成员进行关联
        //  可以利用继承Editor后的成员serializedObject中的FindProperty("成员变量名")方法关联对应成员;
        //  比如:SerializedProperty mySerializedProperty;
        //        mySerializedProperty = serializedObject.FindProperty("自定义脚本中的成员名");
        //  一般在OnEnable函数中初始化
        
        //4.重写OnInspectorGUI函数
        //  该函数控制了Inspector窗口中显示的内容
        //  只需要在其中重写内容便可以自定义窗口
        //  注意:其中的逻辑需要包裹在这两句代码之间
        //  更新序列化对象的表示形式
        //  serializedObject.Update();
        //  之间应用属性修改
        //  serializedObject.ApplyModifiedProperties();

        #endregion

        #region 知识点四 获取脚本对象

        //Editor中的target成员变量 得到的是当前正在被自定义编辑器检视的组件对象

        #endregion

        #region 总结

        //我们为继承Editor的脚本
        //添加[CustomEditor(typeof(想要自定义Inspector窗口的脚本))]特性
        //在该脚本中按照一定的规则进行编写
        //便可为Inspector窗口中的某个脚本自定义窗口布局

        #endregion
    }
}

TestInspectorMono

using UnityEngine;

public class TestInspectorMono : MonoBehaviour
{
    //攻击力
    public int atk;

    //防御力
    public float def;

    //敌对目标对象依附的Gameobject
    public GameObject obj;
}

TestInspectorMonoEditor

using UnityEditor;
using UnityEngine;

//通过这个特性,我们就可以为TestInspectorMono脚本自定义Inspector窗口中的显示了
[CustomEditor(typeof(TestInspectorMono))]
public class TestInspectorMonoEditor : Editor
{
    private SerializedProperty atk;
    private SerializedProperty def;
    private SerializedProperty obj;

    private void OnEnable()
    {
        //这样就得到与测试脚本对应的字段
        atk = serializedObject.FindProperty("atk");
        def = serializedObject.FindProperty("def");
        obj = serializedObject.FindProperty("obj");
    }

    private bool foldOut;

    public override void OnInspectorGUI()
    {
        //base.OnInspectorGUI();//注释掉父类调用后,Inspector窗口默认显示的atk def obj会消失

        serializedObject.Update();

        foldOut = EditorGUILayout.BeginFoldoutHeaderGroup(foldOut, "基础属性");
        if (foldOut)
        {
            GUILayout.Button("测试自定义Inspector窗口");
            EditorGUILayout.IntSlider(atk, 0, 100, "攻击力");
            def.floatValue = EditorGUILayout.FloatField("防御力", def.floatValue);
            EditorGUILayout.ObjectField(obj, new GUIContent("敌对对象"));

            if (GUILayout.Button("打印当前target对象"))
            {
                Debug.Log("组件类型" + target.GetType());
                Debug.Log("组件依附的游戏对象名" + target.name);
            }
        }

        EditorGUILayout.EndFoldoutHeaderGroup();

        serializedObject.ApplyModifiedProperties();
    }
}

23.3 练习题

请问如何才能完全自定义某个脚本在Inspector窗口中的显示?

实现一个继承Editor的脚本

为该脚本添加[CustomEditor(参数)]特性,传入的参数为你想要自定义Inspector窗口的脚本的Type

通过serializedObject.FindProperty关联对应属性的SerializedProperty

重写OnInspectorGUI函数,在该函数中利用GUI、EditorGUI相关API绘制对应控件,来修改SerializedProperty



转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏