【Unity】必ず配置したいオブジェクトの処理を簡単に

2018-10-07

Unityでオーディオとかアドとかのマネージャとして、アプリ全体を通して必ずシーン内に存在して欲しいオブジェクトの初期化をどうするか、 結論としてはRuntimeInitializeOnLoadMethodを使うといいよ、という話。

手動のやりかた

エディタでの開発中にタイトルやゲーム中など各シーンで直接作業する場合にそれぞれのシーンに必要なオブジェクトを配置するのは、変更の際にすべてのシーンをいじらないといけないので避けたい。

ひとつの方法として必要なオブジェクトを起動シーンに配置して、未初期化だったら一旦起動シーンに飛ばして再度戻すという方法をとっていた(DontDestroyOnLoadしてあるものとして):

public class BootController : MonoBehaviour {
private static bool initialized;
private static string nextSceneName = "TitleScene"; // 次のシーン
private static readonly string BOOT_SCENE_NAME = "BootScene";

public static void EnsureBootProcess() {
#if UNITY_EDITOR
if (initialized)
return;
initialized = true;
var scene = SceneManager.GetActiveScene();
if (scene.name != BOOT_SCENE_NAME) {
nextSceneName = scene.name;
SceneManager.LoadScene(BOOT_SCENE_NAME); // いったんブートシーンに飛ばす
}
#endif
}

override protected void Start() {
// 必要な初期化を行う
// 次のシーンに遷移(エディタの場合元のシーンに戻る)
SceneManager.LoadScene(nextSceneName);
}
}

各シーンのコントローラのAwakeで必ずBootScene#EnsureBootProcess()を呼び出してやれば必要なオブジェクトが配置された状態になっている。

しかし問題はいくつかあって、

  1. 各シーンのコントローラから明示的に呼び出してやる必要がある
  2. エディタで実行したときに、Hierarchyの状態が初期化されて全部閉じた状態になってしまう
  3. MonoBehaviourAwakeが2回呼び出される

2と3はいったんシーンを遷移しているのでそのようになってしまう。 これがエディタで作業する際に不便だった。

シーンを遷移しない方法

単にプレハブをInstantiate、またはSceneManager.LoadSceneAdditiveを使えばよい。

自動的に行うようにする

しかしまだいちいち自分で呼び出さないといけないのメンドイよね…と思ってたら、UnityにRuntimeInitializeOnLoadMethodというアトリビュートがあって これを指定すると起動時の最初に呼び出してくれる:

public class ManagerSceneController {
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void OnBoot() {
// 必ず配置するオブジェクト用のシーンを読み込む
string managerSceneName = "ManagerScene";
if (!SceneManager.GetSceneByName(managerSceneName).IsValid())
SceneManager.LoadScene(managerSceneName, LoadSceneMode.Additive);

// 必要な初期化を行う
}
}
  • BeforeSceneLoadでシーン読み込み前(Awake前)、After~でシーン読み込み後(Awake後、Start前)に呼び出される

参考