【Unity(C#)】UniRxを使ってEditor上でDebugしてみた

##Editor上でDebugしたい！
いちいちプレイ押してDebugするの面倒だったので調べました。

Editor上でパラメーターいじりながら調整する状態が理想形です。

今回はコルーチンを使うので難しいのかな～と思いましたが
uniRxを使うといけるという記事を目にしたので試しました。


##プレイモード時に呼び出されるコード
過去に書いた[クソコード](https://qiita.com/OKsaiyowa/items/167a0a6afa536c33fc38)を再利用しました。
アタッチしたオブジェクトがTargetに設定したオブジェクトに向かって進みます。


```C#:アタッチしたオブジェクトがTargetオブジェクトに向かう
    [SerializeField]
    GameObject targetObj;
    
    Coroutine coroutine;

    float x_Abs;
    float y_Abs;
    float z_Abs;

    [SerializeField,Range(1,100)]
    float speedParameter = 10;

     void Update()
    {
        x_Abs = Mathf.Abs(this.gameObject.transform.position.x - targetObj.transform.position.x);
        y_Abs = Mathf.Abs(this.gameObject.transform.position.y - targetObj.transform.position.y);
        z_Abs = Mathf.Abs(this.gameObject.transform.position.z - targetObj.transform.position.z);
    }

    void Move()
    {
        coroutine = StartCoroutine(MoveCoroutine());  
    }

    IEnumerator MoveCoroutine()
    {
        float speed = speedParameter * Time.deltaTime;
       
        while (x_Abs > 0 || y_Abs > 0 || z_Abs > 0)
        {
            this.gameObject.transform.position = Vector3.MoveTowards(this.gameObject.transform.position, targetObj.transform.position, speed);
            yield return null;
        }

         StopCoroutine(coroutine);
         coroutine = null;
           
    }
```

このコードをEditor上で動かすのが今回のゴールです。

##GUILayout.Button
Editor上でDebugする方法として、
ボタンを押したら、メソッドが実行される仕組みを作ります。

その仕組みに必要なのが**GUILayout.Button**です。

使い方は、Editor拡張コードの中に組み込めばOKです。


```C#
#if UNITY_EDITOR
    [CustomEditor(typeof(拡張したいクラスの名前))]
    public class Debug : Editor
    {
       
        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            EditorDebugTest _RootClass = target as 拡張したいクラスの名前;
            
            //少しスペースを空ける
            EditorGUILayout.Space();

            // 押下時に実行したい処理
            if (GUILayout.Button("Inspectorに表示されるボタン名"))
            {
                _RootClass.実行したいメソッド;
            }
        }
    }
#endif
```

##Observable.FromCoroutine
私もまだ学び始めてばかりなので、
"これを書いたらこう動く"程度でしか理解していません。

とにかく、この**FromCoroutine**というのを使えば、
コルーチンを動かした上で
コルーチンの終わりを監視することができるようです。

しかも、**StartCoroutineを必要としないのでEditor上でも動かすことができます**。

これを先程の**GUILayout.Button**と組み合わせます。


```C#
#if UNITY_EDITOR
    [CustomEditor(typeof(拡張したいクラスの名前))]
    public class Debug : Editor
    {
        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            EditorDebugTest _RootClass = target as 拡張したいクラスの名前;

            //少しスペースを空ける
            EditorGUILayout.Space();

            // 押下時に実行したい処理
            if (GUILayout.Button("Inspectorに表示されるボタン名"))
            {
               Observable.FromCoroutine(_RootClass.MoveCoroutine).Subscribe(_ =>
               {
                  _RootClass.実行したいメソッド;
               });
            }
        }
    }
#endif
```
これでEditor上でコルーチンを呼び出すことに成功しました。

ただし、このままではコルーチンが実行された後に
オブジェクトが移動したままになってしまいます。

それだとDebugとして軽く調整するには不十分なので
動き終わったらもとの位置に戻るコードを加えます。

もとの位置に戻るコードは**FromCoroutine**が
コルーチンの終わりを監視しているので、その中に書いてしまえば終了です。



##最終的なコード
```C#:てすと
using System.Collections;
using UnityEditor;
using UnityEngine;
using UniRx;

public class EditorDebugTest : MonoBehaviour
{
    [SerializeField]
    GameObject targetObj;
    
    Coroutine coroutine;

    Vector3 originalPosition;

    float x_Abs;
    float y_Abs;
    float z_Abs;

    [SerializeField,Range(1,100)]
    float speedParameter = 10;

    bool isCoroutineRun;

    void Move()
    {
        coroutine = StartCoroutine(MoveCoroutine());  
    }

    IEnumerator MoveCoroutine()
    {
        float speed = speedParameter * Time.deltaTime;
        originalPosition = this.gameObject.transform.position;

        x_Abs = Mathf.Abs(this.gameObject.transform.position.x - targetObj.transform.position.x);
        y_Abs = Mathf.Abs(this.gameObject.transform.position.y - targetObj.transform.position.y);
        z_Abs = Mathf.Abs(this.gameObject.transform.position.z - targetObj.transform.position.z);
        while (x_Abs > 0 || y_Abs > 0 || z_Abs > 0)
        {
            x_Abs = Mathf.Abs(this.gameObject.transform.position.x - targetObj.transform.position.x);
            y_Abs = Mathf.Abs(this.gameObject.transform.position.y - targetObj.transform.position.y);
            z_Abs = Mathf.Abs(this.gameObject.transform.position.z - targetObj.transform.position.z);
            this.gameObject.transform.position = Vector3.MoveTowards(this.gameObject.transform.position, targetObj.transform.position, speed);
            yield return null;
        }

        if (coroutine != null)
        {
            StopCoroutine(coroutine);
            coroutine = null;
           
        }
    }

     void ResetPosition()
    {
        this.gameObject.transform.position = originalPosition;
    }

#if UNITY_EDITOR
    [CustomEditor(typeof(EditorDebugTest))]
    public class Debug : Editor
    {
       
        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            EditorDebugTest _RootClass = target as EditorDebugTest;

            EditorGUILayout.Space();

            // 押下時に実行したい処理
            if (GUILayout.Button("Test") && EditorApplication.isPlaying == false)
            {
                if (_RootClass.isCoroutineRun == false)
                {
                    _RootClass.isCoroutineRun = true;

                    Observable.FromCoroutine(_RootClass.MoveCoroutine).Subscribe(_ =>
                    {
                        _RootClass.isCoroutineRun = false;
                        _RootClass.ResetPosition();
                    });
                }
            }
        }
    }
#endif

}
```

イメージ通りいけました。
![a.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/57fe9630-0b8c-45cf-d56c-4aa9c383ad92.gif)

---
2019/04/22 追記
##ボタンはEditor上だけで動かしたい

プレイモード中でもEditor上のボタンが反応してしまう状態だったので
`EditorApplication.isPlaying`を書き加えて防ぎました。

```C#

if (GUILayout.Button("Test") && EditorApplication.isPlaying == false)
{
  //押下時の処理
}
```

`EditorApplication.isPlaying`はEditorがプレイモード中かどうかを判定してbool値を返します。


もう少し便利にしました→[続き](https://qiita.com/OKsaiyowa/items/dddfbe1cd9ab7777f58d)