Unity自定義ScriptableObject屬性顯示的三種方式

1. 繼承Editor,重寫OnInspectorGUI方法

Editor官方文檔

效果

實(shí)現(xiàn)

定義一個(gè)測(cè)試類TestClass,一個(gè)可序列化類DataClass

[CreateAssetMenu]
public class TestClass : ScriptableObject
{
    [Range(0, 10)]
    public int intData;
    public string stringData;
    public List<DataClass> dataList;
}

[System.Serializable]
public class DataClass
{
    [Range(0, 100)]
    public int id;
    public Vector3 position;
    public List<int> list;
}
//指定類型
[CustomEditor(typeof(TestClass))]
public class TestClassEditor  : Editor
{
    SerializedProperty intField;
    SerializedProperty stringField;

    void OnEnable()
    {
        //獲取指定字段
        intField = serializedObject.FindProperty("intData");
        stringField = serializedObject.FindProperty("stringData");
    }

    public override void OnInspectorGUI()
    {
        // Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
        serializedObject.Update();
        EditorGUILayout.IntSlider(intField, 0, 100, new GUIContent("initData"));
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PropertyField(stringField);
        if(GUILayout.Button("Select"))
        {
            stringField.stringValue = EditorUtility.OpenFilePanel("", Application.dataPath, "");
        }
        EditorGUILayout.EndHorizontal();

        // Apply changes to the serializedProperty - always do this in the end of OnInspectorGUI.
        //需要在OnInspectorGUI之前修改屬性,否則無法修改值
        serializedObject.ApplyModifiedProperties();
        
        base.OnInspectorGUI();
    }
}

Editor嵌套

通過Edtiro.CreateEditor可實(shí)現(xiàn)Editor的嵌套。

創(chuàng)建一個(gè)類TestClass2,它包含一個(gè)TestClass的屬性。

[CreateAssetMenu]
public class TestClass2 : ScriptableObject
{
    public TestClass data;
}

創(chuàng)建一個(gè)Test2Class的asset。它的Inspector面板的默認(rèn)顯示:


它并沒有把TestClass的屬性顯示出來,如果要查看TestClass的屬性,必須雙擊,跳到相應(yīng)界面,但這樣有看不到TestClass2的屬性。

如果想在Test2Class的Inspector面板中直接看到并且可以修改TestClass的屬性,可以重寫TestClass2的Editor,并在其中嵌套TestClass的Editor。

[CustomEditor(typeof(TestClass2))]
public class TestClass2Editor : Editor
{
    Editor cacheEditor;
    public override void OnInspectorGUI()
    {
        // Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
        serializedObject.Update();
        //顯示TestClass2的默認(rèn)UI
        base.OnInspectorGUI();

        GUILayout.Space(20);
        var data = ( (TestClass2)target ).data;
        if(data != null)
        {
            //創(chuàng)建TestClass的Editor
            if (cacheEditor == null)
                cacheEditor = Editor.CreateEditor(data);
            GUILayout.Label("this is TestClass2");
            cacheEditor.OnInspectorGUI();
        }
    }
}

這樣就可以直接在TestClass2的面板中直接查看和編輯TestClass的屬性。

2. 使用PropertyDrawer

PropertyDrawer官方文檔

如果想修改某種特定類型的顯示,使用繼承Editor的方式就會(huì)變得很麻煩,因?yàn)樗惺褂锰囟愋偷腶sset都需要去實(shí)現(xiàn)一個(gè)自定義的Editor,效率非常低。這種情況就可以通過繼承PropertyDrawer的方式,對(duì)指定類型的屬性,進(jìn)行統(tǒng)一顯示。

效果

為Inspector面板中的所有string屬性添加一個(gè)選擇文件按鈕,選中文件的路徑直接賦值給該變量。


實(shí)現(xiàn)

[CustomPropertyDrawer(typeof(string))]
public class StringPropertyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        Rect btnRect = new Rect(position);
        position.width -= 60;
        btnRect.x += btnRect.width - 60;
        btnRect.width = 60;
        EditorGUI.BeginProperty(position, label, property);
        EditorGUI.PropertyField(position, property, true);
        if (GUI.Button(btnRect, "select"))
        {
            string path = property.stringValue;
            string selectStr = EditorUtility.OpenFilePanel("選擇文件", path, "");
            if (!string.IsNullOrEmpty(selectStr))
            {
                property.stringValue = selectStr;
            }
        }

        EditorGUI.EndProperty();
    }
}

加了一個(gè)PropertyDrawer之后,Inspector面板中的所有string變量都會(huì)額外添加一個(gè)Select按鈕。

注意事項(xiàng)

  1. PropertyDrawer只對(duì)可序列化的類有效,非可序列化的類沒法在Inspector面板中顯示。
  2. OnGUI方法里只能使用GUI相關(guān)方法,不能使用Layout相關(guān)方法。
  3. PropertyDrawer對(duì)應(yīng)類型的所有屬性的顯示方式都會(huì)修改,例如創(chuàng)建一個(gè)帶string屬性的MonoBehaviour:

3. 使用PropertyAttribute

PropertyAttribute官方文檔

如果想要修改部分類的指定類型的屬性的顯示,直接使用PropertyDrawer就無法滿足條件,這時(shí)可以結(jié)合PropertyAttribute和PropertyAttribute來實(shí)現(xiàn)需求。

效果

為部分指定類的int或float屬性的顯示添加滑動(dòng)條,滑動(dòng)條的上下限可根據(jù)類和屬性自行設(shè)置。

實(shí)現(xiàn)

public class RangeAttribute : PropertyAttribute
{
    public float min;
    public float max;

    public RangeAttribute(float min, float max)
    {
        this.min = min;
        this.max = max;
    }
}

[CustomPropertyDrawer(typeof(RangeAttribute))]
public class RangeDrawer : PropertyDrawer
{
    // Draw the property inside the given rect
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // First get the attribute since it contains the range for the slider
        RangeAttribute range = attribute as RangeAttribute;

        // Now draw the property as a Slider or an IntSlider based on whether it's a float or integer.
        if (property.propertyType == SerializedPropertyType.Float)
            EditorGUI.Slider(position, property, range.min, range.max, label);
        else if (property.propertyType == SerializedPropertyType.Integer)
            EditorGUI.IntSlider(position, property, (int)range.min, (int)range.max, label);
        else
            EditorGUI.LabelField(position, label.text, "Use Range with float or int.");
    }
}

修改TestClass和DataClass

[CreateAssetMenu]
public class TestClass : ScriptableObject
{
    [Range(0, 10)]
    public int intData;
    public string stringData;
    public List<DataClass> dataList;
}

[System.Serializable]
public class DataClass
{
    [Range(0, 100)]
    public int id;
    public Vector3 position;
    public List<int> list;
}

其他

  • 需要修改顯示的類都需要滿足Unity的序列化規(guī)則
  • 這幾種顯示方式對(duì)Serializable Class都可以使用,并不需要一定是ScriptableObject。只是在編輯器下,ScriptableObject來保存臨時(shí)數(shù)據(jù)比較常用,所以使用ScriptableObject來做例子。

demo下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容