LoginSignup
25
19

More than 3 years have passed since last update.

【Unity】ZenjectのTutotialをやってみた(1)

Last updated at Posted at 2017-12-24

【Unity】ZenjectのTutotialをやってみた(導入) の続きです

Hello World Example

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

public class TestInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<string>().FromInstance("Hello World!");
        Container.Bind<Greeter>().AsSingle().NonLazy();
    }
}

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

次の手順を実行

  • Unityで新しいシーンを作成する
  • Hierarchyタブにて、右クリックし、 Zenject -> Scene Context を追加する
  • Projectタブで Create -> Zenject -> MonoInstaller を作成する。名前は TestInstaller.cs にする。(このテンプレートを使用せずにこのファイルを直接作成することもできます)
  • TestInstallerスクリプトをシーンに追加する(新たなGameObjectまたはSceneContextと同じGameObjetでも構わない)
  • "Installers"プロパティのインスペクタに新しい行を追加して(+ボタンを押して)、TestInstaller GameObjectをドラッグしてSceneContextのプロパティにTestInstallerへの参照を追加します
  • TestInstallerを開き、上記のコードを貼り付けてください
  • Edit - > Zenject - > Validate Current Scene またはCTRL + SHIFT + Vを押してシーンを検証します。 (この手順は必ずしも必要ではありませんが、良い習慣です)
  • 実行する
  • また、ショートカットCTRL + SHIFT + Rを使用して「検証して実行」することもできます。検証は通常、ゲームの実行に顕著なオーバーヘッドを追加しないように十分に速いです。エラーを検出すると、起動時間を避けるため、反復する方がはるかに高速です。
  • コンソールでの出力を確認する

SceneContext MonoBehaviourはアプリケーションのエントリーポイントで、Zenjectはシーンを実行する前にさまざまな依存関係を設定します。 Zenjectシーンにコンテンツを追加するには、シーン内で使用されているすべての依存関係と、それらの関係を宣言する 'Installer'としてZenjectで参照されるものを記述する必要があります。 "NonLazy"としてマークされているすべての依存関係は、この時点で自動的に作成されます。そのため、上で追加したGreeterクラスは起動時に作成されます。

NonLazyとなっていて、上記のFromInstanceで指定された型を持つコンストラクタに値を注入する ってことでいいのかな?もう少し読み進めてみる

Injection

コンテナの依存関係を宣言する方法はいろいろありますが、これについては次のセクションで説明します。これらの依存関係をクラスに注入する方法もいくつかあります。

  • 1 - Constructor Injection (コンストラクタでの注入)
constructor.cs
public class Foo
{
    IBar _bar;

   public Foo(IBar bar)
    {
        _bar = bar;
    }
}
  • 2 - Field Injection(フィールドへの注入)
field.cs
public class Foo
{
    [Inject]
    IBar _bar;
}

フィールド注入は、コンストラクタが呼び出された直後に発生します。 [Inject]属性でマークされたすべてのフィールドは、コンテナ内で検索され、値が与えられます。これらのフィールドはプライベートまたはパブリックにすることができ、インジェクションは引き続き発生することに注意してください。

  • 3 - Property Injection(プロパティへの注入)
property.cs
public class Foo
{
    [Inject]
    public IBar Bar
    {
        get;
        private set;
    }
}

プロパティインジェクションは、C#のプロパティに適用される以外は、フィールドインジェクションと同じ動作をします。フィールドと同じように、この場合セッターはプライベートまたはパブリックにすることができます。

  • 4 - Method Injection(メソッドへの注入)
method.cs
public class Foo
{
    IBar _bar;
    Qux _qux;

   [Inject]
    public Init(IBar bar, Qux qux)
    {
        _bar = bar;
        _qux = qux;
    }
}

メソッドへの注入はコントラクタへの注入と非常に似ています

注入メソッドは、他のすべての注入タイプの後に呼び出されることに注意してください。このように設計されているため、これらのメソッドを使用してこれらの依存関係の1つを使用する初期化ロジックを実行できます。また、初期化ロジックだけを行う場合は、パラメータリストを空のままにしておくこともできます。

