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.
- 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.
- On the other hand some objects need to know when a scene is started i.e.
Start
is called or have to perform actions inUpdate
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:
- Make sure it is build also if you forget to add it in the scene
- Make it thread safe (not required by unity, usually)
- Implement it lazily (means you create it only when you need it)
- 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:
Inside getter of the
Instance
property you have a line of code:instance = go.AddComponent<T>();
The point about this line is thatAddComponent<>
will cause creation of the script's instance and then callingAwake()
method. So you will create an instance of your sript by this line and it doesn't matter what's written in theAwake()
method. So you can get rid ofAwake()
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 usingInstance
property - you do not need that awake.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 lineT myNewInstance = new T();
the second instance will be actually created. So your code won't protect you from direct creating of the instance.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
Is There a Reason Image.Fromfile Throws an Outofmemoryexception for an Invalid Image Format
How to Pass Parameters by Reference in Java
Predefined Type 'System.Valuetuple'2' Is Not Defined or Imported
Value of Type 'T' Cannot Be Converted To
.Net: Simplest Way to Send Post with Data and Read Response
Hosting External App in Wpf Window
Format of the Initialization String Does Not Conform to Specification Starting at Index 0
Deserializing JSON with Dynamic Keys
How to Get a List of Properties with a Given Attribute
An Object Reference Is Required to Access Non-Static Member
Adding Your Own HTMLhelper in ASP.NET MVC 3
Creating a Zip Archive in Memory Using System.Io.Compression
Import CSV File to Strongly Typed Data Structure in .Net
Hide Tabcontrol Buttons to Manage Stacked Panel Controls
What Is the Purpose of the VShost.Exe File