6 min read

This article is intended for intermediate-level C# programmers or above, and assumes some familiarity with object-oriented programming terminology. The focus is on Unity, but this approach works just as well in any C# environment.

I’ve seen a number of common techniques for implementing singletons in Unity. A little background for those unfamiliar with the term: a singleton is simply an object of which only one instance ever exists, and this instance is globally accessible. This is great for shared data or services that need to be accessible from any script in your game, such as the player’s stats.

There are many ways to do this. One way is to just have a GameObject in your scene with a certain MonoBehaviour, and have other scripts look it up by tag (or even slower, by name.) There are a few issues with this approach. First of all, if you accidentally have two GameObjects with the same name or tag, you’ll be arbitrarily interacting with one instead of the others, Unity will not notify you about this issue and it could lead to bugs. Secondly, looking up an object by name has a performance penalty, and it’s more busy work to tag every object if you want to use the tag lookup method.

Another common way is to just copy-paste the code to make a class into a singleton. If we are following the principle of avoiding code duplication, an easy way to refactor this approach is by rolling the singleton code into a subclass of MonoBehaviour, and having our singletons inherit from that. A problem with this approach is that now we are adding rigidity into our class hierarchy. So we won’t be able to have a singleton that also inherits from a non-singleton subclass of MonoBehaviour.

Both of these approaches also require your singleton to be a MonoBehaviour. This is often convenient, but limiting. For instance, if you are using the Model-View-Controller pattern, you may want your models and controllers to not be MonoBehaviours at all but rather “Plain Old” C# objects.

The approach that I am presenting in this article gets around all of the above limitations, as providing some additional advantages. Instead of the classic singleton pattern of each class having a static instance variable, we will create a “service manager” singleton that will hold all of the instances we want global access to.

The service manager provides the following advantages:

  • Any class can be made into a service with a single line of code.
  • Strong typing makes it unnecessary to cast services when referencing them.
  • Bugs which cause a service instance to be set more than once result in an exception, making them easier to track down.
  • Services don’t have to inherit from a specific class.
  • By containing all service references in one place, It’s easier to clear them out in a single pass.

 Without further ado, here is my implementation of the service manager class:

/// <summary>
/// Simple service manager. Allows global access to a single instance of any class.
/// Copyright (c) 2014-2015 Eliot Lash
/// </summary>
using System;
using System.Collections.Generic;

public class Services {
	//Statics
	private static Services _instance;

	//Instance
	private Dictionary<Type, object> services = new Dictionary<Type, object>();

	public Services() 
	{
		if (_instance != null)
		{
			UnityEngine.Debug.LogError("Cannot have two instances of singleton.");
			return;
		}
		
		_instance = this;
	}

	/// <summary>
	/// Getter for singelton instance.
	/// </summary>
	public static Services instance {
		get {
			if (_instance == null) {
				new Services();
			}
			
			return _instance;
		}
	}

	/// <summary>
	/// Set the specified service instance. Usually called like Set<ExampleService>(this).
	/// </summary>
	/// <param name="service">Service instance object.</param>
	/// <typeparam name="T">Type of the instance object.</typeparam>
	public void Set<T>(T service) where T : class {
		services.Add(typeof(T), service);
	}

	/// <summary>
	/// Gets the specified service instance. Called like Get<ExampleService>().
	/// </summary>
	/// <typeparam name="T">Type of the service.</typeparam>
	/// <returns>Service instance, or null if not initialized</returns>
	public T Get<T>() where T : class {
		T ret = null;
		try {
		 ret = services[typeof(T)] as T;
		}  catch (KeyNotFoundException) {
		}
		return ret;
	}

	/// <summary>
	/// Clears internal dictionary of service instances.
	/// This will not clear out any global state that they contain,
	/// unless there are no other references to the object.
	/// </summary>
	public void Clear() {
		services.Clear();
	}
}

As this is a classic singleton, it will be lazily instantiated the first time it’s used, so all you need to do is save this script into your project to get started.

Now, let’s make a small script into a service. Create an empty GameObject and call it “TestService”. Also create a script called “TestService” and attach it. Now, add the following method:

void Awake() {
		Services.instance.Set<TestService>(this);
	}

Our class is now a service!

Caution: To get around issues of initialization dependency when using MonoBehaviours, use a two-phase init process. All service instances should be set in their Awake() method, and not used by other classes until their respective Start() methods or later. For more information see Execution Order of Event Functions.

We’ll also add a stub method to TestService to demonstrate how it can be used:

public void Foo() {
		Debug.Log("TestService: Foo!");
	}

Now, create another empty GameObject and call it “TestClient”, and attach a new script also called “TestClient”. We’ll change its Start() method to look like so:

void Start () {
		TestService ts = Services.instance.Get<TestService>();
		ts.Foo();

		//If you're only doing one operation with the service, it can be written even more compactly:
		//Services.instance.Get<TestService>().Foo();
	}

Now when you run the game, you should see the test message get written to the Unity console. And that’s all there is to it!

Also, a note on global state. Earlier, I mentioned that clearing out global state is easier with the service manager. The service manager code sample I provided has a method (Services.Clear()) which will clear out its internal dictionary of services, but this will not completely reset their state. Unfortunately, this is a complex topic beyond the scope of this article, but I can offer some suggestions. If you are using MonoBehaviors as your services, calling Services.Clear() and then reloading the scene might be enough to do the trick. Otherwise, you’ll need to find a way to notify each service to clean itself up before clearing the service manager, such as having them all implement an interface with such a method in it.

I hope you’ll give this a try, and enjoy the ease of creating and accessing more error-resistant and strictly typed global services in one line of code.

For more Unity game development tutorials and extra content, visit our Unity page here.

 About the Author

Eliot Lash is an independent game developer and consultant with several mobile titles in the works. In the past, he has worked on Tiny Death Star and the Tap Tap Revenge series. You can find him at eliotlash.com.

LEAVE A REPLY

Please enter your comment!
Please enter your name here