【Unity】【エディタ拡張】配列の要素のSerializedPropertyから配列のインデックスを取得する方法

Listに対する自作Attributeを作ったときに困ったのでメモです。

リストを扱うAttribute

まずListに対して自作Attributeを使うには以下のようにします。

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class ListAttribute : PropertyAttribute {
    
}

#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(ListAttribute))]
public class ListAttributeDrawer : PropertyDrawer
{
    /// <summary>
    /// リストの要素一つ一つが描画されるときに呼ばれる
    /// </summary>
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
                // ここでリスト要素のインデックスをとりたい

        EditorGUI.PropertyField(position, property, label, true);
    }

    public override float GetPropertyHeight (SerializedProperty property, GUIContent label)
    {
        return EditorGUI.GetPropertyHeight(property);
    }
}
#endif

注意点として、ListのAttributeのOnGUIは要素一つ一つを描画するときに呼ばれます。
つまりリスト要素の数だけ呼ばれます。

したがってOnGUIの引数に入ってくるSerializedPropertyはリストではなく要素のプロパティとなります。
そうなると、OnGUIでその要素のインデックスをとりたいケースが生まれますが、これが多少厄介でした。

SerializedPropertyからリストのインデックスを取得する

SerializedPropertyにはインデックスを取得するプロパティなどは用意されていません。
そこで、SerializedPropertyのパスからインデックスを取得します。

変数名がsampleListの要素インデックスがindexのSerializedPropertyは以下のように命名されています。

sampleList.Array.data[index]

したがって、以下のようにすればインデックスを取得できます。

using UnityEngine;
using UnityEditor;
using System.Text.RegularExpressions;

public static class SerializedPropertyExtensions
{
    /// <summary>
    /// リストの要素Indexを返す
    /// </summary>
    public static int GetArrayElementIndex(this SerializedProperty property)
    {
        // プロパティがリストのインデックスであれば、パスは(変数名).Array.data[(インデックス)] 
        // となるため、この文字列からインデックスを取得する

        // リストの要素であるか判定する
        var match = Regex.Match(property.propertyPath, "^([a-zA-Z0-9_]*).Array.data\\[([0-9]*)\\]$");
        if (!match.Success) {
            return -1;
        }

        // Indexを抜き出す
        var splitPath = property.propertyPath.Split('.');
        var regax = new Regex(@"[^0-9]");
        var indexText = regax.Replace(splitPath[splitPath.Length - 1], "");
        int index = 0;
        if (!int.TryParse(indexText, out index)) {
            return -1;
        }

        return index;
    }
}

#endif

ちょっと不安定な方法ですが仕方ないですね・・
今回は使いやすいように拡張メソッドにしています。

参考

www.urablog.xyz