Unity Singleton Manager Classes

Unity singleton manager classes

Like always: it depends. I use singletons of both kinds, components attached to GameObject and standalone classes not derived from MonoBehaviour. IMO the overall question is how are instances bound to the lifcycle of scenes, game objects, ... And not to forget sometimes it is more convenient to have a component especially referencing other MonoBehaviour objects is easier and safer.

  1. There are classes that just need to provide some values like for example a config class that needs to load settings from persistence layer when called. I design theese classes as simple singletons.
  2. On the other hand some objects need to know when a scene is started i.e. Start is called or have to perform actions in Update or other methods. Then I implement them as component and attach them to a game object that survives loading new scenes.

I designed component based singletons (type 2) with two parts: a persistent GameObject called Main, which holds all components and a flat singleton (type 1) called MainComponentManager for managing it. Some demo code:

public class MainComponentManger {
private static MainComponentManger instance;
public static void CreateInstance () {
if (instance == null) {
instance = new MainComponentManger ();
GameObject go = GameObject.Find ("Main");
if (go == null) {
go = new GameObject ("Main");
instance.main = go;
// important: make game object persistent:
Object.DontDestroyOnLoad (go);
}
// trigger instantiation of other singletons
Component c = MenuManager.SharedInstance;
// ...
}
}

GameObject main;

public static MainComponentManger SharedInstance {
get {
if (instance == null) {
CreateInstance ();
}
return instance;
}
}

public static T AddMainComponent <T> () where T : UnityEngine.Component {
T t = SharedInstance.main.GetComponent<T> ();
if (t != null) {
return t;
}
return SharedInstance.main.AddComponent <T> ();
}

Now other singletons that want to register as Main component just look like:

public class AudioManager : MonoBehaviour {
private static AudioManager instance = null;
public static AudioManager SharedInstance {
get {
if (instance == null) {
instance = MainComponentManger.AddMainComponent<AudioManager> ();
}
return instance;
}
}

using C# interface to create a Manager class that implementing a singleton pattern, but the child manager class does not have a singleton pattern

Your Manager class derives from Singleton<Manager>, which provides a method to get an instance of Manager, Singleton<Manager>.GetInstance(). Your GameManager now inherits from Manager and with that from Singleton<Manager>.

As you already do in GameManager.Initialize(), you can set the manager singleton with SetSingleton, and it accepts the GameManager as argument, because it expects a Manager, which your GameManager ist.

But your GameManager still only implements Singleton<Manager>, meaning the GetInstance() method can only return a Manager, not a GameManager, although the manager you get back is actually your GameManager. You could cast that now to a GameManager and it would work, although that would be a rather unclean solution.

That said: I think trying to enforce a singleton pattern via interface is not a clean solution, as was pointed out in comments already. If you want to work with singleton manager instances, I would advise you to look into dependency injection. For Unity I can highly recommend Zenject. If you never worked with DI before it takes a moment to get used to, but used correctly it makes your code cleaner; I for one would not want to miss it in my projects.

Best way to use Singletons in unity

I confirm Ruzihm answer (and I also agree with derHugo comment, I personally prefer SO and reference them where required. Much cleaner than Singleton or DI direct approach).

To be specific accessing the instance member manager.Instance.value is slower and also requires further memory allocation on the heap so it hurts both memory usage and speed performance. (Very small performance issues, but still.)

There's also further room of improvement on the singleton, especially if you do not require it to derive from MonoBehaviour.

You can:

  1. Make sure it is build also if you forget to add it in the scene
  2. Make it thread safe (not required by unity, usually)
  3. Implement it lazily (means you create it only when you need it)
  4. Also, as a general rule is better to seal the singleton class for improved performance (again really slight improvement when you use virtual methods)

This would be the implementation (considering you use the Manager as helper instance and YourManagerMono if you require monobehaviour logic:

public class YourManagerMono : MonoBehaviour
{
}

public sealed class Manager
{
private static readonly Manager instance = new Manager();
//use a gameobject to relate it in the scene
private YourManagerMono monoManager;

public static Manager Instance => instance;

// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Manager() {}

private Manager()
{
//find or create a manager
monoManager = GameObject.FindWithTag("Manager").GetComponent<YourManagerMono>();
if (monoManager == null)
{
monoManager = new GameObject().AddComponent<YourManagerMono>();
monoManager.gameObject.tag = "Manager";
Object.DontDestroyOnLoad(monoManager.gameObject);
}
}
}

A great article about singleton implementation for C# by Jon Skeet (I used implementation 4).

EDIT:

Again I agree with derHugo (in the new comment on this answer). My example is used to show an interesting prospective and to offer as much performance as I can, but if you just require a Monobehaviour with just point 1 and 3 you may go ahead with a generic implementation of singleton from Unify Community (just remember to set the end class as sealed to help the compiler).

Best way to implement Unity Singleton?

Here's some remarks about your code:

  1. Inside getter of the Instance property you have a line of code:
    instance = go.AddComponent<T>();
    The point about this line is that AddComponent<> will cause creation of the script's instance and then calling Awake() method. So you will create an instance of your sript by this line and it doesn't matter what's written in the Awake() method. So you can get rid of Awake() as it does nothing. The only case it will do some helpful work is when you'll try to create the first instance of your script (it'll just set the value of instance to the 1st instance). In other cases it's useless. So if you will create your singleton only by using Instance property - you do not need that awake.

  2. Awake itself is a "kinda constructor" for MonoBehaviour scripts, but it performs differently. The point is that in pure c# you can easily implement singleton with the help of static constructor but you can't do it this way in MonoBehaviour. That means if you already have an instance of your singleton and you'll try to create your singleton instance with the line T myNewInstance = new T(); the second instance will be actually created. So your code won't protect you from direct creating of the instance.

  3. Also, if you'll use gameObject.AddComponent<T>() method, your singletone code won't prevent Unity from creating new script instance.

So, there's no 100% method to create a MonoBehaviour singletone in Unity, but there are some possible workarounds.

For example, take a look here. This solution has much similar to yours but it also uses public T GetOrAddComponent<T> (this Component child) where T: Component helper extension method, which you can borrow to your solution.

Another possible solution is that you can create a non-MonoBehaviour derived c# singleton class like a wrapper for getting a MonoBehaviour class, which you want to have only a single instance. But you'll have to use only this wrapper to manage this MonoBehaviour class, otherwise you will be able to create as much instances of the MonoBehaviour as you wish.

How does Unity wiki's Singleton work?

As the comments say:

MonoBehaviour classes cannot be instanciated with new T() because of the way GameObject components work: they must be attached to a GameObject! As such, the GameObject class provides a way of instantiating a new MonoBehaviour attached to that GameObject: AddComponent<T>() which presumably operates through reflection (other MonoBehaviour methods such as Start() and Update() aren't exactly invoked with reflection, not every frame during runtime, but it's easy to make the comparison that they are; it's about as opaque and magical and has significant overhead, so it may as well be reflection).

Preventing the constructor from being called at all would probably just break everything, so don't bother trying to protect it any more than you already are. Because of the way AddComponent works, calling the constructor through reflection, you can't actually prevent a new instance from being created, but you CAN detect that it happened and do something about. My preferred way is the script going "oh, an instance already exists and its not me" and destroys itself.

Additionally, as components can require other components (RigidBody requires a Collider, MonoBehaviours can specify their own requirements as well) which is specified through an attributes, when AddComponent<T>() is called to add the one component, it will search the class attributes to see if there are any [RequireComponents] specified and add them too, automatically. This would also be done through reflection.



Related Topics



Leave a reply



Submit