VContainer v1.6.0 で HelloWorld

とりあえずVContainerのDocのGetting Startedをやって,Hello Worldを表示する所までやってみました.

vcontainer.hadashikick.jp

検証環境

  • Unity 2020.3.1f1
  • VContainer 1.6.0
  • UniRx 7.1.0

VContainer

Extenject の軽量版,必要十分でシンプルな機能を持つUnity用のDIコンテナライブラリです.
English ですが Document は十分整備されている上,Extenject より機能が絞られているので,
学習コストは Extenject より少ないと思います.もし Extenject を使ったことがあるなら,すぐにとっかかりやすいでしょう.

scrapbox.io

VContainer と Extenject の相違点

こちらは私がざっと触ってみた感じなので,確実な正解ではないと思われます.(誤っていたら指摘お願いいたします.)

  • Extenject は SceneContext ← 分割された複数のInstallerを埋めるということは出来ていたが,
    現在のVContainerの場合,Installerを作成する工程がなく,
    Extenjectで分割されたInstallerは1LifetimeScopeに統合してRegisterの記述をする感じ?

このツイートを見るに過去は Installer を作成する工程はあった模様.

  • Extenject は Multi-Parent は簡単に出来るが,VContainer の場合は出来なさそう?
    Inspector上では Parent は1つしか出来なかった.コードの方で Child Scope 生成あたりで無理くり出来るか否かまだ試してない.
    というよりここら辺は Extenject と VContainer で思想が異なりそう.
    Extenject の Multi-Parent は微妙に Unity の Multi-Scene あたりで重宝したので,VContainer でも上手い具合に試す必要がある.

  • UniRx, UniTask との統合が一応考慮されている (非同期想定)

VContainer × UniRx でハロワ

こちらのGetting Startedをやってみました.
そのままだと味気ないので一応軽くUniRxを交えたり,Interface作ったりして試してみた.

using System;
using UnityEngine;
using UnityEngine.UI;
using UniRx;

namespace Denik.VContainerPractice.HelloWorld
{
    /// <summary>
    /// View クラス
    /// </summary>
    internal class HelloWorldView : MonoBehaviour, ITrigger
    {
        [SerializeField]
        private Button helloButton;

        public IObservable<Unit> OnTriggerAsObservable() => _onTriggerSubject;
        private Subject<Unit> _onTriggerSubject = new Subject<Unit>();

        private void Awake()
        {
            helloButton.OnClickAsObservable()
                .Subscribe(_ => _onTriggerSubject.OnNext(Unit.Default));
        }
    }

    internal interface ITrigger
    {
        IObservable<Unit> OnTriggerAsObservable();
    }
}
using UnityEngine;
using UniRx;
using VContainer;
using VContainer.Unity;

namespace Denik.VContainerPractice.HelloWorld
{
    /// <summary>
    /// LifetimeScope クラス
    /// </summary>
    internal class HelloWorldLifetimeScope : LifetimeScope
    {
        [SerializeField]
        private HelloWorldView helloWorldView;

        protected override void Configure(IContainerBuilder builder)
        {
            builder.Register<HelloWorldService>(Lifetime.Singleton);
            builder.RegisterEntryPoint<GamePresenter>(Lifetime.Singleton);
            builder.RegisterComponent(helloWorldView).AsImplementedInterfaces();
        }
    }

    /// <summary>
    /// Service クラス
    /// </summary>
    internal class HelloWorldService
    {
        public void Hello()
        {
            Debug.Log("Hello world");
        }
    }

    /// <summary>
    /// Presenter クラス
    /// </summary>
    internal class GamePresenter : IStartable
    {
        private readonly HelloWorldService _helloWorldService;
        private readonly ITrigger _helloWorldView;

        [Inject]
        public GamePresenter(HelloWorldService helloWorldService, ITrigger helloWorldView)
        {
            _helloWorldService = helloWorldService;
            _helloWorldView = helloWorldView;
        }

        public void Start()
        {
            _helloWorldView.OnTriggerAsObservable()
                .Subscribe(_ => _helloWorldService.Hello());
        }
    }
}

f:id:xrdnk:20210328221917p:plain

ハロワ!やったね.

終わりに

Extenjectは今まで最低限の機能しか使っていなかったマンなので,VContainerでいいかなあというムーブになりそう.
まだVContainer触れたばかりなので,Extenjectにはこんなの簡単に出来たのに~とかこれから他にも発生するかもしれない.
もう少しVContainer勉強してみます.

余談

弊社では UniRx,UniTask,Extenject のことは三種の神器って呼んでいるんですが,他社もそうなのだろうか….
ちなみにこれからは Extenject から VContainer に移行し,呼び方が新・三種の神器になりそうな感じです.