// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Linq; using UltimateXR.Core; using UnityEngine; using UnityEngine.SceneManagement; #if UNITY_EDITOR using UnityEditor; #endif namespace UltimateXR.Extensions.Unity { /// /// extensions. /// public static class ComponentExt { #region Public Methods /// /// Controls whether to show a given component in the inspector. /// /// The component to show /// Whether to show the component or now public static void ShowInInspector(this Component self, bool show = true) { #if UNITY_EDITOR if (Application.isEditor) { SerializedObject so = new SerializedObject(self); SerializedProperty prop = so.FindProperty(UxrConstants.Editor.PropertyObjectHideFlags); if (show) { prop.intValue &= (int)~HideFlags.HideInInspector; } else { prop.intValue |= (int)HideFlags.HideInInspector; } so.ApplyModifiedProperties(); return; } #endif if (show) { self.hideFlags &= ~HideFlags.HideInInspector; } else { self.hideFlags |= HideFlags.HideInInspector; } } /// /// Controls whether to show a given component in the inspector and whether it is editable. /// /// The object to set /// Whether to show it in the inspector /// Whether it is editable public static void ShowInInspector(this Component self, bool show, bool editable) { #if UNITY_EDITOR SerializedObject so = new SerializedObject(self); SerializedProperty prop = so.FindProperty(UxrConstants.Editor.PropertyObjectHideFlags); if (show) { prop.intValue &= (int)~HideFlags.HideInInspector; } else { prop.intValue |= (int)HideFlags.HideInInspector; } if (editable) { prop.intValue &= (int)~HideFlags.NotEditable; } else { prop.intValue |= (int)HideFlags.NotEditable; } so.ApplyModifiedProperties(); #else if (show) { self.hideFlags &= ~HideFlags.HideInInspector; } else { self.hideFlags |= HideFlags.HideInInspector; } if (editable) { self.hideFlags &= ~HideFlags.NotEditable; } else { self.hideFlags |= HideFlags.NotEditable; } #endif } /// /// Checks whether the component is in a prefab. /// /// Component to check /// Whether the component is in a prefab public static bool IsInPrefab(this Component self) { return self.gameObject.IsInPrefab(); } #if UNITY_EDITOR /// /// Gets the GUID of the prefab the component is in, if it is in a prefab, or the GUID of the prefab the component was /// instantiated from, if it was instantiated from a prefab. /// If the component is not in a prefab and doesn't have a source prefab either, it will return string.Empty. /// /// If the call was successful, returns the GUID or string.Empty /// Whether the prefab GUID could be retrieved /// /// The reason the call can be unsuccessful is because Unity for some reason will report /// a null/empty asset path even though PrefabUtility.IsPartOfPrefabAsset() returns true. /// This behaviour happens when in prefab isolation/context mode in the editor /// public static bool GetPrefabGuid(this Component self, out string prefabGuid) { return self.gameObject.GetPrefabGuid(out prefabGuid, out string _); } /// /// Same as but it also returns the asset path if it exists. /// /// If the call was successful, returns the GUID or string.Empty /// If the call was successful, returns the asset path or string.Empty /// Whether the prefab GUID could be retrieved /// /// The reason the call can be unsuccessful is because Unity for some reason will report /// a null/empty asset path even though PrefabUtility.IsPartOfPrefabAsset() returns true. /// This behaviour happens when in prefab isolation/context mode in the editor /// public static bool GetPrefabGuid(this Component self, out string prefabGuid, out string assetPath) { return self.gameObject.GetPrefabGuid(out prefabGuid, out assetPath); } #endif /// /// Gets the Component of a given type. If it doesn't exist, it is added to the GameObject. /// /// Component whose GameObject will be used to retrieve or add the given component type to /// Component type to get or add /// Existing component or newly added if it didn't exist before public static T GetOrAddComponent(this Component self) where T : Component { T component = self.GetComponent(); if (component == null) { #if UNITY_EDITOR if (Application.isPlaying) { component = self.gameObject.AddComponent(); } else { component = Undo.AddComponent(self.gameObject); } #else component = self.gameObject.AddComponent(); #endif } return component; } /// /// Gets the Component of a given type in the GameObject or any of its parents. It also works on prefabs, where regular /// will not work: /// https://issuetracker.unity3d.com/issues/getcomponentinparent-is-returning-null-when-the-gameobject-is-a-prefab /// /// type to get /// Component in same GameObject or any of its parents. Null if it wasn't found public static T SafeGetComponentInParent(this Component self) { return self.GetComponentInParent() ?? self.GetComponentsInParent(true).FirstOrDefault(); } /// /// Gets the full path under current scene, including all parents, but scene name, for the given component. /// /// /// The path generated might not be unique. If that is the purpose, use instead. /// /// to get the path for /// /// Optional Transform to get the path relative to. If it's not the same Transform or a Transform up in the hierarchy /// it will return the full path /// /// Component path string public static string GetPathUnderScene(this Component self, Transform relativeTo = null) { string path = self.transform.GetPathUnderScene(relativeTo); return self is Transform ? path : $"{path}/{self.GetType().Name}"; } /// /// Gets an unique path in the scene for the given component. It will include scene name, sibling and component indices /// to make it unique. /// /// to get the unique path for /// /// Optional Transform to get the path relative to. If it's not the same Transform or a Transform up in the hierarchy /// it will return the full path /// /// Unique component path string public static string GetUniqueScenePath(this Component self, Transform relativeTo = null) { string path = self.transform.GetUniqueScenePath(relativeTo); return self is Transform ? path : $"{path}/{Array.IndexOf(self.GetComponents(), self):00}{self.GetType().Name}"; } /// /// Gets an unique identifier string for the given component. /// /// Generates an 8 characters hexadecimal hash code of . /// to get UID for. /// 8 characters hexadecimal unique identifier public static string GetSceneUid(this Component self) { return self.GetUniqueScenePath().GetHashCode().ToString("x8"); } /// /// Gets a list of all components of the given type in the open scenes /// /// Type of component to look for /// Whether to include inactive components or not /// List of components public static List GetAllComponentsInOpenScenes(bool includeInactive) where T : Component { List listResult = new List(); for (int i = 0; i < SceneManager.sceneCount; i++) { Scene s = SceneManager.GetSceneAt(i); if (s.isLoaded) { GameObject[] rootGameObjects = s.GetRootGameObjects(); foreach (GameObject go in rootGameObjects) { listResult.AddRange(go.GetComponentsInChildren(includeInactive)); } } } return listResult; } /// /// From a set of components, returns which one of them has a transform that is a common root of all. /// The transform must be the transform of a component in the list. /// /// Components whose transforms to check /// /// Returns which transform from all the components passed as parameters is a common root of all. If no component has a /// transform that is a common root it returns null. /// public static T GetCommonRootComponentFromSet(params T[] components) where T : Component { T commonRoot = null; for (int i = 0; i < components.Length; i++) { if (i == 0) { commonRoot = components[i]; } else { if (commonRoot == null || (components[i] != commonRoot && components[i].transform.HasParent(commonRoot.transform) == false)) { bool found = true; for (int j = 0; j < i - 1; j++) { if (components[i] != components[j] && components[j].transform.HasParent(components[i].transform) == false) { found = false; } } commonRoot = found ? components[i] : null; } } } return commonRoot; } #endregion } }