注入メソッドはいくつでも構いません。この場合は、BaseクラスからDerivedクラスの順に呼び出されます。これは、コンストラクターパラメーターを介して派生クラスの多くの依存関係を基本クラスに転送する必要を避けるために役立ちます。また、コンストラクターの動作と同様に、基本クラス注入メソッドが最初に完了することが保証されます。

注入メソッドを介して受け取った依存関係は、すでに注入されている必要があることに注意してください(唯一の例外は循環依存がある場合です)。 Injectメソッドを使用して基本的な初期化を行う場合、Injectメソッドを使用して初期化する必要がある場合があるので、これは重要です。

MonoBehavioursはコンストラクタを持つことができないので、[Inject]メソッドを使用して依存関係を注入するのがMonoBehavioursの推奨アプローチです。

また、インジェクションメソッドを定義して、戻り値の型IEnumeratorを持たせることもできます。この場合、それらはコルーチンとして開始されます。オブジェクトがMonoBehaviourである場合、オブジェクトはそれ自身のコルーチンとして開始され、そうでなければオブジェクトが存在する "Context" MonoBehaviour(すなわち、ProjectContext、SceneContextまたはGameObjectContext)を使用します。

ただし、[Inject]メソッドの初期化ロジックを最小化することをお勧めします。 Start()またはAwake()を使用して初期化ロジックを代わりに使用することができます(これは、動的にインスタンス化されたプレハブと、編集中にシーンに追加されたオブジェクトの両方で機能するはずです)。

Injectで値の注入が自動でおこなわれると。
呼ばれる順番は、コンストラクタ->フィールドまたはプロパティ->メソッドの順番。
Monobehaviorはコンストラクタがないので、メソッド注入をコンストラクタ替わりに使えばいいと
ただAwakeやStartのコールバックを待つ必要があることを気を付けましょう

Recommendations

ベストプラクティスは、コンストラクタインジェクションやメソッドインジェクションをフィールドインジェクションやプロパティインジェクションより優先することです。

  • コンストラクタインジェクションは、依存関係をクラス作成時に一度だけ解決するよう強制します。これは通常は必要なものです。ほとんどの場合、最初の依存関係のためにパブリックプロパティを公開したくないのは、変更が可能であることを示唆しているからです。
  • コンストラクタインジェクションはクラス間の循環依存性を保証しません。これは一般的には悪いことです。ただし、必要に応じてメソッド注入またはフィールド注入を使用してこれを行うことができます。
  • コンストラクタ/メソッドインジェクションは、ZenjectなどのDIフレームワークなしでコードを再利用することを決めた場合に移植性があります。パブリックプロパティでも同じことができますが、エラーが発生しやすくなります(1つのフィールドを初期化してオブジェクトを無効な状態にするのを忘れた方が簡単です)
  • 最後に、コンストラクタ/メソッドの注入は、別のプログラマがコードを読んでいるときにクラスのすべての依存関係が何であるかを明確にします。彼らは単にメソッドのパラメータリストを見ることができます。これはまた、クラスがあまりにも多くの依存関係を持ち、分割されるべきであるときにもっとも明白になります(コンストラクタのパラメータリストが長く見えるようになるため)

Binding

すべての依存性注入フレームワークは、最終的に型をインスタンスにバインドするためのフレームワークに過ぎません。

Zenjectでは、依存関係マッピングはコンテナと呼ばれるものにバインディングを追加することによって行われます。コンテナは、特定のオブジェクトのすべての依存関係を再帰的に解決することによって、アプリケーション内のすべてのオブジェクトインスタンスを作成する方法を「認識」する必要があります。

コンテナは、指定された型のインスタンスを作成するように要求されると、C#reflectionを使用してコンストラクタ引数のリスト、および[Inject]属性でマークされたすべてのフィールド/プロパティを検索します。次に、コンストラクターを呼び出して新しいインスタンスを作成するために使用する、これらの必要な依存関係のそれぞれを解決しようとします。

したがって、各Zenjectアプリケーションは、バインドコマンドを介して行われるこれらの依存関係のそれぞれの解決方法をコンテナに伝えなければなりません。たとえば、次のクラスを指定します。

