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); } } } } }