へっぽこ日記

Unity向に記事を投稿しています。たまにUIの事とかも呟くかも。

VContainerを試してみる

はじめに

今回はVContainerを試してみる記事です。

ネット上でZenjectと比較してかなり高速でシンプルと聞いたので、実際に試してみました。

 

サンプルプロジェクト

github.com

 

 

試してみる

こんなものを試しに作りました。クリックすると箱がジャンプして、テキストにジャンプした回数が表示されます。

 

MVPパターンで実装していて、入力を環境によって分けています。また、Modelのテストに備えて、ModelをInterfaceで切り離しています。

 

導入

今回はunitypackage経由でインストールしました。

 

1. manifest.jsonに「"com.unity.nuget.mono-cecil": "1.10.1", 」を記述

2. 最新リリースからunitypackageをダウンロード

3. unitypackageインポート

 

準備

Zenjectだとコンストラクタインジェクションは[inject]は必要なかったのですが、VContainerでは必要みたいなのでコンスタントにも[inject]をつけています。

public class Model : IModel
{
   public IReactiveProperty CountProp=>_countProp;
   private readonly IntReactiveProperty _countProp;

   public Model()
   {
      _countProp=new IntReactiveProperty(0);
   }

   public void AddCount()
   {
      _countProp.Value++;
   }
}
public class Presenter : IStartable, IDisposable
{
    private readonly View _view;
    private readonly IModel _model;
    
    private readonly CompositeDisposable _compositeDisposable = new CompositeDisposable();

    [Inject]
    public Presenter(View view, IModel model)
    {
        _view = view;
        _model = model;
    }

    public void Start()
    {
        Bind();
        SetEvent();
    }

    private void Bind()
    {
        _model.CountProp
            .Subscribe(_view.SetCount)
            .AddTo(_compositeDisposable);
    }

    private void SetEvent()
    {
        _view.OnAnimationCallback += () => _model.AddCount();
        
        _view.InputJump()
            .Subscribe(_=>_view.Jump())
            .AddTo(_compositeDisposable);
    }
    
    public void Dispose()
    {
        _compositeDisposable.Dispose();
    }
}

public class View : MonoBehaviour
{
    public event Action OnAnimationCallback;

    [SerializeField] private Transform _cubeTransform;
    private Sequence _sequence;

    [SerializeField] private Text _countText;

    [Inject] IInputProvider _input;

    public void SetCount(int count)
    {
        _countText.text = count.ToString();
    }

    public IObservable InputJump()
    {
        return _input.InputJump();
    }

    public void Jump()
    {
        if (_sequence.IsActive()) return;

        _sequence = DOTween.Sequence()
            .Join(_cubeTransform.DOJump(new Vector3(0, 0, 0), 3f, 1, 1.0f).SetRelative())
            .Join(_cubeTransform.DOPunchRotation(new Vector3(360f, 360f, 360f), 1.0f,5).SetRelative())
            .SetEase(Ease.OutCubic).OnComplete(()=>OnAnimationCallback?.Invoke()).SetLink(this.gameObject);
    }
}

public interface IInputProvider
{ 
    public IObservable InputJump();
}

public class KeyInputProvider : IInputProvider
{
    public IObservable InputJump()
    {
        return InputAsObservable.GetKeyDown(KeyCode.Space);
    }
}

MVPでViewとPresenterを互いを知らない状態にしつつ、Interfaceを設けることでPresenterがModelの存在を知らない状態を実現しています。入力も同じように、Interfaceを設けてEditorとWebGLで切り替えています。

 

登録

Zenjectと同じように登録できました。RegisterEntryPoint()で注入先を指定して、Register()とRegisterComponet()で注入するクラスを登録しています。

    public class MvpLifetimeScope : LifetimeScope
{
    [SerializeField] private View _view;

    protected override void Configure(IContainerBuilder builder)
    {
#if UNITY_EDITOR
        builder.Register<IInputProvider, GamepadInputProvider>(Lifetime.Transient);
#elif UNITY_WEBGL
        builder.Register<IInputProvider,KeyInputProvider>(Lifetime.Transient);
#endif
        builder.Register<IModel,Model>(Lifetime.Transient);

        builder.RegisterComponent(_view);

        builder.RegisterEntryPoint(Presenter);
    }
}

 

おわりに

普段Zenjectを使っている身からすると、VContainerに移行しなくても良いかなと思いました。Zenjectにはあって、VContainerには無い機能もあったりしますし、ZenjectからVContainerへの移行に伴うリファクタを考えると頭が...

 

是非、読者登録をしていただくと助かります!