はじめに
この記事ではゲームエンジン Unityにおいて、シーンを遷移させる時にパラメータを渡す方法として、 ZenjectSceneLoader を利用した方法を紹介していきます。
本記事ではZenject ver 7.3.0をターゲットバージョンとしています。(6.x 以下での動作はわかりません)
この記事を3行にまとめると…
- ZenjectSceneLoaderを使うと現在のシーン上から遷移後のSceneContextに任意のパラメータをBindできる
- 遷移後のシーンで稼働するBehaviour内でパラメータをInjectして利用する
- ProjectContextやDecoratorContextを使わなくてもシーン遷移時パラメータ渡しが出来る!
ZenjectSceneLoaderとは
ZenjectSceneLoaderの主な機能(シーン読み込み時の追加的なBindやSceneContextの親子関係の制御)については
ZenjectSceneLoaderのLoadSceneのオーバーロードメソッドの引数に注目して読むことで見えてくるかと思います。
public AsyncOperation LoadSceneAsync(string sceneName)
public AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadMode)
public AsyncOperation LoadSceneAsync(
string sceneName, LoadSceneMode loadMode, Action<DiContainer> extraBindings)
public AsyncOperation LoadSceneAsync(
string sceneName,
LoadSceneMode loadMode,
Action<DiContainer> extraBindings,
LoadSceneRelationship containerMode)
public AsyncOperation LoadSceneAsync(
string sceneName,
LoadSceneMode loadMode,
Action<DiContainer> extraBindings,
LoadSceneRelationship containerMode,
Action<DiContainer> extraBindingsLate)
UnityスタンダードのSceneLoader.LoadSceneには見られない引数として…
- extraBindings
- containerMode
- extraBindingsLate
の3つがあります。
1つ目と3つ目に出てきた extraBindingsとextraBindingsLateは遷移後のシーンに置かれたSceneContextのInstallメソッド内で呼び出される、追加のBindingを設定できるActionです。
extraBindingsのAction内からDiContainer.BindInstance等でパラメータを設定するようにします。例えばゲームリザルトの情報などをここで設定すれば、遷移先シーンのライフタイムに限定した状態でパラメータを渡す形が作れるわけです。
ただし、**遷移後のシーンにSceneContextが存在しない場合は何も起きません。**注意しましょう。
SceneContextがextraBindingsを解決するまでの内部的な挙動について
まずLoadSceneが呼び出された直後のシーン遷移前にSceneContextのstatic変数として定義されたExtraBindingsInstallMethod へとextraBindingsを配置する下準備が行なわれます。
次にシーン遷移後に移って、遷移後のシーンに配置されたSceneContextがAwakeされるタイミングでInstallメソッドが呼ばれます。
そのInstallメソッド内でExtraBindingsInstallMethodが遷移後のシーンのSceneContext上で呼び出される、という流れになっています。
さらに詳しくは ZenjectSceneLoader やZenjectリポジトリ内からExtraBindingsInstallMethodで検索してみてください。
また、LoadSceneで見慣れない引数として LoadSceneRelationship もありますね。こちらはそのソースに書かれたコメントがそのまま説明になるかと思います。
LoadSceneRelationship.Noneを指定した場合はProjectContextを親コンテナに配置します。(default値です)
LoadSceneRelationship.Child を指定した場合には現在のシーンのSceneContextの子として配置できます。(ただ、この場合は LoadSceneMode.Additive を指定する必要がある? )
LoadSceneRelationship.Siblingは遷移前のシーンの親を次のシーンの親とする設定です。多くの場合はNoneとほぼ同義、とのことです。
最後に、extraBindingsLateについてです。LateとあるようにSceneContextのインスペクター経由で設定できるInstallers等を実行した後に実行されます。
実用例が思い浮かばないですが、インスペクター経由でSceneContextに設定できるInstallersなどが解決された後に実行したいBindがあればextraBindingsLateを利用するとよさそうです。
BindやInstallerの解決順については SceneContext のInstallBindings メソッドを確認してみてください。
さて、次に具体的なコードで説明してみたいと思います。
以下は、ZenjectSceneLoader.LoadSceneAsync の extraBindings引数 を使ってパラメータを一個だけに限定してDiContainerにBindを仕掛けるコードの例です。
public class MySceneManager : IMySceneManager
{
[Inject]
ZenjectSceneLoader sceneLoader;
public void TransitionScene<T>(string targetSceneName, T sceneParameter)
{
Debug.Log("Load scene: " + targetSceneName);
sceneLoader.LoadScene("Scenes/" + targetSceneName, LoadSceneMode.Single,
(diContainer) =>
{
if (sceneParameter != null)
{
diContainer.BindInstance(sceneParameter).AsTransient();
}
});
Debug.Log("Load scene done");
}
TransitionSceneメソッドのsceneParameterで指定した一つの値をextraBindingsのActionからDiContainerにBindしています。containerModeは指定していないのでNoneが設定される、ということになりますね。
上記のシーン間パラメータとして宣言しているテンプレート型 T は、非同期で使うことも考えるとイベントで扱うのと同様、構造体を使うなどハードコピーされるようにしたほうがいい…?
ちなみにMySceneManager といったアプリ全体で利用するものはProjectContextで扱うとよさそうですね。
参考