📦VContainer Usage

DI container that we use

All scenes after _Loader must have script that inherited from SceneLifetimeScope.cs and represents as object in scene hierarchy (recomended to replace it on top of scene hierarchy).

How it looks in Gameplay Scope

Below example how you can register objects in scene. I prefer to use lifetime scope as scene controller, so in Start() it will run game systems (when all have target references):

public class GameplayLifetimeScope : SceneLifetimeScope
{
    [Header("Items")]
    [SerializeField] private LibraryItemSettingsSO _itemsLibrary;

    [Header("Scene references")]
    [SerializeField] private Player _player;
    [SerializeField] private CameraFollower _cameraFollower;

    protected override void Configure(IContainerBuilder builder)
    {
        base.Configure(builder);
        
        // here we need to register all that will be used across scripts in scene
        // basicaly put all top systems and main objects which need for other scripts
        builder.RegisterComponent(_cameraFollower);
        builder.RegisterComponent(_player);
        
        var UI = UIManager.instance;
        builder.RegisterComponent(UI.GetCanvas<GameCanvasUI>());
        builder.RegisterComponent(UI.GetCanvas<InteractPopup>());
        builder.RegisterComponent(UI.GetCanvas<QuestsUI>());
        
        builder.RegisterComponent(CashManager.Instance);
        builder.RegisterComponent(PoolManager.instance);

        builder.RegisterComponent(_itemsLibrary);
        // this line below will create ItemsFactory (not monobehaviour)
        builder.Register<ItemsFactory>(Lifetime.Singleton).WithParameter(transform);
    }

    private void Start() {
        // start scene systems, gameplay logic 
        // all registered objects will already 
        // have references that we have registered in Configure() above
        var itemFactory = Container.Resolve<ItemsFactory>();
        itemFactory.SetupSceneItems(_sceneItems);

        _player.OnStart();
    }
}

And how to handle references that we registered:

public class ItemsFactory {
    public LibraryItemSettingsSO Library => _itemsLibrary;

    private IObjectResolver _container;
    private LibraryItemSettingsSO _itemsLibrary;
    private PoolManager _pool;
    private Transform _sceneTransform;

    // Here is magic that VContainer does 
    // (injects registered objects that we did in LifetimeScope above)
    // Use [Inject] for constuctor or method always so we know that this ->
    // -> will be called by VContainer after Configure call before Awake()
    //
    // Make attention that we also added IObjectResolver that is needed to ->
    // -> inject dependencies for runtime created objects!
    [Inject]
    public ItemsFactory(IObjectResolver container, LibraryItemSettingsSO itemsLibrary, 
                        PoolManager pool, Transform sceneTransform) {
        _pool = pool;
        _container = container;
        _itemsLibrary = itemsLibrary;
        _sceneTransform = sceneTransform;
    }

    private GameObject GetInstance(ItemSettingsSO config) {
        GameObject instance;

        if (config.PoolSize > 0) {
            _pool.CreateScenePool(config.Prefab, config.PoolSize, _sceneTransform,
                                  autoFillCount: config.AutoFillCount);

            instance = _pool.ReuseGameObjectScenePool(config.Prefab);
        }
        else {
            instance = GameObject.Instantiate(config.Prefab);
        }

        // To inject use IInject interface with MonoBehaviour, so we can mark ->
        // -> object that it need inject and use here and in other similar cases
        if (instance.TryGetComponent(out IInject injectObject) && injectObject.NeedInject) {
            _container.Inject(injectObject);
        }

        return instance;
    }
}

For MonoBehaviour use [Inject] with method Init() (name can be different, but for consistency recomended Init):

public class Spawner : MonoBehaviour { .. }

// inside of Spawner class:
[Inject]
public void Init(ItemsFactory itemsFactory) {
    _itemsFactory = itemsFactory;
}

Conclusion:

  1. Register references in SceneLifetimeScope (inherit from), dependencies for registered objects injected automaticaly.

  2. Use [Inject] attribute in classes and add arguments to method which is references that you want to get.

  3. To inject references for runtime created objects get IObjectResolver in [Inject] constructor/method and inject after creation. Use IInject interface to mark injectable objects and check do they need inject (pool case).

VContainer documentation for more information.

Last updated