Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
10
Help us understand the problem. What is going on with this article?
@tor4kichi

ZenjectSceneLoaderでシーン間のパラメータ渡しをシンプルに実現する

はじめに

この記事ではゲームエンジン 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で扱うとよさそうですね。

参考

10
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
10
Help us understand the problem. What is going on with this article?