Unity is a very good game development tool, although while I like most of the features (and trust me it is very hard to make me happy), the code framework is still awkward to use.
Let me explain why I say awkward. Unity is born as rapid application development tool and due to its nature, it has been soon adopted by many “indie” developers. However Unity is now trying to make a leap forward toward the triple A development, but while the company is mainly focusing on adding new editor features, there aren’t many news about the framework itself, which code design is currently too much “one-man band” oriented.
The considerations in this article do not really apply to simple projects or projects developed by one or two persons. For these specific cases, Unity is perfect. The arguments in this article apply instead to medium-big projects developed by a medium-big size team.
I will not talk about all the flaws I found, but I will focus on specifically one, that is how awkward is injecting dependencies inside Unity entities.
To be clear, a dependency is defined as an object needed to another object to execute its code. If a class A needs the instance of a class B to execute its code, B is a dependency for A.
Unity is based on a modern concept of “Entity Framework”. Out there several implementations of entity framework exist and I am even creating one in actionscript 3. Entity framework is a very powerful concept and has many advantages: pushes the users to favor composition over inheritance, keep the classes small and clean focusing on single responsibilities, promotes modularity. This is in a perfect world…
In Unity, the Entites are called GameObjects and the Components are based on MonoBehaviour implementations. Personally I have some golden rules I want to follow when I implement MonoBehaviours for GameObjects:
- MonoBehaviours should always operate on the GameObjects where they are attached to.
- MonoBehaviours are reusable, single responsibility classes.
- GameObjects should not be created without a view (that would be a mesh, collider or something that is really an entity in the game, the only exceptions are for empty GameObjects used as “folders” and other specific cases I will illustrate another time).
- MonoBehaviours can know other components on the same GameObject using GetComponent.
- The behaviour of a GameObject should be extended adding MonoBehaviours instead of adding methods to a single MonoBehaviour.
The problem is, as many of you know already, that Unity does not really allow to follow most of these rules because of the way the dependencies must be solved. In fact what happens if my component needs to communicate with other classes? Let’s say there is a Level class and for some reasons this class must know the monsters that are still alive. When a monster dies, how is it possible to communicate the fact to the Level object?
Currently there are 3 simple solutions to this problem:
- Level is a Monobehaviour created inside a GameObject that has no view. All the monsters will look for the Level class using the GameObject.Find call inside the Start or Awake functions.
- Level is a Singleton.
- Like 1, but Level is found through Object.FindObjectOfType.
Howbeit, I reckon there are problems in following all the three possible solutions:
What’s wrong with GameObject.Find
The first solution breaks my golden rules, which would be not a big issue since they are actually more guidelines for good design. Instead the real problem is the use of GameObject.Find. GameObject.Find is one of the example of how awkward the Unity Framework is for big project development.
What happens if someone in the team decide to rename the GameObject? Should be imposed that the GameObjects once created must not be renamed (or deleted) anymore? GameObject.Find can lead to several run-time errors that cannot be caught in compiling time. This scenario could be really hard to manage when dozens and dozens of GameObjects are searched through this function.
What’s wrong with the Singleton
Singleton is a controversial argument since the pattern has been invented. I am personally against the use of Singleton. However if you ask me why I am against the Singleton I will not answer you with the usual answers (break encapsulation, dependencies hiding, many issues to test the code through unit test, being bound to the implementation of a service instead of its abstraction), but with the hindsight of the practice: your code will become a big pile of crap after a while. This is because Singleton does not involve any design constriction that, while make apparently the coder life easier, will make it a hell later on, when the classes design become an incomprehensible blob of code without a structured flow.
What’s wrong with the Object.FindObjectOfType
How should the Level object be injected inside the Monster class then? There is actually a third solution, that is calling the function Object.FindObjectOfType. But, what is FindObjectOfType? Object.FindObjectOfType could be seen as a wrong implementation of the Service Locator Pattern, with the difference that it is not possible to abstract the implementation of the service from its interface. This is another problem of Unity framework, Unity seems to hate the concept of interface. Interface is the most powerful concept at the core of every well designed code. Instead to push the coders to use interfaces, Unity pushes the coder to uses Monobehaviour, even when the use of Monobehaviour is not necessary.
Mainly there are 3 ways to resolve dependencies: using the Service Locator pattern, injecting dependency manually through constructor or setter and using an Inversion Of Control container. I do not like to use the Service Locator Pattern because the SLP itself is a singleton (or a static class) and this could lead to some severe limitations compared to the IoC container solution. However using an IoC container in Unity is not simple because Unity does not specify a place where the application can be initialized.
Explain what an Inversion of Control container is needs probably another article, so I will be simple with it. An IoC container is a…..container that contains the dependencies that must be injected. It is called IoC because the design is created in such a way that the objects are never created by the user (in fact you can also forget the use of the new keyword), but they are lazily created by the container when they are requested.
There are several IoC containers out there, a lot written in c# as well, but practically no one works in Unity and most of all, they are damn complicated. For these reasons I decided to create an IoC container for Unity trying to keep it as simple as possible. Actually creating a basic IoC container is pretty straightforward and everybody can do it, my implementation has just a few tweaks that makes it simple to use with Unity projects.
Conclusion (for this article)
I decided to conclude this first article here because before to continue I want to know the interest of the people in using an IoC Container in Unity. If the interest is low I will just keep on using it for my personal and professional works only. So if you are interested in knowing more, please leave a comment.
The second part of this article (including source code) is now available here: http://blog.sebaslab.com/ioc-container-for-unity3d-part-2