ProjectDDD/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameObjectUtility.cs

308 lines
12 KiB (Stored with Git LFS)
C#

// Copyright (c) Pixel Crushers. All rights reserved.
using System.Collections.Generic;
using UnityEngine;
namespace PixelCrushers
{
/// <summary>
/// Utility functions for working with GameObjects.
/// </summary>
public static class GameObjectUtility
{
// These methods handle API differences:
public static T FindFirstObjectByType<T>() where T : UnityEngine.Object
{
#if UNITY_2023_1_OR_NEWER
return UnityEngine.Object.FindFirstObjectByType<T>();
#else
return UnityEngine.Object.FindObjectOfType<T>();
#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<T>() where T : UnityEngine.Object
{
#if UNITY_2023_1_OR_NEWER
return UnityEngine.Object.FindObjectsByType<T>(FindObjectsSortMode.None);
#else
return UnityEngine.Object.FindObjectsOfType<T>();
#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
}
/// <summary>
/// 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.
/// </summary>
/// <returns><c>true</c> if a prefab; otherwise, <c>false</c>.</returns>
/// <param name="go">GameObject.</param>
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<GameObject>();
for (int i = 0; i < list.Length; i++)
{
if (list[i] == go) return false;
}
return true;
}
/// <summary>
/// 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.
/// </summary>
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);
}
}
/// <summary>
/// Returns the full hierarchy path
/// </summary>
/// <param name="current"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Finds an in-scene GameObject by name even if it's inactive.
/// </summary>
/// <param name="goName">Name of the GameObject.</param>
/// <param name="checkAllScenes">If true, check all open scenes; otherwise only check active scene.</param>
/// <returns>The GameObject.</returns>
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);
}
}
/// <summary>
/// Finds an in-scene GameObject by name and tag even if it's inactive.
/// </summary>
/// <param name="goName">GameObject name to search for.</param>
/// <param name="tag">Tag to search for.</param>
/// <param name="checkAllScenes">If true, check all open scenes; otherwise only check active scene.</param>
/// <returns></returns>
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<GameObject>();
var allTransforms = Resources.FindObjectsOfTypeAll<Transform>();
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;
}
/// <summary>
/// Finds all objects of a type, including on inactive GameObjects.
/// <param name="checkAllScenes">If true, check all open scenes; otherwise only check active scene.</param>
/// </summary>
public static T[] FindObjectsOfTypeAlsoInactive<T>(bool checkAllScenes = true) where T : Component
{
#if UNITY_2022_3_OR_NEWER || UNITY_2023_1_OR_NEWER
return UnityEngine.Object.FindObjectsByType<T>(FindObjectsInactive.Include, FindObjectsSortMode.None);
#else
var list = new List<T>();
var allT = Resources.FindObjectsOfTypeAll<T>();
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<T>(GameObject[] rootGameObjects, System.Collections.Generic.List<T> list) where T : Component
{
if (rootGameObjects == null) return;
for (int i = 0; i < rootGameObjects.Length; i++)
{
FindObjectsSearchHierarchy<T>(rootGameObjects[i].transform, list);
}
}
private static void FindObjectsSearchHierarchy<T>(Transform t, System.Collections.Generic.List<T> list) where T : Component
{
if (t == null) return;
var components = t.GetComponents<T>();
if (components.Length > 0) list.AddRange(components);
foreach (Transform child in t)
{
FindObjectsSearchHierarchy<T>(child, list);
}
}
/// <summary>
/// Like GetComponentInChildren(), but also searches parents.
/// </summary>
/// <returns>The component, or <c>null</c> if not found.</returns>
/// <param name="gameObject">Game object to search.</param>
/// <typeparam name="T">The component type.</typeparam>
public static T GetComponentAnywhere<T>(GameObject gameObject) where T : Component
{
if (!gameObject) return null;
T component = gameObject.GetComponentInChildren<T>();
if (component) return component;
Transform ancestor = gameObject.transform.parent;
int safeguard = 0;
while (!component && ancestor && safeguard < 256)
{
component = ancestor.GetComponentInChildren<T>();
ancestor = ancestor.parent;
}
return component;
}
/// <summary>
/// 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.
/// </summary>
/// <returns>The game object height if it has a recognized type of collider; otherwise <c>0</c>.</returns>
/// <param name="gameObject">Game object.</param>
public static float GetGameObjectHeight(GameObject gameObject)
{
CharacterController controller = gameObject.GetComponent<CharacterController>();
if (controller != null)
{
return controller.height;
}
else
{
CapsuleCollider capsuleCollider = gameObject.GetComponent<CapsuleCollider>();
if (capsuleCollider != null)
{
return capsuleCollider.height;
}
else
{
BoxCollider boxCollider = gameObject.GetComponent<BoxCollider>();
if (boxCollider != null)
{
return boxCollider.center.y + boxCollider.size.y;
}
else
{
SphereCollider sphereCollider = gameObject.GetComponent<SphereCollider>();
if (sphereCollider != null)
{
return sphereCollider.center.y + sphereCollider.radius;
}
}
}
}
return 0;
}
}
}