もんりぃ is undefined.

育児ネタとか、技術ネタとか。

ZenjectSceneLoader が便利です

はじめに

新年あけましておめでとうございます。本年も何卒よろしくお願い申し上げます。

さて、最近 Zenject と仲良くなれてきている気がする id:monry です。

github.com

Zenject は Unity *1 における DI *2 フレームワークです。

で、その Zenject でマルチシーンを取り扱う際に ZenjectSceneLoader というクラスがとても便利だったので紹介します。

ZenjectSceneLoader とは

一言で言えば UnityEngine.SceneManagement.SceneManager の Zenject 版 で、マニュアルにシレッと書いてある 機能だったりします。

何かと面倒になりがちな Zenject におけるマルチシーン の補助的なライブラリクラスだと思ってもらえると理解が早いかも知れません。

ZenjectSceneLoader の使い方

とても簡単なので、サンプルコードをご覧いただければ理解できるかと思います。

class LoadButton : MonoBehaviour
{
    private ZenjectSceneLoader zenjctSceneLoader;

    [Inject]
    private void Construct(ZenjectSceneLoader sceneLoader)
    {
        this.zenjectSceneLoader = zenjectSceneLoader;
    }

    public void OnClick()
    {
        zenjectSceneLoader.LoadScene("Foo", LoadSceneMode.Additive);
    }
}
  1. Scene 読み込みを実行したいクラスに対して ZenjectSceneLoader を Inject します。
    • サンプルコードでは Method Injection を用いていますが、Field/Property Injection とかでも良いと思います。
  2. 読み込みたいタイミングで LoadScene を用いて読み込みます。
    • 非同期呼び出し用の LoadSceneAsync メソッドも用意されていたりします。
    • LoadSceneAsync の場合 AsyncOperation が返されるので、 UniRx.Async と組み合わせて await させるコトで可読性の高い書き方が出来るようになるかと。
    • LoadSceneMode.Single, LoadSceneMode.Additive の何れも対応しています。

ZenjectSceneLoader の特長

これだけだと便利さは伝わらないと思うので、便利な特長を紹介します。

読み込み時の追加 Binding 可能

LoadSceneAsync()Scene が読み込まれる時に、読み込み先の SceneContext に対して追加の Binding を設定 するための delegate を第3引数に受け取るオーバーロードが提供されています。

zenjectSceneLoader.LoadSceneAsync(
    "Bar",
    LoadSceneMode.Additive,
    (DiContainer container) =>
    {
        // 何か色々 Bind したりとか
        container.Bind<IHoge>().To<Fuga>().AsCached();
    }
);

例えば、共通ダイアログ的な Scene を読み込む際に中身のテキストを追加 Bind するなどの使い方が考えられそうです。

他にも、押したボタンの内容に応じて、LoadSceneMode.Single で読み込みつつ次画面のモードを切り替える などの使い方もできるかもしれません。*3

読み込む Scene の関係性を定義できる

LoadSceneAsync() の第4引数に LoadSceneRelationship という列挙値を渡すことで、 読み込み元の SceneContext から見た読み込み先の SceneContext の関係性 を定義できます。

zenjectSceneLoader.LoadSceneAsync(
    "Baz",
    LoadSceneMode.Additive,
    (container) => { /* 何か Bind */ },
    LoadSceneRelationship.Child // 子 Context として読み込む
);

これにより、元 Context に Bind 済の値を用いつつ、閉じた Context として読み込むことができるようになります。*4

例えば、ダイアログのボタン押下時の処理をダイアログ呼び出し元の Scene 側で Bind して、ダイアログ内のボタンに Inject するような実装が考えられます。

他にも、読み込む毎に処理を Stack していき IEnumerable<AnyDelegate> に Inject して子 Scene 側で纏めて発火させる、といった実装ができるかもしれません。

全ての Context に Bind されている

ここまでの説明から分かるように、特別に Bind を行わずとも Inject が行えるのも特長の一つです。

具体的にどこの Context に Bind されているのかまでは調べておりませんが、恐らく ProjectContext 的なモノに Bind されているんじゃないかと思われます。

まとめ

ZenjectSceneLoader が便利なので、マルチシーン対応する場合の選択肢として利用してみては如何でしょう?

Zenject はドキュメントがとても長いので、なかなか全部の機能を理解することが難しいですが、便利機能がてんこ盛りなのでじっくりドキュメントを読む時間を確保したいものです…。

*1:以外でも使えると思うけど。

*2:Dependency Injection

*3:Single で読み込む場合は後述の LoadSceneRelationship を定義できない( None 限定)ので注意が必要です。

*4:簡易的な Scene Parenting だと思ってヨサソウ?