自定义属性抽屉

有时你拥有包含数据但不是从 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 文档