Unity3DのDIフレームワーク、Zenjectの紹介

Unity3DのDIフレームワーク、Zenjectの紹介

はじめまして。エンジニアの石井と申します。

今回は現在担当プロジェクトで使用しているUnity(ゲームエンジン)用DIフレームワーク、Zenjectを紹介させていただきます。

Zenjectとは

https://github.com/modesttree/Zenject

ZenjectはUnity3D向けに作成された、DI(依存性の注入)フレームワークです。
Zenjectを使うことでオブジェクト間の依存関係を外部から解決することができます。
簡単になにが良いかを説明しますと、
依存オブジェクトを別の入れ物(コンテナ)に格納しておきコンテナ経由で使うことで、
コンストラクタやメソッドの引数で渡していたり、自身でnewしていたり、FindObjectしていた部分をなくすことができます。

Zenjectサンプル

Zenjectを使ってのUIイベント処理の簡単なサンプルを作っていきます。
今回はボタンが押された時のイベントを処理するサンプルです。

1.インストール

AssetStoreからか、
https://github.com/modesttree/Zenject/releases
からダウンロードしてUnityのプロジェクトにインポートしてください。

2.Contextの作成

シーン上にContextを作成します。
右クリックからScene Contextを選んでください。
Zenject_context

3.Installerの作成

コンテナへの格納(Bind)にZenjectはInstallerというスクリプトを使用します。
今回はSingletonとしてオブジェクトをBindしていきます。

シーン上にGameObjectを作成して、
下記スクリプトをAdd Componentしてください。
※直接SceneContextでも問題ありません。
Zenject_Installer

using System;
using Zenject;

public class InstallerSample : MonoInstaller<InstallerSample>
{
    public override void InstallBindings()
    {
        Container.Bind<ZenjectSample>().AsSingle();
        Container.Bind<IInitializable>().To<ZenjectSample>().AsSingle();
        Container.Bind<IDisposable>().To<ZenjectSample>().AsSingle();
    }
}

4.ContextにInstallerをアタッチ

ContextにInstallerをアタッチすることでInstallerが機能します。
SceneContextのInstallersに、作成したInstallerSampleをアタッチしてください。

Zenject_Context_Installer0

5.UIの作成

UIを作成していきます。
下記のようにボタンを2つ作成し、
Zenject_UI_hierarchyZenject_UI

それぞれ下記のようにスクリプトをAdd Componentします。
Zenject Bindingスクリプトにより、Installerに定義しなくてもBindすることができます。
Identifierを使うことで、個別にInjectすることもできます。

Zenject_UI_Parts

using UnityEngine;
using UnityEngine.UI;
using UniRx;

public class UIView : MonoBehaviour
{
    [SerializeField]
    Button button;

    [SerializeField]
    Text text;

    public IObservable<string> OnClickObservable()
    {
        return button.OnClickAsObservable().Select(_ => text.text);
    }
}

イベントの変換はUniRxを使い、Textをstringで送出します。

6.サンプルクラスの作成

サンプルクラスを作成します。
[Inject]アトリビュートにより先ほど作成したUIViewが注入されます。
IInitializable.Initializeメソッドにより初期化され、
各ボタンのClickイベント購読し、押されたらOnClickメソッドが呼ばれるようになっています。
Sceneの切り替えやゲームの終了などでContextが破棄されるタイミングでIDisposable.Disposeメソッドが呼ばれ、
購読を停止しています。

using System;
using System.Collections.Generic;
using UniRx;
using Zenject;

public class ZenjectSample : IInitializable, IDisposable
{
    [Inject]
    List<UIView> buttons;

    List<IDisposable> subscriptions = new List<IDisposable>();

    void IInitializable.Initialize()
    {
        UnityEngine.Debug.Log("Initialize");

        buttons.ForEach(button =>
        {
            subscriptions.Add(button.OnClickObservable().Subscribe(text => OnClick(text)));
        });
    }

    void OnClick(string buttonText)
    {
        UnityEngine.Debug.Log(buttonText);
    }

    public void Dispose()
    {
        subscriptions.ForEach(subscription => subscription.Dispose());
        subscriptions.Clear();

        UnityEngine.Debug.Log("Dispose");
    }
}

7.動作確認

Sceneが読み込まれる際に、Initializeメソッドが呼ばれ、
ボタンが押されるたびにボタンのTextがログに表示され、
最後にDisposeが呼ばれていることがわかります。

Zenject_log

おわりに

簡単に紹介させていただきましたが、
他にもZenjectには様々なContext、Installerが用意されていますので、
依存関係の解決でお困りの方はぜひ一度試してみてください。