【Unity/UniRx】UniRx を EnterPlayMode の Domain Reload に対応させる

UniRx

Unity向けの有名なライブラリにUniRxがあります。

このライブラリはコードが分散されて煩雑になりやすいイベント型の処理をメソッドチェーンで書けるので馴れると非常に便利です。

UniRx – Reactive Extensions for Unity
https://github.com/neuecc/UniRx

Enter Play Mode

Enter Play Mode は Unityエディタ上でプレイボタンを押してゲームを開始する際に行われる初期化処理の一部をスキップできる機能です。

Unity 2019.3 で再生モードへの素早い切り替えが可能に
https://blogs.unity3d.com/jp/2019/11/05/enter-play-mode-faster-in-unity-2019-3/

スキップの効果により初期化の待ち時間が減るのでイテレーション効率が上がります。

ですが、その代償として副作用もあります。
特にスキップの効果が大きい Domain Reload (C#コードの初期化)では static な変数がリセットされないことで問題が発生する可能性があるので、自分でリセットする処理を書かねばなりません。

これについては上記のリンク先(Unityさんのブログ)でも紹介されていますが、RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration) 属性を付与したメソッドが、プレイ時に呼ばれる様になるので、ここで static 変数を初期化します。

UniRx の Domain Reload 対応

UniRx も Domain Reload のスキップを有効にしたところ、2回目以降のプレイで例外が発生し機能しなくなってしまいました。

そこで、コードを以下の様に改造してみたところ動くようになったので紹介します。

Assets/UniRx/Scripts/UnityEngineBridge/ScenePlaybackDetector.cs

OnDidReloadScripts() メソッドに RuntimeInitializeOnLoadMethod 属性を追加しました。

        // This callback is notified after scripts have been reloaded.
        [DidReloadScripts, RuntimeInitializeOnLoadMethod( RuntimeInitializeLoadType.SubsystemRegistration )]
        public static void OnDidReloadScripts()
        {
            // Filter DidReloadScripts callbacks to the moment where playmodeState transitions into isPlaying.
            if (AboutToStartScene)
            {
                IsPlaying = true;
            }
        }

Assets/UniRx/Scripts/UnityEngineBridge/MainThreadDispatcher.cs

前例にならって、OnDidReloadScripts() という名前のメソッドを追加しました。
同様に RuntimeInitializeOnLoadMethod 属性を付けて static変数を初期化する様にしました。

        [RuntimeInitializeOnLoadMethod( RuntimeInitializeLoadType.SubsystemRegistration )]
        static void OnDidReloadScripts()
        {
            instance = null;
            initialized = false;
            isQuitting = false;
        }

おわりに

いかがでしたでしょうか?
もしお役に立てば幸いです。

また、それほどUniRxのコードを理解しているわけではないので、もし誤りがありましたらご容赦ください。