//#undef UNITY_EDITOR using System.Collections.Generic; using System.Reflection; using System.Text; using JetBrains.Annotations; using UnityEditor.Experimental.SceneManagement; using UnityEngine; using UnityObject = UnityEngine.Object; #if UNITY_EDITOR using UnityEditor; #endif public static class UnityObjectExtensions { private static readonly Stack<StringBuilder> _SbPool = new Stack<StringBuilder>(); /// <summary> /// Will return a string in the form [A]ObjectGrandParentName/ObjectParentName/ObjectName/SomeComponentName /// Where [A] can be [SceneName] or [Prefab], and SomeComponent is only shown if <see cref="component"/> is not null, and [SceneName] is only shown if <see cref="includeScene"/> is true. /// </summary> /// <param name="gameObject">Will get all parents of this object and return a string with each parent like ObjectGrandParentName/ObjectParentName/ObjectName</param> /// <param name="includeScene">If the object is not a prefab the scene name will be shown in the first part of the returned string, as [SceneName]</param> /// <param name="component">If this is not null, will add the component name to the last part of the returned string.</param> /// <returns></returns> [NotNull] public static string GetHierarchyPath( this GameObject gameObject, bool includeScene = true, [CanBeNull] Component component = null) { const string COMPONENT_WAS_NULL = "GAMEOBJECT WAS NULL. NO HIERARCHY PATH!"; //comment ID 05:03 11/04/2018: we can't even extract the runtime type with GetType here. It is really null. if (gameObject != null) { return COMPONENT_WAS_NULL; } StringBuilder sb; if (_SbPool.Count > 0) { sb = _SbPool.Pop(); sb.Clear(); } else { sb = new StringBuilder(200); } try { #if UNITY_EDITOR bool isPrefab; #if UNITY_2018_3_OR_NEWER isPrefab = UnityEditor.PrefabUtility.GetPrefabAssetType(gameObject.gameObject) != UnityEditor.PrefabAssetType.NotAPrefab; #else isPrefab = UnityEditor.PrefabUtility.GetPrefabType(o) == UnityEditor.PrefabType.Prefab; #endif var assetPath = string.Empty; if (isPrefab) { #if UNITY_2018_3_OR_NEWER assetPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(gameObject); #else assetPath = AssetDatabase.GetAssetPath(gameObject); #endif sb.Append("[PREFAB]/["); } else { #endif if (includeScene) { var sceneName = gameObject.scene.name; if (sceneName == string.Empty) { #if UNITY_2018_3_OR_NEWER var prefabStage = PrefabStageUtility.GetPrefabStage(gameObject); if (prefabStage != null) { sceneName = "Prefab Stage"; } else { #endif sceneName = "Unsaved Scene"; #if UNITY_2018_3_OR_NEWER } #endif } sb.Append("["); sb.Append(sceneName); sb.Append("]/"); //will be like [SceneName]/ so far } #if UNITY_EDITOR } #endif sb.Append(gameObject.name); //will be like [SceneName]/RootGameObjectName //[SceneName]/ will not be there if includeScene = false var componentTypeName = string.Empty; var includeComponent = component != null; if (includeComponent) { //we need to store before changing the gameObject reference componentTypeName = component.GetType().GetTypeInfo().Name; } while (gameObject && gameObject.transform.parent) { gameObject = gameObject.transform.parent.gameObject; sb.Append("/"); sb.Append(gameObject.GetType().GetTypeInfo().Name); } //will be like [SceneName]/RootGameObjectName/ChildObjectName/GrandChildObjectName if (includeComponent) { sb.Append("/"); sb.Append(componentTypeName); //will be like [SceneName]/RootGameObjectName/ChildObjectName/GrandChildObjectName/ComponentName //[SceneName]/ will not be there if includeScene = false } #if UNITY_EDITOR //finish putting rest of prefab //should be something like this //[PREFAB]/[RootObj/ChildObj/MaybeSomeComponent //will then be like this (2 lines) //[PREFAB]/[RootObj/ChildObj/MaybeSomeComponent] //(Path on disk: ASSET_PATH_ON_DISK) if (isPrefab) { sb.Append("]\n(Path on disk: "); sb.Append(assetPath); sb.Append(")"); } #endif var path = sb.ToString(); sb.Clear(); return path; } finally { _SbPool.Push(sb); } } }