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;
}
}
}