有时,您有包含数据但不从MonoBehaviour派生的自定义对象。除非您为对象类型编写自己的自定义属性抽屉,否则将这些对象添加为MonoBehaviour类中的字段不会产生视觉效果。
下面是添加到MonoBehaviour的自定义对象的简单示例,以及该自定义对象的自定义属性抽屉。
public enum Gender { Male, Female, Other } // 需要Serializable属性,否则将不使用CustomPropertyDrawer [Serializable] public class UserInfo { public string Name; public int Age; public Gender Gender; } // 您可以附加到GameObject的类 public class PropertyDrawerExample : MonoBehaviour { public UserInfo UInfo; } [CustomPropertyDrawer( typeof( UserInfo ) )] public class UserInfoDrawer : PropertyDrawer { public override float GetPropertyHeight( SerializedProperty property, GUIContent label ) { // 6来自字段之间的额外间距(每个2px) returnEditorGUIUtility.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(); } }
首先,我们定义具有所有要求的自定义对象。只是描述用户的简单类。该类在我们的PropertyDrawerExample类中使用,可以将其添加到GameObject中。
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 ) { returnEditorGUIUtility.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(“ ...”)返回的属性。您应该将它们保存在类的私有字段中,以防止不必要的调用
结果
之前
后