test.cs
public class Foo
{
    IBar _bar;

   public Foo(IBar bar)
    {
        _bar = bar;
    }
}

このクラスの依存関係を次のように結びつけることができます。

Container.Bind<Foo>().AsSingle();
Container.Bind<IBar>().To<Bar>().AsSingle();

これは、Foo型の依存関係を必要とするすべてのクラスが、同じインスタンスを使用する必要があることをZenjectに伝え(AsSingle)、必要なときに自動的に作成します。同様に、IBarインターフェース(Fooなど)を必要とするクラスには、タイプBarの同じインスタンスが与えられます。

AsSingleで同一インスタンスを使うことを指定する。
Bind().To()で IFを実装したTに対してBindする。
かな? 続きを読む

bindコマンドの完全な形式は次のとおりです。ほとんどの場合、これらのメソッドのすべてを使用するわけではなく、指定されていないときはすべて論理的なデフォルトを持つことに注意してください

Container.Bind<ContractType>()
    .To<ResultType>()
    .WithId(Identifier)
    .FromConstructionMethod()
    .AsScope()
    .WithArguments(Arguments)
    .When(Condition)
    .CopyIntoAllSubContainers()
    .NonLazy();
  • ContractType = バインディングを作成する型。
    • この値は、注入されているフィールド/パラメータのタイプに対応します。
  • ResultType = バインドを行う型
    • Default : ContractType
    • このタイプは、ContractTypeと等しいか、ContractTypeから派生しなければなりません。指定されていない場合は、ToSelf()とみなされます。これは、ResultTypeがContractTypeと同じであることを意味します。この値は、この型のインスタンスを取得するためにConstructionMethodとして与えられたものによって使用されます
  • Identifier = バインディングを一意に識別するために使用する値。これはほとんどの場合無視することができますが、同じContractTypeの複数のバインディングを区別する必要がある場合には非常に便利です。詳細はこちらを参照してください。
  • ConstructionMethod = ResultTypeのインスタンスが作成/取得されるメソッド。さまざまな構築方法の詳細については、下記参照。
    • Default : FromNew()
    • 例えば、 FromGetter、FromMethod、FromResolve、FromComponentInNewPrefab、FromSubContainerResolve、FromInstanceなどがあります。
  • Scope = この値は、生成されたインスタンスが複数の注入にわたって再利用される頻度を決定します。
    • Default : AsTransient
    • 以下から選択する
  1. AsTransient - インスタンスを再利用しません。 ContractTypeが要求されるたびに、DiContainerはResultType型の新しいインスタンスを返します。
  2. AsCached - ContractTypeが要求されるたびにResultTypeの同じインスタンスを再利用します。これは最初の使用時に遅れて生成されます
  3. AsSingle - DiContainer全体でResultTypeの同じインスタンスを再利用します。最初の使用時に遅延的に生成されます。複数のバインドコマンドを使用して同じインスタンスにバインドできるため、AsCachedの強力なバージョンと考えることができます。 DiContainerにResultTypeのインスタンスが1つだけ確実に存在することも保証されます(つまりResultTypeが「シングルトン」になります)。指定されたコンテナにインスタンスが1つしかないことが保証されます。つまり、サブコンテナ内の同じバインディングを持つAsSingleを使用すると、2番目のインスタンスが生成される可能性があります。
    • ほとんどの場合、おそらくAsSingleを使用するだけですが、AsTransientとAsCachedも用途によっては使用されます。
    • さまざまなスコープタイプの違いを説明するには、次の例を参照してください。
test.cs
public interface IBar
{
}

public class Bar : IBar
{
}

