Skip to content

Instantly share code, notes, and snippets.

@FleshMobProductions
Last active March 7, 2024 07:57
Show Gist options
  • Save FleshMobProductions/a73b4c49db3c02925346a0922fd61e40 to your computer and use it in GitHub Desktop.
Save FleshMobProductions/a73b4c49db3c02925346a0922fd61e40 to your computer and use it in GitHub Desktop.
2 approaches to cache single instances of Unity objects in a globally accessible class
using System;
using System.Collections.Generic;
using UnityEngine;
using UObject = UnityEngine.Object;
namespace FMPUtils
{
// Usage: UnityObjectInstance<Player>.Instance
public static class UnityObjectInstance<T> where T : UObject
{
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = UObject.FindObjectOfType<T>();
if (instance == null)
Debug.Log($"No instance of type {typeof(T)} found");
}
return instance;
}
set => instance = value;
}
// Method not called on abstract classes, luckily instances are destroyed when exiting
// Playmode anyways. so the Instance references will become null
// The other approach would be to find all UnityObjectInstance types possibly via
// reflection and then call the Clear method on them
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void Clear()
{
Instance = null;
}
}
}
using System;
using System.Collections.Generic;
using UnityEngine;
using UObject = UnityEngine.Object;
namespace FMPUtils
{
public static class UnityObjectInstanceCache
{
private static Dictionary<Type, UObject> instanceMap = new Dictionary<Type, UObject>();
private static Dictionary<Type, UObject> interfaceInstanceMap = new Dictionary<Type, UObject>();
public static T GetInstance<T>() where T : UObject
{
UObject instanceAsObj;
bool wasFound = instanceMap.TryGetValue(typeof(T), out instanceAsObj);
if (wasFound && instanceAsObj != null)
return instanceAsObj as T;
T instance = UObject.FindObjectOfType<T>();
if (instance != null)
instanceMap[typeof(T)] = instance;
else
{
Debug.Log($"No instance of type {typeof(T)} found");
if (wasFound)
instanceMap.Remove(typeof(T));
}
return instance;
}
public static T GetInterfaceInstance<T>() where T : class
{
if (!typeof(T).IsInterface)
throw new ArgumentException($"Passed type {typeof(T)} is not an interface");
UObject instanceAsObj;
bool wasFound = instanceMap.TryGetValue(typeof(T), out instanceAsObj);
if (wasFound && instanceAsObj != null)
return instanceAsObj as T;
T instance = UObject.FindObjectOfType(typeof(T)) as T;
if (instance != null)
instanceMap[typeof(T)] = instance as UObject;
else
{
Debug.Log($"No instance of type {typeof(T)} found");
if (wasFound)
instanceMap.Remove(typeof(T));
}
return instance;
}
public static bool ContainsInstance<T>() where T : UObject
{
return instanceMap.ContainsKey(typeof(T));
}
public static bool ContainsInterfaceInstance<T>() where T : class
{
if (!typeof(T).IsInterface)
throw new ArgumentException($"Passed type {typeof(T)} is not an interface");
return interfaceInstanceMap.ContainsKey(typeof(T));
}
// Register instances in Awake() when the game/scene loads, so other scripts can access them in Start()
// This will prevent FindObjectOfType calls when properly setup
public static void RegisterIntance<T>(T instance) where T : UObject
{
if (instance != null)
instanceMap[typeof(T)] = instance;
}
public static void RegisterInterfaceInstance<ObjectInterface,T>(T instance) where T : UObject, ObjectInterface
{
Type interfaceType = typeof(ObjectInterface);
if (!interfaceType.IsInterface)
throw new ArgumentException($"Passed type {interfaceType} is not an interface");
if (instance != null)
interfaceInstanceMap[interfaceType] = instance;
}
// Reset static state if "Domain Reloading" is disabled
#if UNITY_2019_2_OR_NEWER
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
#endif
private static void Clear()
{
instanceMap.Clear();
interfaceInstanceMap.Clear();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment