VR4Medical/ICI/Library/PackageCache/com.unity.xr.core-utils@5b282bc7378d/Runtime/GameObjectUtils.cs
2025-07-29 13:45:50 +03:00

383 lines
17 KiB
C#

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
{
/// <summary>
/// Utility methods for creating GameObjects.
/// Allows systems to subscribe to <see cref="GameObjectInstantiated"/>.
/// </summary>
public static class GameObjectUtils
{
// Local method use only -- created here to reduce garbage collection. Collections must be cleared before use
static readonly List<GameObject> k_GameObjects = new List<GameObject>();
static readonly List<Transform> k_Transforms = new List<Transform>();
/// <summary>
/// Called when a GameObject has been instantiated through the <see cref="GameObjectUtils"/> versions of
/// `Instantiate`.
/// </summary>
public static event Action<GameObject> GameObjectInstantiated;
/// <summary>
/// Creates a new GameObject and returns it.
/// This method also calls <see cref="GameObjectInstantiated"/>.
/// </summary>
/// <returns>The new GameObject.</returns>
public static GameObject Create()
{
var gameObject = new GameObject();
GameObjectInstantiated?.Invoke(gameObject);
return gameObject;
}
/// <summary>
/// Creates a new GameObject and returns it.
/// This method also calls <see cref="GameObjectInstantiated"/>.
/// </summary>
/// <param name="name">The name to be given to the new GameObject.</param>
/// <returns>The new GameObject.</returns>
public static GameObject Create(string name)
{
var gameObject = new GameObject(name);
GameObjectInstantiated?.Invoke(gameObject);
return gameObject;
}
/// <summary>
/// Clones the GameObject, <paramref name="original"/>, and returns the clone.
/// This method also calls <see cref="GameObjectInstantiated"/>.
/// </summary>
/// <seealso cref="UnityObject.Instantiate(UnityObject, Transform, bool)"/>
/// <param name="original">An existing GameObject that you want to make a copy of.</param>
/// <param name="parent">Parent <see cref="Transform"/> to assign to the new object.</param>
/// <param name="worldPositionStays">Set <see langword="true"/> 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 <paramref name="parent"/>.</param>
/// <returns>The instantiated clone.</returns>
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;
}
/// <summary>
/// Clones the GameObject, <paramref name="original"/>, and returns the clone.
/// This method also calls <see cref="GameObjectInstantiated"/>.
/// </summary>
/// <seealso cref="UnityObject.Instantiate(UnityObject, Vector3, Quaternion)"/>
/// <param name="original">An existing GameObject that you want to make a copy of.</param>
/// <param name="position">Position for the new object.</param>
/// <param name="rotation">Orientation of the new object.</param>
/// <returns>The instantiated clone.</returns>
public static GameObject Instantiate(GameObject original, Vector3 position, Quaternion rotation)
{
return Instantiate(original, null, position, rotation);
}
/// <summary>
/// Clones the GameObject, <paramref name="original"/>, and returns the clone.
/// This method also calls <see cref="GameObjectInstantiated"/>.
/// </summary>
/// <seealso cref="UnityObject.Instantiate(UnityObject, Vector3, Quaternion, Transform)"/>
/// <param name="original">An existing GameObject that you want to make a copy of</param>
/// <param name="position">Position for the new object.</param>
/// <param name="rotation">Orientation of the new object.</param>
/// <param name="parent">Parent that will be assigned to the new object</param>
/// <returns>The instantiated clone</returns>
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;
}
/// <summary>
/// Clones the Game Object <paramref name="original"/> and copies the hide flags of each Game Object
/// in its hierarchy to the corresponding Game Object in the copy's hierarchy.
/// </summary>
/// <seealso cref="UnityObject.Instantiate(UnityObject, Transform)"/>
/// <param name="original">The Game Object to make a copy of</param>
/// <param name="parent">Optional parent that will be assigned to the clone of the original Game Object</param>
/// <returns>The clone of the original Game Object</returns>
public static GameObject CloneWithHideFlags(GameObject original, Transform parent = null)
{
var copy = UnityObject.Instantiate(original, parent);
CopyHideFlagsRecursively(original, copy);
return copy;
}
#if UNITY_EDITOR
/// <summary>
/// Clones the Prefab Game Object <paramref name="prefab"/> and copies the hide flags of each Game Object
/// in its hierarchy to the corresponding Game Object in the copy's hierarchy.
/// </summary>
/// <seealso cref="PrefabUtility.InstantiatePrefab(UnityObject, Transform)"/>
/// <param name="prefab">The Prefab Game Object to make a copy of</param>
/// <param name="parent">Optional parent that will be assigned to the clone of the original Game Object</param>
/// <returns>The clone of the original Game Object</returns>
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);
}
}
/// <summary>
/// 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
/// </summary>
/// <typeparam name="T">The type of component to find in the scene</typeparam>
/// <param name="desiredSource">The Game Object we expect to be a parent or owner of the component</param>
/// <returns>A component of the desired type, or NULL if no component was located</returns>
public static T ExhaustiveComponentSearch<T>(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<T>(true);
if (foundObject == null)
{
#if UNITY_2023_1_OR_NEWER
foundObject = UnityObject.FindAnyObjectByType<T>();
#else
foundObject = UnityObject.FindObjectOfType<T>();
#endif
}
if (foundObject != null)
return foundObject;
#if UNITY_EDITOR
if (!Application.isPlaying)
{
var matchingObjects = Resources.FindObjectsOfTypeAll<T>();
foreach (var possibleMatch in matchingObjects)
{
if (!EditorUtility.IsPersistent(possibleMatch))
{
foundObject = possibleMatch;
break;
}
}
}
#endif
return foundObject;
}
/// <summary>
/// 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
/// </summary>
/// <typeparam name="T">The type of component to find in the scene</typeparam>
/// <param name="desiredSource">The GameObject we expect to be a parent or owner of the component</param>
/// <param name="tag">The tag this component must have to match</param>
/// <returns>A component of the desired type, or NULL if no component was located</returns>
public static T ExhaustiveTaggedComponentSearch<T>(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<T>(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<T>();
if (foundObject != null)
{
break;
}
}
}
if (foundObject == null)
{
#if UNITY_2023_1_OR_NEWER
foundObject = UnityObject.FindAnyObjectByType<T>();
#else
foundObject = UnityObject.FindObjectOfType<T>();
#endif
}
#if UNITY_EDITOR
if (foundObject == null && !Application.isPlaying)
{
var loadedMatchingObjects = Resources.FindObjectsOfTypeAll<T>();
foreach (var possibleMatch in loadedMatchingObjects)
{
if (!EditorUtility.IsPersistent(possibleMatch) && possibleMatch.gameObject.CompareTag(tag))
{
foundObject = possibleMatch;
break;
}
}
}
#endif
return foundObject;
}
/// <summary>
/// Retrieves the first component of the given type in a scene
/// </summary>
/// <typeparam name="T">The type of component to retrieve</typeparam>
/// <param name="scene">The scene to search</param>
/// <returns>The first component found in the active scene, or null if none exists</returns>
public static T GetComponentInScene<T>(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<T>();
if (component)
return component;
}
return null;
}
/// <summary>
/// Retrieves all components of the given type in a scene
/// </summary>
/// <typeparam name="T">The type of components to retrieve</typeparam>
/// <param name="scene">The scene to search</param>
/// <param name="components">List that will be filled out with components retrieved</param>
/// <param name="includeInactive">Should Components on inactive GameObjects be included in the found set?</param>
public static void GetComponentsInScene<T>(Scene scene, List<T> 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<T>(includeInactive));
}
}
/// <summary>
/// Retrieves the first component of the given type in the active scene
/// </summary>
/// <typeparam name="T">The type of component to retrieve</typeparam>
/// <returns>The first component found in the active scene, or null if none exists</returns>
public static T GetComponentInActiveScene<T>() where T : Component
{
return GetComponentInScene<T>(SceneManager.GetActiveScene());
}
/// <summary>
/// Retrieves all components of the given type in the active scene
/// </summary>
/// <typeparam name="T">The type of components to retrieve</typeparam>
/// <param name="components">List that will be filled out with components retrieved</param>
/// <param name="includeInactive">Should Components on inactive GameObjects be included in the found set?</param>
public static void GetComponentsInActiveScene<T>(List<T> components, bool includeInactive = false) where T : Component
{
GetComponentsInScene(SceneManager.GetActiveScene(), components, includeInactive);
}
/// <summary>
/// Retrieves all components of the given type in all loaded scenes
/// </summary>
/// <typeparam name="T">The type of components to retrieve</typeparam>
/// <param name="components">List that will be filled out with components retrieved</param>
/// <param name="includeInactive">Should Components on inactive GameObjects be included in the found set?</param>
public static void GetComponentsInAllScenes<T>(List<T> 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);
}
}
/// <summary>
/// Get the direct children GameObjects of this GameObject.
/// </summary>
/// <param name="go">The parent GameObject that we will want to get the child GameObjects on.</param>
/// <param name="childGameObjects">The direct children of a GameObject.</param>
public static void GetChildGameObjects(this GameObject go, List<GameObject> 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);
}
}
/// <summary>
/// Gets a descendant GameObject with a specific name
/// </summary>
/// <param name="go">The parent object that is searched for a named child.</param>
/// <param name="name">Name of child to be found.</param>
/// <returns>The returned child GameObject or null if no child is found.</returns>
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;
}
}
}