A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEngine.SceneManagement; 5using UnityObject = UnityEngine.Object; 6 7namespace Unity.VisualScripting 8{ 9 public static class UnityObjectUtility 10 { 11 public static bool IsDestroyed(this UnityObject target) 12 { 13 // Checks whether a Unity object is not actually a null reference, 14 // but a rather destroyed native instance. 15 16 return !ReferenceEquals(target, null) && target == null; 17 } 18 19 public static bool IsUnityNull(this object obj) 20 { 21 // Checks whether an object is null or Unity pseudo-null 22 // without having to cast to UnityEngine.Object manually 23 24 return obj == null || ((obj is UnityObject) && ((UnityObject)obj) == null); 25 } 26 27 public static string ToSafeString(this UnityObject uo) 28 { 29 if (ReferenceEquals(uo, null)) 30 { 31 return "(null)"; 32 } 33 34 if (!UnityThread.allowsAPI) 35 { 36 return uo.GetType().Name; 37 } 38 39 if (uo == null) 40 { 41 return "(Destroyed)"; 42 } 43 44 try 45 { 46 return uo.name; 47 } 48 catch (Exception ex) 49 { 50 return $"({ex.GetType().Name} in ToString: {ex.Message})"; 51 } 52 } 53 54 public static string ToSafeString(this object obj) 55 { 56 if (obj == null) 57 { 58 return "(null)"; 59 } 60 61 if (obj is UnityObject uo) 62 { 63 return uo.ToSafeString(); 64 } 65 66 try 67 { 68 return obj.ToString(); 69 } 70 catch (Exception ex) 71 { 72 return $"({ex.GetType().Name} in ToString: {ex.Message})"; 73 } 74 } 75 76 public static T AsUnityNull<T>(this T obj) where T : UnityObject 77 { 78 // Converts a Unity pseudo-null to a real null, allowing for coalesce operators. 79 // e.g.: destroyedObject.AsUnityNull() ?? otherObject 80 81 if (obj == null) 82 { 83 return null; 84 } 85 86 return obj; 87 } 88 89 public static bool TrulyEqual(UnityObject a, UnityObject b) 90 { 91 // This method is required when checking two references 92 // against one another, where one of them might have been destroyed. 93 // It is not required when checking against null. 94 95 // This is because Unity does not compare alive state 96 // in the CompareBaseObjects method unless one of the two 97 // operators is actually the null literal. 98 99 // From the source: 100 /* 101 bool lhsIsNull = (object) lhs == null; 102 bool rhsIsNull = (object) rhs == null; 103 if (rhsIsNull && lhsIsNull) 104 return true; 105 if (rhsIsNull) 106 return !Object.IsNativeObjectAlive(lhs); 107 if (lhsIsNull) 108 return !Object.IsNativeObjectAlive(rhs); 109 return lhs.m_InstanceID == rhs.m_InstanceID; 110 */ 111 112 // As we can see, Object.IsNativeObjectAlive is not compared 113 // across the two objects unless one of the operands is actually null. 114 // But it can happen, for example when exiting play mode. 115 // If we stored a static reference to a scene object that was destroyed, 116 // the reference won't get cleared because assembly reloads don't happen 117 // when exiting playmode. But the instance ID of the object will stay 118 // the same, because it only gets reserialized. So if we compare our 119 // stale reference that was destroyed to a new reference to the object, 120 // it will return true, even though one reference is alive and the other isn't. 121 122 if (a != b) 123 { 124 return false; 125 } 126 127 if ((a == null) != (b == null)) 128 { 129 return false; 130 } 131 132 return true; 133 } 134 135 public static IEnumerable<T> NotUnityNull<T>(this IEnumerable<T> enumerable) where T : UnityObject 136 { 137 return enumerable.Where(i => i != null); 138 } 139 140 public static IEnumerable<T> FindObjectsOfTypeIncludingInactive<T>() 141 { 142 for (int i = 0; i < SceneManager.sceneCount; i++) 143 { 144 var scene = SceneManager.GetSceneAt(i); 145 146 if (scene.isLoaded) 147 { 148 foreach (var rootGameObject in scene.GetRootGameObjects()) 149 { 150 foreach (var result in rootGameObject.GetComponentsInChildren<T>(true)) 151 { 152 yield return result; 153 } 154 } 155 } 156 } 157 } 158 } 159}