【Unity】エディターの再生モードがONのときに押せるボタンをインスペクターに追加する

# はじめに

Unityでシーンの実行中にデバッグ機能を任意のタイミングで実行したいため、インスペクターにデバッグ機能を実行するボタンを設置するカスタムエディタークラスを作成したので、その紹介記事となります。

[作成時環境]
Unity 2021.3.30f

# 実行結果
![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553099/cb3759ad-ea9d-fce3-dd7c-8f36ebb86a05.gif)

# ソースコード

インスペクターにボタンを追加するベースクラスを作成し、ベースクラスを継承して使用する想定のものを作成しました。

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class AddEditorPlayingActiveButtonToInspectorBase : MonoBehaviour
{
    public virtual void OnInspectorButton(){}
}

#if UNITY_EDITOR
[CustomEditor(typeof(AddEditorPlayingActiveButtonToInspectorBase), true)]
public class AddEditorPlayingActiveButtonToInspectorBaseEditor : Editor
{
    private const string _buttonText = "Apply";

    public override void OnInspectorGUI()
    {
        GUI.enabled = EditorApplication.isPlaying;  //Editorが実行中の場合GUIがアクティブになる
        //実行用ボタン
        if (GUILayout.Button(_buttonText))
        {
            //class情報を取得
            var settingScript = target as AddEditorPlayingActiveButtonToInspectorBase;

            //処理の実行
            settingScript?.OnInspectorButton();
        }
        GUI.enabled = true;

        //元のInspector部分を表示
        base.OnInspectorGUI();
    }
}
#endif
```

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DebugAction : AddEditorPlayingActiveButtonToInspectorBase
{
    [SerializeField]
    private string _showLogStr = "test";
    
    public override void OnInspectorButton()
    {
        Debug.Log(_showLogStr);
    }
}
```

# ソースコード解説

1. ベースクラスの作成
    
    今回はクラスの継承機能を使用を想定して作成してあります。まず継承元となるベースクラスを作成し、インスペクターで使用するためMonoBehaviourクラスを継承してます。そしてボタンが押されたときに呼ばれる関数を、継承先でoverrideできるようにvirtualで作成します。
    
    ```csharp
    public class AddEditorPlayingActiveButtonToInspectorBase : MonoBehaviour
    {
        public virtual void OnInspectorButton(){}
    }
    ```
    
1. カスタムエディタークラスの作成
    
    次にゲームオブジェクトのインスペクター内にボタンを設置するためにカスタムエディター機能を使用します。Editorクラスを継承したクラスを作成し、CustomEditorのアトリビュートを設定します。CustomEditorの第一引数に先ほど作成した機能を付けるクラスを設定し、第二引数にtrueに設定することで、継承先でもカスタムエディターが使えるようにします。
    
    ```csharp
    [CustomEditor(typeof(AddEditorPlayingActiveButtonToInspectorBase), true)]
    public class AddEditorPlayingActiveButtonToInspectorBaseEditor : Editor
    {
    }
    ```
    [参考]
    [カスタムエディター - Unity マニュアル](https://docs.unity3d.com/ja/2021.3/Manual/editor-CustomEditors.html)
    [CustomEditor-CustomEditor - Unity スクリプトリファレンス](https://docs.unity3d.com/ja/2021.3/ScriptReference/CustomEditor-ctor.html)

1. インスペクター内のインターフェース拡張するカスタムインスペクターを定義
    
    カスタムインスペクターを作成するために、EditorクラスのOnInspectorGUI関数をoverrideします。このOnInspectorGUIに追加するインターフェイスを定義します。また、デフォルトのインターフェイスを表示するてめにbase.OnInspectorGUI()を定義する必要があります。
    
    ```csharp
    [CustomEditor(typeof(AddEditorPlayingActiveButtonToInspectorBase), true)]
    public class AddEditorPlayingActiveButtonToInspectorBaseEditor : Editor
    {
        public override void OnInspectorGUI()
        {
            //元のInspector部分を表示
            base.OnInspectorGUI();
        }
    }
    ```
    [参考]
    [Editor-OnInspectorGUI - Unity スクリプトリファレンス](https://docs.unity3d.com/ja/2021.3/ScriptReference/Editor.OnInspectorGUI.html)
    
1. カスタムインスペクター内に再生モードONのときに有効になるボタンを定義
    
    OnInspectorGUI関数内にボタン処理を記述します。まずGUIの有効無効状態を設定するためにGUI.enabledを使用します。GUI.enabledには現在の再生モードを取得するEditorApplication.isPlayingの値を入れることで、再生モードONのときにGUIが有効になるようにします。
    次にボタンの表示と押下判定をしてくれるGUILayout.Buttonを使用します。GUILayout.Buttonがtrueの時にボタンが押された処理になるので、if文内に押下されたときの処理を記述します。
    ボタンを押された処理は、ベースクラスで用意したOnInspectorButton関数を呼びます。ベースクラスの参照は、Editorクラスが持っているtarget変数をキャストすることで使用できます。
    最後にGUI.enabledにtrueを入れて、GUI表示を正常に戻します。
    
    ```csharp
    [CustomEditor(typeof(AddEditorPlayingActiveButtonToInspectorBase), true)]
    public class AddEditorPlayingActiveButtonToInspectorBaseEditor : Editor
    {
        private const string _buttonText = "Apply";
    
        public override void OnInspectorGUI()
        {
            GUI.enabled = EditorApplication.isPlaying;  //Editorが実行中の場合GUIがアクティブになる
            //実行用ボタン
            if (GUILayout.Button(_buttonText))
            {
                //class情報を取得
                var settingScript = target as AddEditorPlayingActiveButtonToInspectorBase;
    
                //処理の実行
                settingScript?.OnInspectorButton();
            }
            GUI.enabled = true;
    
            //元のInspector部分を表示
            base.OnInspectorGUI();
        }
    }
    ```    
    [参考]
    [EditorApplication-isPlaying - Unity スクリプトリファレンス](https://docs.unity3d.com/ja/2021.3/ScriptReference/EditorApplication-isPlaying.html)
    [GUI-enabled - Unity スクリプトリファレンス](https://docs.unity3d.com/ja/2021.3/ScriptReference/GUI-enabled.html)
    [GUILayout-Button - Unity スクリプトリファレンス](https://docs.unity3d.com/ja/2021.3/ScriptReference/GUILayout.Button.html)
    [Editor-target - Unity スクリプトリファレンス](https://docs.unity3d.com/ja/2021.3/ScriptReference/Editor-target.html)
    
1. 作成したベースクラスを使用する
    
    ゲームオブジェクトのインスペクターに今回作成したボタン機能を追加する場合は、ベースクラスを継承し、OnInspectorButton関数をoverrideすることで使用可能です。OnInspectorButton関数内に実行したい処理を記述すれば完成です。
    
    ```csharp
    public class DebugAction : AddEditorPlayingActiveButtonToInspectorBase
    {
        [SerializeField]
        private string _showLogStr = "test";
        
        public override void OnInspectorButton()
        {
            Debug.Log(_showLogStr);
        }
    }
    ```

# おわりに

もともとは、シーン実行中にインスペクターの設定したパラメーターをもとにオブジェクトを動的生成する処理を任意のタイミングで実行したい用途のために作成した機能をベースクラス化したものとなります。インスペクターにボタン設置するだけでもそこそこ便利だったりするので、その処理のメモも兼ねてます。