LoginSignup
0
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

ばっちりですね。

0
0
0

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
  3. You can use dark theme
What you can do with signing up
0
0