MRが楽しい

MRやVRについて学習したことを書き残す

VContainerのHelloWorldを試す その7(LifetimeScopeの親子関係を利用してデバッグ用シーンを作成する)

本日は VContainer の小ネタ枠です。
LifetimeScopeの親子関係を利用してデバッグ用シーンを作成する方法を記事にします。

前回記事

以下の前回記事のプロジェクトを利用します。
bluebirdofoz.hatenablog.com

LifetimeScopeの親子関係を利用する

前回記事で書いた通り、LifetimeScope の参照解決は子から親に向かって行われます。
このため、異なるクラスで参照解決するを用意して、そこからメインのシーンを読み込ませることで参照するクラスを切り替えることができます。

サンプルシーン

以下の Morning と Night の2つの異なる LifetimeScop を持つサンプルシーンを用意しました。
・MorningParentLifetimeScope.cs

using VContainer;
using VContainer.Unity;

namespace EnqueueParentSample2
{
    public class MorningParentLifetimeScope : LifetimeScope
    {
        protected override void Configure(IContainerBuilder builder)
        {
            // HelloWorldServiceをインタフェースとしてDIコンテナに登録する
            builder.RegisterInstance(new MorningHelloWorldService()).As<IHelloWorldService>();
        }
    }
}

・MorningHelloWorldService.cs

namespace EnqueueParentSample2
{
    public class MorningHelloWorldService : IHelloWorldService
    {
        public void Hello()
        {
            UnityEngine.Debug.Log($"Good morning");
        }
    }
}

・NightParentLifetimeScope.cs

using VContainer;
using VContainer.Unity;

namespace EnqueueParentSample2
{
    public class NightParentLifetimeScope : LifetimeScope
    {
        protected override void Configure(IContainerBuilder builder)
        {
            // NightHelloWorldServiceをインタフェースとしてDIコンテナに登録する
            builder.RegisterInstance(new NightHelloWorldService()).As<IHelloWorldService>();
        }
    }
}

・NightHelloWorldService.cs

namespace EnqueueParentSample2
{
    public class NightHelloWorldService : IHelloWorldService
    {
        public void Hello()
        {
            UnityEngine.Debug.Log($"Good night");
        }
    }
}

それぞれのシーンは以下の通り、シーン内の LifetimeScope を EnqueueParent に設定してメインシーンを読み込む処理を行います。
・NextSceneLoader.cs

using UnityEngine;
using UnityEngine.SceneManagement;
using VContainer.Unity;

namespace EnqueueParentSample2
{
    public class NextSceneLoader : MonoBehaviour
    {
        [SerializeField]
        private LifetimeScope firstSceneLifetimeScope;

        void Start()
        {
            LifetimeScope.EnqueueParent(firstSceneLifetimeScope);
            SceneManager.LoadScene("MainSceneSample", LoadSceneMode.Additive);
        }
    }
}

また、メインシーンの LifetimeScope は親の LifetimeScope を参照するため、IHelloWorldService の参照解決を登録していません。
・GameLifetimeScope.cs

using UnityEngine;
using VContainer;
using VContainer.Unity;

namespace EnqueueParentSample2
{
    public class GameLifetimeScope : LifetimeScope
    {
        [SerializeField]
        private HelloScreen helloScreen;

        protected override void Configure(IContainerBuilder builder)
        {
            // GamePresenterをVContainerのEntryPointに登録する
            builder.RegisterEntryPoint<GamePresenter>();
            // HelloScreenコンポーネントをDIコンテナに登録する
            builder.RegisterComponent<HelloScreen>(helloScreen);
        }
    }
}

動作確認

Morning と Night それぞれのシーンを再生してみます。
以下の通り、Morning のシーンから開いた場合は「Good Morning」の処理が、Night のシーンから開いた場合は「Good Night」の処理が走りました。

シーンの読み込みで参照クラスを切り替えるこの方法は Inspector の切り替えを行わないので Github の登録でファイルの変更を避けてデバッグの切り替え処理を仕込むことなどが可能になります。

ProjectRoot

更に ProjectRoot の LifetimeScope を設定しておけばそのままメインシーンを開いた場合はリリース環境用のクラスを参照させるといった設定も可能になります。
・RootParentLifetimeScope.cs

using VContainer;
using VContainer.Unity;

namespace EnqueueParentSample2
{
    public class RootParentLifetimeScope : LifetimeScope
    {
        protected override void Configure(IContainerBuilder builder)
        {
            // HelloWorldServiceをインタフェースとしてDIコンテナに登録する
            builder.RegisterInstance(new HelloWorldService()).As<IHelloWorldService>();
        }
    }
}

・RootParentLifetimeScope.cs

using VContainer.Unity;

namespace EnqueueParentSample2
{
    public class HelloWorldService : IHelloWorldService
    {
        public void Hello()
        {
            UnityEngine.Debug.Log($"Hello world");
        }
    }
}