using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using UnityObject = UnityEngine.Object; #if UNITY_EDITOR using UnityEditor; #endif namespace Unity.XR.CoreUtils { /// /// Utility methods for creating GameObjects. /// Allows systems to subscribe to . /// public static class GameObjectUtils { // Local method use only -- created here to reduce garbage collection. Collections must be cleared before use static readonly List k_GameObjects = new List(); static readonly List k_Transforms = new List(); /// /// Called when a GameObject has been instantiated through the versions of /// `Instantiate`. /// public static event Action GameObjectInstantiated; /// /// Creates a new GameObject and returns it. /// This method also calls . /// /// The new GameObject. public static GameObject Create() { var gameObject = new GameObject(); GameObjectInstantiated?.Invoke(gameObject); return gameObject; } /// /// Creates a new GameObject and returns it. /// This method also calls . /// /// The name to be given to the new GameObject. /// The new GameObject. public static GameObject Create(string name) { var gameObject = new GameObject(name); GameObjectInstantiated?.Invoke(gameObject); return gameObject; } /// /// Clones the GameObject, , and returns the clone. /// This method also calls . /// /// /// An existing GameObject that you want to make a copy of. /// Parent to assign to the new object. /// Set to instantiate the new object in world space, which places it in the /// same position as the cloned GameObject, or to offset the new object from . /// The instantiated clone. public static GameObject Instantiate(GameObject original, Transform parent = null, bool worldPositionStays = true) { var gameObject = UnityObject.Instantiate(original, parent, worldPositionStays); if (gameObject != null && GameObjectInstantiated != null) GameObjectInstantiated(gameObject); return gameObject; } /// /// Clones the GameObject, , and returns the clone. /// This method also calls . /// /// /// An existing GameObject that you want to make a copy of. /// Position for the new object. /// Orientation of the new object. /// The instantiated clone. public static GameObject Instantiate(GameObject original, Vector3 position, Quaternion rotation) { return Instantiate(original, null, position, rotation); } /// /// Clones the GameObject, , and returns the clone. /// This method also calls . /// /// /// An existing GameObject that you want to make a copy of /// Position for the new object. /// Orientation of the new object. /// Parent that will be assigned to the new object /// The instantiated clone public static GameObject Instantiate(GameObject original, Transform parent, Vector3 position, Quaternion rotation) { var gameObject = UnityObject.Instantiate(original, position, rotation, parent); if (gameObject != null && GameObjectInstantiated != null) GameObjectInstantiated(gameObject); return gameObject; } /// /// Clones the Game Object and copies the hide flags of each Game Object /// in its hierarchy to the corresponding Game Object in the copy's hierarchy. /// /// /// The Game Object to make a copy of /// Optional parent that will be assigned to the clone of the original Game Object /// The clone of the original Game Object public static GameObject CloneWithHideFlags(GameObject original, Transform parent = null) { var copy = UnityObject.Instantiate(original, parent); CopyHideFlagsRecursively(original, copy); return copy; } #if UNITY_EDITOR /// /// Clones the Prefab Game Object and copies the hide flags of each Game Object /// in its hierarchy to the corresponding Game Object in the copy's hierarchy. /// /// /// The Prefab Game Object to make a copy of /// Optional parent that will be assigned to the clone of the original Game Object /// The clone of the original Game Object public static GameObject ClonePrefabWithHideFlags(GameObject prefab, Transform parent = null) { var copy = PrefabUtility.InstantiatePrefab(prefab, parent) as GameObject; CopyHideFlagsRecursively(prefab, copy); return copy; } #endif static void CopyHideFlagsRecursively(GameObject copyFrom, GameObject copyTo) { copyTo.hideFlags = copyFrom.hideFlags; var copyFromTransform = copyFrom.transform; var copyToTransform = copyTo.transform; for (var i = 0; i < copyFromTransform.childCount; ++i) { CopyHideFlagsRecursively(copyFromTransform.GetChild(i).gameObject, copyToTransform.GetChild(i).gameObject); } } /// /// Searches for a component in a scene with a 3 step process, getting more comprehensive with each step /// At edit time will find *all* objects in the scene, even if they are disabled /// At play time, will be unable to find disabled objects that are not a child of desiredSource /// /// The type of component to find in the scene /// The Game Object we expect to be a parent or owner of the component /// A component of the desired type, or NULL if no component was located public static T ExhaustiveComponentSearch(GameObject desiredSource) where T : Component { var foundObject = default(T); // We check in the following order // - Location we expect the object to be // - The entire scene // - All loaded assets (Editor Only) if (desiredSource != null) foundObject = desiredSource.GetComponentInChildren(true); if (foundObject == null) { #if UNITY_2023_1_OR_NEWER foundObject = UnityObject.FindAnyObjectByType(); #else foundObject = UnityObject.FindObjectOfType(); #endif } if (foundObject != null) return foundObject; #if UNITY_EDITOR if (!Application.isPlaying) { var matchingObjects = Resources.FindObjectsOfTypeAll(); foreach (var possibleMatch in matchingObjects) { if (!EditorUtility.IsPersistent(possibleMatch)) { foundObject = possibleMatch; break; } } } #endif return foundObject; } /// /// Searches for a component in a scene with a 3 step process, getting more comprehensive with each step /// At edit time will find *all* objects in the scene, even if they are disabled /// At play time, will be unable to find disabled objects that are not a child of desiredSource /// /// The type of component to find in the scene /// The GameObject we expect to be a parent or owner of the component /// The tag this component must have to match /// A component of the desired type, or NULL if no component was located public static T ExhaustiveTaggedComponentSearch(GameObject desiredSource, string tag) where T : Component { var foundObject = default(T); // We check in the following order // - Location we expect the object to be // - The entire scene // - All loaded assets (Editor Only) if (desiredSource != null) { var matchingObjects = desiredSource.GetComponentsInChildren(true); foreach (var possibleMatch in matchingObjects) { if (possibleMatch.gameObject.CompareTag(tag)) { foundObject = possibleMatch; break; } } } if (foundObject == null) { var matchingObjects = GameObject.FindGameObjectsWithTag(tag); foreach (var possibleMatch in matchingObjects) { foundObject = possibleMatch.GetComponent(); if (foundObject != null) { break; } } } if (foundObject == null) { #if UNITY_2023_1_OR_NEWER foundObject = UnityObject.FindAnyObjectByType(); #else foundObject = UnityObject.FindObjectOfType(); #endif } #if UNITY_EDITOR if (foundObject == null && !Application.isPlaying) { var loadedMatchingObjects = Resources.FindObjectsOfTypeAll(); foreach (var possibleMatch in loadedMatchingObjects) { if (!EditorUtility.IsPersistent(possibleMatch) && possibleMatch.gameObject.CompareTag(tag)) { foundObject = possibleMatch; break; } } } #endif return foundObject; } /// /// Retrieves the first component of the given type in a scene /// /// The type of component to retrieve /// The scene to search /// The first component found in the active scene, or null if none exists public static T GetComponentInScene(Scene scene) where T : Component { // k_GameObjects is cleared by GetRootGameObjects scene.GetRootGameObjects(k_GameObjects); foreach (var gameObject in k_GameObjects) { var component = gameObject.GetComponentInChildren(); if (component) return component; } return null; } /// /// Retrieves all components of the given type in a scene /// /// The type of components to retrieve /// The scene to search /// List that will be filled out with components retrieved /// Should Components on inactive GameObjects be included in the found set? public static void GetComponentsInScene(Scene scene, List components, bool includeInactive = false) where T : Component { // k_GameObjects is cleared by GetRootGameObjects scene.GetRootGameObjects(k_GameObjects); foreach (var gameObject in k_GameObjects) { if (!includeInactive && !gameObject.activeInHierarchy) continue; components.AddRange(gameObject.GetComponentsInChildren(includeInactive)); } } /// /// Retrieves the first component of the given type in the active scene /// /// The type of component to retrieve /// The first component found in the active scene, or null if none exists public static T GetComponentInActiveScene() where T : Component { return GetComponentInScene(SceneManager.GetActiveScene()); } /// /// Retrieves all components of the given type in the active scene /// /// The type of components to retrieve /// List that will be filled out with components retrieved /// Should Components on inactive GameObjects be included in the found set? public static void GetComponentsInActiveScene(List components, bool includeInactive = false) where T : Component { GetComponentsInScene(SceneManager.GetActiveScene(), components, includeInactive); } /// /// Retrieves all components of the given type in all loaded scenes /// /// The type of components to retrieve /// List that will be filled out with components retrieved /// Should Components on inactive GameObjects be included in the found set? public static void GetComponentsInAllScenes(List components, bool includeInactive = false) where T : Component { var sceneCount = SceneManager.sceneCount; for (var i = 0; i < sceneCount; i++) { var scene = SceneManager.GetSceneAt(i); if (scene.isLoaded) GetComponentsInScene(scene, components, includeInactive); } } /// /// Get the direct children GameObjects of this GameObject. /// /// The parent GameObject that we will want to get the child GameObjects on. /// The direct children of a GameObject. public static void GetChildGameObjects(this GameObject go, List childGameObjects) { var goTransform = go.transform; var childCount = goTransform.childCount; if (childCount == 0) return; childGameObjects.EnsureCapacity(childCount); for (var i = 0; i < childCount; i++) { childGameObjects.Add(goTransform.GetChild(i).gameObject); } } /// /// Gets a descendant GameObject with a specific name /// /// The parent object that is searched for a named child. /// Name of child to be found. /// The returned child GameObject or null if no child is found. public static GameObject GetNamedChild(this GameObject go, string name) { k_Transforms.Clear(); go.GetComponentsInChildren(k_Transforms); var foundObject = k_Transforms.Find(currentTransform => currentTransform.name == name); k_Transforms.Clear(); if (foundObject != null) return foundObject.gameObject; return null; } } }