Game Design Pattern— Using Singletons in Unity
Whether you are a rookie or a veteran in game development, you must have heard of singletons before. It is the most commonly used design pattern, even for myself when developing Night at the Clownville. Today, let’s explore a little bit about this design pattern and see for ourselves if it’s worth investing the time to use this in our code.
Singleton is essentially a design pattern that allows only one instance of a class that allows any script to reference to it — fundamentally a global access to this instance. At this point, you might be wondering if singletons are the way to go, since in software design, having global access to an instance is rarely a wise decision. However, there clearly are advantages to using singletons and especially in game design. The Singleton Pattern solves many of the obvious problems when it comes to game design and hence the problem lies not within the design pattern itself but how we choose to use it in our code.
Here’s how I use a singleton in my code in Night at the Clownville. To give context, this class is responsible for instantiating a list of objects or particles on awake. This saves time and resources since objects or particles don’t have be destroyed and instantiated over and over again. Ideally, the concept is to have a class to keep track of all the objects that I want to spawn in the Awake function.
The next time we want to instantiate something from the pool in another script, we simply do this:
Singletons provide a handful of benefits:
1. Global access
As we saw above, the singleton ObjectPooler is an instance of the class itself and holds the many attributes in its own class. And the attributes themselves are publicly accessible to all other scripts too! Now, you can definitely think of scenarios where you need information of a general manager class but you wouldn’t want to make references to it every time. Tightly coupling all other scripts is an unnecessary move sometimes and singletons solve that easily, but ironically, they make even more tight coupling later on. Say, you need an AudioManager class that plays your preferred sound of choice at a specific time in the game and because any script could potentially call it, you make this AudioManager class a singleton so any script could access it anywhere and anytime.
2. Only one instance of that class
Singletons also provide the obvious benefit of having just a single instance of the class itself. Now, if you want to store some information, you have the choice of storing it in a variable or singleton. With singletons, you get only one instance, and full control over it. Instead of creating a new instance accidentally, the one already created will be returned. Seems like a good solid use case for a resource-sharing objective, right?
It sure is. Until it violates the Single Responsibility Principle.
Think of it this way: Singletons seem very efficient at first look because it is convenient to use. But with convenience, we are sacrificing transparency. Down the road, when we have too many singletons, it may be hard to keep track which scripts use which singleton. Thus, singletons are hard to use because:
1. Dependency hiding
If we are using a typical class, we are able to inspect the constructor or method’s signature and it is clear to us which dependencies the class has. However, if a class calls a singleton, how are we supposed to know which singletons it is calling? Anyone using the class later on, will never get to know that the singletons they are calling, or even worse when the singletons have some sort of initialization methods.
2. Singletons are hard to test
As with any global state or variables, we cannot totally isolate classes dependent on the singletons we create. In other words, when we test a class that calls a singleton in the script, we are ultimately testing the singleton as well. What we want, ideally, is a loosely couple script.
Now, having explained the good and bad sides of using singletons, I’m still guilty of this bad habit of choosing convenience every single time during the development of Night at the Clownville. I have since created multiple singletons, from one handling the game saving function to one handling the key bindings. I have to admit, they might have created technical debt already, but I am actively finding ways to clear them off.
One good way that I have learnt is to inject dependency whenever singletons appear too convenient when solving the problem.
Although it is less convenient to construct a whole class and inject long-winded dependencies, this certainly pays dividends in the long run by getting rid of the long-term problems.
Well, at least I’m aware of the code smells now that I have written this piece here. Off to fixing singletons! See you in the next article.
Check this out before you leave:
Singletons in Unity (done right)
Even if you're new to Unity, you probably already know that Singletons can be a controversial topic. You might have…