public class Foo()
{
    public Foo(Bar bar)
    {
    }
}
// この場合それぞれのFooインスタンスに対して新しいBarインスタンスが作られます
Container.Bind<Bar>().AsTransient();
// この場合それぞれのFooインスタンスに対して同じBarインスタンスが渡されます
Container.Bind<Bar>().AsCached();
test.cs
public class Qux
{
    public Qux(IBar bar)
    {
    }
}
// 下記の場合はFooとQuxで異なるBarインスタンスが渡される
// しかしながら、それぞれのFooインスタンスは同一のBarインスタンスが渡されます(Qtxも同様)
Container.Bind<Bar>().AsCached();
Container.Bind<IBar>().To<Bar>().AsCached();
// この場合は、FooもQuxも同じBarインスタンスが渡されます(つまりBarはシングルトン)
Container.Bind<Bar>().AsSingle();
Container.Bind<IBar>().To<Bar>().AsSingle();
  • Arguments = ResultType型の新しいインスタンスを構築するときに使用するオブジェクトのリスト。これは、フォーム内の引数に他のバインディングを追加する代わりに便利です
  • Condition = このバインディングを選択するためには真でなければならない条件。詳細はこちらを参照してください。
  • CopyIntoAllSubContainers = 指定されている場合、このバインディングは、そのバインディングから作成されたサブコンテナから自動的に継承されます。つまり、結果は、すべてのサブコンテナのインストーラにContainer.Bindステートメントをコピーして貼り付けることと等価になります。
  • NonLazy = 通常、ResultTypeは、バインディングが最初に使用されたときにインスタンス化されます(別名「遅延」)。ただし、NonLazyを使用すると、起動時にResultTypeが即座に作成されます。

ContainerでのBindの際にいろいろと設定を決めれるのなー
おそらく後に出てくるInstallerでそれぞれのインスタンスの要素に合わせたBindを行う必要があるんやろうなー

Construction Methods

a. FromNew - C#new演算子を使用して作成します。構築方法が指定されていない場合のデフォルトです。

// 以下は同じ
Container.Bind<Foo>();
Container.Bind<Foo>().FromNew();

b. FromInstance - 指定されたインスタンスをコンテナに追加します。この場合、与えられたインスタンスは注入されないことに注意してください。起動時にインスタンスを注入したい場合は、QueueForInjectを参照してください。

Container.Bind<Foo>().FromInstance(new Foo());

// また、パラメータタイプからContractTypeだけを取るこの短い手を使うこともできます
Container.BindInstance(new Foo());

// これは、プリミティブ型に通常使用するものです
Container.BindInstance(5.13f);
Container.BindInstance("foo");

// もし複数のインスタンスを作るのならば、BindInstancesが使えます
Container.BindInstances(5.13f, "foo", new Foo());

c. FromMethod - カスタムメソッドから作成します

Container.Bind<Foo>().FromMethod(SomeMethod);

Foo SomeMethod(InjectContext context)
{
    ...
    return new Foo();
}

d.FromMethodMultiple - FromMethodと同じですが、一度に複数のインスタンスを返すことができます。

Container.Bind<Foo>().FromMethodMultiple(GetFoos);

IEnumerable<Foo> GetFoos(InjectContext context)
{
    ...
    return new Foo[]
    {
        new Foo(),
        new Foo(),
        new Foo(),
    }
}

e.FromFactory - カスタムファクトリクラスを使用してインスタンスを作成します。この構築方法はFromMethodと似ていますが、ロジックが複雑であるか依存関係が必要な場合に使用します(ファクトリ自体に依存関係が注入される可能性があるため)

class FooFactory : IFactory<Foo>
{
    public Foo Create()
    {
        // ...
        return new Foo();
    }
}

Container.Bind<Foo>().FromFactory<FooFactory>()

f.FromComponentInNewPrefab - 指定されたプレハブを新しいゲームオブジェクトとしてインスタンス化し、MonoBehaviourへと注入を行い、ResultType型の結果を検索します。
ResultTypeは、インタフェースであるか、この場合はUnityEngine.MonoBehaviour / UnityEngine.Componentを継承している必要があります。

Container.Bind<Foo>().FromComponentInNewPrefab(somePrefab);

g.FromComponentInNewPrefabResource - 指定されたリソースパスで見つかったプリファブを新しいゲームオブジェクトとしてインスタンス化し、MonoBehaviourへと注入を行い、ResultType型の結果を検索します。
ResultTypeは、インタフェースであるか、この場合はUnityEngine.MonoBehaviour / UnityEngine.Componentを継承している必要があります。

