【Unity】Zenjectをちょっと触ってみた


はじめに

Zenjectがイイらしいという情報を見かけたので軽く触ってみました。

環境

Unityのバージョンは5.3.5f1, OSはWindows10です

Zenject?

Zenject is a lightweight dependency injection framework built specifically to target Unity 3D

DI (Dependency Injection, 依存性の注入) を扱うためのフレームワークだそうです。

1. とりあえず使ってみる

1. Zenjectのダウンロード

以下URLからダウンロードしてUnityProjectに入れます。
https://github.com/modesttree/Zenject/releases

AssetStoreからもダウンロードできます


筆者は Zenject-WithSampleGames.v4.5.unitypackage をダウンロードしました。

2. Hello World

公式のHelloWorldを参考に実際に動かしていきます。

  1. Hierarchyで右クリックして、Zenject -> Scene Context を選択して、SceneContextを作成



2. 以下のスクリプトを作成して好きなGameObjectにアタッチ。

TestInstaller.cs
using Zenject;
using UnityEngine;
using System.Collections;

public class TestInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.BindInstance("Hello World!");
        Container.Bind<TestRunner>().NonLazy();
    }
}

public class TestRunner
{
    public TestRunner(string message)
    {
        Debug.Log(message);
    }
}

3.SceneContextにTestInstaller付きのオブジェクトをアタッチ

4.Validate ( Ctrl + Shift + V )するとConsoleに以下のようなメッセージが表示されます.

あまり理解できていないのですが、InstallBindings()内にてクラス間の依存関係を定義しているみたいです。

2. サンプルをちょっと覗いてみた

同梱のサンプルゲームに使われているコードがどのような意味を持つのか表にまとめてみました。
各コードはInstallBindings()の中で呼んでいるものとしています。

コード 意味 エラー例  備考
Container.Bind<IF>().To<A>() インターフェースIFをクラスAが継承していることを保証する? AIFを継承していないとエラー IFの代わりにクラスBを入れても動くみたいです
Container.BindFactory<A, B>() クラスBFactory<A>に変換可能であることを保証する? BFactory<A>へ変換できないとエラー Factory<>はZenjectで提供されているクラス
Container.BindInstance<int>(x).AsSingle() int型の変数xが変わらないことを保証する? 1回目のBindInstance呼び出しと2回目のBindInstance呼び出しで異なるint値が渡されるとエラー
Container.BindAllInterfaces<A>().To<B>() クラスAが継承している全てのインターフェースをクラスBが継承していることを保証する? Aが継承するインターフェースの中にをBが継承していないものがあるとエラー

3. Injectを使ってみた

Injectもちょっと触ってみました。 DIを行うためのものらしいです

引数なしメソッドを呼ぶ

シーンを再生するとA()メソッドが実行されます.

TestInstaller.cs
using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<Hoge>().NonLazy();

    }
}

public class Hoge
{
    [Inject]
    void A()
    {
        Debug.Log("A");
    }
}

実行結果

引数ありメソッドを呼ぶ

WithArgumentsを使うとメソッドに引数を渡せるみたいです。

TestInstaller.cs
using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<Hoge>().WithArguments(810, 114514).NonLazy();
    }
}

public class Hoge
{
    [Inject]
    void A(int x, int y)
    {
        Debug.Log("A : x = " + x + ", y = " + y);
    }
}

実行結果

4. ITickableを使ってみた

ITickableを使うことで毎フレーム処理を実行するクラスを作ることができるらしいです。

実際に使ってみる

シーンを再生するとTick()メソッドが毎フレーム実行されます

TestInstaller.cs
using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<ITickable>().To<A>().AsSingle();
    }
}

class A : ITickable
{
    public void Tick()
    {
        Debug.Log("Tick");
    }
}

シーンを再生すると以下のようなLogが出力されます。

5. IInitializableを使ってみた

開始時にInitialize()メソッドを実行させるためのインターフェースらしいです

実際に使ってみる

シーンを再生するとInitialize()メソッドが1度実行されます

TestInstaller.cs
using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<IInitializable>().To<A>();
    }
}

class A : IInitializable
{
    public void Initialize()
    {
        Debug.Log("Initialize");
    }
}

IInitializableをInjectで代用する

以下のように書くことでも同様のことができるみたいです

TestInstaller.cs
using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<A>().NonLazy();
    }
}

class A
{
    [Inject]
    public void Initialize()
    {
        Debug.Log("Initialize");
    }
}

参考URL

GitHub - modesttree/Zenject
https://github.com/modesttree/zenject

アセットストア - Zenject Dependency Injection IOC
https://www.assetstore.unity3d.com/jp/#!/content/17758