25.Inspector窗口拓展自定义数据

25.Inspector窗口拓展-自定义数据


25.1 知识点

自定义属性 在Inspector窗口显示 基础方式

主要知识点:

EditorGUILayout.PropertyField(SerializedProperty对象, 标题);

该API会按照属性类型自己去处理控件绘制的逻辑。

声明自定义数据类,需要序列化特性

using System;

[Serializable]
public class TestCustomClass
{
    public int i;
    public float f;
}

自定义脚本中声明自定义数据类变量

public TestCustomClass myCustom;

编辑器脚本中使用Unity自带的显示方式

private SerializedProperty myCustom;

private void OnEnable()
{
    myCustom = serializedObject.FindProperty("myCustom");
}


public override void OnInspectorGUI()
{
    serializedObject.Update();

    EditorGUILayout.PropertyField(myCustom, new GUIContent("我的自定义属性"));

    serializedObject.ApplyModifiedProperties();
}

代码效果

自定义属性 在Inspector窗口显示 自定义方式

如果我们不想要Unity默认的绘制方式去显示自定义数据结构类相关内容,我们也可以完全自定义布局方式。主要知识点:

  1. SerializedProperty.FindPropertyRelative(属性)
  2. serializedObject.FindProperty(属性.子属性)

自定义显示数据结构类中的变量

private SerializedProperty myCustom;

private SerializedProperty myCustomI;
private SerializedProperty myCustomF;

private void OnEnable()
{   
    myCustom = serializedObject.FindProperty("myCustom");
    
    // 以下两种方式二选其一
    // myCustomI = myCustom.FindPropertyRelative("i");
    // myCustomF = myCustom.FindPropertyRelative("f");
    myCustomI = serializedObject.FindProperty("myCustom.i");
    myCustomF = serializedObject.FindProperty("myCustom.f");
}


public override void OnInspectorGUI()
{
    serializedObject.Update();

    EditorGUILayout.PropertyField(myCustom, new GUIContent("我的自定义属性"));

    myCustomI.intValue = EditorGUILayout.IntField("自定义属性中的I", myCustomI.intValue);
    myCustomF.floatValue = EditorGUILayout.FloatField("自定义属性中的F", myCustomF.floatValue);
    
    
    serializedObject.ApplyModifiedProperties();
}

代码效果

两个变量指向的是同一份内存,改动其中一个会实时同步


25.2 知识点代码

Lesson25_Inspector窗口拓展_自定义数据

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

public class Lesson25_Inspector窗口拓展_自定义数据 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 自定义属性 在Inspector窗口显示 基础方式

        //主要知识点:
        //EditorGUILayout.PropertyField(SerializedProperty对象, 标题);
        //该API会按照属性类型自己去处理控件绘制的逻辑

        #endregion

        #region 知识点二 自定义属性 在Inspector窗口显示 自定义方式

        //如果我们不想要Unity默认的绘制方式去显示 自定义数据结构类 相关内容
        //我们也可以完全自定义布局方式
        //主要知识点:
        //1.SerializedProperty.FindPropertyRelative(属性)
        //2.serializedObject.FindProperty(属性.子属性)

        #endregion
    }
}

TestCustomClass

using System;

[Serializable]
public class TestCustomClass
{
    public int i;
    public float f;
}

TestInspectorMono

using System.Collections.Generic;
using UnityEngine;

public class TestInspectorMono : MonoBehaviour
{
    #region Lesson23_Inspector窗口拓展_基础知识

    //攻击力
    public int atk;

    //防御力
    public float def;

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

    #endregion

    #region Lesson24_Inspector窗口拓展_数组List

    public string[] strs;
    public int[] ints;
    public GameObject[] gameObjects;

    public List<GameObject> listObjs;

    #endregion

    #region Lesson25_Inspector窗口拓展_自定义数据

    public TestCustomClass myCustom;

    #endregion
}

TestInspectorMonoEditor

using UnityEditor;
using UnityEngine;