Container.Bind<Foo>().FromComponentInNewPrefabResource("Some/Path/Foo");

h.FromNewComponentOnNewGameObject - 新しい空のゲームオブジェクトを作成し、その上に指定されたタイプの新しいコンポーネントをインスタンス化します。
ResultTypeは、この場合 UnityEngine.MonoBehaviour / UnityEngine.Componentから派生する必要があります

Container.Bind<Foo>().FromNewComponentOnNewGameObject();

j.FromNewComponentOnNewPrefab - 指定されたプレハブを新しいゲームオブジェクトとしてインスタンス化し、新しいゲームオブジェクトのルート上の指定されたコンポーネントの新しいインスタンスもインスタンス化します。注:プレハブには、指定されたコンポーネントのコピーが含まれている必要はありません。
ResultTypeは、この場合 UnityEngine.MonoBehaviour / UnityEngine.Componentから派生する必要があります

Container.Bind<Foo>().FromNewComponentOnNewPrefab(somePrefab);

k.FromNewComponentOnNewPrefabResource - 指定されたプレファブ(指定されたリソースパスにある)をインスタンス化し、新しいゲームオブジェクトのルート上の指定されたコンポーネントの新しいインスタンスをインスタンス化します。注:プレハブには、指定されたコンポーネントのコピーが含まれている必要はありません。
ResultTypeは、この場合 UnityEngine.MonoBehaviour / UnityEngine.Componentから派生する必要があります

Container.Bind<Foo>().FromNewComponentOnNewPrefabResource("Some/Path/Foo");

l.FromNewComponentOn - 指定されたタイプの新しいコンポーネントを、指定されたゲームオブジェクトに追加します。
ResultTypeは、この場合 UnityEngine.MonoBehaviour / UnityEngine.Componentから派生する必要があります

Container.Bind<Foo>().FromComponent(someGameObject);

n.FromNewComponentSibling - 現在のTransformで与えられた要素の新しいコンポーネントをインスタンス化します。ここでの現在のTransformは、注入されるオブジェクトから行われるため、MonoBehaviour由来の型でなければなりません。
指定されたコンポーネントタイプがすでに現在のトランスフォームにアタッチされている場合、新しいコンポーネントを作成する代わりに、そのコンポーネントタイプが返されることに注意してください。その結果、このbind文はUnityの[RequireComponent]属性と同様に機能します。
ResultTypeは、この場合 UnityEngine.MonoBehaviour / UnityEngine.Componentから派生する必要があります。また、非MonoBehaviourが指定された型を要求した場合、その場合にはTransfromがないので、例外がスローされることにも注意してください。

Container.Bind<Foo>().FromNewComponentSibling();

m.FromComponentInHierarchy - 現在のコンテキストに関連するシーン hierarchy 内のコンポーネント、およびすべての親コンテキストに関連付けられたHierarchy から検索します。
ResultTypeは、インタフェースであるか、この場合はUnityEngine.MonoBehaviour / UnityEngine.Componentから派生する必要があります。
コンテキストがSceneContextである最も一般的なケースでは、シーン全体の階層(GameObjectContextなどのサブコンテキストを除く)を検索します。つまり、現在のコンテキストがシーンコンテキストの場合、GameObject.FindObjectsOfTypeと同様の動作をします。これは大きな検索である可能性があるので、GameObject.FindObjectsOfTypeを慎重に使用するように注意して使用する必要があることに注意してください。
コンテキストがGameObjectContextである場合、ゲームオブジェクトルート(および任意の親コンテキスト)内およびその下を探索するだけです。コンテキストがProjectContextの場合は、プロジェクトコンテキストのプレハブ内でのみ検索されます

Container.Bind<Foo>().FromComponentInHierarchy();

o.FromComponentSibling/FromComponentInParents/FromComponentInChildren - 現在のTransformにアタッチされているコンポーネント中から、指定されたコンポーネントタイプを検索します(InParentsは親、InChildrenは子供から検索)。ここでの現在の変換は、注入されるオブジェクトから行われるため、MonoBehaviour由来の型でなければなりません。
ResultTypeは、インタフェースであるか、この場合はUnityEngine.MonoBehaviour / UnityEngine.Componentから派生する必要があります。非MonoBehaviourが指定された型を要求した場合、その場合は現在の変換が存在しないため、例外がスローされることに注意してください。

