Unity 101 – Singleton Behaviours

Because one is enough

 

The SingletonBehaviour

The SingletonBehaviourone is a generic Behaviour that acts with Singleton behaviour but isn’t truly a Singleton.  Confused?

A Run Around Singletons, and other Singletonish Patterns

Singletons have private constructors so they can only be instantiated when calling a static ‘Instance’ accessor, providing a way to guarantee there is no second instance.  No matter how hard you try, you can only ever have one.

Generic based Singletons need to have a public constructor.  A child of Singleton must implement a default constructor/new() so that the static typed T Instance accessor can create it.

public class Singleton<T> where T : new()
{
    private T _instance;
    public T Instance {
        get {
            if(_instance == null) _instance = new T();
            return _instance;
        }
    }
}
public class Foo : Singleton<Foo>
...

So while it’s nice and easy to use in your code (just inherit, voila!), you can’t guarantee that another piece of code hasn’t called new T(), it’s not truly a Singleton.

Singleton MonoBehaviours

MonoBehaviours need to have default constuctors.  Not only that, you shouldn’t invoke new MonoBehaviour() in your code at any point.  Component lifecycle is managed by Unity.  Unity needs to instantiate components to manage lifecycle in the engine at run-time, but also in the editor.

So a combination of the need for a public constructor, in addition to never truly invoking the constructor, means that MonoBehaviour Singletons can never truly be Singletons, not even generic Singletons.

The Good News

Singletons are a construct that just can’t exist in the traditional sense.  Once you accept that, it’s all good news.

Lifecycle

We can manage and track instantiation ourselves.  Unity provides a way to do this with the MonoBehaviour::Awake() method is always called prior to anything else.  This is where we can check if another instance exists already, and if it does…

Destruction

… we destroy it.  In fact, we’ll destroy the entire gameObject that the second MonoBehaviour is attached to.  This is to stop leaky GameObjects which may have other behaviours attached to it that depend on the MonoBehaviour.  You should know where your Singletons are.  I throw all my Singletons in a GameObject named Singleton that is not destroyed on level load, and make that a prefab and only ever update the prefab.  Whatever you choose, keep it simple.

SingletonBehaviour Design

Since we’re not bound to the Singleton behaviour being a true Singleton, it can be a nice and clean generic class, with a typed static T Singleton.Instance accessor.  To turn a behaviour into a Singleton, you just inherit from Singleton and voila, you have a typed T Instance static accessor.

The entire class is pretty simple. An abridged version below.

public class SingletonBehaviour<T> : SmartBehaviour where T : SmartBehaviour
{
    private static SmartBehaviour _instance;

    public static T Instance {
       get {
          return (T)_instance;
       }
    }

    void Awake() {
       if (_instance == null) {
          // This is the first instance of the behaviour
          _instance = this;
       }
       else {
          // Destroy the entire game object this behaviour is attached to
          GameObject.Destroy(this.gameObject);
       }
   }
}

To use it you just define a class…

class LonelyBehaviour: SingletonBehaviour<LonelyBehaviour>
{
    // So lonely
}

It looks a bit inceptionish, but it works fine.  Oh, and SingletonBehaviour inherits from SmartBehaviour, so you get all the perks there too.  For more on SmartBehaviour head on over to the SmartBehaviour post.

The Code

The code is available on my UnityCore repo along with the other Unity 101 content.  There are also prebuilt packages to pull in all the Unity 101 content to your projects quickly and easily.

Unity 101’s is a series of posts about things I use and find fundamentally helpful in Unity.  They may not be the absolute best practise, but they are my practise.  
— Vaughan

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s