[備忘録] Unity Editor上でスクリプトをGameObjectから削除したときに検知する

自分メモ用です。

# Editor:OnDestroy()のコールされるタイミング
スクリプトがGameObjectから削除(Remove Component)されるタイミングはEditor:OnDestroy()となるが
ただし、下記の条件がある。

1. ヒエラルキーウィンドウで該当するGameObjectの選択が外れたとき
1. ヒエラルキーウィンドウで該当するGameObjectの選択したままエディタでプレイを開始したとき
1. ヒエラルキーウィンドウで該当するGameObjectの選択したままエディタでプレイを終了したとき
1. スクリプトをGameObjectから削除(Remove Component)したとき

今のところわかっているのは上記のものになる。


# プレイモードの状態を知るには
エディタでプレイボタンを押したときにEditor:OnDestroyがコールされるので
そのときのプレイモードを検知する方法。

```csharp:
using UnityEditor;
using UnityEngine;
using System;

[InitializeOnLoad]
static public class EditorPlayMode
{
    const string KEY_EDITOR_PLAY_MODE = "KEY_EDITOR_PLAY_MODE";

    public enum State
    {
        Init,
        PrePlay,
        Play,
        Pause,
        PreStop,
        Stop,
    }

    static State playMode = State.Init;

    static EditorPlayMode()
    {
        EditorApplication.playmodeStateChanged += () =>
        {
            if (EditorApplication.isPaused && EditorApplication.isPlaying)
            {
                playMode = State.Pause;
                EditorUserSettings.SetConfigValue(KEY_EDITOR_PLAY_MODE, playMode.ToString());
            }
            else if (EditorApplication.isPlayingOrWillChangePlaymode && !EditorApplication.isPlaying)
            {
                playMode = State.PrePlay;
                EditorUserSettings.SetConfigValue(KEY_EDITOR_PLAY_MODE, playMode.ToString());
            }
            else if (EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying)
            {
                playMode = State.Play;
                EditorUserSettings.SetConfigValue(KEY_EDITOR_PLAY_MODE, playMode.ToString());
            }
            else if (!EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying)
            {
                playMode = State.PreStop;
                EditorUserSettings.SetConfigValue(KEY_EDITOR_PLAY_MODE, playMode.ToString());
            }
            else if (!EditorApplication.isPlayingOrWillChangePlaymode && !EditorApplication.isPlaying)
            {
                playMode = State.Stop;
                EditorUserSettings.SetConfigValue(KEY_EDITOR_PLAY_MODE, playMode.ToString());
            }
        };
    }

    static public State PlayMode
    {
        get
        {
            // MEMO: State.PrePlay設定後Playが実行されるとインスタンスの再生成で状態が初期化されるため
            //       ファイルへ書き込んで状態を復帰させるように変更
            if (playMode==State.Init)
            {
                string value = EditorUserSettings.GetConfigValue(KEY_EDITOR_PLAY_MODE);

                if(!string.IsNullOrEmpty(value))
                {
                    try
                    {
                        object ret = Enum.Parse(typeof(State), value, true);

                        if (ret != null)
                        {
                            playMode = (State)ret;
                        }
                    }
                    catch(Exception ex)
                    {
                        Debug.LogException(ex);
                    }
                }
            }

            return playMode;
        }
    }
}

```

プレイボタンを押して開始する直前(State.PrePlay)のときのみEditorUserSettingsに保存すればいいのですが
エディタ上なのでスピードとか無視していいしということで他のステータスでも保存してます。

なぜファイルへ保存しているかは、

1. 該当するGameObjectをヒエラルキーウィンドウで選択する
1. プレイを開始する
1. プレイモード用にインスタンスを再生成する
1. このとき既存のインスタンスを削除する(Editor.OnDestroyがコールされる)

という流れでEditorPlayModeクラスのインスタンスでは状態を保持できなくなるため
ファイルへ保存した。


# スクリプトの削除を検知する
Editor:OnDestroy()のタイミングでtargetがnullになっているときが
スクリプトが削除されたタイミングになる。
しかしプレイモードの状態と合わせないと完全なスクリプトの削除タイミングを検知できない。

```csharp:
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(HogeHoge))]
public class HogeHogeEditor : Editor
{
    HogeHoge hogehoge;
    
    void OnEnable()
    {
        hogehoge = target as HogeHoge;
    }

    protected void OnDestroy()
    {
        EditorPlayMode.State playMode = EditorPlayMode.PlayMode;

        if ((target == null) && (playMode != EditorPlayMode.State.PrePlay) && (playMode != EditorPlayMode.State.PreStop))
        {
            Debug.Log("スクリプトが削除された. target:" + (target != null));
            hogehoge.OnDestroy();
        }
    }
}

public class HogeHoge : MonoBehaviour
{
    public void OnDestroy()
    {
        if (Application.isEditor)
        {
            // MEMO: 削除したいGameObjectなど...
            //UnityEngine.Object.DestroyImmediate(gameObject);
        }
    }
}
```


# おわりに
もっと簡単な方法がある気がするのだけどUnityに詳しい人おせーて！ :grin: