Follow

Tired of waiting for Unity to reload script assemblies every time you run your game? You can turn that off:

docs.unity3d.com/Manual/Domain

@peterdrake Except then static variables persist between sessions, which can easily break poorly-implemented singleton patterns. Be *very* careful.

If your builds are taking a long time, I highly recommend letting unity refresh the Library folder (just close Unity, delete the library folder from your project, and re-open unity). It'll take Unity a while to rebuild that folder, but then script builds should be *much* faster for a while.

@LouisIngenthron Can you say more about properly implementing singletons? A web search reveals a deep rabbit hole on this, but all of them use static variables.

@peterdrake Oh, you still have to use statics, you just have to be smart about it. This is the pattern I use that hasn't failed yet. Put it in the Start() function of MonoBehaviours. Try to avoid singletons of non-unity-managed-objects (you'd need a different pattern than the below).

if(Singleton == null)
{
Singleton = this;
DontDestroyOnLoad(gameObject);
}
else if(Singleton != this)
{
Destroy(gameObject); // two in scene
}

As long as you do that for Unity objects, that first null check will use Unity's override == check to also check for destroyed or unreferenceable objects.

And then in the Destroy function:

if(Singleton != null && Singleton == this)
{
Singleton = null;
}

@peterdrake Basically, the key is to make sure to clean up after yourself since the editor won't do it for you. So, if you have a static score integer, make sure somebody's start function resets it to zero. Stuff like that.

@LouisIngenthron Good stuff -- thanks!

Two questions:

1) A "traditional" singleton would provide a static GetInstance method, and keep the constructor private, to prevent a second instance from ever being created. That won't work here because of the way Unity creates GameObjects (and their components), right?

2) In Destroy, why check if Singleton is null? If it's == this, we already know it isn't null ... right?

@peterdrake On 1, correct. That's why I usually make the singleton property a public get but a private set. With the pattern above, that achieves the same goals.

On 2, it's to specifically use Unity's overloaded null checks to make sure it's not pointing to a dead/destroyed reference before checking equality. I'm not sure if the straight equality check does that, so I added a null check as a short-circuit.

@LouisIngenthron If this was already destroyed, could the Destroy method still run?

It's sort of a race condition, so I'm not sure how to set up a test.

@peterdrake No, I don't think so. It would only be to check if a singleton from a previous execution context was still stored in the static reference.

But it's worth noting that there are a number of places where OnDestroy may *not* be called (mostly in-editor, not in-built-game, such as hard exits).

@LouisIngenthron I've almost got it, but can you walk me through a situation where

Singleton != null && Singleton == this

has a different value from:

Singleton == this

@peterdrake It shouldn't, so long as unity's implementation of (Singleton == this) has its own null-check first, which it probably does, but I'm not sure.
So, the situation I'm concerned about would be that Singleton references a destroyed object. Running (Singleton == null) checks if it's null *or* destroyed, which prevents the second check. But I don't know if (Singleton == this) will also break out if it's null or if it will try to run a comparison op between a live object and a destroyed one (which would throw an error).

In other words, it's mostly just me being paranoid and defensively coding.

Sign in to participate in the conversation
Qoto Mastodon

QOTO: Question Others to Teach Ourselves
An inclusive, Academic Freedom, instance
All cultures welcome.
Hate speech and harassment strictly forbidden.