うにてぃブログ

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

【Unity】EditorWindowで使えるScope一覧

Begin / End で表記するGUI系クラスのヘルパークラスである Scopeのメモ

基本的にはGUI.Scopeを継承しており、自身でScopeクラスを追加することもできる。

デフォルトで入っているものは以下であり、使い方と簡単に機能説明を追記する
また最後に自作のScopeも載せておく

GUI

GUI.ScrollViewScope

f:id:hacchi_man:20200126214138p:plain

   private const int ScrollItemCount = 20;
    private Vector2 _scrollPosition;
    
    private void OnGUI()
    {
        // 高さを指定すると領域が確保されてしまうので、UIがおかしくなる
        var rect = GUILayoutUtility.GetRect(position.width, 0);
        rect.height = EditorGUIUtility.singleLineHeight * 5;
        var viewRect = new Rect()
        {
            x = 0,
            y = 0,
            width = rect.width - 20, 
            height = EditorGUIUtility.singleLineHeight * ScrollItemCount,
        };

        using (var scroll = new GUI.ScrollViewScope(rect, _scrollPosition, viewRect))
        {
            _scrollPosition = scroll.scrollPosition;
            for (var i = 0; i < ScrollItemCount; i++)
            {
                GUILayout.Label(i.ToString());
            }
        }
    }

GUI.ClipScope

指定した Rect を確保し、x, y=0から始まる新しい Rect を作成する

f:id:hacchi_man:20200126215838p:plain

   private void OnGUI()
    {
        var rect = GUILayoutUtility.GetRect(position.width, EditorGUIUtility.singleLineHeight * 2);
        using (new GUI.ClipScope(rect))
        {
            rect.x = rect.y = 0;
            rect.height = EditorGUIUtility.singleLineHeight;
            GUI.Label(rect, "AreaLabel1");
            rect.y += EditorGUIUtility.singleLineHeight;
            GUI.Label(rect, "AreaLabel2");
        }
        GUILayout.Label("Label");
    }

GUI.GroupScope

これはClipに機能が足されたものでヘッダーテキスト・背景画像・GUIStyle を適応することができる

f:id:hacchi_man:20200126220547p:plain

       var rect = GUILayoutUtility.GetRect(position.width, EditorGUIUtility.singleLineHeight * 3);
        using (new GUI.GroupScope(rect, "GroupScope"))
        {
            rect.x = 0;
            rect.y = EditorGUIUtility.singleLineHeight;
            rect.height = EditorGUIUtility.singleLineHeight;
            GUI.Label(rect, "AreaLabel1");
            rect.y += EditorGUIUtility.singleLineHeight;
            GUI.Label(rect, "AreaLabel2");
        }
        
        rect = GUILayoutUtility.GetRect(position.width, EditorGUIUtility.singleLineHeight * 2);
        using (new GUI.GroupScope(rect, GUI.skin.box))
        {
            rect.x = rect.y = 0;
            rect.height = EditorGUIUtility.singleLineHeight;
            GUI.Label(rect, "AreaLabel1");
            rect.y += EditorGUIUtility.singleLineHeight;
            GUI.Label(rect, "AreaLabel2");
        }

GUILayout

GUILayout.ScrollViewScope

f:id:hacchi_man:20200126220709p:plain

   private const int ScrollItemCount = 20;
    private Vector2 _scrollPosition;
    
    private void OnGUI()
    {
        using (var scroll = new GUILayout.ScrollViewScope(_scrollPosition))
        {
            _scrollPosition = scroll.scrollPosition;
            for (var i = 0; i < ScrollItemCount; i++)
            {
                GUILayout.Label(i.ToString());
            }
        }
    }

GUILayout.AreaScope

現在のRect から 範囲を指定して、x, y=0から始まる新しい Rect を作成する
スコープ内部で EditorGUILayout.GetControlRect を使うと rect.width が1になってしまうため使用には注意

f:id:hacchi_man:20200126215422p:plain

   private void OnGUI()
    {
        var rect = new Rect(0, 0, position.width, EditorGUIUtility.singleLineHeight * 2);
        using (new GUILayout.AreaScope(rect))
        {
            rect.x = rect.y = 0;
            rect.height = EditorGUIUtility.singleLineHeight;
            GUI.Label(rect, "AreaLabel1");
            rect.y += EditorGUIUtility.singleLineHeight;
            GUI.Label(rect, "AreaLabel2");
        }
        // 領域を確保してないので これだと描画が崩れる
        // GUILayout.Label("Label");
        // GUILayout.AreaScope で利用した分だけ確保する
        GUILayoutUtility.GetRect(position.width, EditorGUIUtility.singleLineHeight * 2);
        GUILayout.Label("Label");
    }

GUILayout.HorizontalScope

スコープ内要素を水平に表示する

f:id:hacchi_man:20200126220931p:plain

   private void OnGUI()
    {
        using (new GUILayout.HorizontalScope())
        {
            GUILayout.Label("Horizontal1");
            GUILayout.Label("Horizontal2");
        }
    }

GUILayout.VerticalScope