Container.Bind<Foo>().FromComponentSibling();
Container.Bind<Foo>().FromComponentInParents();
Container.Bind<Foo>().FromComponentInChildren();

p.FromResource - ResultTypeをResources.Loadを呼び出すことによって作成します。これは、テクスチャ、サウンド、プレハブなど、Resources.Loadがロードできるすべてのタイプをロードするために使用できます。

Container.Bind<Texture>().WithId("Glass").FromResource("Some/Path/Glass");

q.FromScriptableObjectResource -リソースフォルダに保存されていた以前に作成されたインスタンスを複製して、ScriptableObjectクラスを作成する。
この場合、FromResourceとは異なり、Resources.Loadから返される値はScriptableObject.Instantiateを呼び出すことによって複製されます。これは、同じスクリプト可能なオブジェクトリソースの複数のインスタンスを許可するために必要です。

public class Foo : ScriptableObject
{
}

Container.Bind<Foo>().FromScriptableObjectResource("Some/Path/Foo");

r.FromNewScriptableObjectResource -リソースフォルダに保存されていた以前に作成されたインスタンスを複製して、ScriptableObjectクラスを作成する。
指定されたScriptableObjectリソースの新しいコピーをインスタンス化する以外は、FromScriptableObjectResourceと同じです。これは、指定されたScriptableObjectリソースの複数の別個のインスタンスを持つ場合、またはスクリプト可能オブジェクトの保存された値が実行時に変更されても影響を受けないようにする場合に便利です。

s.FromResolve -コンテナの別の探索を実行する(つまり、DiContainer.Resolve ()を呼び出して)インスタンスを取得します。これが機能するためには、ResultTypeは別のbind文でバインドされなければならないことに注意してください。この構築方法は、次の例に示すように、インターフェイスを別のインターフェイスにバインドする場合に特に便利です

public interface IFoo
{
}

public interface IBar : IFoo
{
}

public class Foo : IBar
{
}

Container.Bind<IFoo>().To<IBar>().FromResolve();
Container.Bind<IBar>().To<Foo>();

t.FromResolveGetter -コンテナの別の探索(つまり、DiContainer.Resolve ()を呼び出して、ResultType型の返されたインスタンスの値にアクセスすることによって取得された別の依存関係のプロパティからインスタンスを取得します。これを行うには、ObjectTypeを別のバインドステートメントにバインドする必要があります。

public class Bar
{
}

public class Foo
{
    public Bar GetBar()
    {
        return new Bar();
    }
}

Container.Bind<Foo>();
Container.Bind<Bar>().FromResolveGetter<Foo>(x => x.GetBar());

u.FromSubContainerResolve - サブコンテナを参照してResultTypeを取得します。これが機能するには、サブコンテナにResultTypeのバインディングが必要であることに注意してください。このアプローチは非常に強力です。なぜなら、関連する依存関係をミニコンテナ内でグループ化し、特定のクラス(「ファサード」とも呼ばれます)だけを公開して、このグループの依存関係をより高いレベルで操作できるからです。サブコンテナの使用の詳細については、このセクションを参照してください。サブコンテナを定義するには4つの方法があります。

i.ByMethod - メソッドを使用してサブコンテナを初期化します。

Container.Bind<Foo>().FromSubContainerResolve().ByMethod(InstallFooFacade);

void InstallFooFacade(DiContainer subContainer)
{
    subContainer.Bind<Foo>();
}

ii.ByInstaller - Installerから派生したクラスを使用してサブコンテナを初期化します。これはByMethodよりも、よりクリーンで、エラーを起こしにくい代替手段です。特に、インストーラ自体にデータを注入する必要がある場合に便利です。なぜなら、ByMethodを使用すると、メソッド内でサブコンテナではなく通常のコンテナを誤って使用することが多いからです。

Container.Bind<Foo>().FromSubContainerResolve().ByInstaller<FooFacadeInstaller>();

class FooFacadeInstaller : Installer
{
    public override void InstallBindings()
    {
        Container.Bind<Foo>();
    }
}

iii.ByNewPrefab - 新しいプレハブをインスタンス化してサブコンテナを初期化します。プレハブには、ルートゲームオブジェクトに添付された GameObjectContextコンポーネントが含まれていなければならないことに注意してください。 GameObjectContextの詳細については、この節を参照してください。

Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefab(MyPrefab);

// ここで、このインストーラがプレハブのルートにある `GameObjectContext`に追加されたと仮定します。
// FooがMonoBehaviourの場合には `ZenjectBinding`を使うこともできます
class FooFacadeInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<Foo>();
    }
}

