using System.Collections.Generic;
using UnityEngine;
namespace Ubisoft
{
internal class HtSingletonHolderMonoBehaviour : MonoBehaviour
{
private void OnApplicationQuit()
{
HtSingletonHolder.DestroyInstance();
}
}
public class HtSingletonHolder
{
public interface IConsumer
{
void DestroySingleton(Component component);
}
// Main singleton
private static HtSingletonHolder s_instance = null;
private const string NAME = "[HT]SingletonHolder";
public static void CreateInstance()
{
if (s_instance == null)
{
s_instance = new HtSingletonHolder();
}
}
public static HtSingletonHolder SharedInstance
{
get
{
if (s_instance == null)
{
CreateInstance();
}
return s_instance;
}
}
public static void DestroyInstance()
{
if (s_instance != null)
{
if (s_instance.m_gameContext != null)
{
GameObject o = s_instance.m_gameContext;
s_instance.m_gameContext = null;
HtObject.DestroyObject(o);
}
s_instance = null;
}
}
///
/// Retrieve the component of type T if it has already been added or add it if it hasn't.
///
/// Type of the component to add.
/// Object to nofity when the object of type T is destroyed.
/// This object is usually a singleton that would throw an exception after the T object was destroyed if
/// it's not updated properly.
/// Object of type T added/retrieved.
public static T AddMainComponent(IConsumer consumer = null) where T : Component
{
T returnValue = SharedInstance.m_gameContext.GetComponent();
if (returnValue == null)
{
returnValue = SharedInstance.m_gameContext.AddComponent();
SharedInstance.ComponentCount++;
}
if (consumer == null && returnValue is IConsumer iconsumer)
{
consumer = iconsumer;
}
if (consumer != null)
{
s_instance.AddConsumer(returnValue, consumer);
}
return returnValue;
}
///
/// Remove the component of type T.
///
/// Type of the component to remove.
/// Whether or not the component has to be destroyed. Typically true except when this
/// method is called from a MonoBehaviour's OnDestroy() method since the component is already being destroyed.
public static void RemoveMainComponent(bool destroy) where T : Component
{
T returnValue = SharedInstance.m_gameContext.GetComponent();
if (returnValue != null)
{
if (destroy)
{
HtObject.DestroyObject(returnValue);
}
SharedInstance.ComponentCount--;
// Destroy the singleton holder when it stops holding any components. It will be recreated when a
// new component is added. This way we make sure that it will be destroyed when used in edit mode.
if (SharedInstance.ComponentCount <= 0)
{
DestroyInstance();
}
}
}
public static void RemoveMainComponent() where T : Component
{
T returnValue = SharedInstance.m_gameContext.GetComponent();
if (returnValue != null)
{
HtObject.DestroyObject(returnValue);
SharedInstance.ComponentCount--;
// Destroy the singleton holder when it stops holding any components. It will be recreated when a
// new component is added. This way we make sure that it will be destroyed when used in edit mode.
if (SharedInstance.ComponentCount <= 0)
{
DestroyInstance();
}
}
}
private GameObject m_gameContext;
private Dictionary> m_consumers;
internal int ComponentCount { get; set; }
HtSingletonHolder()
{
ComponentCount = 0;
if (m_gameContext == null)
{
m_gameContext = new GameObject(NAME);
#if UNITY_EDITOR
if (Application.isPlaying)
{
Object.DontDestroyOnLoad(m_gameContext);
}
#else
Object.DontDestroyOnLoad(m_gameContext);
#endif
m_gameContext.AddComponent();
}
}
~HtSingletonHolder()
{
Clean();
}
private void Clean()
{
if (m_consumers != null)
{
foreach (KeyValuePair> pair in m_consumers)
{
foreach (IConsumer consumer in pair.Value)
{
consumer.DestroySingleton(pair.Key);
}
}
m_consumers.Clear();
}
m_gameContext = null;
}
private void AddConsumer(Component component, IConsumer consumer)
{
if (component != null && consumer != null)
{
if (m_consumers == null)
{
m_consumers = new Dictionary>();
}
if (!m_consumers.ContainsKey(component))
{
m_consumers.Add(component, new List());
}
List consumers = m_consumers[component];
if (!consumers.Contains(consumer))
{
consumers.Add(consumer);
}
}
}
}
}