2023-03-17 23:41:38 -04:00

129 lines
3.4 KiB
C#

using System;
using JetBrains.Annotations;
using JohnsonUtils.Utilities;
using UnityEngine;
namespace JohnsonUtils.Common
{
public abstract class AbstractSingleton<T> : MonoBehaviour where T : AbstractSingleton<T>
{
protected static T SharedInstance;
[PublicAPI]
public static bool HasInstance => SharedInstance != null;
protected virtual void Awake()
{
try
{
SharedInstance = (T) Convert.ChangeType(this, typeof(T));
}
catch (InvalidCastException)
{
Debug.Assert(false, "Singleton's T type should be the derived class.");
}
}
protected void DestroyInstance()
{
if (gameObject.GetNumberOfComponents() == 1)
{
Destroy(gameObject);
}
else
{
Destroy(this);
}
}
protected virtual void OnApplicationQuit()
{
SharedInstance = null;
DestroyInstance();
}
}
/// <summary>
/// Singleton class that can be loaded and unloaded with scenes.
/// </summary>
public abstract class Singleton<T> : AbstractSingleton<T> where T : Singleton<T>
{
public static T Instance
{
get
{
Debug.Assert(HasInstance, $"Trying to access a script of type {typeof(T).Name} that is not in the scene.");
return SharedInstance;
}
}
protected override void Awake()
{
if (HasInstance)
{
Debug.LogWarning($"New instance of type {typeof(T).Name} detected. " +
"This new instance is becoming the default instance.");
SharedInstance = (T) Convert.ChangeType(this, typeof(T));
return;
}
base.Awake();
}
protected virtual void OnDestroy()
{
if (SharedInstance == this)
{
SharedInstance = null;
}
}
}
/// <summary>
/// Singleton class that is lazy loaded once and is only unloaded when the game exits.
/// </summary>
public abstract class PersistentSingleton<T> : AbstractSingleton<T> where T : PersistentSingleton<T>
{
[PublicAPI]
public static T Instance
{
get
{
CreateIfDoesNotExist();
return SharedInstance;
}
}
[PublicAPI]
public static void CreateIfDoesNotExist()
{
if (!HasInstance && Application.isPlaying)
{
CreateInstance();
}
}
private static void CreateInstance()
{
var instanceGameObject = new GameObject(typeof(T).Name);
instanceGameObject.AddComponent<T>();
}
protected override void Awake()
{
if (HasInstance)
{
Debug.Log($"Two or more instances of a singleton of type {typeof(T).Name} were found in the scene. " +
"The new instance of the singleton trying to register will be removed.");
DestroyInstance();
return;
}
DontDestroyOnLoadUtils.Add(gameObject);
base.Awake();
}
}
}