iv.ByNewPrefabMethod - 新しいプレハブをインスタンス化してサブコンテナを初期化します。 ByNewPrefabとは異なり、このバインドメソッドはプレハブにGameObjectContextが付いている必要はありません。この場合、 GameObjectContextが動的に追加され、与えられたインストーラメソッドで実行されます。

Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabMethod(MyPrefab, InstallFoo);

void InstallFoo(DiContainer subContainer)
{
    subContainer.Bind<Foo>();
}

v.ByNewPrefabInstaller - 新しいプレハブをインスタンス化してサブコンテナを初期化します。 ByNewPrefabMethodと同じですが、メソッドではなく指定されたインストーラで動的に生成されたGameObjectContextを初期化します。

Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabInstaller<FooInstaller>(MyPrefab);

class FooInstaller : Installer
{
    public override void InstallBindings()
    {
        Container.Bind<Foo>();
    }
}

vi.ByNewPrefabResource - Resources.Loadで取得した新しいプレハブをインスタンス化するサブコンテナを初期化します。プレハブには、ルートゲームオブジェクトに添付された GameObjectContextコンポーネントが含まれていなければならないことに注意してください。

Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabResource("Path/To/MyPrefab");

vii.ByNewPrefabResourceMethod - Resources.Loadで取得した新しいプレハブをインスタンス化するサブコンテナを初期化します。 ByNewPrefabResourceと違って、このバインドメソッドはプレハブにGameObjectContextが付いている必要はありません。この場合、 GameObjectContextが動的に追加され、与えられたインストーラメソッドで実行されます。

Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabResourceMethod("Path/To/MyPrefab", InstallFoo);

void InstallFoo(DiContainer subContainer)
{
    subContainer.Bind<Foo>();
}

viii.ByNewPrefabResourceInstaller - Resources.Loadで取得した新しいプレハブをインスタンス化するサブコンテナを初期化します。 ByNewPrefabResourceMethodと同じですが、メソッドではなく指定されたインストーラで動的に作成されたGameObjectContextを初期化します。

Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabResourceInstaller<FooInstaller>("Path/To/MyPrefab");

class FooInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<Foo>();
    }
}

また、ここでの "Scope"(例えば、FromCached、FromSingle、またはFromTransient)は、サブコンテナ自体を参照し、サブコンテナ内の依存関係を参照していないことにも注意してください。 FromSingleの場合、 'Singleton'サブコンテナは、ByMethodの場合はメソッド、ByInstallerの場合はインストーラタイプ、ByNewPrefabまたはByNewPrefabResourceの場合はプレハブで識別されます。

fromNew 以外あんまり使いどころがわからなかった。使ってみないとわかんないか

Installers

多くの場合、各サブシステムに関連するバインディングのコレクションがいくつか存在するため、これらのバインディングを再利用可能なオブジェクトにグループ化することは理にかなっています。 Zenjectでは、この再利用可能なオブジェクトは「インストーラ」と呼ばれます。次のように新しいインストーラを定義できます。

public class FooInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<Bar>().AsSingle();
        Container.BindInterfacesTo<Foo>().AsSingle();
        // etc...
    }
}

