// Search a scene for references private void SearchScene( string scenePath, List<SearchResultGroup> searchResult, SceneSetup[] initialSceneSetup ) { Scene scene = EditorSceneManager.GetSceneByPath( scenePath ); if( EditorApplication.isPlaying && !scene.isLoaded ) return; bool canContainSceneObjectReference = scene.isLoaded && ( !EditorSceneManager.preventCrossSceneReferences || sceneObjectsToSearchScenesSet.Contains( scenePath ) ); if( !canContainSceneObjectReference ) { bool canContainAssetReference = assetsToSearchSet.Count > 0 && ( EditorApplication.isPlaying || AssetHasAnyReference( scenePath ) ); if( !canContainAssetReference ) return; } if( !EditorApplication.isPlaying ) scene = EditorSceneManager.OpenScene( scenePath, OpenSceneMode.Additive ); currentSearchResultGroup = new SearchResultGroup( scenePath, true ); // Search through all the GameObjects in the scene GameObject[] rootGameObjects = scene.GetRootGameObjects(); for( int i = 0; i < rootGameObjects.Length; i++ ) SearchGameObjectRecursively( rootGameObjects[i] ); // If no references are found in the scene and if the scene is not part of the initial scene setup, close it if( currentSearchResultGroup.NumberOfReferences == 0 ) { if( !EditorApplication.isPlaying ) { bool sceneIsOneOfInitials = false; for( int i = 0; i < initialSceneSetup.Length; i++ ) { if( initialSceneSetup[i].path == scenePath ) { if( !initialSceneSetup[i].isLoaded ) EditorSceneManager.CloseScene( scene, false ); sceneIsOneOfInitials = true; break; } } if( !sceneIsOneOfInitials ) EditorSceneManager.CloseScene( scene, true ); } } else { // Some references are found in this scene, save the results searchResult.Add( currentSearchResultGroup ); } }
private string CachePath { get { return Application.dataPath + "/../Library/AssetUsageDetector.cache"; } } // Path of the cache file // Search for references! public SearchResult Run( Parameters searchParameters ) { if( searchParameters.objectsToSearch == null ) { Debug.LogError( "objectsToSearch list is empty" ); return new SearchResult( false, null, null ); } List<SearchResultGroup> searchResult = null; // Get the scenes that are open right now SceneSetup[] initialSceneSetup = !EditorApplication.isPlaying ? EditorSceneManager.GetSceneManagerSetup() : null; // Make sure the AssetDatabase is up-to-date AssetDatabase.SaveAssets(); try { currentDepth = 0; searchedObjectsCount = 0; searchStartTime = EditorApplication.timeSinceStartup; this.fieldModifiers = searchParameters.fieldModifiers | BindingFlags.Instance | BindingFlags.DeclaredOnly; this.propertyModifiers = searchParameters.propertyModifiers | BindingFlags.Instance | BindingFlags.DeclaredOnly; this.searchDepthLimit = searchParameters.searchDepthLimit; // Initialize commonly used variables searchResult = new List<SearchResultGroup>(); // Overall search results if( typeToVariables == null ) typeToVariables = new Dictionary<Type, VariableGetterHolder[]>( 4096 ); else if( prevFieldModifiers != fieldModifiers || prevPropertyModifiers != propertyModifiers ) typeToVariables.Clear(); if( searchedObjects == null ) searchedObjects = new Dictionary<string, ReferenceNode>( 32768 ); else searchedObjects.Clear(); if( callStack == null ) callStack = new List<object>( 64 ); else callStack.Clear(); if( objectsToSearchSet == null ) objectsToSearchSet = new HashSet<Object>(); else objectsToSearchSet.Clear(); if( sceneObjectsToSearchSet == null ) sceneObjectsToSearchSet = new HashSet<Object>(); else sceneObjectsToSearchSet.Clear(); if( sceneObjectsToSearchScenesSet == null ) sceneObjectsToSearchScenesSet = new HashSet<string>(); else sceneObjectsToSearchScenesSet.Clear(); if( assetsToSearchSet == null ) assetsToSearchSet = new HashSet<Object>(); else assetsToSearchSet.Clear(); if( assetsToSearchPathsSet == null ) assetsToSearchPathsSet = new HashSet<string>(); else assetsToSearchPathsSet.Clear(); if( assetDependencyCache == null ) { LoadCache(); searchStartTime = EditorApplication.timeSinceStartup; } else if( !searchParameters.noAssetDatabaseChanges ) { foreach( var cacheEntry in assetDependencyCache.Values ) cacheEntry.verified = false; } foreach( var cacheEntry in assetDependencyCache.Values ) cacheEntry.searchResult = CacheEntry.Result.Unknown; if( typeToSearchFunction == null ) { typeToSearchFunction = new Dictionary<Type, Func<Object, ReferenceNode>>() { { typeof( GameObject ), SearchGameObject }, { typeof( Material ), SearchMaterial }, { typeof( RuntimeAnimatorController ), SearchAnimatorController }, { typeof( AnimatorOverrideController ), SearchAnimatorController }, { typeof( AnimatorController ), SearchAnimatorController }, { typeof( AnimatorStateMachine ), SearchAnimatorStateMachine }, { typeof( AnimatorState ), SearchAnimatorState }, { typeof( AnimatorStateTransition ), SearchAnimatorStateTransition }, { typeof( BlendTree ), SearchBlendTree }, { typeof( AnimationClip ), SearchAnimationClip }, #if UNITY_2017_1_OR_NEWER { typeof( SpriteAtlas ), SearchSpriteAtlas }, #endif }; } prevFieldModifiers = fieldModifiers; prevPropertyModifiers = propertyModifiers; searchPrefabConnections = false; searchMonoBehavioursForScript = false; searchRenderers = false; searchMaterialsForShader = false; searchMaterialsForTexture = false; // Store the searched objects(s) in HashSets HashSet<string> folderContentsSet = new HashSet<string>(); foreach( Object obj in searchParameters.objectsToSearch ) { if( obj == null || obj.Equals( null ) ) continue; if( obj.IsFolder() ) folderContentsSet.UnionWith( Utilities.EnumerateFolderContents( obj ) ); else AddSearchedObjectToFilteredSets( obj ); } foreach( string filePath in folderContentsSet ) { // Skip scene assets if( filePath.EndsWith( ".unity" ) ) continue; Object[] assets = AssetDatabase.LoadAllAssetsAtPath( filePath ); if( assets == null || assets.Length == 0 ) continue; for( int i = 0; i < assets.Length; i++ ) AddSearchedObjectToFilteredSets( assets[i] ); } foreach( Object obj in objectsToSearchSet ) { if( obj is Texture ) { searchRenderers = true; searchMaterialsForTexture = true; } else if( obj is Material ) { searchRenderers = true; } else if( obj is MonoScript ) { searchMonoBehavioursForScript = true; } else if( obj is Shader ) { searchRenderers = true; searchMaterialsForShader = true; } else if( obj is GameObject ) { searchPrefabConnections = true; } } // Find the scenes to search for references HashSet<string> openScenes = null; if( EditorApplication.isPlaying ) { // In Play mode, only open scenes can be searched openScenes = new HashSet<string>(); for( int i = 0; i < EditorSceneManager.loadedSceneCount; i++ ) { Scene scene = EditorSceneManager.GetSceneAt( i ); if( scene.IsValid() ) openScenes.Add( scene.path ); } } HashSet<string> scenesToSearch = new HashSet<string>(); if( ( searchParameters.searchInScenes & SceneSearchMode.AllScenes ) == SceneSearchMode.AllScenes ) { // Get all scenes from the Assets folder string[] sceneGuids = AssetDatabase.FindAssets( "t:SceneAsset" ); for( int i = 0; i < sceneGuids.Length; i++ ) { string scenePath = AssetDatabase.GUIDToAssetPath( sceneGuids[i] ); if( !EditorApplication.isPlaying || openScenes.Contains( scenePath ) ) scenesToSearch.Add( scenePath ); } } else { if( ( searchParameters.searchInScenes & SceneSearchMode.OpenScenes ) == SceneSearchMode.OpenScenes ) { // Get all open (and loaded) scenes for( int i = 0; i < EditorSceneManager.loadedSceneCount; i++ ) { Scene scene = EditorSceneManager.GetSceneAt( i ); if( scene.IsValid() ) scenesToSearch.Add( scene.path ); } } bool searchInScenesInBuildTickedAll = ( searchParameters.searchInScenes & SceneSearchMode.ScenesInBuildSettingsAll ) == SceneSearchMode.ScenesInBuildSettingsAll; if( searchInScenesInBuildTickedAll || ( searchParameters.searchInScenes & SceneSearchMode.ScenesInBuildSettingsTickedOnly ) == SceneSearchMode.ScenesInBuildSettingsTickedOnly ) { // Get all scenes in build settings EditorBuildSettingsScene[] scenesTemp = EditorBuildSettings.scenes; for( int i = 0; i < scenesTemp.Length; i++ ) { if( ( searchInScenesInBuildTickedAll || scenesTemp[i].enabled ) && ( !EditorApplication.isPlaying || openScenes.Contains( scenesTemp[i].path ) ) ) scenesToSearch.Add( scenesTemp[i].path ); } } } // By default, search only serializable variables for references searchSerializableVariablesOnly = !searchParameters.searchNonSerializableVariables; // Initialize the nodes of searched asset(s) foreach( Object obj in objectsToSearchSet ) { BeginSearchObject( obj ); string objHash = obj.Hash(); ReferenceNode referenceNode; if( !searchedObjects.TryGetValue( objHash, out referenceNode ) || referenceNode == null ) searchedObjects[objHash] = PopReferenceNode( obj ); } // Progressbar values int searchProgress = 0; int searchTotalProgress = scenesToSearch.Count; if( EditorApplication.isPlaying ) searchTotalProgress++; // DontDestroyOnLoad scene // Don't search assets if searched object(s) are all scene objects as assets can't hold references to scene objects if( searchParameters.searchInAssetsFolder && assetsToSearchSet.Count > 0 ) { currentSearchResultGroup = new SearchResultGroup( "Project View (Assets)", false ); // Get the paths of all assets that are to be searched IEnumerable<string> assetPaths; if( searchParameters.searchInAssetsSubset == null ) { string[] allAssetPaths = AssetDatabase.GetAllAssetPaths(); searchTotalProgress += allAssetPaths.Length; assetPaths = allAssetPaths; } else { folderContentsSet.Clear(); foreach( Object obj in searchParameters.searchInAssetsSubset ) { if( obj == null || obj.Equals( null ) ) continue; if( !obj.IsAsset() ) continue; if( obj.IsFolder() ) folderContentsSet.UnionWith( Utilities.EnumerateFolderContents( obj ) ); else folderContentsSet.Add( AssetDatabase.GetAssetPath( obj ) ); } searchTotalProgress += folderContentsSet.Count; assetPaths = folderContentsSet; } // Calculate the path(s) of the assets that won't be searched for references HashSet<string> excludedAssetsPathsSet = new HashSet<string>(); if( searchParameters.excludedAssetsFromSearch != null ) { foreach( Object obj in searchParameters.excludedAssetsFromSearch ) { if( obj == null || obj.Equals( null ) ) continue; if( !obj.IsAsset() ) continue; if( obj.IsFolder() ) excludedAssetsPathsSet.UnionWith( Utilities.EnumerateFolderContents( obj ) ); else excludedAssetsPathsSet.Add( AssetDatabase.GetAssetPath( obj ) ); } } if( searchParameters.dontSearchInSourceAssets ) excludedAssetsPathsSet.UnionWith( assetsToSearchPathsSet ); foreach( string path in assetPaths ) { if( searchParameters.showProgressBar && ++searchProgress % 30 == 1 && EditorUtility.DisplayCancelableProgressBar( "Please wait...", "Searching assets", (float) searchProgress / searchTotalProgress ) ) throw new Exception( "Search aborted" ); if( excludedAssetsPathsSet.Contains( path ) ) continue; // If asset resides inside the Assets directory and is not a scene asset if( path.StartsWith( "Assets/" ) && !path.EndsWith( ".unity" ) ) { if( !AssetHasAnyReference( path ) ) continue; Object[] assets = AssetDatabase.LoadAllAssetsAtPath( path ); if( assets == null || assets.Length == 0 ) continue; for( int i = 0; i < assets.Length; i++ ) { // Components are already searched while searching the GameObject if( assets[i] is Component ) continue; BeginSearchObject( assets[i] ); } } } // If a reference is found in the Project view, save the results if( currentSearchResultGroup.NumberOfReferences > 0 ) searchResult.Add( currentSearchResultGroup ); } // Search non-serializable variables for references only if we are currently searching a scene and the editor is in play mode if( EditorApplication.isPlaying ) searchSerializableVariablesOnly = false; if( scenesToSearch.Count > 0 ) { // Calculate the path(s) of the scenes that won't be searched for references HashSet<string> excludedScenesPathsSet = new HashSet<string>(); if( searchParameters.excludedScenesFromSearch != null ) { foreach( Object obj in searchParameters.excludedScenesFromSearch ) { if( obj == null || obj.Equals( null ) ) continue; if( !obj.IsAsset() ) continue; if( obj.IsFolder() ) { string[] folderContents = AssetDatabase.FindAssets( "t:SceneAsset", new string[] { AssetDatabase.GetAssetPath( obj ) } ); if( folderContents == null ) continue; for( int i = 0; i < folderContents.Length; i++ ) excludedScenesPathsSet.Add( AssetDatabase.GUIDToAssetPath( folderContents[i] ) ); } else if( obj is SceneAsset ) excludedScenesPathsSet.Add( AssetDatabase.GetAssetPath( obj ) ); } } foreach( string scenePath in scenesToSearch ) { if( searchParameters.showProgressBar && EditorUtility.DisplayCancelableProgressBar( "Please wait...", "Searching scene: " + scenePath, (float) ++searchProgress / searchTotalProgress ) ) throw new Exception( "Search aborted" ); // Search scene for references if( string.IsNullOrEmpty( scenePath ) ) continue; if( excludedScenesPathsSet.Contains( scenePath ) ) continue; SearchScene( scenePath, searchResult, initialSceneSetup ); } } // Search through all the GameObjects under the DontDestroyOnLoad scene (if exists) if( EditorApplication.isPlaying ) { if( searchParameters.showProgressBar && EditorUtility.DisplayCancelableProgressBar( "Please wait...", "Searching scene: DontDestroyOnLoad", 1f ) ) throw new Exception( "Search aborted" ); currentSearchResultGroup = new SearchResultGroup( "DontDestroyOnLoad", false ); GameObject[] rootGameObjects = GetDontDestroyOnLoadObjects(); for( int i = 0; i < rootGameObjects.Length; i++ ) SearchGameObjectRecursively( rootGameObjects[i] ); if( currentSearchResultGroup.NumberOfReferences > 0 ) searchResult.Add( currentSearchResultGroup ); } InitializeSearchResultNodes( searchResult ); // Log some c00l stuff to console Debug.Log( "Searched " + searchedObjectsCount + " objects in " + ( EditorApplication.timeSinceStartup - searchStartTime ).ToString( "F2" ) + " seconds" ); return new SearchResult( true, searchResult, initialSceneSetup ); } catch( Exception e ) { Debug.LogException( e ); try { InitializeSearchResultNodes( searchResult ); } catch { } return new SearchResult( false, searchResult, initialSceneSetup ); } finally { currentSearchResultGroup = null; currentObject = null; EditorUtility.ClearProgressBar(); } }
public void RefreshSearchResultGroup(SearchResultGroup searchResultGroup, bool noAssetDatabaseChanges) { if (searchResultGroup == null) { Debug.LogError("SearchResultGroup is null!"); return; } int searchResultGroupIndex = -1; for (int i = 0; i < result.Count; i++) { if (result[i] == searchResultGroup) { searchResultGroupIndex = i; break; } } if (searchResultGroupIndex < 0) { Debug.LogError("SearchResultGroup is not a part of SearchResult!"); return; } if (searchResultGroup.Type == SearchResultGroup.GroupType.Scene && EditorApplication.isPlaying && !EditorSceneManager.GetSceneByPath(searchResultGroup.Title).isLoaded) { Debug.LogError("Can't search unloaded scene while in Play Mode!"); return; } if (searchHandler == null) { searchHandler = new AssetUsageDetector(); } SceneSearchMode searchInScenes = m_searchParameters.searchInScenes; Object[] searchInScenesSubset = m_searchParameters.searchInScenesSubset; bool searchInAssetsFolder = m_searchParameters.searchInAssetsFolder; Object[] searchInAssetsSubset = m_searchParameters.searchInAssetsSubset; try { if (searchResultGroup.Type == SearchResultGroup.GroupType.Assets) { m_searchParameters.searchInScenes = SceneSearchMode.None; m_searchParameters.searchInScenesSubset = null; } else if (searchResultGroup.Type == SearchResultGroup.GroupType.Scene) { m_searchParameters.searchInScenes = SceneSearchMode.None; m_searchParameters.searchInScenesSubset = new Object[1] { AssetDatabase.LoadAssetAtPath <SceneAsset>(searchResultGroup.Title) }; m_searchParameters.searchInAssetsFolder = false; m_searchParameters.searchInAssetsSubset = null; } else { m_searchParameters.searchInScenes = (SceneSearchMode)1024; // A unique value to search only the DontDestroyOnLoad scene m_searchParameters.searchInScenesSubset = null; m_searchParameters.searchInAssetsFolder = false; m_searchParameters.searchInAssetsSubset = null; } m_searchParameters.lazySceneSearch = false; m_searchParameters.noAssetDatabaseChanges = noAssetDatabaseChanges; // Make sure the AssetDatabase is up-to-date AssetDatabase.SaveAssets(); List <SearchResultGroup> searchResult = searchHandler.Run(m_searchParameters).result; if (searchResult != null) { int newSearchResultGroupIndex = -1; for (int i = 0; i < searchResult.Count; i++) { if (searchResultGroup.Title == searchResult[i].Title) { newSearchResultGroupIndex = i; break; } } if (newSearchResultGroupIndex >= 0) { result[searchResultGroupIndex] = searchResult[newSearchResultGroupIndex]; } else { searchResultGroup.Clear(); } } } finally { m_searchParameters.searchInScenes = searchInScenes; m_searchParameters.searchInScenesSubset = searchInScenesSubset; m_searchParameters.searchInAssetsFolder = searchInAssetsFolder; m_searchParameters.searchInAssetsSubset = searchInAssetsSubset; } }