UPDATE!
Lesson #84 in the Unity 2D course (toward the very end of Block Breaker) now addresses the problem and provides Sam’s solution.
This thread is no longer necessary.
THE PROBLEM
As of the time I write this, there is a problem in the singleton pattern being taught. The singleton code is put into the Awake() method. The idea is that this code will be run as soon as the singleton object is created, check to see how many such items there are, and if there is more than one, the new item will destroy itself.
This has created some problems since the “Destroy()” method has a delay and will not actually destroy the object until after the Update() method is complete. That means during the Awake(), Start(), and Update() methods of the first frame in the scene, you may have duplicates in your hierarchy, which can cause havoc with things like FindObjectsOfType. Some people in the courses are having problems with this now. Since FindObjectOfType returns the first instance it finds, it’s important that there’s only one.
The current singleton code works like this:
private void Awake()
{
int gameStatusCount = FindObjectsOfType<GameSession>().Length;
if (gameStatusCount > 1)
{
Destroy(gameObject);
}
else
{
DontDestroyOnLoad(gameObject);
}
}
MY PROPOSED SOLUTION
Originally, when Ben taught the singleton pattern, he taught it with this code, which seemed very confusing at the time (which is why I think they changed it to something simpler):
public class GameSession : MonoBehaviour {
public static GameSession instance = null;
void Awake () {
if (instance != null && instance != this) {
Destroy (gameObject);
print ("Duplicate GameSession self-destructing!");
} else {
instance = this;
GameObject.DontDestroyOnLoad(gameObject);
}
}
It’s weird to have an object with a reference to itself, but it’s not that hard to grasp. It’s like a person talking about themselves in the third person. Anthony does that sometimes.
When the first instance of this code is run, it assigns itself to the name “instance”. When the second instance of the code is run, it sees that “instance” already has a reference so it doesn’t bother. Neither will the third, the fourth, etc. As long as “instance” already refers to something, nothing else is going to assign itself to “instance”.
This is helpful because it means that the variable “instance” always refers to the same instance (unless something happens to that instance like it gets destroyed or something). Even if we somehow grab the wrong instance when finding objects, we can use its “instance” variable to find the proper one.
This seems like better code to use, but it’s not perfect. I’ve heard it doesn’t work very well once you start using advanced features like threading and such. It also requires a little bit of self-discipline to use properly because we have to remember to use the “.instance” reference after finding the object so that you are always referencing the same “singleton”.
I still don’t think this is actually a singleton since it still allows more than one object to exist for a brief period, but at least if you use the “.instance” reference, then you are never accessing the wrong one.
So, if GameSession has a variable like int playerLives = 3;
and a public function like GetPlayerLives()
to return that variable, for example, you might access it this way from a different script:
gameSession = FindObjectOfType<GameSession>();
playerLives = gameSession.instance.GetPlayerLives();
“.instance” is a reference that exists on all of your GameSession script and is always a reference to the same gameSession object, no matter which of them it was referenced from. So this will work.
The problem now is that if your FindObjectOfType found the wrong duplicate class, then that object will be destroyed soon. This means you are trying to access a reference variable on a class that no longer exists? That means critical errors. That means really bad things.
We have to make sure that once you find the right instance, the gameSession always points back to the proper instance and never again needs to access the first gameSession you found.
This just requires one additional step:
gameSession = FindObjectOfType<GameSession>();
gameSession = gameSession.instance;
playerLives = gameSession.GetPlayerLives();
The first line gets us a gameSession. Any one. We don’t care if its the right one or not, since they all have a reference to the right one anyway. The second line redefines gameSession to refer to the proper instance instead of the one we found. After that, gameSession can be used normally in the script. We don’t even really need the “.instance” anymore.
UPDATED AND IMPROVED
Rob and cornucanis brought to my attention that adding a static property to my code would make the code easier and safer to use. A property looks a lot like a variable except it doesn’t store data. Instead, it’s used to access another variable. This allows you to access a variable using a different name or access level. In this case, we’re making a property that has public read-only access while the variable itself is completely private. By convention, properties have their first letter capitalized.
The property I’ll be using is called “Singleton”. When accessed, it returns the “instance” variable from the same class. Because its a “static” property, it belongs to the class itself rather than any instance so we don’t have to worry about finding objects.
Properties are declared like variables except they are followed by accessors that immediately tell you what the property does. There are a couple different ways of writing these and most people seem to prefer the shortest way possible. I have kind of a weird way of writing out my accessors because I’m still learning to use them and I want to be absolutely clear and see at a glance what the property is doing.
Let’s get crackin’.
public class GameSession : MonoBehaviour {
private static GameSession instance = null;
static GameSession Singleton {get{return instance;}}
void Awake () {
if (instance != null && instance != this) {
Destroy (gameObject);
print ("Duplicate GameSession self-destructing!");
} else {
instance = this;
GameObject.DontDestroyOnLoad(gameObject);
}
}
Since we are using static variables and properties, there’s no need to find a gameSession object as I was trying to do above. These static values will also be shared by the overall GameSession class, which we can access from anywhere in our code. When we access GameSession, we won’t be able to see “instance” because it is a private variable but we will be able to see “Singleton”. Since it is also private and only has a “get” accessor (and no “set” accessor), we can get information from Singleton from outside the class but we can’t change it. Accessing “Singleton” returns the reference stored in “instance”.
Again, just to be clear: “instance” is a variable, which holds information. “Singleton” is a property, which provides a means of accessing “instance” but holds no information itself.
So, if GameSession has a variable like int playerLives = 3;
and a public function like GetPlayerLives()
to return that variable, for example, you might access it this way from a different script:
GameSession.Singleton.GetPlayerLives();
Much easier. No finding objects or anything like that.
STILL NOT A SINGLETON, BUT GOOD ENOUGH
Again, just to be absolutely clear: This isn’t really a singleton. It’s close enough for our purposes but I want you to understand the difference just in case you talk with more experienced programmers or read stuff in forums or something.
A true singleton needs to be procedurally generated and requires some active code that decides whether or not to instantiate a singleton based on whether one already exists or not.
The singleton-like scripts we are making are self-contained scripts that come into play and then decide for themselves whether they should stay in play.
CONCLUSION
I think this is the best solution for a beginner until we learn some more advanced coding techniques. I’ve looked online for singletons, but most of them are for trying to handle “perfect singletons” that cover lots of edge cases, and include things like threading, locking, constructors, and a bunch of other stuff.
Right now, I think this is the best solution that is easy for beginners to understand and implement. (Maybe not absolute beginners, but certainly once you’ve made some progress through the course.)
The problem with the Singleton pattern is that we need it before we really have the foundational knowledge to understand it. The second problem is that there really seems to be different ways to handle it based on what kind of edge cases you want to protect against. This proposed solution is to handle a case where we frequently need to find our singleton objects at the start of each scene.
If you have another solution that works for you, feel free to share it. If you see anything wrong with something in this post, feel free to warn your brethren.
Sam’s Proposed Solution
Sam has recently suggested a simple fix to Rick’s code that may solve the issue we are having. It’s so simple that I’m a little embarrassed. At the time that I write this, this solution is still being tested by the gamedev.tv team and not yet officially approved - but it looks okay to me. I won’t be using it since I’ve grown accustomed to the advantages of the solution proposed above, but I admire the simplicity of this fix, which just adds one line of code:
int numberGameSessions = FindObjectsOfType<GameSession>().Length;
if (numberGameSessions > 1)
{
gameObject.SetActive(false); //<--- this
Destroy(gameObject);
}
else
{
DontDestroyOnLoad(gameObject);
}
By also setting the gameObject to inactive when you tag it for destruction, you prevent any other scripts from finding it or interacting with it. So it doesn’t matter if there’s two, because one is inactive and essentially invisible.