【Unity】【エディタ拡張】PropertyDrawerでデフォルトのGUIを描画する

PropertyDrawerでデフォルトのGUIを描画する方法です。
Inspectorを拡張する際には簡単にデフォルトGUIを描画できるのにPropertyDrawerだと難しいので作りました。

Unity2018.3.6

ソースコード

実装としては次のようにUtilityクラスを定義します。

public static class PropertyDrawerUtility
{
    public static void DrawDefaultGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        property = property.serializedObject.FindProperty(property.propertyPath);
        var fieldRect = position;
        fieldRect.height = EditorGUIUtility.singleLineHeight;

        using ( new EditorGUI.PropertyScope(fieldRect, label, property)) 
        {
            if (property.hasChildren) {
                // 子要素があれば折り畳み表示
                property.isExpanded = EditorGUI.Foldout (fieldRect, property.isExpanded, label);
            }
            else {
                // 子要素が無ければラベルだけ表示
                EditorGUI.LabelField(fieldRect, label);
                return;
            }
            fieldRect.y += EditorGUIUtility.singleLineHeight;
            fieldRect.y += EditorGUIUtility.standardVerticalSpacing;

            if (property.isExpanded) {

                using (new EditorGUI.IndentLevelScope()) 
                {
                    // 最初の要素を描画
                    property.NextVisible(true);
                    var depth = property.depth;
                    EditorGUI.PropertyField(fieldRect, property, true);
                    fieldRect.y += EditorGUI.GetPropertyHeight(property, true);
                    fieldRect.y += EditorGUIUtility.standardVerticalSpacing;

                    // それ以降の要素を描画
                    while(property.NextVisible(false)) {
                        
                        // depthが最初の要素と同じもののみ処理
                        if (property.depth != depth) {
                            break;
                        }
                        EditorGUI.PropertyField(fieldRect, property, true);
                        fieldRect.y += EditorGUI.GetPropertyHeight(property, true);
                        fieldRect.y += EditorGUIUtility.standardVerticalSpacing;
                    }
                }
            }
        }
    }

    public static float GetDefaultPropertyHeight(SerializedProperty property, GUIContent label)
    {
        property = property.serializedObject.FindProperty(property.propertyPath);
        var height = 0.0f;
        
        // プロパティ名
        height += EditorGUIUtility.singleLineHeight;
        height += EditorGUIUtility.standardVerticalSpacing;

        if (!property.hasChildren) {
            // 子要素が無ければラベルだけ表示
            return height;
        }
        
        if (property.isExpanded) {
        
            // 最初の要素
            property.NextVisible(true);
            var depth = property.depth;
            height += EditorGUI.GetPropertyHeight(property, true);
            height += EditorGUIUtility.standardVerticalSpacing;
            
            // それ以降の要素
            while(property.NextVisible(false))
            {
                // depthが最初の要素と同じもののみ処理
                if (property.depth != depth) {
                    break;
                }
                height += EditorGUI.GetPropertyHeight(property, true);
                height += EditorGUIUtility.standardVerticalSpacing;
            }
            // 最後はスペース不要なので削除
            height -= EditorGUIUtility.standardVerticalSpacing;
        }

        return height;
    }
}

使い方

あとはPropertyDrawerOnGUI()GetPropertyHeight()からこれを呼ぶだけです。

[CustomPropertyDrawer(typeof(Example))]
public class ExampleDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        PropertyDrawerUtility.DrawDefaultGUI(position, property, label);
        return;
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return PropertyDrawerUtility.GetDefaultPropertyHeight(property, label);
    }
}

これでデフォルトのGUIが描画されます。