Are you using IoC containers or ECS framework in your projects?

Hello,

I want to re-elaborate my previous question in a different and simpler way. If you felt the necessity to use a custom framework to exploit different ways to approach fundamental coding design problems, like it could be an Inversion of Control container library or an Entity Component System framework (but anything else as well), I would like to know why you decided to use it.
I don’t think these kind of tools are beneficial to use if they are used just to test them without knowing why they are useful, so I would like to hear from you the logic that took you to use these alternative solutions to the pure Unity framework.
I hope my question makes sense.

Most projects I use now, both prototype and commercial all use Zenject.

I like how IoC can separate out parts of the coding so you’re not working solely in Unity space.

My common workflow is, use Unity for the engine, in regards to rendering, physics, etc and then have all code related to gameplay as a pure C# codebase. Most of our code that requires Unity (MonoBehaviours, etc) are just containers, for example, a class that describes what’s inside of a prefab, or things the pure C# environment will need to access, change etc.

For me, doing things this way, means I don’t get errors that are caused by Unity, or if I do, it’s because I forgot to link something myself.

As regarding why I chose to use IoC for the projects are, if we have something that can be shared between projects, then it should (and most often is, when the shared code is written correctly) a matter of some lines that bind some implementation to an interface.

If I understood correctly, you use an IoC container to decouple the logic from the view (monobehaviours/gameobject). However Monobehaviours are already meant to decouple the logic from the view (a Monobehaviour, if well written, can be used across gameobjects and reused among projects). Why do you need a further level of abstraction?

I find MonoBehaviours aren’t as easily changed. If I wanted to test something in the game, let’s say something debug, like mouse instead of touch. I could just change the bind at runtime, and it’ll all work. With MonoBehaviours, I have to put them on/off by hand (or have some manager do this). With IoC, I can pick and choose using a simple line of code.

Great thanks. Yes, abstraction of the interface from the implementation is a great improvement over the standard unity way of coding. In fact, Unity itself doesn’t promote the use of interfaces at all, making even the most basic code design solutions harder to adopt. In this sense, IoC containers are a good tool to use.
For example I may have an IBonusReward interface that can be used everywhere, but according the level loaded, I can use a different implementation. Every level has its own Composition Root, but that’s the only thing that will change across the whole code.
So yes, abstraction of the interface from the implementation is fundamental.
However I am sure there are other reasons to use an IoC container, any further reasoning?

1 Like

The more I tried to shape my game the “Unity way”, the more I’m drifted towards what you have written here.

Seperating the game from Unity is all good in theory, but how is your implementation really?

Do you have a single hook MonoBehaviour that gives you a connection to the engine? Or do you somehow connect each C# object with their MonoBehaviour counterpart (and I guess Zenject comes into play here)?

It would be greatly appreciated if you share your experiences.

Hey there.

I don’t have much doubts anymore about what approach to take. You can Google svelto ECS to find my solution. New articles and possibly a unity talk will come too.

If I would name two things that helps me to keep my code readable and well organized, it will be interfaces and Zenject.

Today I had an idea, that it would be nice, if obstacles in my game somehow stopped after hero passes through them. So they should gradually stop moving, stop rotating, shadow offset should decrease to zero to make a “landing effect” and maybe they could became a bit darken.

I have made an interface like this:

    public interface IStoppable
    {
        void Stop();
        void Run();
    }

and made my Rotator, Mover, Colorizer and ShadowCaster classes implement it. Now I made a Stopper class, like this:

    public class Stopper : MonoBehaviour, IHandleMessage<LevelStateChangedMessage>
    {
        private IEnumerable<IStoppable> stoppables;

        public void Handle(LevelStateChangedMessage message)
        {
            if (message.LevelState == LevelState.Reseted)
            {
                foreach (var stoppable in stoppables)
                {
                    stoppable.Run();
                }
            }
        }

        private void Start()
        {
            stoppables = GetComponentsInChildren<IStoppable>();
        }

        [Inject]
        public void Construct(IMessenger messenger)
        {
            messenger.Subscribe(this);
        }

        private void OnTriggerEnter2D(Collider2D other)
        {
            if (other.gameObject.CompareTag(ObjectNames.Hero))
            {
                foreach (var stoppable in stoppables)
                {
                    stoppable.Stop();
                }
            }
        }
    }

And that’s it. In case that hero passes collider behind the obstacles everything that that is stoppable will stop.

And Zenject? When I was implementing IStoppable on my ShadowCaster class, I wanted shadow to disapear gradually, so ShadowCaster now needs to know what time is it. I have added this line:

[Inject] private ILevelTimeProvider levelTimeProvider;

Just this line is enough and now I can anywhere within ShadowCaster class use

var time = levelTimeProvider.Time;

to get level time.

And when I am looking at the Stopper class, there is also third tool that helps me a lot. It is event aggregator pattern (in my implementation called IMessanger). Stopper needs to now when the level is reseted, so he can restart all the obstacles and I think that role of IMessenger in this is pretty self-explanatory.

Without these “tools” I would not be able to finish a project. They allow me to focus just on the one class I am currently working on and I don’t have to think about anything else.

1 Like

do you mind continue the discussion here please? [Open Source] Svelto.ECS - Lightweight Entity Component System for C# and Unity - Community Showcases - Unity Discussions

I’d like to populate that thread

When I understood the limitation of using an IoC container as a mere substitute of the singleton pattern, that means using injection without Inversion of Control in mind, I understood that the IoC container must be used in very specific ways. IoC containers come mainly from development fields that are heavily GUI based on the client side. In fact, IoC container are often designed to work together with GUI frameworks, like the Microsoft WPF and many others.
When I decided to use an IoC container as a way to inject dependencies in Unity (substituting singletons to set communications between entities), I always thought that it could have been a stretch to use it in a game.
During the evolution of my reasoning, I understood that IoC containers must be used with encapsulation in mind (and Inversion of Control), therefore can be used in very limited ways, essentially:

  • to inject dependencies in very limited scopes, using hierarchical containers. For example every Model/View/Presenter should have their own container so that a view can inject a presenter but the same presenter cannot be injected anywhere else. However if we talk about IoC containers and GUI framework, this process is usually totally hidden from the developer, as it’s done by the framework itself.

  • to extend behaviours, effectively using composition. However in this case the behaviour should be totally abstracted so that can be reused in several places

  • it could be used also to inject services (but I see this as main difference between IoC containers and Service Providers)

  • to inject “managers” that would register the class that need to be managed (template pattern).

What you are doing is basically the point 4. Injecting an array of data is something I never liked, because could bring to another level of horrible code if used without inversion of control in mind, but in this case avoids the need to inject a manager with an exposed Register (IStoppable) function. Now Stopper for you is a manager that handles (with correct inversion of control) an array of IStoppable (template pattern). However you know what Stopper is? It’s a System and IStoppable is a component (well, it’s simplified of course).

I realized that using IoC containers with correct inversion of control in mind would have brought me basically to the Entity Component System design. That’s why I abandoned Svelto.IoC (now deprecated) and started to work on Svelto.ECS already a couple of years ago. Nowadays I would NEVER start a new game from scratch using anything else than ECS (our game CardLife is ECS only and the new Robocraft features are now ECS based). However I would surely use an IoC container as part of a GUI framework.

Sure! I will move my original message over there.

An IoC container is not for separating your logic from your view. That is the idea of MVC architecture. IoC container is used to make objects not to locate their own dependencies and rather just expecting them to be provided.