using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public static class SceneObjectLookupCache { private static readonly Dictionary Cache = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary AnyTypeCache = new Dictionary(); private static readonly Dictionary FirstTypeCache = new Dictionary(); private static bool hooked; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetStaticState() { Cache.Clear(); AnyTypeCache.Clear(); FirstTypeCache.Clear(); hooked = false; } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void Bootstrap() { if (hooked) return; hooked = true; SceneManager.sceneLoaded -= OnSceneLoaded; SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.activeSceneChanged -= OnActiveSceneChanged; SceneManager.activeSceneChanged += OnActiveSceneChanged; } public static GameObject Find(string nameOrPath) { if (string.IsNullOrWhiteSpace(nameOrPath)) return null; string key = nameOrPath.Trim(); if (Cache.TryGetValue(key, out GameObject cached)) { if (IsUsableCachedGameObject(cached, key)) return cached; Cache.Remove(key); } GameObject found = GameObject.Find(key); if (found != null) Cache[key] = found; return found; } public static T FindComponent(string nameOrPath) where T : Component { GameObject go = Find(nameOrPath); return go != null ? go.GetComponent() : null; } public static T FindAny() where T : UnityEngine.Object { var type = typeof(T); if (AnyTypeCache.TryGetValue(type, out var cached)) { if (IsUsableCachedObject(cached)) return cached as T; AnyTypeCache.Remove(type); } T found = UnityEngine.Object.FindAnyObjectByType(); if (found != null) AnyTypeCache[type] = found; return found; } public static T FindFirst() where T : UnityEngine.Object { var type = typeof(T); if (FirstTypeCache.TryGetValue(type, out var cached)) { if (IsUsableCachedObject(cached)) return cached as T; FirstTypeCache.Remove(type); } T found = UnityEngine.Object.FindFirstObjectByType(); if (found != null) FirstTypeCache[type] = found; return found; } public static Transform FindTransform(string nameOrPath) { GameObject go = Find(nameOrPath); return go != null ? go.transform : null; } public static void Clear() { Cache.Clear(); AnyTypeCache.Clear(); FirstTypeCache.Clear(); } private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { Cache.Clear(); AnyTypeCache.Clear(); FirstTypeCache.Clear(); } private static void OnActiveSceneChanged(Scene oldScene, Scene newScene) { Cache.Clear(); AnyTypeCache.Clear(); FirstTypeCache.Clear(); } private static bool IsUsableCachedObject(UnityEngine.Object cached) { if (cached == null) return false; if (cached is Component component) return component != null && component.gameObject != null && component.gameObject.activeInHierarchy; if (cached is GameObject go) return go.activeInHierarchy; return true; } private static bool IsUsableCachedGameObject(GameObject cached, string key) { if (cached == null || !cached.activeInHierarchy) return false; int slashIndex = key.IndexOf('/'); if (slashIndex < 0) return cached.name == key; return BuildHierarchyPath(cached.transform) == key; } private static string BuildHierarchyPath(Transform transform) { if (transform == null) return string.Empty; var path = transform.name; while (transform.parent != null) { transform = transform.parent; path = transform.name + "/" + path; } return path; } }