スコープ内要素を垂直に表示する

f:id:hacchi_man:20200126221024p:plain

   private void OnGUI()
    {
        using (new GUILayout.HorizontalScope())
        {
            using (new GUILayout.VerticalScope())
            {
                GUILayout.Label("Vertical1");
                GUILayout.Label("Vertical2");
                
            }
            GUILayout.Label("Horizontal3");
        }
    }

EditorGUI

EditorGUI.PropertyScope

これを利用することで Prefab化時に差分が出た場合に
変更箇所をわかりやすくしてくれる

f:id:hacchi_man:20200126222904p:plain

using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class SampleBehaviour : MonoBehaviour
{
    [SerializeField]
    private SampleClass _sample;
    
    [SerializeField]
    private int _noChangeintValue1;
}

[Serializable]
public class SampleClass
{
    [SerializeField]
    private int _intValue1;
    [SerializeField]
    private int _intValue2;   
}

#if UNITY_EDITOR

[CustomPropertyDrawer(typeof(SampleClass))]
public class SampleClassDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        using (var scope = new EditorGUI.PropertyScope(position, label, property))
        {
            label = scope.content;
            position.height = EditorGUIUtility.singleLineHeight;
            EditorGUI.PropertyField(position, property.FindPropertyRelative("_intValue1"));
            position.y += EditorGUIUtility.singleLineHeight;
            EditorGUI.PropertyField(position, property.FindPropertyRelative("_intValue2"));
        }
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return EditorGUIUtility.singleLineHeight * 2;
    }
}

#endif

EditorGUI.ChangeCheckScope

スコープ内に変更があった場合 change.changed が true になる

   private int _intValue;
    
    private void OnGUI()
    {
        using (var change = new EditorGUI.ChangeCheckScope())
        {
            _intValue = EditorGUILayout.IntField(_intValue);
            if (change.changed)
            {
                Debug.Log("Change int Value");
            }
        }
    }

EditorGUI.IndentLevelScope

スコープ内のインデントを変更する

f:id:hacchi_man:20200126223620p:plain

   private void OnGUI()
    {
        EditorGUILayout.LabelField("indentLevel:" + EditorGUI.indentLevel);
        using (new EditorGUI.IndentLevelScope(1))
        {
            EditorGUILayout.LabelField("indentLevel:" + EditorGUI.indentLevel);
        }
        using (new EditorGUI.IndentLevelScope(5))
        {
            EditorGUILayout.LabelField("indentLevel:" + EditorGUI.indentLevel);
        }
    }

EditorGUI.DisabledScope

スコープ内の操作可否を変更する

f:id:hacchi_man:20200126224251p:plain

   private bool _isValid;
    private int _enableValue;
    private int _disableValue;
    
    private void OnGUI()
    {
        _isValid = EditorGUILayout.ToggleLeft("Switch", _isValid);
        using (new EditorGUI.DisabledScope(!_isValid))
        {
            EditorGUILayout.LabelField("Switch On");
            _enableValue = EditorGUILayout.IntField(_enableValue);
        }
        using (new EditorGUI.DisabledScope(_isValid))
        {
            EditorGUILayout.LabelField("Switch Off");
            _disableValue = EditorGUILayout.IntField(_disableValue);
        }
    }

EditorGUI.DisabledGroupScope

操作可否をネストできるスコープ

f:id:hacchi_man:20200126224843p:plain

   private bool _isValid;
    private bool _isValidSub;
    
    private void OnGUI()
    {
        _isValid = EditorGUILayout.ToggleLeft("Switch", _isValid);
        _isValidSub = EditorGUILayout.ToggleLeft("SwitchSub", _isValidSub);
        using (new EditorGUI.DisabledGroupScope(!_isValid))
        {
            EditorGUILayout.LabelField("Switch On");
            using (new EditorGUI.IndentLevelScope(1))
            {
                using (new EditorGUI.DisabledScope(!_isValidSub))
                {
                    EditorGUILayout.LabelField("SubSwitch On");
                }

                using (new EditorGUI.DisabledScope(_isValidSub))
                {
                    EditorGUILayout.LabelField("SubSwitch On");
                }
            }
        }
        using (new EditorGUI.DisabledGroupScope(_isValid))
        {
            EditorGUILayout.LabelField("Switch Off");
            using (new EditorGUI.IndentLevelScope(1))
            {
                using (new EditorGUI.DisabledScope(!_isValidSub))
                {
                    EditorGUILayout.LabelField("SubSwitch On");
                }

                using (new EditorGUI.DisabledScope(_isValidSub))
                {
                    EditorGUILayout.LabelField("SubSwitch On");
                }
            }
        }
    }

EditorGUILayout

EditorGUILayout.ScrollViewScope

f:id:hacchi_man:20200126225053p:plain

   private const int ScrollItemCount = 20;
    private Vector2 _scrollPosition;

    private void OnGUI()
    {
        using (var scrollView = new EditorGUILayout.ScrollViewScope(_scrollPosition))
        {
            _scrollPosition = scrollView.scrollPosition;
            for (var i = 0; i < ScrollItemCount; i++)
            {
                EditorGUILayout.LabelField(i.ToString());
            }
        }
    }

