Welcome to part 2 of the Design Patterns series.
This episode will be about the Singleton Pattern!
Singleton Pattern
Singleton is a pattern that restricts an object to one single instance and has a global point to access it.
Ensure a class has one instance, and provide a global point of access to it.
Singleton is a very popular and commonly used design pattern. This is because it has all it’s charms in being easy accessible. But that’s also it’s danger.
Example
Game Manager
A typical example of a singleton usage in games are game managers. An access point to store data. Simple data such as score, data and sometimes time can all be managed by one singleton based manager.
Scrabble letter
When you build a scrabble game, you’ll have one bag full of letters. This bag can be seen as a Singleton. There’s only one bag and every player is allowed to access it when it’s their turn. It’s a great way to store your letters data.
(More info about this here: click here)
Intentions of using singleton pattern
- Eliminating instantiating multiple objects
- Globally accessible
- Central point for data
Demo
Demo introduction
For this demo we continue further with previous episode Façade Pattern.
This means I used Unity 2019.4.10 for this demo.
Please check out previous demo for full set up.
We’ll use singleton to add a score and high score system to this project.
This is a very common use for games to create a singleton for so, let’s try it!
Code
Singleton
Singleton.cs
For this singleton we’ll use Unity’s recommended singleton class they describe here: https://wiki.unity3d.com/index.php/Singleton
This is an abstract class we use as a base class for all our singleton usages.
This means that each of the classes inherited from this singleton class will not be able to instantiated multiple times and will be accessed by a static instance property.
namespace Singleton.Code { /// <summary> /// Singleton class from https://wiki.unity3d.com/index.php/Singleton /// /// Inherit from this base class to create a singleton. /// e.g. public class MyClassName : Singleton<MyClassName> {} /// </summary> public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour { // Check to see if we're about to be destroyed. private static bool m_ShuttingDown = false; private static object m_Lock = new object(); private static T m_Instance; /// <summary> /// Access singleton instance through this propriety. /// </summary> public static T Instance { get { if (m_ShuttingDown) { Debug.LogWarning("[Singleton] Instance '" + typeof(T) + "' already destroyed. Returning null."); return null; } lock (m_Lock) { if (m_Instance == null) { // Search for existing instance. m_Instance = (T) FindObjectOfType(typeof(T)); // Create new instance if one doesn't already exist. if (m_Instance == null) { // Need to create a new GameObject to attach the singleton to. var singletonObject = new GameObject(); m_Instance = singletonObject.AddComponent<T>(); singletonObject.name = typeof(T).ToString() + " (Singleton)"; // Make instance persistent. DontDestroyOnLoad(singletonObject); } } return m_Instance; } } } private void OnApplicationQuit() { m_ShuttingDown = true; } private void OnDestroy() { m_ShuttingDown = true; } } }
GameManagerSingleton.cs
Now we have the main Singleton added to our project, we can inherit our game manager from it.
namespace Singleton.Code { // inherit from singleton to have global access public class GameManagerSingleton : Singleton<GameManagerSingleton> { // player preference key private const string HighScorePref = "HighScore"; // score public int Score { get; private set; } = 0; // high score cached variable private int _highScore = 0; // high score getter which checks if current score should overwrite. public int HighScore { get { if (Score <= _highScore) { return _highScore; } _highScore = Score; PlayerPrefs.SetInt(HighScorePref, HighScore); return _highScore; } } private void Start() { // get last saved high score from player preferences _highScore = PlayerPrefs.GetInt(HighScorePref); } // returns new score public int AddScore() { Score++; return Score; } // resets score to 0 public void ResetScore() { Score = 0; } } }
Project adjustment
GameFacade.cs
To make the scoring system work, we must adjust the game façade to call the game manager single ton score properties and update visuals.
namespace Singleton.Code { public class GameFacade : MonoBehaviour { // text prefixes private const string ScoreText = "Score: "; private const string HighScoreText = "HighScore: "; ... [SerializeField] private Text _scoreText; [SerializeField] private Text _highScoreText; // game started boolean to prevent updates on initial call private bool _gameStarted = false; ... private void Start() { // initializing objects ... // initialize texts _scoreText.text = ScoreText + GameManagerSingleton.Instance.Score; _highScoreText.gameObject.SetActive(false); ... } ... private void OnEnemySurvived() { _foodMenu.EnableButtons(); // added check so score does not update while game hasn't been fully initialized. if (_gameStarted) { _scoreText.text = ScoreText + GameManagerSingleton.Instance.AddScore(); } // to make sure score doesn't get updated on first call _gameStarted = true; } private void OnEnemyDied() { // enable reset button when enemy died _reset.gameObject.SetActive(true); // refresh highscore text _highScoreText.text = HighScoreText + GameManagerSingleton.Instance.HighScore; // enable highscore text _highScoreText.gameObject.SetActive(true); } private void Reset() { // remove listeners sorcerer.OnSurvived -= RandomTarget; sorcerer.OnDied -= OnEnemyDied; // reset current scene and score GameManagerSingleton.Instance.ResetScore(); SceneManager.LoadScene(SceneManager.GetActiveScene().name); } } }
I removed most of the code to highlight all the singleton usages. To see the remaining code, please check: https://justinbieshaar.com/facade-pattern/#gamefacadecs
Now we’ve only added two new text fields and update the texts using game manager singleton score properties
Git
Find the repository here with full source code:
https://github.com/jscotty/DesignPatterns
Or download the unity package here:
Breakdown
I made this simplified UML diagram to break down what happens.
As you can see the façade requests the current score and high score from the game manager singleton. The game manager singleton will check it’s score and return it’s value.
All the façade cares about is the data the game manager singleton results. The game manager singleton will be the only one responsible of managing and storing it, rest everyone is allowed to know it.
I always see Singleton Patterns like T-Shirts with a message on it. The person wearing it is the singleton since there is only one possible instance of this person. He choose to wear a shirt with a message everyone is allowed to see (public t-shirt).
If you start to use Singletons with this idea behind, it can have great uses for your project!
Commonly the Façade pattern is also using the Singleton pattern because mostly one Façade is required. But in this demo that wasn’t necessary needed.
Resources
Derek Banas
Game programming patterns book
This is a great book for game development design patterns. I highly recommend to buy it or read for free on their website. Read for free or buy here: https://gameprogrammingpatterns.com
Assets
For the demo below I used three free art packages!
– Evil Wizard
– Free Pixel Art Hill
– Free Pixel Foods
Hope you enjoyed this episode!
Were you already familiar with the Singleton Pattern? It has a great use and can help a lot with speeding up your production.
Note: be aware that this pattern also get over used too much. Sometimes developers think their whole game can exist of singletons creating lot of security problems to your application. So be selective when to use it or not.
If any questions, feel free to message me on Instagram @justinbieshaar or in comments below!
Happy coding everyone! 👨💻
Greetings,
Justin Scott
Leave a Reply