読者です 読者をやめる 読者になる 読者になる

(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

全てのシーンに存在し、かつ、一つしか存在してはいけないマネージャー的存在の実装方法【Unity】


このエントリーをはてなブックマークに追加

この記事でのバージョン
Unity 5.3.4f1 Personal


はじめに

Unityでゲームを作っていると、

全てのシーンに存在し、かつ、一つしかいないマネージャー的な存在が必要になりますよね。

例えばBGMやSEなどの管理を行うAudioManagerなんてものとか。


一つしか存在してはいけないマネージャー的存在の実装については、以前紹介しました。



問題は"全てのシーンに存在"の方ですが、素直に全シーンに配置してしまうのは面倒だし、

シーンが変わる度にマネージャーが新しくなるので、シーンを跨いでの処理ができなくなります。


かといってDontDestroyOnLoadを使ってマネージャーが消えないようにすると、

シーンを移動した時にマネージャーが重複してしまいます。


最初のシーンだけに設置するということも考えられますが、

これだと他のシーンだけでテストが出来ません。


今回はそれらの問題を全て解消した方法の方法のご紹介です!


手順は以下のような感じです。

"Awakeより前"と"自動"というのが肝です。

  1. マネージャーだけを置く、マネージャー用シーンを作成
  2. RuntimeInitializeOnLoadMethodを使ってAwakeより前にマネージャー用シーンを自動ロード


イメージとしては以下のような感じ


f:id:kan_kikuchi:20160805133436g:plain


エディタ実行時にマネージャーシーンがロードされているのが分かると思います。

なお、ログを出して確認した所、

エディタ上はもちろん、iOSやAndroidの実機でもAwakeより先にロードされていました。


この方法での利点や欠点はざっくり以下のような感じです。


利点

  • Awake時点で確実にマネージャーが存在している
  • 他のシーンにマネージャーを配置する必要がない
  • どのシーンから開始してもマネージャーがいる
  • マネージャーが重複しない
  • マネージャー用シーンをHierarchyに手動でドロップする必要がない


欠点

  • エディタを実行しないとマネージャーがHierarchyにいないのでエディタ拡張で使えない



-----------------------------追記


シーン自体はAwake前にロード出来てるみたいですが、

ロードしたシーンに含まれるオブジェクトに対して、Awake時点でのアクセスは出来ないようです……。


なので、Awake時点でマネージャーにアクセスしたい場合は下記の"別の方法"にある

ゲームオブジェクトを生成して、そこにマネージャークラスをAdd

する方法がオススメです。


-----------------------------


ManagerSceneAutoLoader

ではでは、コードです。



上記のManagerSceneAutoLoader.csを作成し、

マネージャー用のシーンManagerSceneを作成し、ビルド対象に登録するだけで準備OKです。

また、共通のライトやカメラがあれば、マネージャー用シーンに置いてもいいかもしれません。


f:id:kan_kikuchi:20160805134157p:plain


RuntimeInitializeOnLoadMethod属性と

その引数のRuntimeInitializeLoadType.BeforeSceneLoadのおかげで

LoadManagerSceneがゲーム開始時(シーン読み込み前)に自動で実行されるようになっています。


ちなみにRuntimeInitializeOnLoadMethod とは以下のようなものです。

ユーザーのアクション無しで、ランタイムでゲームがロードされたときにランタイムのクラスメソッドの初期化を行う事ができます。




別の方法

今回はマネージャー用のシーンを作成して、それをロードする方法を紹介しましたが、

ゲームオブジェクトを生成して、そこにマネージャークラスをAddしたり、

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void CreateSelf(){
  //Managerという名前のGameObjectを作成し、ManagerというクラスをAddする
  new GameObject("Manager", typeof(Manager));
}


ManagerのPrefabを作っておいて、そこからインスタンスを生成する、

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void CreateSelf(){
  //Managerという名前のPrefabをResources以下に作成しておき、それをロード、インスタンスを生成する
  GameObject.Instantiate (Resources.Load<GameObject> ("Manager"));
}


というやり方も可能です。


おわりに

元々は最初のシーンでマネージャーシーンを加算し、

確認する時はマルチシーン編集を利用して、

マネージャーシーンを手動でHierarchyに置いていたのですが、

以下の記事でawake前にRuntimeInitializeOnLoadMethodが実行出来る事を知り、

今回の方法に変えました。



地味に面倒だったので助かりました……!