Inversion of Control in Unity

Many developers come to Unity from a background in object-oriented programming and look to use the same patterns in Unity that they used elsewhere. You don’t neglect what you have learnt elsewhere just because you are using a different tool but sometimes you have to apply those lessons differently with the new tool. Unity has its own idea about how you should architect a game and before you start fighting against this it is best to try working with it – you will be a lot more productive if you do.

Everything is a MonoBehaviour

The first principle of working with Unity’s architecture is that everything should be a MonoBehaviour. Which means everything should be a component on a game object. Even the stuff that doesn’t represent a feature of a specific game object, like code that initialises the scene or code that saves the current game state – just create an empty game object, call it something sensible like “scene controller” or “services” and add these components there.

If your code is a MonoBehaviour then Unity knows about it, the Unity editor knows about it, and the tools that Unity provides will work with it. If your code is not a MonoBehaviour then it sits outside of Unity’s knowledge and ability to manage it and you can’t configure it in the Unity editor.

(Scriptable Object is an exception to this but you probably don’t need that.)

The editor

There is one disadvantage to making everything a MonoBehaviour – you have to work with two IDEs. The first is your coding tool – usually MonoDevelop or Visual Studio – and the second is the Unity editor. You can’t spend your day entirely inside your coding tool of choice, you have to mess with the Unity editor as well. But that’s okay. You create the components in your code editor and you configure them in the Unity editor, just like you would any other asset.

Inversion of Control

All of which is an introduction to my real reason for writing this article – a lot of developers new to Unity struggle with inversion of control, or more precisely the apparent lack of it. You may come from any programming background, but if you are used to having a dependency injection container or service locators you may well regard Unity as inferior and you may even decide to create a dependency injection container for it. Before you do, take a look at what Unity does out of the box.

In common use inversion of control falls into three categories –

  1. Service locators
  2. Dependency injection
  3. Events

Unity has tools for all of these. Some of them may not look like the tools you are accustomed to but they have much the same goals and produce similar results.

Service locators

Unity’s service locator is the Find… methods of the Object class – FindObjectOfType and FindObjectsOfType. These methods will find the components in the current scene that match a particular type, and if you followed my advice above then all of your code is inside components of some object or objects in the scene. So if, for example, you have a component called “ScoreService” that submits scores to your server, just call FindObjectOfType<ScoreService>() to locate it.

Some developers complain that the find methods are slow, but

  1. They will rarely impact the performance of your game
  2. If they do impact performance, use dependency injection instead

Dependency injection

You know that thing in the Unity editor where you drag one game object onto a property of another to provide the component instance for that property. That’s dependency injection. It may feel a bit strange configuring it in the editor, but it’s a lot easier to understand than the pages of code or XML used to configure most dependency injection containers.

Unfortunately configuring dependencies in the Unity editor is so easy you won’t feel as smart as you did when you used Spring or Robotlegs – even your designers can configure this dependency injection.

Events

There’s three ways to handle callbacks into an object.

1. SendMessage

Unity has had the SendMessage method since the dawn of time. It works but is less than ideal because it requires matching a string in the caller to a method name in the callee, which requires setting global rules about the naming of methods (or losing any benefit of SendMessage over just calling the method directly).

2. Actions

If you code in C#, you get Actions for free as part of the language. Actions are delegates that don’t return a value and as such are great for event dispatching. Unfortunately, the Unity editor doesn’t understand them so they have to be configured in code, but that’s what you thought you wanted anyway isn’t it.

3. Events

Since the introduction of Unity’s new GUI in version 4.6 Unity includes an event system that can be configured in the editor – so if a game object dispatches an event you can configure the listeners for that event inside the editor rather than in code. It feels odd at first, but if you embrace the idea of building your components in a code editor and configuring them in the Unity editor then, as with the dependency injection, this will make absolute sense. Try it.

Conclusion

Unity has it’s own tools for managing inversion of control. They are unlike the tools in most web frameworks but they do the same job and in some cases they are even easier to use.

The first game I wrote in Unity was a struggle as we broke all of the above rules to code the game the way we did with other tools. With my second and subsequent games I follow the Unity patterns and I build games faster and have more fun doing it.

This entry was tagged .