EditorGUILayout.HorizontalScope

スコープ内を水平に表示する
GUILayout.HorizontalScopeは幅を考慮してくれるが、こちらは幅を考慮しない

f:id:hacchi_man:20200126225207p:plain

   private void OnGUI()
    {
        using (new EditorGUILayout.HorizontalScope())
        {
            EditorGUILayout.LabelField("Label1");
            EditorGUILayout.LabelField("Label2");
            EditorGUILayout.LabelField("Label3");
        }
    }

EditorGUILayout.VerticalScope

スコープ内を垂直に表示する

f:id:hacchi_man:20200126225337p:plain

   private void OnGUI()
    {
        using (new EditorGUILayout.HorizontalScope())
        {
            using (new EditorGUILayout.VerticalScope())
            {
                EditorGUILayout.LabelField("VerticalLabel1");
                EditorGUILayout.LabelField("VerticalLabel2");
            }
            EditorGUILayout.LabelField("HorizontalLabel");
        }
    }

EditorGUILayout.FadeGroupScope

Animation するトグルメニューを表示できる

f:id:hacchi_man:20200126230040g:plain

   private AnimBool _animBool;
    private Color _color;
    private string _text = "";
    private int _number;
    
    void OnEnable()
    {
        _animBool = new AnimBool(true);
        _animBool.valueChanged.AddListener(new UnityAction(Repaint));
    }

    private void OnGUI()
    {
        _animBool.target = EditorGUILayout.ToggleLeft("Show extra fields", _animBool.target);

        //Extra block that can be toggled on and off.
        using (var group = new EditorGUILayout.FadeGroupScope(_animBool.faded))
        {
            if (group.visible)
            {
                using (new EditorGUI.IndentLevelScope())
                {
                    
                    EditorGUILayout.PrefixLabel("Color");
                    _color = EditorGUILayout.ColorField(_color);
                    EditorGUILayout.PrefixLabel("Text");
                    _text = EditorGUILayout.TextField(_text);
                    EditorGUILayout.PrefixLabel("Number");
                    _number = EditorGUILayout.IntSlider(_number, 0, 10);
                }
            }
        }
    }

EditorGUILayout.ToggleGroupScope

トグルを入れ子にできる
親トグルがOnではないと子トグルは操作ができない

f:id:hacchi_man:20200126230403p:plain
f:id:hacchi_man:20200126230420p:plain

   private bool _toggle;
    private bool[] _toggles = new bool[]
    {
        true, true, true
    };
    
    private void OnGUI()
    {
        using (var _toggleGroup = new EditorGUILayout.ToggleGroupScope("Align position", _toggle))
        {
            _toggle = _toggleGroup.enabled;
            _toggles[0] = EditorGUILayout.Toggle("x", _toggles[0]);
            _toggles[1] = EditorGUILayout.Toggle("y", _toggles[1]);
            _toggles[2] = EditorGUILayout.Toggle("z", _toggles[2]);
        }
    }

EditorGUIUtility

EditorGUIUtility.IconSizeScope

GUIContent のアイコンサイズを変更できる
1行高さ (16) を超えると画像が見切れるので高さを指定する必要がある

f:id:hacchi_man:20200126231546p:plain

   private void OnGUI()
    {
        EditorGUILayout.LabelField("8 * 8");
        using (new EditorGUIUtility.IconSizeScope(Vector2.one * 8))
        {
            EditorGUILayout.LabelField(EditorGUIUtility.TrIconContent("BuildSettings.SelectedIcon"));
        }
        
        EditorGUILayout.LabelField("16 * 16");
        using (new EditorGUIUtility.IconSizeScope(Vector2.one * 16))
        {
            EditorGUILayout.LabelField(EditorGUIUtility.TrIconContent("BuildSettings.SelectedIcon"));
        }
        
        EditorGUILayout.LabelField("32 * 32");
        using (new EditorGUIUtility.IconSizeScope(Vector2.one * 32))
        {
            EditorGUILayout.LabelField(EditorGUIUtility.TrIconContent("BuildSettings.SelectedIcon"), GUILayout.Height(32));
        }
    }

カスタムスコープ

                 // Hierarchy で複数選択時 スコープ内 のIntField等の値部分を 「-」表示できる
        public class MixedValueScope : GUI.Scope
        {
            private readonly bool _cacheValue;

            public MixedValueScope()
            {
                _cacheValue = EditorGUI.showMixedValue;
                EditorGUI.showMixedValue = true;
            }
            
            protected override void CloseScope()
            {
                EditorGUI.showMixedValue = _cacheValue;
            }
        }
        
                 // スコープ内の 色を変更する
        public class ColorScope : GUI.Scope
        {
            private readonly Color _cacheValue;

            public ColorScope(Color color)
            {
                _cacheValue = GUI.color;
                GUI.color = color;
            }
            
            protected override void CloseScope()
            {
                GUI.color = _cacheValue;
            }
        }