The Singleton Pattern
If you crack open Design Patterns Elements of Reusable Object-Oriented Software, you will find the following intent for the singleton pattern:
“Ensure a class only has one instance, and provide a global point of access to it.”
It has other things going for it like the ability to subclass.
In Unity code you may see an odd implementation which achieves the intent without really following the pattern to a T.
public class MySingletonClass: Monobehavior { public static MySingletonClass instance; void Awake () { if (instance && instance != this) Destroy (this.gameObject); else instance == this; } }
In Design Patterns, the implementation actually limits access to the singleton object through a static member function, which checks for an existing object and either returns it or makes one to return if one does not exist. Constructors are also cut off outside of the class. But, most of the time we are dealing with GameObject components in Unity. So, we often stop once we see that there is only one and there is public access to it.
An implementation closer to that in Design Patterns would look like this:
public class MySingletonClass: Monobehavior { private static MySingletonClass instance; public static MySingletonClass Instance () { if (instance) return instance; return new GameObject().AddComponent<MySingletonClass>(); //Or if it requires a little tweaking in the inspector first //Make a prefab and use Resources.Load(string path) like so: //return (MySingletonClass) Instantiate (Resource.Load ("MySingletonPrefab")); } }
However, we can do better. This is a fairly consistent pattern, and is therefore a candidate for reusable code. By including the following generic class in a project, a singleton of any class can be created in one line of code.
The generic singleton class really only affects the way the singleton is referenced. So, it is easily changed if you no longer wish to use a class as a singleton, as you may be convinced by the rest of this post. But, I offer this tool, as it is better than what you will normally see in Unity tutorials.
Counter-Example
This post is inspired by a current project of mine, a game with a hex-grid movement system. There are numerous resources dedicated to creating such a system. Here are some links if you are interested:
Catlike Coding has a lengthy tutorial on building hex worlds with many features. There are 24 tutorials in this series at the time of this post.
Red Blob Games documents a wealth of knowledge on the mathematics of hex grids. This was my primary resource.
The first few problems I set out to solve were creating the grid, determining which tiles were within a certain range of a selected tile, and path finding. Red Blob Games has an excellent introduction to path finding. I positioned my hexagons according to the Red Blob Games geometry section, stored it in a 2D array as shown in the storage mapping section, and highlighted the movable spaces using the approach in the range-obstacles section.
The magic really didn’t happen until I started path finding. After reading the Red Blob Games article on path finding, I decided my use case did not really require anything more than a breadth first search solution. My goal was to make a Fire Emblem-like movement system where your only options are what is in range during a single turn. I had already performed a breadth first expansion of the frontier to highlight the tiles within range. All I needed to do was add a line of code to my existing loop recording the “came_from” tiles as shown in the last example in this section. This creates a pseudo linked list from any tile in range back to the start position. Reverse it to get the path. Done!
But! I stored the “came_from” tiles in a 2D array, just like the example. However, it felt like that reference should belong to the tiles themselves, you know, like how a linked list actually is structured. I slept on it and decided to move the references to the tile class. My reasoning: multiple grids. Storing “came_from” references in a 2D array, I was locked to the size of that array. Every tile had to map nicely for the data to make any sense. Storing the references on the tiles, mapping didn’t matter. If one tile had another in its “Neighbors” list, the latter was accessible from the former. The grid was completely irrelevant in terms of navigation. (Though my grid construction methods did automate neighbor assignment for adjacent “on-grid” tiles.)
Here is a quick look at what the implementation yielded:
The beauty of this feature, is that it was born solely from a change in structure. By letting go of rigid control, new possibilities were able to emerge from the existing code. By letting go of the idea of a governing grid, the tiles were able to do their own thing. This in turn lead to more flexibility in how grids could be created, used, and animated. In relation to singletons, by not assuming only one class instance would be needed, I enabled everything that is possible if multiple class instances were allowed.
Assumptions
The underlying assumptions of the singleton pattern are that only one instance of a class is needed and that public access is needed. In Game Programming Patterns, Robert Nystrom points out that both of these things are not always needed at once, and that we often reach for a singleton to meet just one of these requirements. He also goes on to point out a lot of other flaws in the pattern: global state is a head ache to debug, it encourages coupling, it is not concurrency friendly, and lazy initialization can hurt performance.
Rather than split hairs about implementation, I would like to call into question the assumptions themselves, specifically that of the single instance. Sacrificing plurality of an object type for the apparent boon of public access can be tempting but quite detrimental. For example, if I were to have assumed a single grid in my hex implementation, I would have ruled out (or made difficult) a number of features: multi-layered maps (think buildings with multiple floors), moving platforms, grids of different scale or orientation, and on or off-grid portals and other links (single or bi-directional).
One such shortcoming can be seen in the NavMesh implementation in Unity. There is a solution in NavMesh Building Components. However, upon examination, it is still a little sloppy. In AddData(), which is called whenever the transform of the navigable object is changed, we find this line:
m_NavMeshDataInstance = NavMesh.AddNavMeshData(m_NavMeshData, transform.position, transform.rotation);
Following the trail to this documentation page indicates that the NavMesh system was built under the assumption that there would only ever be one. This assumption rules out moving platforms… unless you want to push NavMeshData every single frame.
I don’t intend to talk down at Unity. It is a flexible tool which has empowered many developers (myself included) and removed a lot of the barriers to coding and game creation. And I deeply believe in their mission statement. I only seek to point out how the assumption of a single class instance can limit a product.
What other places can we question whether one is really all that will be needed? Even the example code in Design Patterns defines a MazeFactory as a singleton. Why? What would be possible if we allowed multiple MazeFactories to be popping out Mazes simultaneously? Chunking and multi-threading a huge maze is one possibility. Try to reason about your assumptions and make sure they are valid. They might just be walling you in.