LoginSignup
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-10-09

前回の記事の続きです。

リファクタリングとバグ修正です。

PlayMode→EditMode

PlayMode中に動かしたオブジェクトはEditModeに切り替えると元に戻ります。
MovePlayMode.gif
これはもともとのEditorの機能ですよね。

EditMode→PlayMode

前回の記事でEditor上でDebugできることは確認できたのですが、
動かしている最中にPlayModeに移ると、その位置で停止してしまいます。
MoveEditorMode.gif

これはなんとかしないと、かえって効率が悪くなりそうです。

EditorApplication.playModeStateChanged

Editor上で再生状態が変更されたときに呼びだされるコールバックです。
EditMode→PlayModeに移る瞬間にポジションをもとに戻してあげればいいので
EditorApplication.playModeStateChangedにポジションを戻す処理を登録しとけばいけそうです。

Editor拡張です
        static MyDebug rootClass;

        //コンストラクタでPlayMode,EditModeの切り替え時の処理を登録
        static MyDebugEditor()
        {
            EditorApplication.playModeStateChanged += OnPlayMode;
        }

        static void OnPlayMode(PlayModeStateChange playModeStateChange)
        {
            //途中でPlayModeに切り替えても元に戻す
            if (playModeStateChange == PlayModeStateChange.ExitingEditMode)
            {
                //ポジションをもとに戻す処理を書いたメソッド
                rootClass.ResetPosition();
            }
        }

PlayMode⇔EditModeの変更がそれぞれ用意されています。
今回はExitingEditModeを使うのでEditModeが終了してPlayModeが始まる直前です。

[ExecuteInEditMode]

[ExecuteInEditMode]を使うことでコルーチンのコードがスッキリしました。
まずは使う前の記事です。

コルーチンの処理

        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;
        }

EditModeでは、MonoBehaviourのUpdate内の処理は呼ばれないので
本来Update内に記述しておけばスッキリするコードをコルーチン内に長々と書いています。

しかし、[ExecuteInEditMode]を使うことでEditModeでもUpdateを呼んでくれます。
下記が修正後です。

修正後

[ExecuteInEditMode]
public class MyDebug : MonoBehaviour{

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

    IEnumerator MoveCoroutine()
    {
        while (x_Abs > 0 || y_Abs > 0 || z_Abs > 0)
        {
            this.gameObject.transform.position = Vector3.MoveTowards(this.gameObject.transform.position, targetTrasform.position, speed);
            yield return null;
        }
    }

最終的なコード

using System.Collections;
using UnityEngine;
using UnityEditor;
using UniRx;

[ExecuteInEditMode]
public class MyDebug : MonoBehaviour
{
    [SerializeField]
    Transform targetTrasform;

    [SerializeField, Range(5, 30)]
    float speedParameter = 10;

    [Header("---ここからデバッグ設定---")]
    [SerializeField]
    bool debug_SpaceKey;

    [SerializeField]
    float backTime = 3;

    Vector3 thisObjPos;
    Coroutine runCoroutine;

    float x_Abs;
    float y_Abs;
    float z_Abs;

    bool isStart;

    void Update()
    {
        //エラーで警告
        if (targetTrasform == null)
        {
            Debug.LogError("Please set target");
            return;
        }

        x_Abs = Mathf.Abs(this.gameObject.transform.position.x - targetTrasform.position.x);
        y_Abs = Mathf.Abs(this.gameObject.transform.position.y - targetTrasform.position.y);
        z_Abs = Mathf.Abs(this.gameObject.transform.position.z - targetTrasform.position.z);


        //プレイモード時テスト用
        if (Input.GetKeyDown(KeyCode.Space) && debug_SpaceKey)
        {
            Fall();
        }
    }

    //Editor上でのみ使うメソッド。座標を開始位置まで戻す
    public void ResetPosition()
    {
        this.gameObject.transform.position = thisObjPos;
    }

    //利用時はこれを呼ぶ
    public void Fall()
    {
        if (runCoroutine == null)
        {
            runCoroutine = StartCoroutine(MoveCoroutine());
        }

    }

    IEnumerator MoveCoroutine()
    {
        //Targetまで移動
        while (x_Abs > 0 || y_Abs > 0 || z_Abs > 0)
        {
            float speed = speedParameter * Time.fixedDeltaTime;
            this.gameObject.transform.position = Vector3.MoveTowards(this.gameObject.transform.position, targetTrasform.position, speed);
            yield return null;
        }

        //Editor上で動かした場合のみここを通る。元の座標に戻るまでの秒数
        if (EditorApplication.isPlaying == false)
        {
            yield return new WaitForSeconds(backTime);
        }

        //PlayMode中はrunCoroutineにMoveCoroutineが入ってるので止めて空にしとく
        if (runCoroutine != null)
        {
            StopCoroutine(runCoroutine);
            runCoroutine = null;
        }
    }

    //ここからEditor拡張
#if UNITY_EDITOR
    [CustomEditor(typeof(MyDebug))]
    public class MyDebugEditor : Editor
    {
        static MyDebug rootClass;

        //コンストラクタでPlayMode,EditModeの切り替え時の処理を登録
        static MyDebugEditor()
        {
            EditorApplication.playModeStateChanged += OnPlayMode;
        }

        static void OnPlayMode(PlayModeStateChange playModeStateChange)
        {
            //まだ一回もTargetのpositionを保存していなかった時の為
            if (rootClass.isStart == false)
            {
                rootClass.thisObjPos = rootClass.gameObject.transform.position;
            }

            //途中でPlayModeに切り替えても元に戻す
            if (playModeStateChange == PlayModeStateChange.ExitingEditMode)
            {
                rootClass.ResetPosition();
            }
        }

        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            rootClass = target as MyDebug;
            EditorGUILayout.Space();

            // 押下時に実行したい処理
            if (GUILayout.Button("Test") && EditorApplication.isPlaying == false)
            {
                //エラーで警告
                if (rootClass.targetTrasform == null)
                {
                    Debug.LogError("Please set target");
                    return;
                }

                if (rootClass.isStart == false)
                {
                    //開始位置の座標保存
                    rootClass.thisObjPos = rootClass.gameObject.transform.position;

                    rootClass.isStart = true;

                    Observable.FromMicroCoroutine(rootClass.MoveCoroutine).Subscribe(_ =>
                    {
                        rootClass.isStart = false;
                        rootClass.ResetPosition();
                    }).AddTo(rootClass);
                }
            }

            serializedObject.Update();
            serializedObject.ApplyModifiedProperties();
        }
    }
#endif
}

MovePerfect.gif

ばっちりですね。

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
0