うにてぃブログ

主にUnityとC#に関する記事を書いていきます

【Unity】GUI, GUILayout, EditorGUI, EditorGUILayout の違い

GUI や Editor の表示時に使うクラスとしてGUI, GUILayout, EditorGUI, EditorGUILayoutがあるが 名前と機能が似ててよくわからなくなるのでまとめてみる(まとめるとは言ってない)

公式リファレンスの説明

まず公式リファレンスの説明から見てみる

GUI

GUI クラスは手動で位置を決める Unity の GUI インターフェースです。

UnityEngine.GUI - Unity スクリプトリファレンス

GUILayout

GUILayout クラスは GUI の自動レイアウトを行うためのインターフェースです。

UnityEngine.GUILayout - Unity スクリプトリファレンス

EditorGUI

このクラスは GUI クラスの機能と同じように動作します。また、EditorGUILayout クラスと機能が一致するよう実装されています。

UnityEditor.EditorGUI - Unity スクリプトリファレンス

EditorGUILayout

Auto laid out version of EditorGUI. (EditorGUILayout クラスは EditorGUI の自動レイアウトを行うためのインターフェースです。)

UnityEditor.EditorGUILayout - Unity スクリプトリファレンス

公式リファレンスの説明より

説明どおり、Layout とついているものは Rect を指定しなくともよく、逆についていないものは Rect を指定する必要があります

また Editor とついているものは UnityEditor の namespace があるので Editorでしか利用できません

そのため、Runtime の OnGUI で Editor* は使用できなくなります

FlexibleSpace

FlexibleSpace は EditorGUILayout にはなく GUILayout のみにあります。 また EditorGUILayout には Space がありますが、EditorGUI には Space がありません そのため GUI に 機能があれば EditorGUI には機能がないと考えられます

ユーザ入力部

GUI* には ユーザが入力できるものが下記のみしかない

  • Button
  • HorizontalSlider
  • PasswordField
  • TextArea
  • TextField
  • Toggle

EditorGUI* なら型に合わせていろいろ使うことができます

Label の描画

GUI も EditorGUI も label 描画時の処理は どちらも style.Draw を呼んでおり最終的には同一である

    // GUI
    private static void DoLabel(Rect position, GUIContent content, GUIStyle style)
    {
      Event current = Event.current;
      if (current.type != EventType.Repaint)
        return;
      style.Draw(position, content, false, false, false, false);
      if (string.IsNullOrEmpty(content.tooltip) || !position.Contains(current.mousePosition) || !GUIClip.visibleRect.Contains(current.mousePosition))
        return;
      GUIStyle.SetMouseTooltip(content.tooltip, position);
    }

    // EditorGUI
    internal static void LabelFieldInternal(
      Rect position,
      GUIContent label,
      GUIContent label2,
      GUIStyle style)
    {
      int controlId = GUIUtility.GetControlID(EditorGUI.s_FloatFieldHash, FocusType.Passive, position);
      position = EditorGUI.PrefixLabel(position, controlId, label);
      if (Event.current.type != EventType.Repaint)
        return;
      style.Draw(position, label2, controlId);
    }

GUI と GUILayout

GUI.label と GUILayout.Label を見てみる

GUILayout では Rect を style から取得しているだけで GUI.label が呼ばれていた

    // GUI
    private static void DoLabel(Rect position, GUIContent content, GUIStyle style)
    {
      Event current = Event.current;
      if (current.type != EventType.Repaint)
        return;
      style.Draw(position, content, false, false, false, false);
      if (string.IsNullOrEmpty(content.tooltip) || !position.Contains(current.mousePosition) || !GUIClip.visibleRect.Contains(current.mousePosition))
        return;
      GUIStyle.SetMouseTooltip(content.tooltip, position);
    }

    // GUILayout
    private static void DoLabel(GUIContent content, GUIStyle style, GUILayoutOption[] options)
    {
      GUI.Label(GUILayoutUtility.GetRect(content, style, options), content, style);
    }

ついでに、EditorGUILayout.LabelField も見てみると GUILayout と同じく EditorGUI が呼ばれていた

    public static void LabelField(
      GUIContent label,
      GUIContent label2,
      GUIStyle style,
      params GUILayoutOption[] options)
    {
      if (!style.wordWrap)
      {
        EditorGUI.LabelField(EditorGUILayout.s_LastRect = EditorGUILayout.GetControlRect(true, 16f, options), label, label2, style);
      }
      else
      {
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PrefixLabel(label, style);
        Rect rect = GUILayoutUtility.GetRect(label2, style, options);
        int indentLevel = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;
        EditorGUI.LabelField(rect, label2, style);
        EditorGUI.indentLevel = indentLevel;
        EditorGUILayout.EndHorizontal();
      }
    }

Begin / End

例えばBeginHorizontal / EndHorizontal は GUI にも EditorGUI にもある

中身を見てみるとスコープや戻り値が違うだけで処理的には完全に同一でした

    // GUILayout
    public static void BeginHorizontal(
      GUIContent content,
      GUIStyle style,
      params GUILayoutOption[] options)
    {
      GUILayoutGroup guiLayoutGroup = GUILayoutUtility.BeginLayoutGroup(style, options, typeof (GUILayoutGroup));
      guiLayoutGroup.isVertical = false;
      if (style == GUIStyle.none && content == GUIContent.none)
        return;
      GUI.Box(guiLayoutGroup.rect, content, style);
    }

    // EditorGUI
    internal static Rect BeginHorizontal(
      GUIContent content,
      GUIStyle style,
      params GUILayoutOption[] options)
    {
      GUILayoutGroup guiLayoutGroup = GUILayoutUtility.BeginLayoutGroup(style, options, typeof (GUILayoutGroup));
      guiLayoutGroup.isVertical = false;
      if (style != GUIStyle.none || content != GUIContent.none)
        GUI.Box(guiLayoutGroup.rect, GUIContent.none, style);
      return guiLayoutGroup.rect;
    }

つまり

EditorGUI は Editor で使うための GUI の拡張である

Layout がつくものは Rect を指定せずに使え、Rect の指定をしていないだけで内部的には同一処理である

GUI に機能がある場合 EditorGUI にはない

主な用途

GUI、GUILayout

ランタイムで表示するUI用、GUI.enable を用いた入力制御

EditorGUI

CustomPropertyDrawer を利用した場合、こまかい UI 調整をしたい場合

EditorGUILayout

EditorWindow や OnInspectorGUIで利用