4 thoughts on “Inversion of Control in Unity

  1. See, funny thing is that since I stopped using absolutely anything with “Monobehaviour” written on it, I’ve had a much easier time.

    I even tried to use a Monobehaviour script to depth-sort a 2.5D scene but the interface methods OnBecameVisible and OnBecameInvisible are extremely sketchy and fire multiple times for no reason. It’s still used but there are several layers of checks in the methods that really shouldn’t need to be there.

    I even dropped Mechanim because it cannot be synchronised with frame-by-frame execution of code. Which happens to be essential in 2D games because of the need for specific animation frames to pair with code execution.

    My solution to the problems with Monobehaviours is not fighting them or working with them, but completely ignoring them. I’ve had far more productivity since I’ve relegated them to showing graphics and what little of the new UI that works reliably.

  2. I’m only writing this because I’ve read the previous comment. For me, the funny thing is that I started using Unity by making my own, non-MonoBehaviour classes. And gradually, I shifted to switching to MonoBehaviours more nad more, because when you do that, you’re working WITH Unity, not AGAINST it.
    I still don’t think that literally everything should be a MonoBehaviour. Obvious example: a data class designed to be saved to PlayerPrefs or XML file. But using things that Unity already has, instead of reinventing wheels whenever you feel slightly inconvenienced or do not want to learn something new, is definitely the way to go.

  3. I must disagree with this entire post altogether: the features you suggest are not at all IoC !

    Service Locator: when looking for a particular type using FindObjectOfType, you code is *coupled* to that type, and that type cannot be modified or only resolved at runtime. A major advantage of the service locator is that you decouple your code from the actual service implementation (by encapsulating the details into the service locator). This is not achieved here. FindObjectOfType is merely a helper function for finding active objects in the hierarchy…
    (for more information, see here: https://msdn.microsoft.com/en-us/library/ff648968.aspx)

    Dependency Injection: same as previous point, i would argue that simply “hooking up” object references in the inspector can be considered dependency injection. Again, your code relies on concrete types, instead of different mechanisms for resolving the actual type that will be passed into it (configuration? convention? etc). The inspector also has little support for inheritence / interface implementations so suppose that *did* act as DI, you would not be able to store a concrete object type reference in a field of an interface type (you’d like to create tests with a “mock” version of the interface for your code in many of the cases when using DI).

    Events: Action exists in .NET (not in the Unity world) as well, although i wouldn’t call it a “mechanism” for messaging (otherwise other IOC frameworks wouldn’t be needed for that ,right?)

    UnityEvents are kind of like standard events (only persistent so you can play with them inside the editor). SendMessage is a pretty hacky way of sending data between different objects (nothing is enforced). I wouldn’t compare this in any way to the way messaging works in IoC frameworks.

    To reiterate my opinion – this is not IOC at all…

  4. @Lior Tal

    Hi Lior, thank you for your thoughts.

    I don’t know which is your IOC container of choice but whichever it is that architecture is not the definitive definition of what is and isn’t inversion of control. I deliberately break my argument down to the three main patterns where IOC is used – service locators, dependency injection and events – so we can avoid needless discussions about what is or isn’t IOC. What matters is the practicalities of these three patterns that are most used in IOC architectures.

    I do not claim that the tools Unity provides have all the features of a modern IOC container, but I do claim that they are real, are useful, are IOC and fulfil the needs of most developers. Which is why I say in the article to look at what Unity provides before creating an IOC container of your own.

    A service locator is generally defined as a central registry that provides an object at run-time. FindObjectOfType is a way of obtaining an object from the game at runtime. It may not have all the features of the service locators you are used to but it is a service locator and it has the benefit of being very easy to use. While many service locators decouple the type, this is not a requirement of the pattern (and is anyway only applicable to object oriented programming and the pattern is more general than that). The important feature is that the service locator decouples the instance.

    Dependency injection is generally defined as passing dependencies to a dependent object rather than the dependent object creating or fetching the dependency itself. That is what you are doing when assigning an instance to a property in the editor. Again, inheritance and interfaces are not required features of the pattern and would unnecessarily restrict it to object-oriented programming only. In some architectures a DI system that doesn’t handle inheritance and interfaces would not be useful, but in Unity this is not the case. The DI provided in the Unity editor is extremely useful and fulfils the needs of most developers.

    As with service locators and dependency injection, don’t confuse what your IOC container of choice does with the only definitive definition of events. Events are generally termed as a system where one part of a system sends a message to another to perform an action.

    SendMessage is an (admittedly limited and crude) way to do this. However, to make it effective for decoupling requires such strict rules that I wouldn’t consider it useful. I’m sorry if that wasn’t clear in the article.

    Actions are a very effective way for objects to register for messages sent by another object. They are a more generalised version of .net’s event system and I find them more useful because they don’t have the rules and restrictions around the data passed when the event is raised.

    Unity events are indeed like standard events. That’s why I recommend them. You could even use them to set up a central event bus if you wanted to but I wouldn’t recommend that.

    To reiterate my opinion and what I said in the article, these are not like most developers experience with IOC containers but they are IOC and they meet the needs of most Unity developers. Developers should consider them before making or using a separate IOC container.

Leave a Reply

Your email address will not be published. Required fields are marked *