自定義屬性抽屜

有時你擁有包含資料但不是從 MonoBehaviour 派生的自定義物件。將這些物件作為 MonoBehaviour 類中的欄位新增將沒有視覺效果,除非你為物件的型別編寫自己的自定義屬性抽屜。

下面是新增到 MonoBehaviour 的自定義物件的簡單示例,以及自定義物件的自定義屬性抽屜。

public enum Gender {
    Male,
    Female,
    Other
}

// Needs the Serializable attribute otherwise the CustomPropertyDrawer wont be used
[Serializable]
public class UserInfo {
    public string Name;
    public int Age;
    public Gender Gender;
}

// The class that you can attach to a GameObject
public class PropertyDrawerExample : MonoBehaviour {
    public UserInfo UInfo;
}

[CustomPropertyDrawer( typeof( UserInfo ) )]
public class UserInfoDrawer : PropertyDrawer {

    public override float GetPropertyHeight( SerializedProperty property, GUIContent label ) {
        // The 6 comes from extra spacing between the fields (2px each)
        return EditorGUIUtility.singleLineHeight * 4 + 6;
    }

    public override void OnGUI( Rect position, SerializedProperty property, GUIContent label ) {
        EditorGUI.BeginProperty( position, label, property );

        EditorGUI.LabelField( position, label );

        var nameRect = new Rect( position.x, position.y + 18, position.width, 16 );
        var ageRect = new Rect( position.x, position.y + 36, position.width, 16 );
        var genderRect = new Rect( position.x, position.y + 54, position.width, 16 );

        EditorGUI.indentLevel++;

        EditorGUI.PropertyField( nameRect, property.FindPropertyRelative( "Name" ) );
        EditorGUI.PropertyField( ageRect, property.FindPropertyRelative( "Age" ) );
        EditorGUI.PropertyField( genderRect, property.FindPropertyRelative( "Gender" ) );

        EditorGUI.indentLevel--;

        EditorGUI.EndProperty();
    }
}

首先,我們定義具有所有要求的自定義物件。只是一個描述使用者的簡單類。這個類在我們可以新增到 GameObject 的 PropertyDrawerExample 類中使用。

public enum Gender {
    Male,
    Female,
    Other
}

[Serializable]
public class UserInfo {
    public string Name;
    public int Age;
    public Gender Gender;
}

public class PropertyDrawerExample : MonoBehaviour {
    public UserInfo UInfo;
}

自定義類需要 Serializable 屬性,否則將不使用 CustomPropertyDrawer

接下來是 CustomPropertyDrawer

首先,我們必須定義一個派生自 PropertyDrawer 的類。類定義還需要 CustomPropertyDrawer 屬性。傳遞的引數是你希望此抽屜用於的物件的型別。

[CustomPropertyDrawer( typeof( UserInfo ) )]
public class UserInfoDrawer : PropertyDrawer {

接下來,我們重寫 GetPropertyHeight 函式。這允許我們為我們的屬性定義自定義高度。在這種情況下,我們知道我們的屬性將包含四個部分:標籤,名稱,年齡和性別。因此我們使用 EditorGUIUtility.singleLineHeight * 4 ,我們新增另外 6 個畫素,因為我們想要在每個欄位之間留出兩個畫素。

public override float GetPropertyHeight( SerializedProperty property, GUIContent label ) {
    return EditorGUIUtility.singleLineHeight * 4 + 6;
}

接下來是實際的 OnGUI 方法。我們啟動了與 EditorGUI.BeginProperty([…]) ,並結束與功能 EditorGUI.EndProperty() 。我們這樣做是為了如果這個屬性是預製件的一部分,那麼實際的預製覆蓋邏輯將適用於這兩種方法之間的所有內容。

public override void OnGUI( Rect position, SerializedProperty property, GUIContent label ) {
    EditorGUI.BeginProperty( position, label, property );

之後,我們顯示一個包含欄位名稱的標籤,我們已經為欄位定義了矩形。

EditorGUI.LabelField( position, label );

var nameRect = new Rect( position.x, position.y + 18, position.width, 16 );
var ageRect = new Rect( position.x, position.y + 36, position.width, 16 );
var genderRect = new Rect( position.x, position.y + 54, position.width, 16 );

每個欄位間隔 16 + 2 畫素,高度為 16(與 EditorGUIUtility.singleLineHeight 相同)

接下來,我們使用一個選項卡縮排 UI 以獲得更好的佈局,顯示屬性,取消縮排 GUI,並以 EditorGUI.EndProperty 結束。

EditorGUI.indentLevel++;

EditorGUI.PropertyField( nameRect, property.FindPropertyRelative( "Name" ) );
EditorGUI.PropertyField( ageRect, property.FindPropertyRelative( "Age" ) );
EditorGUI.PropertyField( genderRect, property.FindPropertyRelative( "Gender" ) );

EditorGUI.indentLevel--;

EditorGUI.EndProperty();

我們使用 EditorGUI.PropertyField 顯示欄位,需要一個矩形作為位置,一個 SerializedProperty 表示要顯示的屬性。我們通過在 OnGUI 函式中傳遞的屬性上呼叫 FindPropertyRelative(“…”) 來獲取屬性。請注意,這些是區分大小寫的,無法找到非公共屬性! **

對於此示例,我不儲存屬性返回 property.FindPropertyRelative(“…”)。你應該將它們儲存在類中的私有欄位中,以防止不必要的呼叫

結果

之前

StackOverflow 文件

StackOverflow 文件