今日はMulti Scene Editingを利用して設計する上で注意すべき項目についてまとめてみます。他にも「こんな事ある」的な事があれば、教えてもらえれば超嬉しいです。
- 読み込んだシーンへの参照は1フレーム後に行う
- 「シーンの相互参照」は出来ない
- Scene.UnloadにはUnloadUnusedAssetsは含まれない
- オブジェクトはActiveなシーンに生成される
- Activeなシーンの切替は最小限に留める
- SceneManagerはStart以降に使用する
- Sceneは毎回取得する
- 注意書き
- 関連
読み込んだシーンへの参照は1フレーム後に行う
シーンはResourcesやAssetBundleからPrefabを取得する場合と異なり、即座にロードが完了する訳ではありません。これは、非同期(Async)ではないSceneManager.LoadSceneを使用した場合も同様です。具体的には、シーンのロードはフレームの最後辺りに実行されるみたいです。
なので、Multi Scene Editingでシーンについて編集する場合、LoadSceneを実行したシーンからロードしたシーンへ参照を行う場合、1フレーム後に行う必要があります。
これはSceneManager.sceneLoaded(5.4で追加されたシーン呼出時のコールバック)を使用した場合も同様です。
なお、もう少しイケメン的に行いたい場合は、Sceneクラスを「LoadScene後に」取得し、IsLoadedを監視すると良さそうです。
「シーンの相互参照」は出来ない
クロスシーンリファレンス(シーンの相互参照)は出来ないみたいです。Unity 5.4 b13ではGUIレベルで出来ないようになっていました。
このため、他のシーンと連携する場合、スクリプトを用いて実行時に参照を解決する必要が出てきます。
出てくるのですが、上の「シーンが読込完了するのは1フレーム後」の制約のお陰で、この相互関係も非常にややこしい話になっています。
今のところオススメとしてはサブシーンのオブジェクト群が親シーンに対してシングルトンなりFind経由で参照を行うタイプみたいです。
Scene.UnloadにはUnloadUnusedAssetsは含まれない
必要ならば自分で呼ぶ必要があります。
オブジェクトはActiveなシーンに生成される
オブジェクトはActiveなシーンへ生成されます。これは一つの点において少し厄介です。
例えばControllerオブジェクトがGameObjectを生成した場合、アクティブなシーンによって結果は異なります。
これが起こす問題の一つは「Multi Scene Editingを念頭にSceneをUnloadしてもUnloadしたシーンが生成したオブジェクトが破棄されない」点です。
例えば、ボタンを押したらシーンをアンロードするコードを作成した場合、アクティブなシーンによってはGameObject(Clone)は破棄されますし、逆に破棄されず残ります。
これの解決する方法は二種類あります。
一つはオブジェクトを指定したシーンへ移動する方法です。SceneManager.MoveGameObjectToSceneを使用すれば、指定のオブジェクトを特定のシーンへ移行させる事が出来ます。
もう一つの方法は、生成したオブジェクトへの参照を親が保持し続ける方法です。そしてUnloadでオブジェクトを破棄する際に生成したオブジェクトも破棄します。
生成したオブジェクトは大抵生成しっぱなしではなく何らかのコントロールを行う事が多いので、概ねこちらの方が良さそうな気がします。
もう一つ、Activeなシーンをどんどん入れ替える事も考えられますが、これは余りお勧めしません。
Activeなシーンの切替は最小限に留める
「Activeなシーン」は、単に太字のシーンというだけでなく特別な意味があります。それは上記で説明したような「オブジェクトを生成するシーン」というだけでなく、LighmtapやNavmesh等、シーンに紐づく設定が選択される要因でもあります。
またLightmapは「Activeなシーンを切り替えた際、切り替える以前に呼んでいたライトマップが破綻する」現象がモバイル限定で発生するらしいので、その辺りも含めてActiveの切替はあまり行わない方が良さそうに見えます。
SceneManagerはStart以降に使用する
これはバグなのか仕様なのか判断出来ていませんが、SceneManagerのAPI…例えばSceneManager.GetSceneByName等で呼び出したSceneオブジェクトは、Awakeのタイミングでは正常に動かないっぽいです。
例えばSceneManager.GetSceneByNameをAwakeで呼び出した時、エディタでMulti Scene Editingを行っている時でもロードされてない状態として扱われます。
そのため、例えば起動時にロードしていないシーンをロードする…的なコードをAwakeで実行すると、シーンが二重にロードされたりします。
Sceneは毎回取得する
Sceneですが、Load前とLoad中〜Load後で別物になっている事があります。
なので、下のようにロード前とロード後に取得した場合で挙動が異なります。
ちなみにSceneはクラスではないです(k_yanase さん指摘ありがとうございます!)
ただシーンをUnityエンジン側に問い合わせる際に使用するハンドルはロード後は同一のものを使うっぽいので、アクセス時に取得するといった事はしなくても良さそうです。
(シーンをロード(1)→アンロード→ロード(2)した場合、(1)で取得したSceneと(2)で取得したSceneは別物な点に注意。
注意書き
この項目は追記予定です。
また、情報が古くなる可能性があります。