InstallBindingsメソッドをオーバーライドすることによってバインディングを追加します。これは、インストーラが追加されたContext(通常はSceneContext)によって呼び出されます。MonoInstallerはMonoBehaviourですので、FooInstallerをGameObjectに追加することができます。 GameObjectであるため、パブリックメンバーを追加してUnityインスペクタからインストーラを設定することもできます。これにより、シーン内での参照の追加、アセットへの参照、または単にデータのチューニングが可能になります(データのチューニングの詳細については、ここを参照)。

インストーラがトリガされるためには、それはSceneContextオブジェクトのインストーラプロパティにアタッチされなければならないことに注意してください。インストーラは、 SceneContextに与えられた順序で(スクリプト化可能なオブジェクトインストーラ、次にモノインストーラ、そしてプレインストールインストーラを使用して)、通常この順序は重要ではありません。

多くの場合、インストーラをMonoInstallerから派生させて、インスペクタ設定を行うことができます。 MonoBehaviourである必要がない場合に使うことができる、単に `Installer 'という別の基本クラスもあります。

以下のように別のインストーラからインストーラを呼び出すこともできます。

public class BarInstaller : Installer<BarInstaller>
{
    public override void InstallBindings()
    {
        ...
    }
}

public class FooInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        BarInstaller.Install(Container);
    }
}

この場合、BarInstallerは Installer <>(一般的な引数に注意してください)であり、MonoInstallerではなく、単にBarInstaller.Install(Container)を呼び出すことができ、BarInstallerをシーンに追加する必要はありません。BarInstaller.Installを呼び出すとすぐにBarInstallerの一時インスタンスが作成され、そのインスタンスにInstallBindingsが呼び出されます。これは、このインストーラーがインストールを行うたびに繰り返されます。また、 Installer <>基本クラスを使うときは、必ず Installer <>の総称引数として渡す必要があります。これは、 Installer <>基本クラスが静的メソッド BarInstaller.Installを定義できるようにするために必要です。ランタイムパラメータをサポートするために、この方法で設計されています(後述)。

インストーラを使用する主な理由の1つは、シーンごとにすべてのバインディングを一度に宣言するだけでなく、再利用可能にすることです。これは、 Installer <>型のインストーラでは問題ありません。あなたが使いたいすべてのシーンに対して上記のように FooInstaller.Installを呼び出すことができるからです。しかし、複数のシーンでMonoInstallerを再利用するにはどうしたらいいでしょうか?

これを行うには3つの方法があります。

  1. Prefab instances within the scene(シーン内のプレハブインスタンス). MonoInstallerをシーン内のゲームオブジェクトにアタッチしたら、そこからプレハブを作成できます。これは、MonoInstallerのインスペクタで行った設定をシーン間で共有することができます(また、必要に応じてシーン単位のオーバーライドも可能です)。シーンに追加したら、それをドラッグして ContextのInstallersプロパティにドロップすることができます。
  2. Prefabs. インストーラプレハブをプロジェクトタブから直接 SceneContextのInstallerPrefabsプロパティにドラッグすることもできます。この場合、シーン内でInstantiateされたプレハブを持つときのように、シーンごとのオーバーライドを行うことはできませんが、シーン内の混乱を避けることができます。
  3. Prefabs within Resources folder. Resouresフォルダの下にインストーラプレハブを置き、Resourcesパスを使用してコードから直接インストールすることもできます。使い方の詳細はこちらをご覧ください。

MonoInstallerと Installer <>に加えて、(特に設定のための)いくつかの利点を持つScriptableObjectInstallerを使うこともできます - 詳しくはこちらをご覧ください。

他のインストーラからインストーラを呼び出すときは、パラメータを渡すことが一般的です。それがどのように行われたかについては、ここを参照してください。

Installerは、Bindメソッドをまとめたものって理解でいいのかね。んで、インストーラ内でインストーラを呼ぶこともできると。

【Unity】ZenjectのTutotialをやってみた(2)へ(作成中)

ここまででMonobehaviorを継承したクラスに対するDIが紹介しました。
次回は、通常のC#クラスでの使用方法部分を翻訳します。(Google先生が)
もしくは、簡単なゲームに適応してみてどういう使い方をしたらいいのか調べてみたいと思います。

25
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
19