In this lecture, we're going to solve this pesky little problem with our audio player, where when
we change the scene, the track restarts and we're going to fix this using something known as the singleton
pattern.
So before we talk about the singleton pattern, let's actually understand what's happening in our game.
In our main menu, we have all of the objects that exist within this scene, including our audio player.
But if we go ahead and hit play and then change our scene by hitting our start button, everything from
that original scene is being destroyed and then everything from our game scene is being loaded in.
So our old audio player gets destroyed and we create a new one.
And in creating our new one, we start playing our track from the beginning.
So we're going to need a way of grabbing our audio player from our main menu scene when the game starts
and pulling it across into other levels of our game.
And if we turn to the Wikipedia article for the Singleton pattern, it says that the singleton pattern
is a software design pattern that restricts the instantiation of a class to one single instance.
And this can be very useful when exactly one object is needed to coordinate actions across the system.
If we continue to read, though, it does also say that it's considered to be an anti pattern and is
frequently used in scenarios where it's not beneficial.
And that's because the true singleton pattern not only requires us to only have a single instance of
an object in our game, but also that that instance is globally available to everything that needs it.
And having global or public variables in our game can cause us some problems, especially when we're
using the singleton pattern.
So we have to be very careful about how and when we use it.
But if we jump back into unity, I'm going to show you two different ways that we can use this singleton
pattern.
The first will create a single instance of our audio player, which will persist across our game but
not be publicly available.
And the other one will be a more traditional singleton pattern, which does have global access.
So to get us started, let's go over to our audio player and then into our audio player script and to
set up the non global version, we're going to do all of this in a wake.
And in here we're going to call a method that we're about to write called Manage Singleton.
Then underneath our wake method, let's write this method, which is going to also be void, and we're
calling it managed singleton.
And we currently have an audio player in every scene of our game.
So we're going to have to do a couple of things.
First of all, when we load into the scene, we want to check if an audio player already exists.
And one way of doing this is to use find objects of type.
So just like find objects, but with objects plural instead.
And rather than using the chevrons like we're used to, we're actually going to use parentheses this
time and we're going to say Get type.
What this get type does is it will return the type of the class that we're looking at.
So in this case, it will be an audio player.
So we could instead look for audio players instead of get type.
But this is a bit more generic so that if we want to use this in other scripts, we can just copy it
in without having to amend too much of the code.
So this as it is, we'll find all of the objects of type audio player, and from here we want to find
the length of this list.
Now this length is going to come in useful and help us get rid of the red squiggly error.
Let's store this in an integer variable called the instance count.
So this will keep track of how many instances of our audio player exist.
When we load a scene from here, we can then ask that if the instance count is greater than one, then
we've got more than one audio player in our scene and we need to start getting rid of them and we can
get rid of objects by just destroying them.
So we can say Destroy game object.
And that's the game object with the lowercase g.
Otherwise, if we don't have more than one audio player, we want to set this audio player up so that
it can transition between our scenes and not get destroyed when a new scene is loaded.
So to do this, let's say else don't destroy on load and pass in this game object.
This will then mark our audio player so that when we change scenes, the existing audio player will
come with us.
So that's one way of doing it.
But I am noticing a very small problem and that is that before we destroy a game object, we also want
to set our game object set active to false.
Now this might seem a bit redundant because we're literally about to destroy it, but because of the
way unity executes its code, there's a very, very small chance other objects in our game may try and
access this version of our audio player before we go and destroy it.
So by disabling it in a wake, we're stopping anything else, trying to grab it and use it before we
destroy it.
Now, to see this in action, let's save our script and head over into unity.
And now, if we go ahead and hit play, everything should work as it did before.
But if we hit start, then the music continues to play.
And if we look in our hierarchy, if we expand our game and let's pause for a moment, will not be able
to find our audio manager in our game anymore.
And that is because it's now under this don't destroy on load header.
So this audio player in here is the audio player from our first scene rather than our currency.
So with our audio player working as expected, we could leave things there.
But I did say that there was another, more pure way to write a singleton.
So let's jump back over into our audio player script and have a look at how we might do this.
Well, it's actually going to be fairly similar, but rather than trying to find objects of type and
then checking how long that list is, we're going to store a variable at the top of our script.
It's going to be a type audio player in this case.
So we're storing a reference to our own class type, and we'll call this the instance.
And to get this working how we want, we're going to also add a new keyword in front of our audio player.
So just like how we can have public or private, we can also have some other keywords as well.
And one of those keywords is something known as a static.
So this is currently a private static audio player.
And static variables are interesting because they persist through all instances of a class.
So if we had multiple audio players in our scene, they would all share this exact same static instance.
This is great for our singleton.
So in our managed singleton method, let's just comment out the first two lines for a moment and we
can instead say that if our instance does not equal null, then we want to go ahead and destroy the
game object that's trying to create itself.
Otherwise we want to set the instance variable equal to this object so we can use the this keyword which
indicates this version of the audio player.
So the logic here is that if we're the first audio player to come along, then our if statement will
be false and we'll set ourselves to be the instance of this static audio player.
Otherwise, if we're the second or maybe third one to come along, then our instance variable already
has a version of an audio player stored in it.
So we know we're not the first ones to come along and we can go ahead and destroy ourselves.
This instance, though, is still private, so if we scroll up to the top, a true singleton pattern
would have this instance be public.
So we could do this by either setting this to be public.
But we would then have to worry about other scripts coming in and changing what this instance is.
Or we could create a getter so a public audio player get instance and this would just return our instance.
Making this global as it is, gives us some benefits that we can now reference the instance of our audio
player dot any of our public methods.
This means we wouldn't have to explicitly find our audio player in the scene first.
We could just reference this static instance and have access to all of its public methods.
To show you what I mean.
If we head over to our shooter method very quickly, we're currently saying audio player play, shooting
clip, and we're able to do that because we've already found a reference to the audio player in our
scene.
But if we want to bypass all of this storing variables and finding things in a wake, we could instead
just say audio player, dot get instance, dot play shooting clip, and that would bypass any need to
find that object in the hierarchy.
So a couple of good benefits there, but also a handful of drawbacks which we haven't really discussed.
By doing things this way and having a public instance available, we start to lose control of exactly
where things are in our game.
This is mostly fine for a small project like ours, but as your game gets much larger, you can have
a lot of things.
Interacting and trying to hunt down problems with singletons is an absolute nightmare.
So where possible, always try and avoid globally accessible instances if you can get away with it.
For our audio player, we definitely can.
So let's not do things this way and back over in our audio player.
We could keep our static instance if we wanted to, and in fact I think we will, but we won't create
the public getter method for it.
Then down in Manage Singleton, let's just clear out those two commented lines of code that we don't
need anymore.
And now if we jump back into unity just to make sure that everything is still working as expected,
let's go ahead and hit play.
Our audio player is now in Don't Destroy On Load and our music has started.
If we change sane, our music continues to play and our audio player is still there, ready and waiting
for us.
So there's two slightly different ways of implementing the singleton pattern and also how to make it
global if you ever wanted or needed to do that.
And with this knowledge of Singleton in hand, the next step is going to be to look at other objects
in our game that may or may not benefit from also being Singleton's so no challenge for this lecture,
but before you go on to the next lecture, just have a think about which objects you think might benefit
from being a singleton.
And then in the next lecture, we'll start looking at some of the other problems that still exist in
our game.
So I'll see you there.