// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Utilities; using System; using System.Collections.Generic; using System.Threading.Tasks; using UnityEngine; namespace Microsoft.MixedReality.Toolkit { /// /// Extension methods for Unity's GameObject class /// public static class GameObjectExtensions { /// /// Export mesh data of current GameObject, and children if enabled, to file provided in OBJ format /// public static async Task ExportOBJAsync(this GameObject root, string filePath, bool includeChildren = true) { await OBJWriterUtility.ExportOBJAsync(root, filePath, includeChildren); } /// /// Set all GameObject children active or inactive based on argument /// /// GameObject parent to traverse from /// Indicates whether children GameObjects should be active or not /// /// Does not call SetActive on the top level GameObject, only its children /// public static void SetChildrenActive(this GameObject root, bool isActive) { for (int i = 0; i < root.transform.childCount; i++) { root.transform.GetChild(i).gameObject.SetActive(isActive); } } /// /// Set the layer to the given object and the full hierarchy below it. /// /// Start point of the traverse /// The layer to apply public static void SetLayerRecursively(this GameObject root, int layer) { if (root == null) { throw new ArgumentNullException(nameof(root), "Root transform can't be null."); } foreach (var child in root.transform.EnumerateHierarchy()) { child.gameObject.layer = layer; } } /// /// Set the layer to the given object and the full hierarchy below it and cache the previous layers in the out parameter. /// /// Start point of the traverse /// The layer to apply /// The previously set layer for each object public static void SetLayerRecursively(this GameObject root, int layer, out Dictionary cache) { if (root == null) { throw new ArgumentNullException(nameof(root)); } cache = new Dictionary(); foreach (var child in root.transform.EnumerateHierarchy()) { cache[child.gameObject] = child.gameObject.layer; child.gameObject.layer = layer; } } /// /// Reapplies previously cached hierarchy layers /// /// Start point of the traverse /// The previously set layer for each object public static void ApplyLayerCacheRecursively(this GameObject root, Dictionary cache) { if (root == null) { throw new ArgumentNullException(nameof(root)); } if (cache == null) { throw new ArgumentNullException(nameof(cache)); } foreach (var child in root.transform.EnumerateHierarchy()) { int layer; if (!cache.TryGetValue(child.gameObject, out layer)) { continue; } child.gameObject.layer = layer; cache.Remove(child.gameObject); } } /// /// Determines whether or not a game object's layer is included in the specified layer mask. /// /// The game object whose layer to test. /// The layer mask to test against. /// True if 's layer is included in , false otherwise. public static bool IsInLayerMask(this GameObject gameObject, LayerMask layerMask) { LayerMask gameObjectMask = 1 << gameObject.layer; return (gameObjectMask & layerMask) == gameObjectMask; } /// /// Apply the specified delegate to all objects in the hierarchy under a specified game object. /// /// Root game object of the hierarchy. /// Delegate to apply. public static void ApplyToHierarchy(this GameObject root, Action action) { action(root); Transform[] items = root.GetComponentsInChildren(); for (var i = 0; i < items.Length; i++) { action(items[i].gameObject); } } /// /// Find the first component of type in the ancestors of the specified game object. /// /// Type of component to find. /// Game object for which ancestors must be considered. /// Indicates whether the specified game object should be included. /// The component of type . Null if it none was found. public static T FindAncestorComponent(this GameObject gameObject, bool includeSelf = true) where T : Component { return gameObject.transform.FindAncestorComponent(includeSelf); } /// /// Perform an action on every component of type T that is on this GameObject /// /// Component Type /// this gameObject /// Action to perform. public static void ForEachComponent(this GameObject gameObject, Action action) where T : Component { foreach (T i in gameObject.GetComponents()) { action(i); } } /// /// Destroys GameObject appropriately depending if in edit or playmode /// /// GameObject to destroy /// time in seconds at which to destroy GameObject if applicable public static void DestroyGameObject(GameObject gameObject, float t = 0.0f) { UnityObjectExtensions.DestroyObject(gameObject, t); } /// /// Checks if any MonoBehaviour on the given GameObject is using the RequireComponentAttribute requiring type T /// /// Only functions when called within a UNITY_EDITOR context. Outside of UNITY_EDITOR, always returns false /// The potentially required component /// the GameObject requiring the component /// A list of types that do require the component in question /// true if appears in any RequireComponentAttribute, otherwise false public static bool IsComponentRequired(this GameObject gameObject, out List requiringTypes) where T : Component { var genericType = typeof(T); requiringTypes = null; #if UNITY_EDITOR foreach (var monoBehaviour in gameObject.GetComponents()) { if (monoBehaviour == null) { continue; } var monoBehaviourType = monoBehaviour.GetType(); var attributes = Attribute.GetCustomAttributes(monoBehaviourType); foreach (var attribute in attributes) { if (attribute is RequireComponent requireComponentAttribute) { if (requireComponentAttribute.m_Type0 == genericType || requireComponentAttribute.m_Type1 == genericType || requireComponentAttribute.m_Type2 == genericType) { if (requiringTypes == null) { requiringTypes = new List(); } requiringTypes.Add(monoBehaviourType); } } } } #endif // UNITY_EDITOR return requiringTypes != null; } } }