うにてぃブログ

UnityやUnreal Engineの記事を書いていきます

【Unity】UnityEditor で List を横並びに表示させる

クラスの IList を UnityEditor 上に表示させるときに
縦並びよりは横並びの方が見やすいのではないかと思ったので作成してみた

表示の調整のために Rect をいじっているため結構読みにくいです

using UnityEditor;
using UnityEngine;
 
public static class ListHorizontal
{
    private static readonly Dictionary<string, Vector2> _scrollPositionDic = new Dictionary<string, Vector2>();
 
    public static void Draw(SerializedProperty self, float labelWidth = 80f, float fieldWidth = 150f)
    {
        if (!self.isArray || self.propertyType == SerializedPropertyType.String)
        {
            EditorGUILayout.PropertyField(self, new GUIContent(self.displayName), true);
            return;
        }
 
        if (!_scrollPositionDic.ContainsKey(self.propertyPath))
            _scrollPositionDic.Add(self.propertyPath, Vector2.zero);
        
        var buttonWidth = 20f;
        var scrollbarHeight = 14f;
        
        // スクロール分を確保
        var height = EditorGUIUtility.singleLineHeight + scrollbarHeight;
        if (self.arraySize > 0)
        {
            var iterator = self.GetArrayElementAtIndex(0).Copy();
            var depth = iterator.depth;
            for (var enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false)
            {
                if (iterator.depth <= depth)
                    continue;
 
                height += EditorGUI.GetPropertyHeight(iterator);
            }
        }
 
        var scrollRect = EditorGUILayout.GetControlRect(false, height);
        height -= scrollbarHeight;
        var viewRect = new Rect(scrollRect.x, scrollRect.y, labelWidth, height);
        using (new GUI.GroupScope(viewRect, GUI.skin.box))
        {
            viewRect.x = viewRect.y = 0;
            viewRect.xMin += 5;
            {
                viewRect.height = EditorGUIUtility.singleLineHeight;
                viewRect.width -= buttonWidth;
                EditorGUI.LabelField(viewRect, self.displayName);
                viewRect.x += viewRect.width;
                viewRect.width = buttonWidth;
                if (GUI.Button(viewRect, "+"))
                    self.InsertArrayElementAtIndex(self.arraySize);
 
            }
            viewRect.x = 0;
            if (self.arraySize > 0)
            {
                viewRect.width = labelWidth;
                viewRect.xMin += 10;
                var iterator = self.GetArrayElementAtIndex(0).Copy();
                var depth = iterator.depth;
                for (var enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false)
                {
                    if (iterator.depth <= depth)
                        continue;
 
                    viewRect.y += viewRect.height;
                    EditorGUI.LabelField(viewRect, iterator.displayName);
                }
            }
        }
        
        scrollRect.width -= labelWidth;
        scrollRect.x += labelWidth;
        viewRect.x = viewRect.y = 0;
        viewRect.width = fieldWidth * self.arraySize;
        viewRect.height = height;
        using (var scroll = new GUI.ScrollViewScope(scrollRect, _scrollPositionDic[self.propertyPath], viewRect))
        {
            _scrollPositionDic[self.propertyPath] = scroll.scrollPosition;
            
            viewRect.width = fieldWidth * self.arraySize;
            viewRect.height = height;
            using (new GUI.GroupScope(viewRect))
            {
                for (var i = 0; i < self.arraySize; i++)
                {
                    viewRect.x = i * fieldWidth;
                    viewRect.y = 0f;
                    viewRect.width = fieldWidth;
                    viewRect.height = height;
                    
                    using (new GUI.GroupScope(viewRect, GUI.skin.box))
                    {
                        viewRect.x = 0f;
                        viewRect.height = EditorGUIUtility.singleLineHeight; 
                        {
                            viewRect.width -= buttonWidth;
                            viewRect.xMin += 5;
                            EditorGUI.LabelField(viewRect, i.ToString());
                            viewRect.x += viewRect.width;
                            viewRect.width = buttonWidth;
                            if (GUI.Button(viewRect, "-"))
                            {
                                self.DeleteArrayElementAtIndex(i);
                                return;
                            }
                        }
                        viewRect.x = 0f;
                        viewRect.width = fieldWidth;
                    
                        var iterator = self.GetArrayElementAtIndex(i).Copy();
                        var depth = iterator.depth;
                        for (var enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false)
                        {
                            if (iterator.depth <= depth)
                                continue;
 
                            viewRect.y += viewRect.height; 
                            EditorGUI.PropertyField(viewRect, iterator, GUIContent.none, false);
                        }
                    }
                }
            }
        }
    }
}

サンプル

本来であればこのように表示されるが

f:id:hacchi_man:20200303012937p:plain:w300

今回の水平表示を利用した場合こちらのように表示される
IList の中に IList を利用した場合の対応は行っていないので IList が無いクラスでご利用ください

f:id:hacchi_man:20200303013125p:plain:w600

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
 
public class SampleMonoBehaviour : MonoBehaviour
{
    [SerializeField]
    private List<GameObject> _gameObjects;
    
    [SerializeField]
    private List<SampleClass> _samples = new List<SampleClass>
    {
        new SampleClass()
    };
    
    [SerializeField]
    private Vector3 _vector3;
        
    [Serializable]
    private class SampleClass
    {
        [SerializeField]
        private int _intValue;
        [SerializeField]
        private string _stringValue;
        [SerializeField]
        private Vector2 _vector2Value;
    }
}
 
[CustomEditor(typeof(SampleMonoBehaviour))]
public class SampleMonoBehaviourEditor : Editor
{
    public override void OnInspectorGUI()
    {
        serializedObject.UpdateIfRequiredOrScript();
        EditorGUILayout.PropertyField(serializedObject.FindProperty("_gameObjects"));
        ListHorizontal.Draw(serializedObject.FindProperty("_samples"));
        EditorGUILayout.PropertyField(serializedObject.FindProperty("_vector3"));
        serializedObject.ApplyModifiedProperties();
    }
}