Configurable Enter Play Mode (再生モードへの素早い切り替え)の 対応


Configurable Enter Play Modeとは?

Unity2019.3から「Configurable Enter Play Mode」という機能を使うことで、Unity上でゲームを起動する時間が早くなる機能(素早い再生モード)が追加されました。
使いかたや対応方法についての詳細についてはこちら。Configurable Enter Play Modeとは?
宴では3.8.0から暫定的に対応をしましたが、宴以外にもプロジェクトに存在するすべてのプログラムも対応していないと正常に機能しません。
この機能はあくまでUnityエディタ上だけの機能なので、ビルドした実機上のゲームの起動時間が早くなるわけではないです。
起動時間が1/10近くなるなど効果は大きく便利なことは便利なのですが、現時点で使うには相当なハイリスクだと思うので、技術的なトラブルを自力で調査・回避できてUnityのバージョンアップや仕様変更に伴うメンテナンスができる自信があって使いたい人だけが使うのが良いかと思います。

注意点

このUnityの新しい機能は過去のプログラムの書き方が通用しなくなる「破壊的な変更」です。
「Reload Domain」のみをオフにすればあまり問題はないのですが、「Reload Scene」もオフにするのはお勧めしません。
宴本体はできる限り対応しましたが、過去の宴のサンプルやQ&Aでのプログラムの一部は正常に動作しなくなる可能性があります。
「Reload Domain」のみをオフにしても、効果は十分得られると思うので使うとしてもそちらの設定をお勧めします。

「Reload Scene」オフにすると、書き換えが必要となるよくある例を以下に挙げます。

古いサンプル

using UnityEngine;
using Utage;

public class Sample : MonoBehaviour
{
    AdvEngine Engine { get { return engine ?? (engine = FindObjectOfType<AdvEngine>()); } }
    [SerializeField]
    AdvEngine engine;
}


新しいサンプル

using UnityEngine;
using Utage;
using UtageExtensions;


public class Sample : MonoBehaviour
{
    AdvEngine Engine { get { return this.GetComponentCacheFindIfMissing( ref engine); } }
    [SerializeField]
    AdvEngine engine;
}

このように、書くようにしてください。

詳しい説明

事情が非常に複雑なのですが、一応詳しく説明します。
もともとは宴のAdvEngineを使ったコンポーネントの単純なサンプルです。
宴のユーザーは、Unityに慣れていない人も多いため「インスペクター上でAdvEngineにオブジェクトを設定する」ということをしないまま、サンプルが動かないと思って再び質問してくる人が多かったので、
「設定されていなかったら、自動的にシーン上から探してきて設定する」という安全策を追加していました。
つまり、インスペクターで普通にオブジェクトを設定する場合は全く必要がないものです。そもそもFindObjectOfTypeは非常に重いので本来は極力使うべきではないです。

古いサンプルは「ヌル合体演算子」というC#の基本機能を使っています。
ですが、Unityのオブジェクトは「==演算子をoverrideしているためNULLじゃなくても==NULLが成り立つ」という裏仕様があるため、ヌル合体演算子だと予期せぬエラーになることがあります。
それは一応わかっていたのですが「通常のゲーム起動であればオブジェクトの初期値はC#本来のNULLになっているので動作に問題はない」「overrideされたヌルチェックよりもヌル合体演算子のほうが高速のはず」ということもあって、宴にはヌル合体演算子を使っているコードが多くありました。
ですが、「素早い再生モード」を使っている場合は、デフォルト値はNULLではなくUnityが生成するインスタンスが設定されるようです。
Unity.Objectの場合は「NULL判定ではNULLだけど、実際はNULLじゃない」オブジェクトが設定されるようで、これがデフォルト値として設定されてしまいます。
(ちなみにListやArrayの場合はprivateで明示的にNULLを設定しててもサイズ0のインスタンスになりますのでその対応も必要です)
このため「初期値はNULLのはずなのでヌル合体演算子を使う」といった古いコードは全て通用しなくなり、書き直す必要があります。
宴の内部では似たような手法でGetComponentしたキャッシュを保持することをしてるものが多いのですが、それも全て書き直してあります。
新しいコードは、拡張メソッド「GetComponentCacheFindIfMissing」で新しい方法に対応しています。
宴の拡張メソッドを使う場合はネームスペース「UtageExtensions」が必要になるため、using UtageExtensions;を追加するのを忘れないようにしてください。

また、そもそも最初からインスペクター上で普通にオブジェクトを設定すればよいだけなので、その場合は普通のコードを書くだけです。

using UnityEngine;
using Utage;


public class Sample : MonoBehaviour
{
    AdvEngine Engine { get { return engine; } }
    [SerializeField]
    AdvEngine engine;
}

重要な点

通常のUnityの使い方をする場合は全く気にしなくてよいものです。
古いサンプルの書き方が問題になるのは、あくまで「Configurable Enter Play Mode」というUnity2019.3の最新の実験的な機能を使う場合にだけです。
また「素早い再生モード」による不具合は宴以外が原因である可能性もありますので、対応は非常に難しいです。
別のアセットや自作のプログラムが原因であっても、宴の部分でエラーが起きる可能性がありますので、
「宴以外がない状態のプロジェクトで不具合を再現可能な場合」または「宴のプログラムの不具合箇所と対応方法が特定できる場合」に限ってのみ対応可能ですが、それ以外では不具合報告を受けても調査自体できないかと思います。