Unityでエディタを拡張してみた

エディタの拡張をしてやりたいことがあるわけではないけれど、簡単に拡張できるらしいので試してみた。

Unityのマニュアルにエディターウィンドウの拡張について書かれ例が載っている。

Example.cs

”Editor”という名前のフォルダー内にスクリプトがある必要があるそうなので、Assetsフォルダの下にEditorフォルダを作りそこにExampleという名前のC#Scriptを作ってみた。コードはサンプルからちょっと変更して日本語のメニュー、タイトル、ラベルを試してみた。EditorWindowクラスから派生する必要がある。開いたら余白が多いウィンドウになったのでmaxSize,minSizeを指定して小さくしてみた。

using UnityEngine;
using UnityEditor;
using System.Collections;

public class Example : EditorWindow
{
    [MenuItem("独自のメニュー/独自のウィンドウ")]
    public static void ShowWindow()
    {
        //既存のウィンドウのインスタンスを表示。ない場合は作成します。
        EditorWindow.GetWindow(typeof(Example));
    }
    void OnGUI()
    {
        this.titleContent = new GUIContent("タイトル");
        this.maxSize = new Vector2(200, 100);
        this.minSize = new Vector2(200, 100);

        // 実際のウィンドウのコードはここに書きます
        GUILayout.Label("ラベル", EditorStyles.boldLabel);
    }
}

MyWindow.cs

マニュアルには「詳しくは、EditorWindow (エディターウィンドウ)に関するページを参照してください。」と書かれている。サンプルをMyWindow.csにコピー&ペーストしてみた。

サンプルのコードを見ると、OnGUI()の中にEditorGUILayoutがたくさん出てくるので、マニュアルの検索窓でEditorGUILayoutを検索してみた。

いくつか関数の先のサンプルコードをMyWindow.csにコピー&ペーストして追加してみた。

#region 文字列 ~#endregionはVisual Studioで閉じたり開いたりできる。閉じたときは#region 文字列の文字列の所が表示される。

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEditor.AnimatedValues;

// https://docs.unity3d.com/ja/current/Manual/editor-EditorWindows.html
// https://docs.unity3d.com/ja/current/ScriptReference/EditorWindow.html
// https://docs.unity3d.com/ja/current/ScriptReference/EditorGUILayout.html

public class MyWindow : EditorWindow
{
    #region EditorWindow
    string myString = "Hello World";
    bool groupEnabled;
    bool myBool = true;
    float myFloat = 1.23f;
    #endregion

    #region EditorGUILayout.BeginFadeGroup
    AnimBool m_ShowExtraFields;
    string m_String;
    Color m_Color = Color.white;
    int m_Number = 0;
    #endregion

    #region EditorGUILayout.BeginScrollView
    Vector2 scrollPos;
    string t = "This is a string inside a Scroll view!";
    #endregion

    #region EditorGUILayout.CurveField
    AnimationCurve curveX = AnimationCurve.Linear(0, 0, 10, 10);
    AnimationCurve curveY = AnimationCurve.Linear(0, 0, 10, 10);
    AnimationCurve curveZ = AnimationCurve.Linear(0, 0, 10, 10);
    #endregion

    // Window メニューに "My Window" というメニュー項目を追加
    [MenuItem("Window/My Window")]
    public static void ShowWindow()
    {
        //既存のウィンドウのインスタンスを表示。ない場合は作成します。
        EditorWindow.GetWindow(typeof(MyWindow));
    }

    #region EditorGUILayout.BeginFadeGroup
    void OnEnable()
    {
        m_ShowExtraFields = new AnimBool(true);
        m_ShowExtraFields.valueChanged.AddListener(Repaint);
    }
    #endregion