//通过这个特性,我们就可以为TestInspectorMono脚本自定义Inspector窗口中的显示了
[CustomEditor(typeof(TestInspectorMono))]
public class TestInspectorMonoEditor : Editor
{
    #region Lesson23_Inspector窗口拓展_基础知识

    private SerializedProperty atk;
    private SerializedProperty def;
    private SerializedProperty obj;

    private bool foldOut;

    #endregion

    #region Lesson24_Inspector窗口拓展_数组和List

    private SerializedProperty strs;
    private SerializedProperty ints;
    private SerializedProperty gameObjects;

    private SerializedProperty listObjs;

    private int count;

    private bool arrayAndListFoldOut;

    #endregion

    #region Lesson25_Inspector窗口拓展_自定义数据

    private SerializedProperty myCustom;

    private SerializedProperty myCustomI;
    private SerializedProperty myCustomF;

    #endregion

    private void OnEnable()
    {
        #region Lesson23_Inspector窗口拓展_基础知识

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

        #endregion

        #region Lesson24_Inspector窗口拓展_数组和List

        //默认得到的数组和List容量为空
        strs = serializedObject.FindProperty("strs");
        ints = serializedObject.FindProperty("ints");
        gameObjects = serializedObject.FindProperty("gameObjects");
        listObjs = serializedObject.FindProperty("listObjs");

        //初始化当前容量 否则 每次一开始都是0
        count = listObjs.arraySize;

        #endregion

        #region Lesson25_Inspector窗口拓展_自定义数据

        myCustom = serializedObject.FindProperty("myCustom");

        //以下两种方式二选其一
        //myCustomI = myCustom.FindPropertyRelative("i");
        //myCustomF = myCustom.FindPropertyRelative("f");
        myCustomI = serializedObject.FindProperty("myCustom.i");
        myCustomF = serializedObject.FindProperty("myCustom.f");

        #endregion
    }


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

        serializedObject.Update();

        #region Lesson23_Inspector窗口拓展_基础知识

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

        #endregion

        #region Lesson24_Inspector窗口拓展_数组和List

        EditorGUILayout.PropertyField(strs, new GUIContent("字符串数组"));
        EditorGUILayout.PropertyField(ints, new GUIContent("整形数组"));
        EditorGUILayout.PropertyField(gameObjects, new GUIContent("游戏对象数组"));
        // EditorGUILayout.PropertyField(listObjs, new GUIContent("游戏对象List"));


        arrayAndListFoldOut = EditorGUILayout.BeginFoldoutHeaderGroup(arrayAndListFoldOut, "数组和List属性");
        if (arrayAndListFoldOut)
        {
            //容量设置
            count = EditorGUILayout.IntField("List容量", count);

            //是否要缩减 移除尾部的内容
            //从后往前去移除 避免移除不干净
            //当容量变少时 才会走这的逻辑
            for (int i = listObjs.arraySize - 1; i >= count; i--)
                listObjs.DeleteArrayElementAtIndex(i);

            //根据容量绘制需要设置的每一个索引位置的对象
            for (int i = 0; i < count; i++)
            {
                //去判断如果数组或者LIst容量不够 去通过插入的形式去扩容
                if (listObjs.arraySize <= i)
                    listObjs.InsertArrayElementAtIndex(i);

                SerializedProperty indexPro = listObjs.GetArrayElementAtIndex(i);
                EditorGUILayout.ObjectField(indexPro, new GUIContent($"索引{i}"));
            }
        }

        EditorGUILayout.EndFoldoutHeaderGroup();

        #endregion

        #region Lesson25_Inspector窗口拓展_自定义数据

        EditorGUILayout.PropertyField(myCustom, new GUIContent("我的自定义属性"));

        myCustomI.intValue = EditorGUILayout.IntField("自定义属性中的I", myCustomI.intValue);
        myCustomF.floatValue = EditorGUILayout.FloatField("自定义属性中的F", myCustomF.floatValue);

        #endregion

        serializedObject.ApplyModifiedProperties();
    }
}


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

×

喜欢就点赞,疼爱就打赏