Reputation: 457
I'm a beginner of Unity.
I had a question while studying.
I refer to the document below.
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T _instance;
private static object _lock = new object();
public static T Instance
{
get
{
if (applicationIsQuitting) {
Debug.LogWarning("[Singleton] Instance '"+ typeof(T) +
"' already destroyed on application quit." +
" Won't create again - returning null.");
return null;
}
lock(_lock)
{
if (_instance == null)
{
_instance = (T) FindObjectOfType(typeof(T));
if ( FindObjectsOfType(typeof(T)).Length > 1 )
{
Debug.LogError("[Singleton] Something went really wrong " +
" - there should never be more than 1 singleton!" +
" Reopening the scene might fix it.");
return _instance;
}
if (_instance == null)
{
GameObject singleton = new GameObject();
_instance = singleton.AddComponent<T>();
singleton.name = "(singleton) "+ typeof(T).ToString();
DontDestroyOnLoad(singleton);
Debug.Log("[Singleton] An instance of " + typeof(T) +
" is needed in the scene, so '" + singleton +
"' was created with DontDestroyOnLoad.");
} else {
Debug.Log("[Singleton] Using instance already created: " +
_instance.gameObject.name);
}
}
return _instance;
}
}
}
private static bool applicationIsQuitting = false;
public void OnDestroy () {
applicationIsQuitting = true;
}
}
if instance was null, Why GameObject to add to AddComponent ?
and Why use that FindObject function ?
and Why use Singleton in Unity?
I don't Know Singleton Overall flow..
please give to me Code Review..
As a beginner, I do not know much. I need your help.
Please give me your think.
Upvotes: 0
Views: 3558
Reputation: 344
I thought I had an elegant solution to this problem (generic singleton):
public static class Singleton
{
/************************************************************/
#region Functions
public static T Get<T>(
bool findObjectOfType = false,
bool dontDestroyOnLoad = false,
bool unparentGameObject = false) where T : MonoBehaviour
{
if (findObjectOfType && SingletonInstance<T>.instance == null)
{
TrySet(Object.FindObjectOfType<T>(), dontDestroyOnLoad);
LogManager.Log($"called Get<{typeof(T)}>() before instance was set; calling FindObjectOfType<{typeof(T)}>");
}
return SingletonInstance<T>.instance;
}
public static bool TrySet<T>(
T instance,
bool dontDestroyOnLoad = false,
bool unparentGameObject = false) where T : MonoBehaviour
{
// NOTE: method does not need to be called; BUT if called, FindObjectOfType() is avoided during lazy init
if (SingletonInstance<T>.instance != null)
{
LogManager.Log($"{instance.name} called Set<{typeof(T)}>() when singleton already exists");
if (!IsSingleton(instance))
{
LogManager.Log($"there are two different singleton instances, calling DestroyImmediate for {instance.name}");
Object.DestroyImmediate(instance.gameObject);
}
return false;
}
else if (instance != null)
{
SingletonInstance<T>.instance = instance;
if (unparentGameObject) instance.transform.SetParent(null);
if (dontDestroyOnLoad) Object.DontDestroyOnLoad(instance.gameObject);
return true;
}
else
{
LogManager.LogError($"called Set<{typeof(T)}>() when given instance is null");
return false;
}
}
public static bool IsSingleton<T>(T instance) where T : MonoBehaviour
{
return ReferenceEquals(SingletonInstance<T>.instance, instance);
}
#endregion
/************************************************************/
#region Subclasses
private static class SingletonInstance<T> where T : MonoBehaviour
{
public static T instance;
}
#endregion
/************************************************************/
}
where a class
that has/is a singleton looks like:
/************************************************************/
#region Properties
public static TestRunner Instance => Singleton.Get<TestRunner>(findObjectOfType: true);
#endregion
/************************************************************/
#region Functions
private void Awake() => Singleton.TrySet(this, dontDestroyOnLoad);
#endregion
/************************************************************/
or if you wanted to use abstraction, you could create an abstract MonoSingleton class that looks like:
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
/************************************************************/
#region Fields
[Header("Singleton Settings")]
[Tooltip("whether Object.DontDestroyOnLoad() is called on this")]
[SerializeField] private bool dontDestroyOnLoad;
[Tooltip("whether this GameObject unparents itself")]
[SerializeField] private bool unparentGameObject;
#endregion
/************************************************************/
#region Properties
private T _Instance => this as T;
public static T Instance => Singleton.Get<T>();
#endregion
/************************************************************/
#region Functions
protected void Awake()
{
if (Singleton.TrySet(_Instance, dontDestroyOnLoad, unparentGameObject))
{
MonoSingleton_Awake();
}
}
protected void OnDestroy()
{
if (Singleton.IsSingleton(_Instance))
{
MonoSingleton_OnDestroy();
}
}
protected void OnEnable()
{
if (Singleton.IsSingleton(_Instance))
{
MonoSingleton_OnEnable();
}
}
protected void OnDisable()
{
if (Singleton.IsSingleton(_Instance))
{
MonoSingleton_OnDisable();
}
}
protected virtual void MonoSingleton_Awake() {}
protected virtual void MonoSingleton_OnEnable() {}
protected virtual void MonoSingleton_OnDisable() {}
protected virtual void MonoSingleton_OnDestroy() {}
#endregion
/************************************************************/
}
where the final implementation of a MonoSingleton<T>
would look like:
public class NewMonoSingleton : MonoSingleton<NewMonoSingleton>
{
}
where you would override
Awake
, OnDestroy
, OnEnable
, OnDisable
if you need to. Otherwise, the script is pretty light.
Upvotes: 0
Reputation: 125275
if instance was null, Why GameObject to add to AddComponent ?
If the instance of the script you want to create is null if (_instance == null)
:
1.Create new GameObject
GameObject singleton = new GameObject();
2.Create new instance of that script and attach it to the GameObject that is created above. In Unity, a component must be attached to a GameObject. The AddComponent
function is used to attach a component to a GameObject.
_instance = singleton.AddComponent<T>();
Why use that FindObject function ?
If the instance of the script you want to create is null if (_instance == null)
, check if that script instance already exist in the scene. The FindObjectOfType
function is used to find this type of script only. Let's say that we have a script called SceneLoader
and we pass SceneLoader
to the Singleton
class, it will check if an instance of SceneLoader
is already in the scene and return that instance of it. It will return null
if it does not exist.
Why use Singleton in Unity?
You use it when you only want to have one instance of a type of script in the scene. Also, with DontDestroyOnLoad
, it means that this instance will still be there even when next scene is loaded. It will not be destroyed like other scripts.
please give to me Code Review
You can ask for code improvement on the codereview site. If you are new to Unity you can find Unity project tutorials on their site that can get you started easily here.
Upvotes: 4