    void OnGUI()
    {
        #region EditorWindow
        GUILayout.Label("Base Settings", EditorStyles.boldLabel);
        myString = EditorGUILayout.TextField("Text Field", myString);

        groupEnabled = EditorGUILayout.BeginToggleGroup("Optional Settings", groupEnabled);
        myBool = EditorGUILayout.Toggle("Toggle", myBool);
        myFloat = EditorGUILayout.Slider("Slider", myFloat, -3, 3);
        EditorGUILayout.EndToggleGroup();
        #endregion

        #region EditorGUILayout.BeginFadeGroup
        // EditorGUILayout.BeginFadeGroup
        m_ShowExtraFields.target = EditorGUILayout.ToggleLeft("Show extra fields", m_ShowExtraFields.target);

        //Extra block that can be toggled on and off.
        if (EditorGUILayout.BeginFadeGroup(m_ShowExtraFields.faded))
        {
            EditorGUI.indentLevel++;
            EditorGUILayout.PrefixLabel("Color");
            m_Color = EditorGUILayout.ColorField(m_Color);
            EditorGUILayout.PrefixLabel("Text");
            m_String = EditorGUILayout.TextField(m_String);
            EditorGUILayout.PrefixLabel("Number");
            m_Number = EditorGUILayout.IntSlider(m_Number, 0, 10);
            EditorGUI.indentLevel--;
        }
        #endregion

        #region EditorGUILayout.BeginHorizontal
        EditorGUILayout.EndFadeGroup();

        Rect r = EditorGUILayout.BeginHorizontal("Button");
        if (GUI.Button(r, GUIContent.none))
            Debug.Log("Go here");
        GUILayout.Label("I'm inside the button");
        GUILayout.Label("So am I");
        EditorGUILayout.EndHorizontal();
        #endregion

        #region EditorGUILayout.BeginScrollView
        EditorGUILayout.BeginHorizontal();
        scrollPos =
            EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Width(100), GUILayout.Height(100));
        GUILayout.Label(t);
        EditorGUILayout.EndScrollView();
        if (GUILayout.Button("Add More Text", GUILayout.Width(100), GUILayout.Height(100)))
            t += " \nAnd this is more text!";
        EditorGUILayout.EndHorizontal();
        if (GUILayout.Button("Clear"))
            t = "";
        #endregion

        #region EditorGUILayout.CurveField
        curveX = EditorGUILayout.CurveField("Animation on X", curveX);
        curveY = EditorGUILayout.CurveField("Animation on Y", curveY);
        curveZ = EditorGUILayout.CurveField("Animation on Z", curveZ);

        if (GUILayout.Button("Generate Curve"))
            AddCurveToSelectedGameObject();
        #endregion
    }
    #region EditorGUILayout.CurveField
    void AddCurveToSelectedGameObject()
    {
        if (Selection.activeGameObject)
        {
            FollowAnimationCurve comp =
                Selection.activeGameObject.AddComponent<FollowAnimationCurve>();

            comp.SetCurves(curveX, curveY, curveZ);
        }
        else
        {
            Debug.LogError("No Game Object selected for adding an animation curve");
        }
    }
    #endregion
}

#region EditorGUILayout.CurveField
public class FollowAnimationCurve : MonoBehaviour
{
    public AnimationCurve curveX;
    public AnimationCurve curveY;
    public AnimationCurve curveZ;

    public void SetCurves(AnimationCurve xC, AnimationCurve yC, AnimationCurve zC)
    {
        curveX = xC;
        curveY = yC;
        curveZ = zC;
    }

    void Update()
    {
        transform.position = new Vector3(curveX.Evaluate(Time.time), curveY.Evaluate(Time.time), curveZ.Evaluate(Time.time));
    }
}
#endregion

簡単に拡張できることがわかった。

EditorGUILayoutは将来削除される可能性がある

EditorGUILayoutのVersionが2019.4になっていたので、2021.1にしたらページがないと言われた。

検索したときにもうひとつEditorGUILayoutというタイトル名のページがあった。

Experimental: this API is experimental and might be changed or removed in the future.

https://docs.unity3d.com/ja/current/ScriptReference/Experimental.Networking.PlayerConnection.EditorGUILayout.html

将来は無くなってしまうのだろうか…