// Asset Usage Detector - by Suleyman Yasir KULA (yasirkula@gmail.com) using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Reflection; using Object = UnityEngine.Object; #if UNITY_2018_3_OR_NEWER using PrefabStage = UnityEditor.SceneManagement.PrefabStage; using PrefabStageUtility = UnityEditor.SceneManagement.PrefabStageUtility; #endif namespace AssetUsageDetectorNamespace { public enum Phase { Setup, Processing, Complete }; public class AssetUsageDetectorWindow : EditorWindow { private const float PLAY_MODE_REFRESH_INTERVAL = 1f; // Interval to refresh the editor window in play mode private const string PREFS_SEARCH_SCENES = "AUD_SceneSearch"; private const string PREFS_SEARCH_ASSETS = "AUD_AssetsSearch"; private const string PREFS_SEARCH_PROJECT_SETTINGS = "AUD_ProjectSettingsSearch"; private const string PREFS_DONT_SEARCH_SOURCE_ASSETS = "AUD_AssetsExcludeSrc"; private const string PREFS_SEARCH_DEPTH_LIMIT = "AUD_Depth"; private const string PREFS_SEARCH_FIELDS = "AUD_Fields"; private const string PREFS_SEARCH_PROPERTIES = "AUD_Properties"; private const string PREFS_SEARCH_NON_SERIALIZABLES = "AUD_NonSerializables"; private const string PREFS_LAZY_SCENE_SEARCH = "AUD_LazySceneSearch"; private const string PREFS_PATH_DRAWING_MODE = "AUD_PathDrawing"; private const string PREFS_SHOW_PROGRESS = "AUD_Progress"; private const string PREFS_SHOW_TOOLTIPS = "AUD_Tooltips"; private static readonly GUIContent windowTitle = new GUIContent( "Asset Usage Detector" ); private static readonly Vector2 windowMinSize = new Vector2( 325f, 220f ); private AssetUsageDetector core = new AssetUsageDetector(); private SearchResult searchResult; // Overall search results private List objectsToSearch = new List() { new ObjectToSearch( null ) }; private Phase currentPhase = Phase.Setup; private bool searchInOpenScenes = true; // Scenes currently open in Hierarchy view private bool searchInScenesInBuild = true; // Scenes in build private bool searchInScenesInBuildTickedOnly = true; // Scenes in build (ticked only or not) private bool searchInAllScenes = true; // All scenes (including scenes that are not in build) private bool searchInAssetsFolder = true; // Assets in Project window private bool dontSearchInSourceAssets = true; // objectsToSearch won't be searched for internal references private bool searchInProjectSettings = true; // Player Settings, Graphics Settings etc. private List searchInAssetsSubset = new List() { null }; // If not empty, only these assets are searched for references private List excludedAssets = new List() { null }; // These assets won't be searched for references private List excludedScenes = new List() { null }; // These scenes won't be searched for references private int searchDepthLimit = 4; // Depth limit for recursively searching variables of objects private bool restoreInitialSceneSetup = true; // Close the additively loaded scenes that were not part of the initial scene setup private string errorMessage = string.Empty; private bool lazySceneSearch = false; private bool searchNonSerializableVariables = true; private bool noAssetDatabaseChanges = false; private bool showDetailedProgressBar = false; private BindingFlags fieldModifiers, propertyModifiers; private SearchResultDrawParameters searchResultDrawParameters = new SearchResultDrawParameters( PathDrawingMode.ShortRelevantParts, false, false ); private double nextPlayModeRefreshTime = 0f; private readonly ObjectToSearchListDrawer objectsToSearchDrawer = new ObjectToSearchListDrawer(); private readonly ObjectListDrawer searchInAssetsSubsetDrawer = new ObjectListDrawer( "Search following asset(s) only:", false ); private readonly ObjectListDrawer excludedAssetsDrawer = new ObjectListDrawer( "Don't search following asset(s):", false ); private readonly ObjectListDrawer excludedScenesDrawer = new ObjectListDrawer( "Don't search in following scene(s):", false ); private readonly GUIContent restoreInitialSceneSetupLabel = new GUIContent( "Restore initial scene setup (Recommended)", "For example, if scene A was open when the search was started and scenes A and B are open after the search is completed, clicking the \"Reset Search\" button will automatically close scene B" ); private readonly GUIContent showTooltipsLabel = new GUIContent( "Show tooltips", "Display nodes' contents in a tooltip when cursor hovers over them" ); private Vector2 scrollPosition = Vector2.zero; private bool shouldRepositionSelf; private Rect windowTargetPosition; private static AssetUsageDetectorWindow mainWindow = null; // Add "Asset Usage Detector" menu item to the Window menu [MenuItem( "Window/Asset Usage Detector/Active Window" )] private static void OpenActiveWindow() { if( !mainWindow ) { mainWindow = GetWindow(); mainWindow.titleContent = windowTitle; mainWindow.minSize = windowMinSize; } mainWindow.Show(); } [MenuItem( "Window/Asset Usage Detector/New Window" )] private static void OpenNewWindow() { Rect? windowTargetPosition = null; if( mainWindow ) { Rect position = mainWindow.position; position.position += new Vector2( 50f, 50f ); windowTargetPosition = position; } mainWindow = CreateInstance(); mainWindow.titleContent = windowTitle; mainWindow.minSize = windowMinSize; if( windowTargetPosition.HasValue ) { mainWindow.shouldRepositionSelf = true; mainWindow.windowTargetPosition = windowTargetPosition.Value; } mainWindow.Show( true ); } // Quickly initiate search for the selected assets [MenuItem( "GameObject/Search for References/This Object Only", priority = 49 )] [MenuItem( "Assets/Search for References", priority = 1000 )] private static void SearchSelectedAssetReferences( MenuCommand command ) { // This happens when this button is clicked via hierarchy's right click context menu // and is called once for each object in the selection. We don't want that, we want // the function to be called only once if( command.context ) { EditorApplication.update -= CallSearchSelectedAssetReferencesOnce; EditorApplication.update += CallSearchSelectedAssetReferencesOnce; } else ShowAndSearch( Selection.objects ); } [MenuItem( "GameObject/Search for References/Include Children", priority = 49 )] private static void SearchSelectedAssetReferencesWithChildren( MenuCommand command ) { if( command.context ) { EditorApplication.update -= CallSearchSelectedAssetReferencesWithChildrenOnce; EditorApplication.update += CallSearchSelectedAssetReferencesWithChildrenOnce; } else ShowAndSearch( Selection.objects, true ); } // Show the menu item only if there is a selection in the Editor [MenuItem( "GameObject/Search for References/This Object Only", validate = true )] [MenuItem( "GameObject/Search for References/Include Children", validate = true )] [MenuItem( "Assets/Search for References", validate = true )] private static bool SearchSelectedAssetReferencesValidate( MenuCommand command ) { return Selection.objects.Length > 0; } // Quickly show the AssetUsageDetector window and initiate a search public static void ShowAndSearch( IEnumerable searchObjects, bool? shouldSearchChildren = null ) { ShowAndSearchInternal( searchObjects, null, shouldSearchChildren ); } // Quickly show the AssetUsageDetector window and initiate a search public static void ShowAndSearch( AssetUsageDetector.Parameters searchParameters, bool? shouldSearchChildren = null ) { if( searchParameters == null ) { Debug.LogError( "searchParameters can't be null!" ); return; } ShowAndSearchInternal( searchParameters.objectsToSearch, searchParameters, shouldSearchChildren ); } private static void CallSearchSelectedAssetReferencesOnce() { EditorApplication.update -= CallSearchSelectedAssetReferencesOnce; SearchSelectedAssetReferences( new MenuCommand( null ) ); } private static void CallSearchSelectedAssetReferencesWithChildrenOnce() { EditorApplication.update -= CallSearchSelectedAssetReferencesWithChildrenOnce; SearchSelectedAssetReferencesWithChildren( new MenuCommand( null ) ); } private static void ShowAndSearchInternal( IEnumerable searchObjects, AssetUsageDetector.Parameters searchParameters, bool? shouldSearchChildren ) { if( mainWindow != null && !mainWindow.ReturnToSetupPhase( true, true ) ) { Debug.LogError( "Need to reset the previous search first!" ); return; } OpenActiveWindow(); mainWindow.objectsToSearch.Clear(); if( searchObjects != null ) { foreach( Object obj in searchObjects ) mainWindow.objectsToSearch.Add( new ObjectToSearch( obj, shouldSearchChildren ) ); } if( searchParameters != null ) { mainWindow.ParseSceneSearchMode( searchParameters.searchInScenes ); mainWindow.searchInAssetsFolder = searchParameters.searchInAssetsFolder; mainWindow.dontSearchInSourceAssets = searchParameters.dontSearchInSourceAssets; mainWindow.searchInProjectSettings = searchParameters.searchInProjectSettings; mainWindow.searchDepthLimit = searchParameters.searchDepthLimit; mainWindow.fieldModifiers = searchParameters.fieldModifiers; mainWindow.propertyModifiers = searchParameters.propertyModifiers; mainWindow.searchNonSerializableVariables = searchParameters.searchNonSerializableVariables; mainWindow.lazySceneSearch = searchParameters.lazySceneSearch; mainWindow.noAssetDatabaseChanges = searchParameters.noAssetDatabaseChanges; mainWindow.showDetailedProgressBar = searchParameters.showDetailedProgressBar; mainWindow.searchInAssetsSubset.Clear(); if( searchParameters.searchInAssetsSubset != null ) { foreach( Object obj in searchParameters.searchInAssetsSubset ) mainWindow.searchInAssetsSubset.Add( obj ); } mainWindow.excludedAssets.Clear(); if( searchParameters.excludedAssetsFromSearch != null ) { foreach( Object obj in searchParameters.excludedAssetsFromSearch ) mainWindow.excludedAssets.Add( obj ); } mainWindow.excludedScenes.Clear(); if( searchParameters.excludedScenesFromSearch != null ) { foreach( Object obj in searchParameters.excludedScenesFromSearch ) mainWindow.excludedScenes.Add( obj ); } } mainWindow.InitiateSearch(); mainWindow.Repaint(); } private void Awake() { LoadPrefs(); } private void OnEnable() { mainWindow = this; #if UNITY_2018_3_OR_NEWER PrefabStage.prefabStageClosing -= ReplacePrefabStageObjectsWithAssets; PrefabStage.prefabStageClosing += ReplacePrefabStageObjectsWithAssets; #endif } private void OnDisable() { #if UNITY_2018_3_OR_NEWER PrefabStage.prefabStageClosing -= ReplacePrefabStageObjectsWithAssets; #endif if( mainWindow == this ) mainWindow = null; } private void OnDestroy() { if( core != null ) core.SaveCache(); SavePrefs(); if( searchResult != null && currentPhase == Phase.Complete ) searchResult.RestoreInitialSceneSetup( true ); } private void OnFocus() { mainWindow = this; } private void SavePrefs() { EditorPrefs.SetInt( PREFS_SEARCH_SCENES, (int) GetSceneSearchMode( false ) ); EditorPrefs.SetBool( PREFS_SEARCH_ASSETS, searchInAssetsFolder ); EditorPrefs.SetBool( PREFS_DONT_SEARCH_SOURCE_ASSETS, dontSearchInSourceAssets ); EditorPrefs.SetBool( PREFS_SEARCH_PROJECT_SETTINGS, searchInProjectSettings ); EditorPrefs.SetInt( PREFS_SEARCH_DEPTH_LIMIT, searchDepthLimit ); EditorPrefs.SetInt( PREFS_SEARCH_FIELDS, (int) fieldModifiers ); EditorPrefs.SetInt( PREFS_SEARCH_PROPERTIES, (int) propertyModifiers ); EditorPrefs.SetInt( PREFS_PATH_DRAWING_MODE, (int) searchResultDrawParameters.pathDrawingMode ); EditorPrefs.SetBool( PREFS_SEARCH_NON_SERIALIZABLES, searchNonSerializableVariables ); EditorPrefs.SetBool( PREFS_LAZY_SCENE_SEARCH, lazySceneSearch ); EditorPrefs.SetBool( PREFS_SHOW_TOOLTIPS, searchResultDrawParameters.showTooltips ); EditorPrefs.SetBool( PREFS_SHOW_PROGRESS, showDetailedProgressBar ); } private void LoadPrefs() { ParseSceneSearchMode( (SceneSearchMode) EditorPrefs.GetInt( PREFS_SEARCH_SCENES, (int) ( SceneSearchMode.OpenScenes | SceneSearchMode.ScenesInBuildSettingsTickedOnly | SceneSearchMode.AllScenes ) ) ); searchInAssetsFolder = EditorPrefs.GetBool( PREFS_SEARCH_ASSETS, true ); dontSearchInSourceAssets = EditorPrefs.GetBool( PREFS_DONT_SEARCH_SOURCE_ASSETS, true ); searchInProjectSettings = EditorPrefs.GetBool( PREFS_SEARCH_PROJECT_SETTINGS, true ); searchDepthLimit = EditorPrefs.GetInt( PREFS_SEARCH_DEPTH_LIMIT, 4 ); // Fetch public, protected and private non-static fields and properties from objects by default fieldModifiers = (BindingFlags) EditorPrefs.GetInt( PREFS_SEARCH_FIELDS, (int) ( BindingFlags.Public | BindingFlags.NonPublic ) ); propertyModifiers = (BindingFlags) EditorPrefs.GetInt( PREFS_SEARCH_PROPERTIES, (int) ( BindingFlags.Public | BindingFlags.NonPublic ) ); try { searchResultDrawParameters.pathDrawingMode = (PathDrawingMode) EditorPrefs.GetInt( PREFS_PATH_DRAWING_MODE, (int) PathDrawingMode.ShortRelevantParts ); } catch { searchResultDrawParameters.pathDrawingMode = PathDrawingMode.ShortRelevantParts; } searchNonSerializableVariables = EditorPrefs.GetBool( PREFS_SEARCH_NON_SERIALIZABLES, true ); lazySceneSearch = EditorPrefs.GetBool( PREFS_LAZY_SCENE_SEARCH, false ); searchResultDrawParameters.showTooltips = EditorPrefs.GetBool( PREFS_SHOW_TOOLTIPS, false ); showDetailedProgressBar = EditorPrefs.GetBool( PREFS_SHOW_PROGRESS, false ); } private SceneSearchMode GetSceneSearchMode( bool hideOptionsInPlayMode ) { SceneSearchMode sceneSearchMode = SceneSearchMode.None; if( searchInOpenScenes ) sceneSearchMode |= SceneSearchMode.OpenScenes; if( !hideOptionsInPlayMode || !EditorApplication.isPlaying ) { if( searchInScenesInBuild ) sceneSearchMode |= searchInScenesInBuildTickedOnly ? SceneSearchMode.ScenesInBuildSettingsTickedOnly : SceneSearchMode.ScenesInBuildSettingsAll; if( searchInAllScenes ) sceneSearchMode |= SceneSearchMode.AllScenes; } return sceneSearchMode; } private void ParseSceneSearchMode( SceneSearchMode sceneSearchMode ) { searchInOpenScenes = ( sceneSearchMode & SceneSearchMode.OpenScenes ) == SceneSearchMode.OpenScenes; searchInScenesInBuild = ( sceneSearchMode & SceneSearchMode.ScenesInBuildSettingsAll ) == SceneSearchMode.ScenesInBuildSettingsAll || ( sceneSearchMode & SceneSearchMode.ScenesInBuildSettingsTickedOnly ) == SceneSearchMode.ScenesInBuildSettingsTickedOnly; searchInScenesInBuildTickedOnly = ( sceneSearchMode & SceneSearchMode.ScenesInBuildSettingsAll ) != SceneSearchMode.ScenesInBuildSettingsAll; searchInAllScenes = ( sceneSearchMode & SceneSearchMode.AllScenes ) == SceneSearchMode.AllScenes; } private void Update() { // Refresh the window at a regular interval in play mode to update the tooltip if( EditorApplication.isPlaying && currentPhase == Phase.Complete && searchResultDrawParameters.showTooltips && EditorApplication.timeSinceStartup >= nextPlayModeRefreshTime ) { nextPlayModeRefreshTime = EditorApplication.timeSinceStartup + PLAY_MODE_REFRESH_INTERVAL; ; Repaint(); } if( shouldRepositionSelf ) { shouldRepositionSelf = false; position = windowTargetPosition; } } private void OnGUI() { // Make the window scrollable scrollPosition = EditorGUILayout.BeginScrollView( scrollPosition, Utilities.GL_EXPAND_WIDTH, Utilities.GL_EXPAND_HEIGHT ); GUILayout.BeginVertical(); GUILayout.Space( 10 ); // Show the error message, if it is not empty if( errorMessage.Length > 0 ) EditorGUILayout.HelpBox( errorMessage, MessageType.Error ); GUILayout.Space( 10 ); if( currentPhase == Phase.Processing ) { // If we are stuck at this phase, then we have encountered an exception GUILayout.Label( ". . . Search in progress or something went wrong (check console) . . ." ); restoreInitialSceneSetup = EditorGUILayout.ToggleLeft( restoreInitialSceneSetupLabel, restoreInitialSceneSetup ); if( GUILayout.Button( "RETURN", Utilities.GL_HEIGHT_30 ) ) ReturnToSetupPhase( restoreInitialSceneSetup, false ); } else if( currentPhase == Phase.Setup ) { if( objectsToSearchDrawer.Draw( objectsToSearch ) ) errorMessage = string.Empty; GUILayout.Space( 10 ); Color c = GUI.backgroundColor; GUI.backgroundColor = Color.cyan; GUILayout.Box( "SEARCH IN", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH ); GUI.backgroundColor = c; searchInAssetsFolder = EditorGUILayout.ToggleLeft( "Project window (Assets folder)", searchInAssetsFolder ); if( searchInAssetsFolder ) { GUILayout.BeginHorizontal(); GUILayout.Space( 35 ); GUILayout.BeginVertical(); searchInAssetsSubsetDrawer.Draw( searchInAssetsSubset ); excludedAssetsDrawer.Draw( excludedAssets ); dontSearchInSourceAssets = EditorGUILayout.ToggleLeft( "Don't search \"Find references of\" themselves for references", dontSearchInSourceAssets ); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } Utilities.DrawSeparatorLine(); if( searchInAllScenes && !EditorApplication.isPlaying ) GUI.enabled = false; searchInOpenScenes = EditorGUILayout.ToggleLeft( "Currently open (loaded) scene(s)", searchInOpenScenes ); if( !EditorApplication.isPlaying ) { searchInScenesInBuild = EditorGUILayout.ToggleLeft( "Scenes in Build Settings", searchInScenesInBuild ); if( searchInScenesInBuild ) { GUILayout.BeginHorizontal(); GUILayout.Space( 35 ); searchInScenesInBuildTickedOnly = EditorGUILayout.ToggleLeft( "Ticked only", searchInScenesInBuildTickedOnly, Utilities.GL_WIDTH_100 ); searchInScenesInBuildTickedOnly = !EditorGUILayout.ToggleLeft( "All", !searchInScenesInBuildTickedOnly, Utilities.GL_WIDTH_100 ); GUILayout.EndHorizontal(); } GUI.enabled = true; searchInAllScenes = EditorGUILayout.ToggleLeft( "All scenes in the project", searchInAllScenes ); } GUILayout.BeginHorizontal(); GUILayout.Space( 35 ); GUILayout.BeginVertical(); excludedScenesDrawer.Draw( excludedScenes ); GUILayout.EndVertical(); GUILayout.EndHorizontal(); Utilities.DrawSeparatorLine(); searchInProjectSettings = EditorGUILayout.ToggleLeft( "Project Settings (Player Settings, Graphics Settings etc.)", searchInProjectSettings ); GUILayout.Space( 10 ); //GUILayout.Box( "SEARCH SETTINGS", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH ); //GUILayout.BeginHorizontal(); //GUILayout.Label( new GUIContent( "> Search depth: " + searchDepthLimit, "Depth limit for recursively searching variables of objects" ), Utilities.GL_WIDTH_250 ); //searchDepthLimit = (int) GUILayout.HorizontalSlider( searchDepthLimit, 0, 4 ); //GUILayout.EndHorizontal(); //GUILayout.Label( "> Search variables:" ); //GUILayout.BeginHorizontal(); //GUILayout.Space( 35 ); //if( EditorGUILayout.ToggleLeft( "Public", ( fieldModifiers & BindingFlags.Public ) == BindingFlags.Public, Utilities.GL_WIDTH_100 ) ) // fieldModifiers |= BindingFlags.Public; //else // fieldModifiers &= ~BindingFlags.Public; //if( EditorGUILayout.ToggleLeft( "Non-public", ( fieldModifiers & BindingFlags.NonPublic ) == BindingFlags.NonPublic, Utilities.GL_WIDTH_100 ) ) // fieldModifiers |= BindingFlags.NonPublic; //else // fieldModifiers &= ~BindingFlags.NonPublic; //GUILayout.EndHorizontal(); //GUILayout.Label( "> Search properties:" ); //GUILayout.BeginHorizontal(); //GUILayout.Space( 35 ); //if( EditorGUILayout.ToggleLeft( "Public", ( propertyModifiers & BindingFlags.Public ) == BindingFlags.Public, Utilities.GL_WIDTH_100 ) ) // propertyModifiers |= BindingFlags.Public; //else // propertyModifiers &= ~BindingFlags.Public; //if( EditorGUILayout.ToggleLeft( "Non-public", ( propertyModifiers & BindingFlags.NonPublic ) == BindingFlags.NonPublic, Utilities.GL_WIDTH_100 ) ) // propertyModifiers |= BindingFlags.NonPublic; //else // propertyModifiers &= ~BindingFlags.NonPublic; //GUILayout.EndHorizontal(); //GUILayout.Space( 10 ); //searchNonSerializableVariables = EditorGUILayout.ToggleLeft( "Search non-serializable fields and properties", searchNonSerializableVariables ); //GUILayout.Space( 10 ); GUI.backgroundColor = Color.cyan; GUILayout.Box( "SETTINGS", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH ); GUI.backgroundColor = c; lazySceneSearch = EditorGUILayout.ToggleLeft( "Lazy scene search: scenes are searched in detail only when they are manually refreshed (faster search)", lazySceneSearch ); noAssetDatabaseChanges = EditorGUILayout.ToggleLeft( "I haven't modified any assets/scenes since the last search (faster search)", noAssetDatabaseChanges ); showDetailedProgressBar = EditorGUILayout.ToggleLeft( "Show detailed progress bar (slower search)", showDetailedProgressBar ); GUILayout.Space( 10 ); // Don't let the user press the GO button without any valid search location if( !searchInAllScenes && !searchInOpenScenes && !searchInScenesInBuild && !searchInAssetsFolder && !searchInProjectSettings ) GUI.enabled = false; if( GUILayout.Button( "GO!", Utilities.GL_HEIGHT_30 ) ) { InitiateSearch(); GUIUtility.ExitGUI(); } } else if( currentPhase == Phase.Complete ) { // Draw the results of the search GUI.enabled = false; objectsToSearchDrawer.Draw( objectsToSearch ); GUILayout.Space( 10 ); GUI.enabled = true; restoreInitialSceneSetup = EditorGUILayout.ToggleLeft( restoreInitialSceneSetupLabel, restoreInitialSceneSetup ); if( GUILayout.Button( "Reset Search", Utilities.GL_HEIGHT_30 ) ) ReturnToSetupPhase( restoreInitialSceneSetup, false ); if( searchResult == null ) { EditorGUILayout.HelpBox( "ERROR: searchResult is null", MessageType.Error ); return; } else if( !searchResult.SearchCompletedSuccessfully ) EditorGUILayout.HelpBox( "ERROR: search was interrupted, check the logs for more info", MessageType.Error ); Color c = GUI.backgroundColor; GUI.backgroundColor = Color.green; GUILayout.Box( "Don't forget to save scene(s) if you made any changes!", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH ); GUI.backgroundColor = c; if( searchResult.NumberOfGroups == 0 ) { GUILayout.Space( 10 ); GUILayout.Box( "No references found...", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH ); } else { //GUILayout.Space( 10 ); //GUILayout.BeginHorizontal(); //// Select all the references after filtering them (select only the GameObject's) //if( GUILayout.Button( "Select All\n(GameObject-wise)", Utilities.GL_HEIGHT_35 ) ) //{ // GameObject[] objects = searchResult.SelectAllAsGameObjects(); // if( objects != null && objects.Length > 0 ) // Selection.objects = objects; //} //// Select all the references without filtering them //if( GUILayout.Button( "Select All\n(Object-wise)", Utilities.GL_HEIGHT_35 ) ) //{ // Object[] objects = searchResult.SelectAllAsObjects(); // if( objects != null && objects.Length > 0 ) // Selection.objects = objects; //} //GUILayout.EndHorizontal(); //GUILayout.Space( 10 ); searchResultDrawParameters.showTooltips = EditorGUILayout.ToggleLeft( showTooltipsLabel, searchResultDrawParameters.showTooltips ); noAssetDatabaseChanges = EditorGUILayout.ToggleLeft( "I haven't modified any assets/scenes since the last search (faster Refresh)", noAssetDatabaseChanges ); searchResultDrawParameters.noAssetDatabaseChanges = noAssetDatabaseChanges; Utilities.DrawSeparatorLine(); GUILayout.Label( "Path drawing mode:" ); GUILayout.BeginHorizontal(); GUILayout.Space( 35 ); if( EditorGUILayout.ToggleLeft( "Full: Draw the complete paths to the references", searchResultDrawParameters.pathDrawingMode == PathDrawingMode.Full ) ) searchResultDrawParameters.pathDrawingMode = PathDrawingMode.Full; GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Space( 35 ); if( EditorGUILayout.ToggleLeft( "Shorter: Draw only the most relevant parts (that start with a UnityEngine.Object) of the complete paths", searchResultDrawParameters.pathDrawingMode == PathDrawingMode.ShortRelevantParts ) ) searchResultDrawParameters.pathDrawingMode = PathDrawingMode.ShortRelevantParts; GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Space( 35 ); if( EditorGUILayout.ToggleLeft( "Shortest: Draw only the last two nodes of the complete paths", searchResultDrawParameters.pathDrawingMode == PathDrawingMode.Shortest ) ) searchResultDrawParameters.pathDrawingMode = PathDrawingMode.Shortest; GUILayout.EndHorizontal(); searchResult.DrawOnGUI( searchResultDrawParameters ); } } GUILayout.Space( 10 ); GUILayout.EndVertical(); EditorGUILayout.EndScrollView(); } private void InitiateSearch() { if( objectsToSearch.IsEmpty() ) errorMessage = "ADD AN ASSET TO THE LIST FIRST!"; else if( !EditorApplication.isPlaying && !Utilities.AreScenesSaved() ) { // Don't start the search if at least one scene is currently dirty (not saved) errorMessage = "SAVE OPEN SCENES FIRST!"; } else { errorMessage = string.Empty; currentPhase = Phase.Processing; SavePrefs(); #if UNITY_2018_3_OR_NEWER ReplacePrefabStageObjectsWithAssets( PrefabStageUtility.GetCurrentPrefabStage() ); #endif // Start searching searchResult = core.Run( new AssetUsageDetector.Parameters() { objectsToSearch = new ObjectToSearchEnumerator( objectsToSearch ).ToArray(), searchInScenes = GetSceneSearchMode( true ), searchInAssetsFolder = searchInAssetsFolder, searchInAssetsSubset = !searchInAssetsSubset.IsEmpty() ? searchInAssetsSubset.ToArray() : null, excludedAssetsFromSearch = !excludedAssets.IsEmpty() ? excludedAssets.ToArray() : null, dontSearchInSourceAssets = dontSearchInSourceAssets, excludedScenesFromSearch = !excludedScenes.IsEmpty() ? excludedScenes.ToArray() : null, searchInProjectSettings = searchInProjectSettings, //fieldModifiers = fieldModifiers, //propertyModifiers = propertyModifiers, //searchDepthLimit = searchDepthLimit, //searchNonSerializableVariables = searchNonSerializableVariables, lazySceneSearch = lazySceneSearch, noAssetDatabaseChanges = noAssetDatabaseChanges, showDetailedProgressBar = showDetailedProgressBar } ); currentPhase = Phase.Complete; } } #if UNITY_2018_3_OR_NEWER // Try replacing searched objects who are part of currently open prefab stage with their corresponding prefab assets public void ReplacePrefabStageObjectsWithAssets( PrefabStage prefabStage ) { if( prefabStage == null || !prefabStage.stageHandle.IsValid() ) return; #if UNITY_2020_1_OR_NEWER GameObject prefabAsset = AssetDatabase.LoadAssetAtPath( prefabStage.assetPath ); #else GameObject prefabAsset = AssetDatabase.LoadAssetAtPath( prefabStage.prefabAssetPath ); #endif if( prefabAsset == null || prefabAsset.Equals( null ) ) return; for( int i = 0; i < objectsToSearch.Count; i++ ) { Object obj = objectsToSearch[i].obj; if( obj != null && !obj.Equals( null ) && obj is GameObject && prefabStage.IsPartOfPrefabContents( (GameObject) obj ) ) { GameObject prefabStageObjectSource = ( (GameObject) obj ).FollowSymmetricHierarchy( prefabStage.prefabContentsRoot, prefabAsset ); if( prefabStageObjectSource != null ) objectsToSearch[i].obj = prefabStageObjectSource; List subAssets = objectsToSearch[i].subAssets; for( int j = 0; j < subAssets.Count; j++ ) { obj = subAssets[j].subAsset; if( obj != null && !obj.Equals( null ) && obj is GameObject && prefabStage.IsPartOfPrefabContents( (GameObject) obj ) ) { prefabStageObjectSource = ( (GameObject) obj ).FollowSymmetricHierarchy( prefabStage.prefabContentsRoot, prefabAsset ); if( prefabStageObjectSource != null ) subAssets[j].subAsset = prefabStageObjectSource; } } } } } #endif private bool ReturnToSetupPhase( bool restoreInitialSceneSetup, bool sceneSetupRestorationIsOptional ) { if( searchResult != null && restoreInitialSceneSetup && !EditorApplication.isPlaying && !searchResult.RestoreInitialSceneSetup( sceneSetupRestorationIsOptional ) ) return false; searchResult = null; errorMessage = string.Empty; currentPhase = Phase.Setup; return true; } } }