- Rasmus Nielsen
Devlog January 2022 - Design patterns
Hello and welcome! My name is Rasmus. I'm the lead developer at GAMUCATEX. This time I will be covering design patterns in programming that we, developers have used over the past month in our game.
Our team has been working hard on cleaning up code, thus there is a lack of new gameplay features, and we will therefore focus on some of the design patterns that were used to achieve a cleaner codebase. This means that this devlog is quite technical, will cover a few code examples and there will be a number of programming terms used throughout the devlog. This devlog therefore assumes that you at least have more than beginner-level knowledge of programming.
We would like for this devlog to serve as inspiration, and get you, the reader to further research these and more topics and potentially implement similar patterns in your own projects. This devlog covers a couple of examples of how our team developed these patterns in C# and the Unity game engine, implementation can vary slightly in other languages/engines.
Our focus for this devlog will be on two design patterns, that being the Observer and Singleton patterns. Some of this information and the inspiration for using such patterns comes from the following book: https://gameprogrammingpatterns.com/ (and Stackoverflow)
The observer pattern is used to make classes rely less on each other, and allows for observer functions to be called automatically whenever a certain subject function is called.
This pattern allows us to set up a subject and some observers. The subject is an event, in which calling the event is similar to how you would do a typical function call. That event can have observers subscribed to it, without the subject's class having any reference to its subscribers, thus completely ridding of that the subject's dependency of other classes. The observers are just a normal function that has the same parameters/generic types as the subject. So whenever the subject's event is called, all other subscribed functions are also called. The pattern also easily allows a one-to-many relationship between a subject and its observers. The observers' or subject's class do not need to inherit from any specific class, they can be declared as you would a typical class. Same goes for the subscribed function. The only thing necessary is to write a single line of code which subscribes to that event, and would look something like this in Unity.
SubjectClass.ExampleEvent += ExampleObserverFunction;
What also becomes really interesting, is that you can as easily unsubscribe from the event. So for example if a certain object, necessary for something to function is destroyed, you can unsubscribe from the event, and so that previously subscribed function is not called automatically when the event is called.
I will be covering an example of how we use the observer pattern next, but first you need a bit of context of the project we are working on: A turn-based card game, where yours and the opponent's turns are carried out at the same time. When the turn is ended, cards that have been played during the turn will have their abilities triggered.
An example of how we use the observer pattern can be seen in the illustration below. "OnTurnEnd" is the event that is called when the player clicks to end their turn, when the turn is ended, a bunch of things will happen, in multiple different classes, including: Triggering card abilities, progressing the game state, handling associated visuals and more. So in this example, we only call the subject's event, and all other subscribed functions are then called completely automatically, and without dependencies from the subject.
The singleton pattern is a way to create a global instance of a certain class, that makes it easy to access valuable information from that class in other classes.
The singleton pattern is more simple than you might think, and somewhat controversial, but is still something that most programmers use, however, some tend to use it too much. The singleton pattern is great when you have a class that has information that many other classes will need, as it allows for easy access to that class and its variables/functions.
The way the singleton pattern works is by creating a static instance of a class, which would look something like this:
public static ExampleClass instance; //Some code instance = this;
This is especially useful for some classes that manages a large amount of elements in the project, which many classes could rely on, and those who only require a single instance of themselves. However, you still would like to minimize the use of singletons, as they spawn a global variable, and with bigger projects can make the code harder to read and maintain for the developers, and actually couples things more, which is something that you would like to avoid as much as possible in programming.
So why did we in our team decide to use singletons in some instances?
The answer is quite simple, it is that the previously mentioned pros outweigh the cons in a few areas of the codebase. Our team has a necessity for some managers or similar classes, that only requires a single instance and have valuable information that we would like to access in a bunch of different classes.
That is it for this devlog, thanks for reading! Hopefully my ramblings made some sense.
We would appreciate any kind of feedback on how we carry out the development of our codebase, and if there are better alternatives for any of these examples, we would love to hear what you have to say.
Next time we will be sharing some new gameplay elements, especially with the settlement manager system we have in the works!