LoginSignup
1

More than 3 years have passed since last update.

posted at

updated at

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

Editor上でDebugしたい!

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

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

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

プレイモード時に呼び出されるコード

過去に書いたクソコードを再利用しました。
アタッチしたオブジェクトがTargetに設定したオブジェクトに向かって進みます。

アタッチしたオブジェクトが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です。

#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と組み合わせます。

#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
コルーチンの終わりを監視しているので、その中に書いてしまえば終了です。

最終的なコード

てすと
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


2019/04/22 追記

ボタンはEditor上だけで動かしたい

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


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

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

もう少し便利にしました→続き

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1