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