Unityエディタって優秀ですよね。
プレイボタンを押せば、編集中のシーンをいつでも実行できるのは魅力的です。
しかし、ゲームを作ってると、最初に共通の初期化を行いたいケースってあると思います。
C++ではmain関数をエントリに処理されますが、Unityではどうでしょう?
この方法が使えます。
[RuntimeInitializeOnLoadMethod]
このAttributeが指定されたメソッドは、ゲームがロードされた直後に呼び出されます。
この特徴を利用して、InitializeScene.unity と、Initialize.cs を用意します。
(名前は分かりやすければ何でもOKです)
using UnityEngine;
using UnityEngine.SceneManagement;
public class Initialize
{
private const string InitializeSceneName = "InitializeScene";
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void RuntimeInitializeApplication()
{
Debug.Log("RuntimeInitializeApplication");
if (!SceneManager.GetSceneByName(InitializeSceneName).IsValid())
{
SceneManager.LoadScene(InitializeSceneName, LoadSceneMode.Additive);
}
}
}
このスクリプトはGameObjectにアタッチする必要はありません。
ゲームを再生すると、カレントシーンにInitializeSceneを追加ロードします。
どのシーンから起動してもInitializeSceneは必ず追加ロードされるので、このシーンに初期化用のGameObjectを置いてスクリプトを付けておけば、共通の初期化を呼び出せます。
GameObjectが DontDestroyOnLoad なシングルトンであれば、グローバルなマネージャクラスを作ることもできます。デバッグ用のマネージャクラスなど、どのシーンから起動しても処理したいものはInitializeScene に置いて運用すると大変便利です。
ただし、気を付けなければならい点が2つあります。
1つ目はInitializeSceneは、SceneInBuildの最後に配置しておく必要があること。
2つ目は処理の流れです。
- RuntimeInitializeApplicationが呼び出される
- 現在のシーンのオブジェクトのAwakeが呼び出される
- InitlizeシーンのオブジェクトのAwakeが呼び出される
- 現在のシーンのオブジェクトのStartが呼び出される
- InitlizeシーンのオブジェクトのStartが呼び出される
現在のシーンのオブジェクトが先に処理される点に注意してください。
AwakeやStartに書く処理がInitializeSceneのオブジェクトに依存しないように設計しましょう。
例えば、2の時点ではSimpleSoundManagerは存在しないので、参照してはいけません。
もしStart時にBGMを再生したい場合は4でコルーチンを起動し、WaitForEndOfFrameしてからSimpleSoundManager.Instanceでアクセスして処理すると良いでしょう。