// Copyright (c) Pixel Crushers. All rights reserved. using System.Collections.Generic; using UnityEngine; namespace PixelCrushers { /// /// Utility functions for working with GameObjects. /// public static class GameObjectUtility { // These methods handle API differences: public static T FindFirstObjectByType() where T : UnityEngine.Object { #if UNITY_2023_1_OR_NEWER return UnityEngine.Object.FindFirstObjectByType(); #else return UnityEngine.Object.FindObjectOfType(); #endif } public static UnityEngine.Object FindFirstObjectByType(System.Type type) { #if UNITY_2023_1_OR_NEWER return UnityEngine.Object.FindFirstObjectByType(type); #else return UnityEngine.Object.FindObjectOfType(type); #endif } public static T[] FindObjectsByType() where T : UnityEngine.Object { #if UNITY_2023_1_OR_NEWER return UnityEngine.Object.FindObjectsByType(FindObjectsSortMode.None); #else return UnityEngine.Object.FindObjectsOfType(); #endif } public static UnityEngine.Object[] FindObjectsByType(System.Type type) { #if UNITY_2023_1_OR_NEWER return UnityEngine.Object.FindObjectsByType(type, FindObjectsSortMode.None); #else return UnityEngine.Object.FindObjectsOfType(type); #endif } /// /// Determines if a GameObject reference is a non-instantiated prefab or a scene object. /// If `go` is `null`, active in the scene, or its parent is active in the scene, it's /// considered a scene object. Otherwise this method searches all scene objects for /// matches. If it doesn't find any matches, this is a prefab. /// /// true if a prefab; otherwise, false. /// GameObject. public static bool IsPrefab(GameObject go) { if (go == null) return false; if (go.activeInHierarchy) return false; if ((go.transform.parent != null) && go.transform.parent.gameObject.activeSelf) return false; var list = FindObjectsByType(); for (int i = 0; i < list.Length; i++) { if (list[i] == go) return false; } return true; } /// /// Returns true if the end of a transform's hierarchy path matches a specified path. /// For example, if the transform's hierarchy is A/B/C/D, it will match a /// requiredPath of C/D. /// public static bool DoesGameObjectPathMatch(Transform t, string requiredPath) { if (t == null) return false; if (t.name.Contains("/")) { var fullPath = GetHierarchyPath(t); return fullPath.EndsWith(requiredPath); } else { return string.Equals(t.name, requiredPath); } } /// /// Returns the full hierarchy path /// /// /// public static string GetHierarchyPath(Transform t) { if (t == null) return string.Empty; if (t.parent == null) return t.name; return GetHierarchyPath(t.parent) + "/" + t.name; } /// /// Finds an in-scene GameObject by name even if it's inactive. /// /// Name of the GameObject. /// If true, check all open scenes; otherwise only check active scene. /// The GameObject. public static GameObject GameObjectHardFind(string goName, bool checkAllScenes = true) { if (string.IsNullOrEmpty(goName)) return null; // Try the normal method to find an active GameObject first: GameObject result = GameObject.Find(goName); if (result != null) return result; // Otherwise check all GameObjects, active and inactive: if (checkAllScenes) { for (int i = 0; i < UnityEngine.SceneManagement.SceneManager.sceneCount; i++) { var rootGameObjects = UnityEngine.SceneManagement.SceneManager.GetSceneAt(i).GetRootGameObjects(); result = GameObjectHardFindRootObjects(goName, string.Empty, rootGameObjects); if (result != null) return result; } return null; } else { var activeSceneRootGameObjects = UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects(); return GameObjectHardFindRootObjects(goName, string.Empty, activeSceneRootGameObjects); } } /// /// Finds an in-scene GameObject by name and tag even if it's inactive. /// /// GameObject name to search for. /// Tag to search for. /// If true, check all open scenes; otherwise only check active scene. /// public static GameObject GameObjectHardFind(string goName, string tag, bool checkAllScenes = true) { // Try the normal method to find active GameObjects with tag first: GameObject result = null; var gameObjects = GameObject.FindGameObjectsWithTag(tag); foreach (var go in gameObjects) { if (string.Equals(go.name, goName)) return go; } // Otherwise check all GameObjects, active and inactive: if (checkAllScenes) { var allRootGOs = new List(); var allTransforms = Resources.FindObjectsOfTypeAll(); foreach (var t in allTransforms) { var root = t.root; if (root.hideFlags == HideFlags.None) { allRootGOs.Add(t.gameObject); } } result = GameObjectHardFindRootObjects(goName, tag, allRootGOs.ToArray()); if (result != null) return result; return null; } else { var activeSceneRootGameObjects = UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects(); return GameObjectHardFindRootObjects(goName, tag, activeSceneRootGameObjects); } } private static GameObject GameObjectHardFindRootObjects(string goName, string tag, GameObject[] rootGameObjects) { if (rootGameObjects == null) return null; for (int i = 0; i < rootGameObjects.Length; i++) { var result = GameObjectSearchHierarchy(rootGameObjects[i].transform, goName, tag); if (result != null) return result; } return null; } private static GameObject GameObjectSearchHierarchy(Transform t, string goName, string tag) { if (t == null) return null; if (string.Equals(t.name, goName) && (string.IsNullOrEmpty(tag) || string.Equals(t.tag, tag))) return t.gameObject; foreach (Transform child in t) { var result = GameObjectSearchHierarchy(child, goName, tag); if (result != null) return result; } return null; } /// /// Finds all objects of a type, including on inactive GameObjects. /// If true, check all open scenes; otherwise only check active scene. /// public static T[] FindObjectsOfTypeAlsoInactive(bool checkAllScenes = true) where T : Component { #if UNITY_2022_3_OR_NEWER || UNITY_2023_1_OR_NEWER return UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); #else var list = new List(); var allT = Resources.FindObjectsOfTypeAll(); foreach (var t in allT) { var root = t.transform.root; if (root.hideFlags == HideFlags.None) { list.Add(t); } } return list.ToArray(); #endif } private static void FindObjectsSearchRootObjects(GameObject[] rootGameObjects, System.Collections.Generic.List list) where T : Component { if (rootGameObjects == null) return; for (int i = 0; i < rootGameObjects.Length; i++) { FindObjectsSearchHierarchy(rootGameObjects[i].transform, list); } } private static void FindObjectsSearchHierarchy(Transform t, System.Collections.Generic.List list) where T : Component { if (t == null) return; var components = t.GetComponents(); if (components.Length > 0) list.AddRange(components); foreach (Transform child in t) { FindObjectsSearchHierarchy(child, list); } } /// /// Like GetComponentInChildren(), but also searches parents. /// /// The component, or null if not found. /// Game object to search. /// The component type. public static T GetComponentAnywhere(GameObject gameObject) where T : Component { if (!gameObject) return null; T component = gameObject.GetComponentInChildren(); if (component) return component; Transform ancestor = gameObject.transform.parent; int safeguard = 0; while (!component && ancestor && safeguard < 256) { component = ancestor.GetComponentInChildren(); ancestor = ancestor.parent; } return component; } /// /// Gets the height of the game object based on its collider. This only works if the /// game object has a CharacterController, CapsuleCollider, BoxCollider, or SphereCollider. /// /// The game object height if it has a recognized type of collider; otherwise 0. /// Game object. public static float GetGameObjectHeight(GameObject gameObject) { CharacterController controller = gameObject.GetComponent(); if (controller != null) { return controller.height; } else { CapsuleCollider capsuleCollider = gameObject.GetComponent(); if (capsuleCollider != null) { return capsuleCollider.height; } else { BoxCollider boxCollider = gameObject.GetComponent(); if (boxCollider != null) { return boxCollider.center.y + boxCollider.size.y; } else { SphereCollider sphereCollider = gameObject.GetComponent(); if (sphereCollider != null) { return sphereCollider.center.y + sphereCollider.radius; } } } } return 0; } } }