UnityにおけるVContainerを用いたPrefab生成とDIによるGetComponentの置き換え

本資料では、Unity プロジェクトにおいて VContainer を活用した依存性注入(DI)とファクトリパターンの実装例を示します。
従来、Prefab 生成後に GetComponent を用いてコンポーネントを取得していた処理を、DI によるファクトリからのインスタンス生成に置き換える方法について説明します。


1. 背景

  • 従来の手法:
    Instantiate した Prefab から GetComponent<IgaguriController>() を呼び出して、コンポーネントを取得していました。
  • 課題:
    • 依存性がコード内に隠蔽され、テストが難しくなる
    • 依存関係が暗黙的になり、保守性が低下する可能性がある
  • 解決策:
    DI コンテナ(VContainer)とファクトリパターンを用いて、Prefab のインスタンス化と依存性注入を一元管理することで、依存性を明示化しテスト容易性を向上させます。

2. サンプルコード

2.1 ファクトリインターフェースの定義

public interface IIgaguriFactory
{
    IgaguriController Create();
}

2.2 ファクトリの実装

DI コンテナから提供される IObjectResolver を利用して、Prefab のインスタンス生成と依存性注入を行います。
igaguriPrefab は DI コンテナに識別子付きで登録します。

using UnityEngine;
using VContainer;

public class IgaguriFactory : IIgaguriFactory
{
    private readonly IObjectResolver resolver;
    private readonly GameObject igaguriPrefab;

    public IgaguriFactory(IObjectResolver resolver, [Inject(Id = "IgaguriPrefab")] GameObject prefab)
    {
        this.resolver = resolver;
        this.igaguriPrefab = prefab;
    }

    public IgaguriController Create()
    {
        // Prefab をインスタンス化
        var instance = Object.Instantiate(igaguriPrefab);
        // インスタンスに対して DI を実施
        resolver.Inject(instance);
        // IgaguriController コンポーネントを返す
        return instance.GetComponent<IgaguriController>();
    }
}

2.3 LifetimeScope の登録

GameLifetimeScope で、Prefab とファクトリを DI コンテナに登録します。
エディタ上で igaguriPrefab を設定できるようにし、識別子付きで登録します。

using UnityEngine;
using VContainer;
using VContainer.Unity;

public class GameLifetimeScope : LifetimeScope
{
    public GameObject igaguriPrefab; // エディタで設定

    protected override void Configure(IContainerBuilder builder)
    {
        // igaguriPrefab を識別子付きで登録
        builder.RegisterInstance(igaguriPrefab).WithId("IgaguriPrefab");
        // IgaguriFactory をファクトリインターフェースとして登録
        builder.Register<IIgaguriFactory, IgaguriFactory>(Lifetime.Singleton);
    }
}

2.4 IgaguriGenerator の修正

IgaguriGenerator では、従来の GetComponent 呼び出しを、DI によって注入されたファクトリからインスタンス生成を行う方式に変更します。

using UnityEngine;
using VContainer;

public class IgaguriGenerator : MonoBehaviour
{
    [Inject]
    private IIgaguriFactory igaguriFactory;  // DI によりファクトリを注入

    public float rayLength = 100f;       // 可視化する Ray の長さ
    public Color rayColor = Color.black;  // Ray の色

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            // ファクトリから IgaguriController を生成
            IgaguriController controller = igaguriFactory.Create();

            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            // 生成した Ray をデバッグ表示
            Debug.DrawRay(ray.origin, ray.direction * rayLength, rayColor, 1.0f);

            // IgaguriController の Shoot を呼び出す
            controller.Shoot(ray.direction * 2000);
        }
    }
}

3. 利点と考慮点

利点

  • 依存性の明示化:
    ファクトリパターンと DI により、依存関係がコンストラクタやインターフェースを通じて明確になります。
  • テスト容易性:
    ファクトリや DI コンテナをモックに差し替えることで、ユニットテストが容易になります。
  • 一元管理:
    Prefab の生成と依存性注入を一元管理することで、コードの保守性が向上します。

考慮点

  • 実装の複雑さ:
    シンプルなケースでは従来の GetComponent 呼び出しで十分な場合もあり、DI の導入が過剰にならないか検討する必要があります。
  • プロジェクトの規模:
    大規模なプロジェクトや複雑な依存関係を管理する際に、DI とファクトリパターンのメリットがより発揮されます。

4. 結論

本サンプルは、VContainer を利用して Prefab の生成と依存性注入を行い、従来の GetComponent 呼び出しを DI によるファクトリ生成に置き換える方法を示しています。
このアプローチは依存性を明示化し、テスト容易性と保守性を向上させる良い実践例です。
ただし、プロジェクトの規模や要求に応じて、適用の必要性や実装の複雑さを検討することが重要です。