Creating Manager Classes in Unity
Manager classes such as a GameManager, AudioManager, or UIManager allow you to centralize the logic for game elements across your game. For example, any time you want a triggered audio clip to play you could have code to enable the audio spread out in 15 different places across your scripts. This would make it a pain to later chase down where that code resides if you need to update your clips. Instead, the best practice is to centralize logic into a Manager script so that you can easily find any code related to that function.
In this guide I’ll walk through creating a GameManager which will be responsible for controlling the state of the game and an AudioManager to control our sound effects.
Creating the GameManager
The GameManager will serve a few functions. For gameplay, we can use the GM to check whether our player has the required object in order to win the game (in this case a stolen keycard to unlock a vault to win the game), and also to trigger the winning cutscene if the player arrives at the vault and has the card. We can also use it to control the ability for our player to skip through cutscenes and resume gameplay.
To create the GM we create a new C# script called GameManager and attach it to a new empty gameobject with the same name. You will notice a script titled GameManager gets its own unique gear icon in the project view.
Using the Singleton Design Pattern
In my last guide I covered setting up a Singleton design pattern which is an excellent framework for easy reference of your Manager class scripts. For simplicity I will paste the completed singleton code here but if you are interested in learning more about how this works and why we do it check out that guide.
With our Singleton set up, we can begin to create new properties associated with our game. As mentioned at the beginning of this guide, we need a property to store whether or not our player has collected the keycard required to win the game. In this case we create a new bool property for HasCard. When working in manager classes it is important that we create this as a property (by adding get & set parameters) rather than just a variable.
If we needed to, we can execute code for get or set in this property by adding squiggly braces after each instead of the semi-colon. Here we can simply create an auto property with the below syntax on one line:
Now is where the use of the singleton comes into play. In this game we need to tell the GameManager that our player has the keycard once a certain cutscene is triggered which shows him stealing in. Without a Singleton we would need to first use GameObject.Find to locate the gameobject and then get a handle to the game manager script with GetComponent:
Instead, with a Singleton, we can forego using GetComponent and GameObject.Find with the simple line of code below:
Finding GameObjects and using GetComponent is considered not optimal in terms of performance, and switching to a Singleton pattern can streamline your code implementation while optimizing the performance of your game.
However one potential downside for using Singleton and properties in general in your manager classes is that Unity cannot “see” properties in the default inspector. Our “HasCard” bool variable will not be visible in the inspector unless we enter debug mode:
Now that we have the access to our “set” property of the HasCard bool variable done, we also need to “get” that value when we trigger the collider for our endgame cutscene to check and make sure HasCard is true before we trigger the ending scene. We can simply check this with an if statement and reference our GameManager singleton once again:
The logic above reads that if our player collides with the win level cutscene trigger and has the card play the cutscene, otherwise debug that our player does not yet have the key. Everything is working properly and that’s it for our GameManager for now. Let’s get into the AudioManager setup to trigger our victorious audio clip at this same point.
Creating the AudioManager
Similar to our GameManager we can create an AudioManager to centrally control when the various voiceover and music clips should be played in our game. We create an AudioManager gameobject as well as a new AudioManager script, and implement the Singleton pattern into our script.
Our AudioManager gameobject will have two child objects — Voiceover and Music. Each one will need an AudioSource component, and what we will do when we need to play an audio clip is to pass in the relevant clip to that AudioSource.
Now we can create a function inside our AudioManager for PlayVoiceover() and we can call this from any of our other scripts at the moment we need to play a clip by using AudioManager.Instance.PlayVoiceover() and pass in which clip should be played.
Our audio clip to play is stored on our voiceover trigger game objects in the inspector as variable “clipToPlay”:
Now to pass in these clips to our AudioManager, our VoiceOverTriggers script can simply read as follows:
Finally, we need to replace the pseudocode in our AudioManager to actually play these clips once passed in. We create public variables to store our voiceover and music audio source game objects on the AudioManager game object and drag in the two child objects to their designated fields.
Finally we can assign the clipToPlay to our voiceover AudioSource and play it:
We can do the same thing for our PlayMusic() function which will pass in a victorious music clip to play from our WinLevel trigger which is storing the associated clip to play on its collider:
We also want to be sure to turn Play on Awake off on our VoiceOver and Music audiosources and set the Spatial Blend to 2D so our audio plays at the right time and without any distortion based on relative space to the player / camera.
All set! We have a working GameManager and AudioManager set up via Singleton, making them very easy to reference from the rest of our scripts. Our game is nearly complete — in the next guide I’ll cover implementing a loading screen along with our main menu and we’ll be ready to publish. See ya there!