Hello, almost all examples of Singletons I’ve seen so far have the singleton class extend MonoBehaviour which seems strange to me.
I’m pretty new to Unity and C# so I may be (probably) wrong, but It seems to me that besides the fact that extending MonoBehaviour makes it easier to “grab” stuff in the scene, this approach only has downsides, for example:
you need to create a game object and then attach the singleton,
you need to use DontDestroyOnLoad to make it persist between scenes
So I guess my question would be: when creating singletons, is it not better to have them NOT extend MonoBehaviour ? In this case there’s no need to attach them to any GameObject and they are available everywhere and all the time ?
Hi, it’s a good question, I will follow other answer ^^.
For me that depend of your singleton usage. A singleton in unity could be by example a chat window that you need on all your game scene so it need to extend of monobehaviour. But if you need no function from MonoBehaviour(and no render) I think yes you don’t need to extend your singleton…
But it’s a general rules, if your class doesn’t need any function from MonoBehaviour why create this class as a subclass of monobehaviours…
No you don’t need to inherit MB. There’s more than one way to achieve a singleton like behavior, depends on what you’re doing, and your style. I’ll share the singles I’m using
using UnityEngine;
public static class SingleScript<T> where T : ScriptableObject
{
private static T _instance;
public static T instance
{
get
{
if (_instance == null)
{
_instance = Object.FindObjectOfType<T>();
if (_instance == null)
{
var path = "Singles/" + typeof(T).Name;
_instance = Resources.Load<T>(path);
if (_instance == null)
{
if (typeof(ScriptableObject).IsAssignableFrom(typeof(T)))
{
_instance = ScriptableObject.CreateInstance<T>();
#if UNITY_EDITOR
UnityEditor.AssetDatabase.CreateAsset(_instance, path);
#endif
}
}
}
}
return _instance;
}
}
}
public static class SingleBehaviour<T> where T : MonoBehaviour
{
private static T _instance;
public static T get()
{
return get(true);
}
public static T get(bool create)
{
if (_instance == null)
{
_instance = Object.FindObjectOfType<T>();
if (_instance == null)
{
if (create)
_instance = new GameObject(typeof(T).Name).AddComponent<T>();
else
Debug.Log("Couldn't find object of type: " + typeof(T).Name);
}
}
return _instance;
}
}
Then I have a globals class:
public static class globals
{
public static TextUI text { get { return SingleBehaviour<TextUI>.get(false); } }
public static Database database { get { return SingleScript<Database>.instance; } }
public static Tasks tasks { get { return SingleBehaviour<Tasks>.get(); } }
}
Another reason to extend MonoBehaviour is easy access to serialized fields in the inspector (variables of the script visible and changeable in the editor). Of course you can create an Editor Window for that:
using UnityEngine;
using UnityEditor;
class GameManagerWindow : EditorWindow
{
[MenuItem ("Window/Game Manager")]
public static void ShowWindow ()
{
EditorWindow.GetWindow(typeof(GameManagerWindow));
}
void OnGUI ()
{
GameManager.something = GUILayout.HorizontalSlider(GameManager.something, 1, 5);
GUILayout.Label ("Current value of something is " + GameManager.something);
}
}
Note that this script has to be in the Assets\Editor folder, you can create it if it doesn’t exist. And then there’s the Singleton:
using UnityEngine;
public class GameManager
{
public static float something;
protected GameManager() {}
private static GameManager _instance = null;
public static GameManager Instance
{
get
{
return GameManager._instance == null ? new GameManager() : GameManager._instance;
}
}
}
GameManager.something can now be read and even changed from anywhere, manipulated in the custom Editor Window and it survives scene loading, it even survives dropping out of play mode
If you want it to survive going into play mode, there’s a simple trick with a MonoBehaviour:
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class NewBehaviourScript : MonoBehaviour
{
public float something;
void OnApplicationQuit ()
{
PlayerPrefs.SetFloat("something", something);
}
void Awake ()
{
something = PlayerPrefs.GetFloat("something");
}
}
This “something” variable is never reset to anything, you change it in play mode or edit, this can be done with a Singleton : MonoBehaviour if you want really persistent variables…