自定义属性抽屉
有时你拥有包含数据但不是从 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(“…”)。你应该将它们保存在类中的私有字段中,以防止不必要的调用
结果
之前
后