Unity : どのシーンから起動しても行いたい初期化処理を楽に実現する方法

開発効率を意識すると、どのシーンからでもアプリが実行できるようにすべきです
そしてそのために、共通の起動時初期化処理を用意することが多いと思います
「初期化処理を行うオブジェクトを各シーンに配置する」という方法が経験上多かったですが、少々面倒だったり...
今回は、そんな手間を省けて、シーンもスッキリした状態を保てるような方法を見つけたので紹介します

RuntimeInitializeOnLoadMethod

  • この属性を指定したメソッドは起動時に呼び出される
    • メソッドを定義してるのがMonoBehaviour非継承クラスでもOK
  • プロジェクト内のスクリプトであれば問答無用で呼び出されるので注意
    • プロジェクト内での使用に留めた方が良さそう
      • 黙って勝手に使うとバグだと思われるので、合意をとった上で使った方がいい
  • Awake()より先に行いたい処理もここに書けば確実 (なんかの設定の変更とか)
  • 詳しくは テラシュールさんの記事 を参照

どのシーンから起動しようと問答無用で呼ばれるので、ある程度制御してやる必要があります
ここでは、起動時のシーンに応じた初期化処理を行う例を紹介します

ソースコード

using System;
using UnityEngine;
using UnityEngine.SceneManagement;

public class EntryPoint
{
    [RuntimeInitializeOnLoadMethod]
    static void Initialize()
    {
        CreateGlobalObject();
    }

    static void CreateGlobalObject()
    {
        string currentMainSceneName = SceneManager.GetActiveScene().name;
        if (!EnumUtil.TryParse(currentMainSceneName, out SceneType sceneType)) {
            return;
        }

        GameObject globalObject = new GameObject("GlobalObject");
        Object.DontDestroyOnLoad(globalObject);
    }
}

public enum SceneType
{
    Test,
}

static class EnumUtil
{
    public static bool TryParse<TEnum>(string s, out TEnum e) where TEnum : struct
    {
        return Enum.TryParse(s, out e) && Enum.IsDefined(typeof(TEnum), e);
    }
}

解説

   [RuntimeInitializeOnLoadMethod]
    static void Initialize()
    {
        CreateGlobalObject();
    }
  • アプリ起動時にCreateGlobalObject()を実行
       string currentMainSceneName = SceneManager.GetActiveScene().name;
        if (!EnumUtil.TryParse(currentMainSceneName, out SceneType sceneType) || sceneType == SceneType.Invalid) {
            return;
        }
  • 現在のメインシーン名をSceneTypeにパースし、失敗した場合は処理しない
  • ホワイトリスト(=SceneType)によるチェック
    • この例だと、シーン名が"Test"であるシーン以外では処理しない
       GameObject globalObject = new GameObject("GlobalObject");
        Object.DontDestroyOnLoad(globalObject);
  • "GlobalGameObejct"という名前のオブジェクトを生成し、常駐オブジェクトに設定
   public static bool TryParse<TEnum>(string s, out TEnum e) where TEnum : struct
    {
        return Enum.TryParse(s, out e) && Enum.IsDefined(typeof(TEnum), e);
    }

参考

tsubakit1.hateblo.jp qiita.com