VContainerという新しい選択肢 〜UnityのDIライブラリ〜


PONOS Advent Calendar 2020 の11日目の記事です。
昨日は@blockさんのUnity 2020から自動実装プロパティのバッキングフィールドにSerializeField属性をつけるとプロパティ名が表示されるにするでした。

VContainerって何?

VContainerとはhadashiAさんが作成されたUnity用の軽量DIライブラリとなっています。
現在主流となっているZenjectより軽量で高速となっています。
ではなぜ今VContainerを記事にしようと思ったかといいますと、ver1.0.0がリリースされたからです(現時点では1.3.0がリリースされています)

インストール

環境
Unity:2019.4.1f1
VContainer:1.3.0

こちらのInstallationよりUPMかunitypackageのどちらかの方法でインストールします。今回私はUPMを使用しました。
UPMの使い方については詳しくは記載しませんのでこちらを参考にしてください。

使い方

今回は導入から簡単なサンプルを紹介していこうと思っていますので一番初歩として「Hello World」をテキストに表示するサンプルにしようと思います。

まずLifetimeScopeを生成します。方法はProjectタブにて右クリックでCreate→C# ScriptにてHogeHogeLifetimeScopeと入力するとLifetimeScopeを継承したファイルが作成されます。今回はSampleLifetimeScopeという名前で作成しました。
次にHierarchyにて空のオブジェクトを作成し、先ほど作成したSampleLifetimeScopeをアタッチします。

これから実際の処理を記述していきます。

SampleLifetimeScope.cs
using VContainer;
using VContainer.Unity;
using UnityEngine;

namespace VContainerSample
{
    public class SampleLifetimeScope : LifetimeScope
    {
        [SerializeField] SampleView view;

        protected override void Configure(IContainerBuilder builder)
        {
            builder.Register<ISampleText, SampleText>(Lifetime.Scoped);
            builder.RegisterComponent(view);
        }
    }
}
SampleText.cs
namespace VContainerSample
{
    public interface ISampleText
    {
        string Text { get; }
    }

    public class SampleText : ISampleText
    {
        public string Text => $"Hello World";
    }
}
SampleView.cs
using UnityEngine;
using UnityEngine.UI;
using VContainer;

namespace VContainerSample
{
    public class SampleView : MonoBehaviour
    {
        [SerializeField] Text text;

        [Inject]
        public void Construct(ISampleText sampleText)
        {
            text.text = sampleText.Text;
        }
    }
}

これでSampleView.csのTextに「Hello World」が表示されるようになります。

Zenjectとの比較

SampleMonoInstaller.cs
using UnityEngine;
using Zenject;

namespace ZenjectSample
{
    public class SampleMonoInstaller : MonoInstaller
    {
        [SerializeField] GameObject view;

        public override void InstallBindings()
        {
            Container.Bind<ISampleText>().To<SampleText>().AsCached();
            Container.InjectGameObjectForComponent<SampleView>(view);
        }
    }
}
SampleView.cs
using UnityEngine;
using UnityEngine.UI;
using Zenject;

namespace ZenjectSample
{
    public class SampleView : MonoBehaviour
    {
        [SerializeField] Text text;

        [Inject]
        public void Construct(ISampleText sampleText)
        {
            text.text = sampleText.Text;
        }
    }
}

Zenjectで同様の処理を書いたらこのようになります。
VContainerとの違いはLifetimeScopeとMonoInstallerになると思います。
ZenjectにおけるBindの役割はVContainerの場合はConfigureにてIContainerBuilderへRegistになっており、クラスからインターフェースへの変換はbuilder.Register<ISampleText, SampleText>(Lifetime.Scoped)で行っています。これはZenjectのContainer.Bind<ISampleText>().To<SampleText>().AsCached()と同じような処理となっています。
このように簡単な部分だけを見るとZenjectからVContainerへの移行はとっつきやすいと考えられます。

まとめ

新しく登場したVContainerについてとても簡単なプログラムの作り方をまとめました。
これから新しく生まれるプロジェクトでは採用されるようになっていくとどんどん情報も出てきて賑わってくれると思っています。
軽量で高速という部分についてはこちらにて記載されておりますので、興味のある方は参考にしてみてください。
上記のサンプルだけでも実行してみたらその速度の違いに私は驚きました。
次はもう少しゲームよりなサンプルを作って、実際にどう作っていけば良いかを解説していければと思っています。
ぜひ皆さんも触ってみてください。

VContainerを組み込んだゲームサンプルこちらも併せてご覧になってください。

明日は@honeniqの記事です。ぜひ見てください!