自定義屬性抽屜
有時你擁有包含資料但不是從 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(“…”)。你應該將它們儲存在類中的私有欄位中,以防止不必要的呼叫
結果
之前
後