kumak1’s blog

kumak1のイラストや技術ログ

Unity の Reorderable のエディタ拡張を作る

unity.com

Unity 2020.2 の Inspector では、配列は並び替えが可能な Reorderable なものに変わりました。

自作クラスのデフォルト表示

下記のように [Serializable] を指定した自作クラスについても、手軽に ReorderableGUIを Inspector で確認できます。便利ですね。

f:id:kumak1:20201223135307p:plain

using System;
using UnityEngine;

[Serializable]
public class TestObject
{
    public GameObject Prefab;
    public Vector3 Position;
    public Vector3 Rotate;
}

public class TestComponent : MonoBehaviour
{
    public TestObject[] TestObjects = {new TestObject(),};
}

けれど、ちょっと見づらいですね。イマイチポイントを挙げると以下のとおり。

  • 一覧性に欠けている
    • Foldoutを閉じた状態 : Element 1 といった連番のラベル情報しか表示されない
      • → 配列の中身を識別できる固有の情報を表示したい
    • Foldoutを開いた状態 :
      • → インデントを用いて、データのまとまりを表現したい

自作クラスのエディタ拡張

ということで、エディタ拡張を書いて解決をしてみます。 EditorGUILayout は使えないので、 EditorGUI で Rect をしっかり指定します。

f:id:kumak1:20210104010656p:plain

using System;
using UnityEngine;
using UnityEditor;

[Serializable]
public class TestObject
{
    public GameObject Prefab;
    public Vector3 Position;
    public Vector3 Rotate;
}

public class TestComponent : MonoBehaviour
{
    public TestObject[] TestObjects = {new TestObject(),};
}

[CustomPropertyDrawer(typeof(TestObject))]
public class TestObjectDrawer : PropertyDrawer
{
    private static float GetPropertyHeight(SerializedProperty property = null)
    {
        var height = property == null
            ? EditorGUIUtility.singleLineHeight
            : EditorGUI.GetPropertyHeight(property, true);

        return height + EditorGUIUtility.standardVerticalSpacing;
    }

    private static bool FoldoutField(ref Rect rect, SerializedProperty property, string label, string propertyName)
    {
        var prop = property.FindPropertyRelative(propertyName);
        prop.isExpanded = EditorGUI.Foldout(rect, prop.isExpanded, GUIContent.none);
        EditorGUI.PropertyField(rect, prop, new GUIContent(label));
        rect.y += GetPropertyHeight(prop);

        return prop.isExpanded;
    }

    private static void Field(ref Rect rect, SerializedProperty property, string label, string propertyName)
    {
        var prop = property.FindPropertyRelative(propertyName);
        EditorGUI.PropertyField(rect, prop, new GUIContent(label), true);
        rect.y += GetPropertyHeight(prop);
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        position.height = EditorGUIUtility.singleLineHeight;

        if (FoldoutField(ref position, property, "Prefab", "Prefab"))
        {
            EditorGUI.indentLevel++;
            Field(ref position, property, "Position", "Position");
            Field(ref position, property, "Rotate", "Rotate");
            EditorGUI.indentLevel--;
        }
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        var enableSkinName = property.FindPropertyRelative("Prefab");
        var height = GetPropertyHeight(enableSkinName);

        if (enableSkinName.isExpanded)
        {
            height += GetPropertyHeight(property.FindPropertyRelative("Position"));
            height += GetPropertyHeight(property.FindPropertyRelative("Rotate"));
        }

        return height;
    }
}

これでようやく使い物になりそうな見た目になりましたね。 ちょっとした気遣いで、UnityEditor上の作業はすごく楽になるので、「ゲームの内容には関係ないし・・」とか思わず、ちょいちょいっと書いていきましょい