Restructured for new direction.
This commit is contained in:
9
Assets/Plugins/AssetUsageDetector/Editor.meta
Normal file
9
Assets/Plugins/AssetUsageDetector/Editor.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19f677a9eb83d3942af6d4c5fa8dbeee
|
||||
folderAsset: yes
|
||||
timeCreated: 1520032274
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "AssetUsageDetector.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"Unity.Addressables",
|
||||
"Unity.Addressables.Editor"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.unity.addressables",
|
||||
"expression": "0.0.0",
|
||||
"define": "ASSET_USAGE_ADDRESSABLES"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.visualeffectgraph",
|
||||
"expression": "0.0.0",
|
||||
"define": "ASSET_USAGE_VFX_GRAPH"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8579ab42c9ab63d4bac5fb07bd390b46
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/AssetUsageDetector.Editor.asmdef
|
||||
uploadId: 894428
|
||||
1359
Assets/Plugins/AssetUsageDetector/Editor/AssetUsageDetector.cs
Normal file
1359
Assets/Plugins/AssetUsageDetector/Editor/AssetUsageDetector.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c0dea52dcdb16e4e9b13f8dacc1590f
|
||||
timeCreated: 1520032279
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/AssetUsageDetector.cs
|
||||
uploadId: 894428
|
||||
@@ -0,0 +1,207 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace AssetUsageDetectorNamespace
|
||||
{
|
||||
public partial class AssetUsageDetector
|
||||
{
|
||||
#region Helper Classes
|
||||
private class CacheEntry
|
||||
{
|
||||
public enum Result { Unknown = 0, No = 1, Yes = 2 };
|
||||
|
||||
public string hash;
|
||||
public string[] dependencies;
|
||||
public long[] fileSizes;
|
||||
|
||||
public bool verified;
|
||||
public Result searchResult;
|
||||
|
||||
public CacheEntry( string path )
|
||||
{
|
||||
Verify( path );
|
||||
}
|
||||
|
||||
public CacheEntry( string hash, string[] dependencies, long[] fileSizes )
|
||||
{
|
||||
this.hash = hash;
|
||||
this.dependencies = dependencies;
|
||||
this.fileSizes = fileSizes;
|
||||
}
|
||||
|
||||
public void Verify( string path )
|
||||
{
|
||||
string hash = AssetDatabase.GetAssetDependencyHash( path ).ToString();
|
||||
if( this.hash != hash )
|
||||
{
|
||||
this.hash = hash;
|
||||
Refresh( path );
|
||||
}
|
||||
|
||||
verified = true;
|
||||
}
|
||||
|
||||
public void Refresh( string path )
|
||||
{
|
||||
dependencies = AssetDatabase.GetDependencies( path, false );
|
||||
if( fileSizes == null || fileSizes.Length != dependencies.Length )
|
||||
fileSizes = new long[dependencies.Length];
|
||||
|
||||
int length = dependencies.Length;
|
||||
for( int i = 0; i < length; i++ )
|
||||
{
|
||||
if( !string.IsNullOrEmpty( dependencies[i] ) )
|
||||
{
|
||||
FileInfo assetFile = new FileInfo( dependencies[i] );
|
||||
fileSizes[i] = assetFile.Exists ? assetFile.Length : 0L;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This dependency is empty which causes issues when passed to FileInfo constructor
|
||||
// Find a non-empty dependency and move it to this index
|
||||
for( int j = length - 1; j > i; j--, length-- )
|
||||
{
|
||||
if( !string.IsNullOrEmpty( dependencies[j] ) )
|
||||
{
|
||||
dependencies[i--] = dependencies[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
length--;
|
||||
}
|
||||
}
|
||||
|
||||
if( length != fileSizes.Length )
|
||||
{
|
||||
Array.Resize( ref dependencies, length );
|
||||
Array.Resize( ref fileSizes, length );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
// An optimization to fetch the dependencies of an asset only once (key is the path of the asset)
|
||||
private Dictionary<string, CacheEntry> assetDependencyCache;
|
||||
private CacheEntry lastRefreshedCacheEntry;
|
||||
|
||||
private string CachePath { get { return Application.dataPath + "/../Library/AssetUsageDetector.cache"; } } // Path of the cache file
|
||||
|
||||
public void SaveCache()
|
||||
{
|
||||
if( assetDependencyCache == null )
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
using( FileStream stream = new FileStream( CachePath, FileMode.Create ) )
|
||||
using( BinaryWriter writer = new BinaryWriter( stream ) )
|
||||
{
|
||||
writer.Write( assetDependencyCache.Count );
|
||||
|
||||
foreach( var keyValuePair in assetDependencyCache )
|
||||
{
|
||||
CacheEntry cacheEntry = keyValuePair.Value;
|
||||
string[] dependencies = cacheEntry.dependencies;
|
||||
long[] fileSizes = cacheEntry.fileSizes;
|
||||
|
||||
writer.Write( keyValuePair.Key );
|
||||
writer.Write( cacheEntry.hash );
|
||||
writer.Write( dependencies.Length );
|
||||
|
||||
for( int i = 0; i < dependencies.Length; i++ )
|
||||
{
|
||||
writer.Write( dependencies[i] );
|
||||
writer.Write( fileSizes[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
Debug.LogException( e );
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadCache()
|
||||
{
|
||||
if( File.Exists( CachePath ) )
|
||||
{
|
||||
using( FileStream stream = new FileStream( CachePath, FileMode.Open, FileAccess.Read ) )
|
||||
using( BinaryReader reader = new BinaryReader( stream ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
int cacheSize = reader.ReadInt32();
|
||||
assetDependencyCache = new Dictionary<string, CacheEntry>( cacheSize );
|
||||
|
||||
for( int i = 0; i < cacheSize; i++ )
|
||||
{
|
||||
string assetPath = reader.ReadString();
|
||||
string hash = reader.ReadString();
|
||||
|
||||
int dependenciesLength = reader.ReadInt32();
|
||||
string[] dependencies = new string[dependenciesLength];
|
||||
long[] fileSizes = new long[dependenciesLength];
|
||||
for( int j = 0; j < dependenciesLength; j++ )
|
||||
{
|
||||
dependencies[j] = reader.ReadString();
|
||||
fileSizes[j] = reader.ReadInt64();
|
||||
}
|
||||
|
||||
assetDependencyCache[assetPath] = new CacheEntry( hash, dependencies, fileSizes );
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
assetDependencyCache = null;
|
||||
Debug.LogWarning( "Couldn't load cache (probably cache format has changed in an update), will regenerate cache.\n" + e.ToString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate cache for all assets for the first time
|
||||
if( assetDependencyCache == null )
|
||||
{
|
||||
assetDependencyCache = new Dictionary<string, CacheEntry>( 1024 * 8 );
|
||||
|
||||
string[] allAssets = AssetDatabase.GetAllAssetPaths();
|
||||
if( allAssets.Length > 0 )
|
||||
{
|
||||
double startTime = EditorApplication.timeSinceStartup;
|
||||
|
||||
try
|
||||
{
|
||||
for( int i = 0; i < allAssets.Length; i++ )
|
||||
{
|
||||
if( i % 30 == 0 && EditorUtility.DisplayCancelableProgressBar( "Please wait...", "Generating cache for the first time (optional)", (float) i / allAssets.Length ) )
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
Debug.LogWarning( "Initial cache generation cancelled, cache will be generated on the fly as more and more assets are searched." );
|
||||
break;
|
||||
}
|
||||
|
||||
assetDependencyCache[allAssets[i]] = new CacheEntry( allAssets[i] );
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
Debug.Log( "Cache generated in " + ( EditorApplication.timeSinceStartup - startTime ).ToString( "F2" ) + " seconds" );
|
||||
Debug.Log( "You can always reset the cache by deleting " + Path.GetFullPath( CachePath ) );
|
||||
|
||||
SaveCache();
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
Debug.LogException( e );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71ea9a3fd0b82594d8130d882dbfc844
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/AssetUsageDetectorCache.cs
|
||||
uploadId: 894428
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93aaae685d4c3db44baeb91a0296855e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/AssetUsageDetectorSearchFunctions.cs
|
||||
uploadId: 894428
|
||||
@@ -0,0 +1,289 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AssetUsageDetectorNamespace
|
||||
{
|
||||
public static class AssetUsageDetectorSettings
|
||||
{
|
||||
private static readonly GUILayoutOption GL_WIDTH_60 = GUILayout.Width( 60f );
|
||||
|
||||
#region Colors
|
||||
private static Color? m_settingsHeaderColor = null;
|
||||
public static Color SettingsHeaderColor
|
||||
{
|
||||
get { if( m_settingsHeaderColor == null ) m_settingsHeaderColor = GetColor( "AUD_SettingsHeaderTint", Color.cyan ); return m_settingsHeaderColor.Value; }
|
||||
set { if( m_settingsHeaderColor == value ) return; m_settingsHeaderColor = value; SetColor( "AUD_SettingsHeaderTint", value ); }
|
||||
}
|
||||
|
||||
private static Color? m_searchResultGroupHeaderColor = null;
|
||||
public static Color SearchResultGroupHeaderColor
|
||||
{
|
||||
get { if( m_searchResultGroupHeaderColor == null ) m_searchResultGroupHeaderColor = GetColor( "AUD_ResultGroupHeaderTint", Color.cyan ); return m_searchResultGroupHeaderColor.Value; }
|
||||
set { if( m_searchResultGroupHeaderColor == value ) return; m_searchResultGroupHeaderColor = value; SetColor( "AUD_ResultGroupHeaderTint", value ); }
|
||||
}
|
||||
|
||||
private static Color? m_rootRowsBackgroundColor = null;
|
||||
public static Color RootRowsBackgroundColor
|
||||
{
|
||||
get { if( m_rootRowsBackgroundColor == null ) m_rootRowsBackgroundColor = GetColor( "AUD_RootRowsTint", EditorGUIUtility.isProSkin ? new Color( 0f, 1f, 1f, 0.15f ) : new Color( 0f, 1f, 1f, 0.25f ) ); return m_rootRowsBackgroundColor.Value; }
|
||||
set { if( m_rootRowsBackgroundColor == value ) return; m_rootRowsBackgroundColor = value; SetColor( "AUD_RootRowsTint", value ); }
|
||||
}
|
||||
|
||||
private static Color? m_rootRowsBorderColor = null;
|
||||
public static Color RootRowsBorderColor
|
||||
{
|
||||
get { if( m_rootRowsBorderColor == null ) m_rootRowsBorderColor = GetColor( "AUD_RootRowsBorderColor", EditorGUIUtility.isProSkin ? new Color( 0.15f, 0.15f, 0.15f, 1f ) : new Color( 0.375f, 0.375f, 0.375f, 1f ) ); return m_rootRowsBorderColor.Value; }
|
||||
set { if( m_rootRowsBorderColor == value ) return; m_rootRowsBorderColor = value; SetColor( "AUD_RootRowsBorderColor", value ); }
|
||||
}
|
||||
|
||||
private static Color? m_mainReferencesBackgroundColor = null;
|
||||
public static Color MainReferencesBackgroundColor
|
||||
{
|
||||
get { if( m_mainReferencesBackgroundColor == null ) m_mainReferencesBackgroundColor = GetColor( "AUD_MainRefRowsTint", EditorGUIUtility.isProSkin ? new Color( 0f, 0.35f, 0f, 1f ) : new Color( 0.25f, 0.75f, 0.25f, 1f ) ); return m_mainReferencesBackgroundColor.Value; }
|
||||
set { if( m_mainReferencesBackgroundColor == value ) return; m_mainReferencesBackgroundColor = value; SetColor( "AUD_MainRefRowsTint", value ); }
|
||||
}
|
||||
|
||||
private static Color? m_selectedRowsParentTint = null;
|
||||
public static Color SelectedRowParentsTint
|
||||
{
|
||||
get { if( m_selectedRowsParentTint == null ) m_selectedRowsParentTint = GetColor( "AUD_SelectedRowParentsTint", EditorGUIUtility.isProSkin ? new Color( 0.36f, 0.36f, 0.18f, 1f ) : new Color( 0.825f, 0.825f, 0.55f, 1f ) ); return m_selectedRowsParentTint.Value; }
|
||||
set { if( m_selectedRowsParentTint == value ) return; m_selectedRowsParentTint = value; SetColor( "AUD_SelectedRowParentsTint", value ); }
|
||||
}
|
||||
|
||||
private static Color? m_selectedRowOccurrencesColor = null;
|
||||
public static Color SelectedRowOccurrencesColor
|
||||
{
|
||||
get { if( m_selectedRowOccurrencesColor == null ) m_selectedRowOccurrencesColor = GetColor( "AUD_SelectedRowOccurrencesTint", EditorGUIUtility.isProSkin ? new Color( 0f, 0.3f, 0.75f, 1f ) : new Color( 0.25f, 0.75f, 1f, 1f ) ); return m_selectedRowOccurrencesColor.Value; }
|
||||
set { if( m_selectedRowOccurrencesColor == value ) return; m_selectedRowOccurrencesColor = value; SetColor( "AUD_SelectedRowOccurrencesTint", value ); }
|
||||
}
|
||||
|
||||
private static Color? m_treeLinesColor = null;
|
||||
public static Color TreeLinesColor
|
||||
{
|
||||
get { if( m_treeLinesColor == null ) m_treeLinesColor = GetColor( "AUD_TreeLinesColor", EditorGUIUtility.isProSkin ? new Color( 0.65f, 0.65f, 0.65f, 1f ) : new Color( 0.375f, 0.375f, 0.375f, 1f ) ); return m_treeLinesColor.Value; }
|
||||
set { if( m_treeLinesColor == value ) return; m_treeLinesColor = value; SetColor( "AUD_TreeLinesColor", value ); }
|
||||
}
|
||||
|
||||
private static Color? m_highlightedTreeLinesColor = null;
|
||||
public static Color HighlightedTreeLinesColor
|
||||
{
|
||||
get { if( m_highlightedTreeLinesColor == null ) m_highlightedTreeLinesColor = GetColor( "AUD_HighlightTreeLinesColor", Color.cyan ); return m_highlightedTreeLinesColor.Value; }
|
||||
set { if( m_highlightedTreeLinesColor == value ) return; m_highlightedTreeLinesColor = value; SetColor( "AUD_HighlightTreeLinesColor", value ); }
|
||||
}
|
||||
|
||||
private static Color? m_searchMatchingTextColor = null;
|
||||
public static Color SearchMatchingTextColor
|
||||
{
|
||||
get { if( m_searchMatchingTextColor == null ) m_searchMatchingTextColor = GetColor( "AUD_SearchTextColor", Color.red ); return m_searchMatchingTextColor.Value; }
|
||||
set { if( m_searchMatchingTextColor == value ) return; m_searchMatchingTextColor = value; SetColor( "AUD_SearchTextColor", value ); ForEachAssetUsageDetectorWindow( ( window ) => window.OnSettingsChanged( highlightedSearchTextColorChanged: true ) ); }
|
||||
}
|
||||
|
||||
private static Color? m_tooltipDescriptionTextColor = null;
|
||||
public static Color TooltipDescriptionTextColor
|
||||
{
|
||||
get { if( m_tooltipDescriptionTextColor == null ) m_tooltipDescriptionTextColor = GetColor( "AUD_TooltipUsageTextColor", EditorGUIUtility.isProSkin ? new Color( 0f, 0.9f, 0.9f, 1f ) : new Color( 0.9f, 0f, 0f, 1f ) ); return m_tooltipDescriptionTextColor.Value; }
|
||||
set { if( m_tooltipDescriptionTextColor == value ) return; m_tooltipDescriptionTextColor = value; SetColor( "AUD_TooltipUsageTextColor", value ); ForEachAssetUsageDetectorWindow( ( window ) => window.OnSettingsChanged( tooltipDescriptionsColorChanged: true ) ); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Size Adjustments
|
||||
private static float? m_extraRowHeight = null;
|
||||
public static float ExtraRowHeight
|
||||
{
|
||||
get { if( m_extraRowHeight == null ) m_extraRowHeight = EditorPrefs.GetFloat( "AUD_ExtraRowHeight", 0f ); return m_extraRowHeight.Value; }
|
||||
set { if( m_extraRowHeight == value ) return; m_extraRowHeight = value; EditorPrefs.SetFloat( "AUD_ExtraRowHeight", value ); ForEachAssetUsageDetectorWindow( ( window ) => window.OnSettingsChanged() ); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other Settings
|
||||
private static bool? m_showRootAssetName = null;
|
||||
public static bool ShowRootAssetName
|
||||
{
|
||||
get { if( m_showRootAssetName == null ) m_showRootAssetName = EditorPrefs.GetBool( "AUD_ShowRootAssetName", true ); return m_showRootAssetName.Value; }
|
||||
set { if( m_showRootAssetName == value ) return; m_showRootAssetName = value; EditorPrefs.SetBool( "AUD_ShowRootAssetName", value ); }
|
||||
}
|
||||
|
||||
private static bool? m_pingClickedObjects = null;
|
||||
public static bool PingClickedObjects
|
||||
{
|
||||
get { if( m_pingClickedObjects == null ) m_pingClickedObjects = EditorPrefs.GetBool( "AUD_PingClickedObj", true ); return m_pingClickedObjects.Value; }
|
||||
set { if( m_pingClickedObjects == value ) return; m_pingClickedObjects = value; EditorPrefs.SetBool( "AUD_PingClickedObj", value ); }
|
||||
}
|
||||
|
||||
private static bool? m_selectClickedObjects = null;
|
||||
public static bool SelectClickedObjects
|
||||
{
|
||||
get { if( m_selectClickedObjects == null ) m_selectClickedObjects = EditorPrefs.GetBool( "AUD_SelectClickedObj", false ); return m_selectClickedObjects.Value; }
|
||||
set { if( m_selectClickedObjects == value ) return; m_selectClickedObjects = value; EditorPrefs.SetBool( "AUD_SelectClickedObj", value ); }
|
||||
}
|
||||
|
||||
private static bool? m_selectDoubleClickedObjects = null;
|
||||
public static bool SelectDoubleClickedObjects
|
||||
{
|
||||
get { if( m_selectDoubleClickedObjects == null ) m_selectDoubleClickedObjects = EditorPrefs.GetBool( "AUD_SelectDoubleClickedObj", true ); return m_selectDoubleClickedObjects.Value; }
|
||||
set { if( m_selectDoubleClickedObjects == value ) return; m_selectDoubleClickedObjects = value; EditorPrefs.SetBool( "AUD_SelectDoubleClickedObj", value ); }
|
||||
}
|
||||
|
||||
private static bool? m_markUsedAssetsSubAssetsAsUsed = null;
|
||||
public static bool MarkUsedAssetsSubAssetsAsUsed
|
||||
{
|
||||
get { if( m_markUsedAssetsSubAssetsAsUsed == null ) m_markUsedAssetsSubAssetsAsUsed = EditorPrefs.GetBool( "AUD_MarkUsedAssetsSubAssetsAsUsed", true ); return m_markUsedAssetsSubAssetsAsUsed.Value; }
|
||||
set { if( m_markUsedAssetsSubAssetsAsUsed == value ) return; m_markUsedAssetsSubAssetsAsUsed = value; EditorPrefs.SetBool( "AUD_MarkUsedAssetsSubAssetsAsUsed", value ); }
|
||||
}
|
||||
|
||||
private static bool? m_showUnityTooltip = null;
|
||||
public static bool ShowUnityTooltip
|
||||
{
|
||||
get { if( m_showUnityTooltip == null ) m_showUnityTooltip = EditorPrefs.GetBool( "AUD_ShowUnityTooltip", false ); return m_showUnityTooltip.Value; }
|
||||
set { if( m_showUnityTooltip == value ) return; m_showUnityTooltip = value; EditorPrefs.SetBool( "AUD_ShowUnityTooltip", value ); }
|
||||
}
|
||||
|
||||
private static bool? m_showCustomTooltip = null;
|
||||
public static bool ShowCustomTooltip
|
||||
{
|
||||
get { if( m_showCustomTooltip == null ) m_showCustomTooltip = EditorPrefs.GetBool( "AUD_ShowCustomTooltip", true ); return m_showCustomTooltip.Value; }
|
||||
set { if( m_showCustomTooltip == value ) return; m_showCustomTooltip = value; EditorPrefs.SetBool( "AUD_ShowCustomTooltip", value ); ForEachAssetUsageDetectorWindow( ( window ) => window.OnSettingsChanged() ); }
|
||||
}
|
||||
|
||||
private static float? m_customTooltipDelay = null;
|
||||
public static float CustomTooltipDelay
|
||||
{
|
||||
get { if( m_customTooltipDelay == null ) m_customTooltipDelay = EditorPrefs.GetFloat( "AUD_CustomTooltipDelay", 0.7f ); return m_customTooltipDelay.Value; }
|
||||
set { if( m_customTooltipDelay == value ) return; m_customTooltipDelay = value; EditorPrefs.SetFloat( "AUD_CustomTooltipDelay", value ); }
|
||||
}
|
||||
|
||||
private static bool? m_showTreeLines = null;
|
||||
public static bool ShowTreeLines
|
||||
{
|
||||
get { if( m_showTreeLines == null ) m_showTreeLines = EditorPrefs.GetBool( "AUD_ShowTreeLines", true ); return m_showTreeLines.Value; }
|
||||
set { if( m_showTreeLines == value ) return; m_showTreeLines = value; EditorPrefs.SetBool( "AUD_ShowTreeLines", value ); }
|
||||
}
|
||||
|
||||
private static bool? m_applySelectedRowParentsTintToRootRows = null;
|
||||
public static bool ApplySelectedRowParentsTintToRootRows
|
||||
{
|
||||
get { if( m_applySelectedRowParentsTintToRootRows == null ) m_applySelectedRowParentsTintToRootRows = EditorPrefs.GetBool( "AUD_SelectedRowParentsTintAtRoot", true ); return m_applySelectedRowParentsTintToRootRows.Value; }
|
||||
set { if( m_applySelectedRowParentsTintToRootRows == value ) return; m_applySelectedRowParentsTintToRootRows = value; EditorPrefs.SetBool( "AUD_SelectedRowParentsTintAtRoot", value ); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
[SettingsProvider]
|
||||
public static SettingsProvider CreatePreferencesGUI()
|
||||
{
|
||||
return new SettingsProvider( "Project/yasirkula/Asset Usage Detector", SettingsScope.Project )
|
||||
{
|
||||
guiHandler = ( searchContext ) => PreferencesGUI(),
|
||||
keywords = new System.Collections.Generic.HashSet<string>() { "Asset", "Usage", "Detector" }
|
||||
};
|
||||
}
|
||||
|
||||
public static void PreferencesGUI()
|
||||
{
|
||||
float labelWidth = EditorGUIUtility.labelWidth;
|
||||
EditorGUIUtility.labelWidth += 60f;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
ShowRootAssetName = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Show Root Asset's Name For Sub-Assets (Requires Refresh)", ShowRootAssetName );
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
PingClickedObjects = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Ping Clicked Objects", PingClickedObjects );
|
||||
SelectClickedObjects = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Select Clicked Objects", SelectClickedObjects );
|
||||
SelectDoubleClickedObjects = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Select Double Clicked Objects", SelectDoubleClickedObjects );
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
MarkUsedAssetsSubAssetsAsUsed = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Hide unused sub-assets in \"Unused Objects\" list if their parent assets are used (Requires Refresh)", MarkUsedAssetsSubAssetsAsUsed );
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
ShowUnityTooltip = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Show Unity Tooltip", ShowUnityTooltip );
|
||||
ShowCustomTooltip = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Show Custom Tooltip", ShowCustomTooltip );
|
||||
EditorGUI.indentLevel++;
|
||||
CustomTooltipDelay = FloatField( "Delay", CustomTooltipDelay, 0.7f );
|
||||
EditorGUI.indentLevel--;
|
||||
TooltipDescriptionTextColor = ColorField( "Tooltip Descriptions Text Color", TooltipDescriptionTextColor, EditorGUIUtility.isProSkin ? new Color( 0f, 0.9f, 0.9f, 1f ) : new Color( 0.9f, 0f, 0f, 1f ) );
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
ExtraRowHeight = Mathf.Max( 0f, FloatField( "Extra Row Height", ExtraRowHeight, 0f ) );
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
SettingsHeaderColor = ColorField( "Settings Header Color", SettingsHeaderColor, Color.cyan );
|
||||
SearchResultGroupHeaderColor = ColorField( "Group Header Color", SearchResultGroupHeaderColor, Color.cyan );
|
||||
RootRowsBackgroundColor = ColorField( "Root Rows Background Color", RootRowsBackgroundColor, EditorGUIUtility.isProSkin ? new Color( 0f, 1f, 1f, 0.15f ) : new Color( 0f, 1f, 1f, 0.25f ) );
|
||||
RootRowsBorderColor = ColorField( "Root Rows Border Color", RootRowsBorderColor, EditorGUIUtility.isProSkin ? new Color( 0.15f, 0.15f, 0.15f, 1f ) : new Color( 0.375f, 0.375f, 0.375f, 1f ) );
|
||||
MainReferencesBackgroundColor = ColorField( "Main References Background Color", MainReferencesBackgroundColor, EditorGUIUtility.isProSkin ? new Color( 0f, 0.35f, 0f, 1f ) : new Color( 0.25f, 0.75f, 0.25f, 1f ) );
|
||||
SelectedRowParentsTint = ColorField( "Selected Row Parents Tint", SelectedRowParentsTint, EditorGUIUtility.isProSkin ? new Color( 0.36f, 0.36f, 0.18f, 1f ) : new Color( 0.825f, 0.825f, 0.55f, 1f ) );
|
||||
EditorGUI.indentLevel++;
|
||||
ApplySelectedRowParentsTintToRootRows = !EditorGUILayout.Toggle( "Ignore Root Rows", !ApplySelectedRowParentsTintToRootRows );
|
||||
EditorGUI.indentLevel--;
|
||||
SelectedRowOccurrencesColor = ColorField( "Selected Row All Occurrences Tint", SelectedRowOccurrencesColor, EditorGUIUtility.isProSkin ? new Color( 0f, 0.3f, 0.75f, 1f ) : new Color( 0.25f, 0.75f, 1f, 1f ) );
|
||||
SearchMatchingTextColor = ColorField( "Matching Search Text Color", SearchMatchingTextColor, Color.red );
|
||||
|
||||
ShowTreeLines = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Show Tree Lines", ShowTreeLines );
|
||||
EditorGUI.indentLevel++;
|
||||
TreeLinesColor = ColorField( "Normal Color", TreeLinesColor, EditorGUIUtility.isProSkin ? new Color( 0.65f, 0.65f, 0.65f, 1f ) : new Color( 0.375f, 0.375f, 0.375f, 1f ) );
|
||||
HighlightedTreeLinesColor = ColorField( "Highlighted Color", HighlightedTreeLinesColor, Color.cyan );
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
EditorGUIUtility.labelWidth = labelWidth;
|
||||
|
||||
if( EditorGUI.EndChangeCheck() )
|
||||
ForEachAssetUsageDetectorWindow( ( window ) => window.Repaint() );
|
||||
}
|
||||
|
||||
private static Color ColorField( string label, Color value, Color defaultValue )
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
Color result = EditorGUILayout.ColorField( label, value );
|
||||
if( GUILayout.Button( "Reset", GL_WIDTH_60 ) )
|
||||
result = defaultValue;
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static float FloatField( string label, float value, float defaultValue )
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
float result = EditorGUILayout.FloatField( label, value );
|
||||
if( GUILayout.Button( "Reset", GL_WIDTH_60 ) )
|
||||
result = defaultValue;
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Color GetColor( string pref, Color defaultColor )
|
||||
{
|
||||
if( EditorGUIUtility.isProSkin )
|
||||
pref += "_Pro";
|
||||
|
||||
if( !EditorPrefs.HasKey( pref ) )
|
||||
return defaultColor;
|
||||
|
||||
string[] parts = EditorPrefs.GetString( pref ).Split( ';' );
|
||||
return new Color32( byte.Parse( parts[0] ), byte.Parse( parts[1] ), byte.Parse( parts[2] ), byte.Parse( parts[3] ) );
|
||||
}
|
||||
|
||||
private static void SetColor( string pref, Color32 value )
|
||||
{
|
||||
if( EditorGUIUtility.isProSkin )
|
||||
pref += "_Pro";
|
||||
|
||||
EditorPrefs.SetString( pref, string.Concat( value.r.ToString(), ";", value.g.ToString(), ";", value.b.ToString(), ";", value.a.ToString() ) );
|
||||
}
|
||||
|
||||
private static void ForEachAssetUsageDetectorWindow( System.Action<AssetUsageDetectorWindow> action )
|
||||
{
|
||||
foreach( AssetUsageDetectorWindow window in Resources.FindObjectsOfTypeAll<AssetUsageDetectorWindow>() )
|
||||
{
|
||||
if( window )
|
||||
action( window );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13295073724765e45aa3b77486e515f4
|
||||
timeCreated: 1639982865
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/AssetUsageDetectorSettings.cs
|
||||
uploadId: 894428
|
||||
@@ -0,0 +1,753 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace AssetUsageDetectorNamespace
|
||||
{
|
||||
public enum Phase { Setup, Processing, Complete };
|
||||
|
||||
public class AssetUsageDetectorWindow : EditorWindow, IHasCustomMenu
|
||||
{
|
||||
private enum WindowFilter { AlwaysReturnActive, ReturnActiveIfNotLocked, AlwaysReturnNew };
|
||||
|
||||
private const string PREFS_SEARCH_SCENES = "AUD_SceneSearch";
|
||||
private const string PREFS_SEARCH_SCENE_LIGHTING_SETTINGS = "AUD_LightingSettingsSearch";
|
||||
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_UNUSED_MATERIAL_PROPERTIES = "AUD_SearchUnusedMaterialProps";
|
||||
private const string PREFS_LAZY_SCENE_SEARCH = "AUD_LazySceneSearch";
|
||||
#if ASSET_USAGE_ADDRESSABLES
|
||||
private const string PREFS_ADDRESSABLES_SUPPORT = "AUD_AddressablesSupport";
|
||||
#endif
|
||||
private const string PREFS_CALCULATE_UNUSED_OBJECTS = "AUD_FindUnusedObjs";
|
||||
private const string PREFS_HIDE_DUPLICATE_ROWS = "AUD_HideDuplicates";
|
||||
private const string PREFS_HIDE_REDUNDANT_PREFAB_REFERENCES_IN_ASSETS = "AUD_HideRedundantPRefsInAssets";
|
||||
private const string PREFS_HIDE_REDUNDANT_PREFAB_REFERENCES_IN_SCENES = "AUD_HideRedundantPRefsInScenes";
|
||||
private const string PREFS_SHOW_PROGRESS = "AUD_Progress";
|
||||
|
||||
private static readonly GUIContent windowTitle = new GUIContent( "Asset Usage Detector" );
|
||||
private static readonly Vector2 windowMinSize = new Vector2( 325f, 220f );
|
||||
|
||||
private static readonly GUILayoutOption GL_WIDTH_12 = GUILayout.Width( 12f );
|
||||
|
||||
private static readonly GUIContent sharedGUIContent = new GUIContent();
|
||||
|
||||
private readonly GUIContent hideRedundantPrefabReferencesInAssetsLabel = new GUIContent( "Hide redundant prefab references in Assets", "Hides redundant/non-overridden references in prefab variants and nested prefabs. " +
|
||||
"This will help focus on only the references that actually matter. For example:\n\n" +
|
||||
"- Material CloudMat is assigned to prefab Cloud and its variant CloudBig. Since changing Cloud's material will also affect CloudBig, search results won't show CloudBig's reference" );
|
||||
private readonly GUIContent hideRedundantPrefabReferencesInScenesLabel = new GUIContent( "Hide redundant prefab references in Scenes", "Hides redundant/non-overridden references in prefab instances. " +
|
||||
"This will help focus on only the references that actually matter. For example:\n\n" +
|
||||
"- Prefab Healthbar is nested inside prefab Player. An instance of Player exists in the current scene and its Healthbar isn't modified. Since modifying Healthbar in Player prefab will also affect " +
|
||||
"the instance in the scene, search results won't show the instance in the scene while searching for Healthbar's references" );
|
||||
|
||||
private GUIStyle lockButtonStyle;
|
||||
|
||||
private readonly AssetUsageDetector core = new AssetUsageDetector();
|
||||
private SearchResult searchResult; // Overall search results
|
||||
|
||||
// This isn't readonly so that it can be serialized
|
||||
private List<ObjectToSearch> objectsToSearch = new List<ObjectToSearch>() { new ObjectToSearch( null ) };
|
||||
|
||||
[SerializeField] // Since titleContent persists between Editor sessions, so should the IsLocked property because otherwise, "[L]" in title becomes confusing when the EditorWindow isn't actually locked
|
||||
private bool m_isLocked;
|
||||
private bool IsLocked
|
||||
{
|
||||
get { return m_isLocked; }
|
||||
set
|
||||
{
|
||||
if( m_isLocked != value )
|
||||
{
|
||||
m_isLocked = value;
|
||||
titleContent = value ? new GUIContent( "[L] " + windowTitle.text, EditorGUIUtility.IconContent( "InspectorLock" ).image ) : windowTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 searchInSceneLightingSettings = true; // Window-Rendering-Lighting settings
|
||||
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<Object> searchInAssetsSubset = new List<Object>() { null }; // If not empty, only these assets are searched for references
|
||||
private List<Object> excludedAssets = new List<Object>() { null }; // These assets won't be searched for references
|
||||
private List<Object> excludedScenes = new List<Object>() { null }; // These scenes won't be searched for references
|
||||
|
||||
private bool lazySceneSearch = true;
|
||||
#if ASSET_USAGE_ADDRESSABLES
|
||||
private bool addressablesSupport = false;
|
||||
#endif
|
||||
private bool searchUnusedMaterialProperties = true;
|
||||
private bool calculateUnusedObjects = false;
|
||||
private bool hideDuplicateRows = true;
|
||||
private bool hideRedundantPrefabReferencesInAssets = false;
|
||||
private bool hideRedundantPrefabReferencesInScenes = false;
|
||||
private bool noAssetDatabaseChanges = false;
|
||||
private bool showDetailedProgressBar = true;
|
||||
|
||||
private SearchRefactoring searchRefactoring = null; // Its value can be assigned via ShowAndSearch
|
||||
|
||||
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 bool drawObjectsToSearchSection = true;
|
||||
|
||||
private Vector2 scrollPosition = Vector2.zero;
|
||||
|
||||
private bool shouldRepositionSelf;
|
||||
private Rect windowTargetPosition;
|
||||
|
||||
void IHasCustomMenu.AddItemsToMenu( GenericMenu contextMenu )
|
||||
{
|
||||
contextMenu.AddItem( new GUIContent( "Lock" ), IsLocked, () => IsLocked = !IsLocked );
|
||||
contextMenu.AddSeparator( "" );
|
||||
contextMenu.AddItem( new GUIContent( "Settings" ), false, () => SettingsService.OpenProjectSettings( "Project/yasirkula/Asset Usage Detector" ) );
|
||||
|
||||
if( currentPhase == Phase.Setup )
|
||||
{
|
||||
contextMenu.AddSeparator( "" );
|
||||
contextMenu.AddItem( new GUIContent( "Refresh Sub-Assets of Searched Objects" ), false, () =>
|
||||
{
|
||||
for( int i = objectsToSearch.Count - 1; i >= 0; i-- )
|
||||
objectsToSearch[i].RefreshSubAssets();
|
||||
} );
|
||||
}
|
||||
else if( currentPhase == Phase.Complete )
|
||||
{
|
||||
if( searchResult != null && searchResult.NumberOfGroups > 0 )
|
||||
{
|
||||
contextMenu.AddSeparator( "" );
|
||||
contextMenu.AddItem( new GUIContent( "Collapse All" ), false, searchResult.CollapseAllSearchResultGroups );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shows lock button at the top-right corner
|
||||
// Credit: http://leahayes.co.uk/2013/04/30/adding-the-little-padlock-button-to-your-editorwindow.html
|
||||
private void ShowButton( Rect position )
|
||||
{
|
||||
if( lockButtonStyle == null )
|
||||
lockButtonStyle = "IN LockButton";
|
||||
|
||||
IsLocked = GUI.Toggle( position, IsLocked, GUIContent.none, lockButtonStyle );
|
||||
}
|
||||
|
||||
private static AssetUsageDetectorWindow GetWindow( WindowFilter filter )
|
||||
{
|
||||
AssetUsageDetectorWindow[] windows = Resources.FindObjectsOfTypeAll<AssetUsageDetectorWindow>();
|
||||
AssetUsageDetectorWindow window = System.Array.Find( windows, ( w ) => w && !w.IsLocked );
|
||||
if( !window )
|
||||
window = System.Array.Find( windows, ( w ) => w );
|
||||
|
||||
if( window && ( filter == WindowFilter.AlwaysReturnActive || ( !window.IsLocked && filter == WindowFilter.ReturnActiveIfNotLocked ) ) )
|
||||
{
|
||||
window.Show();
|
||||
window.Focus();
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
Rect? windowTargetPosition = null;
|
||||
if( window )
|
||||
{
|
||||
Rect position = window.position;
|
||||
position.position += new Vector2( 50f, 50f );
|
||||
windowTargetPosition = position;
|
||||
}
|
||||
|
||||
window = CreateInstance<AssetUsageDetectorWindow>();
|
||||
window.titleContent = windowTitle;
|
||||
window.minSize = windowMinSize;
|
||||
|
||||
if( windowTargetPosition.HasValue )
|
||||
{
|
||||
window.shouldRepositionSelf = true;
|
||||
window.windowTargetPosition = windowTargetPosition.Value;
|
||||
}
|
||||
|
||||
window.Show( true );
|
||||
window.Focus();
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
[MenuItem( "Window/Asset Usage Detector/Active Window" )]
|
||||
private static void OpenActiveWindow()
|
||||
{
|
||||
GetWindow( WindowFilter.AlwaysReturnActive );
|
||||
}
|
||||
|
||||
[MenuItem( "Window/Asset Usage Detector/New Window" )]
|
||||
private static void OpenNewWindow()
|
||||
{
|
||||
GetWindow( WindowFilter.AlwaysReturnNew );
|
||||
}
|
||||
|
||||
// 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<Object> searchObjects, bool? shouldSearchChildren = null )
|
||||
{
|
||||
GetWindow( WindowFilter.ReturnActiveIfNotLocked ).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;
|
||||
}
|
||||
|
||||
GetWindow( WindowFilter.ReturnActiveIfNotLocked ).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 void ShowAndSearchInternal( IEnumerable<Object> searchObjects, AssetUsageDetector.Parameters searchParameters, bool? shouldSearchChildren )
|
||||
{
|
||||
if( !ReturnToSetupPhase() )
|
||||
{
|
||||
Debug.LogError( "Need to reset the previous search first!" );
|
||||
return;
|
||||
}
|
||||
|
||||
objectsToSearch.Clear();
|
||||
if( searchObjects != null )
|
||||
{
|
||||
foreach( Object obj in searchObjects )
|
||||
objectsToSearch.Add( new ObjectToSearch( obj, shouldSearchChildren ) );
|
||||
}
|
||||
|
||||
if( searchParameters != null )
|
||||
{
|
||||
ParseSceneSearchMode( searchParameters.searchInScenes );
|
||||
searchInSceneLightingSettings = searchParameters.searchInSceneLightingSettings;
|
||||
searchInAssetsFolder = searchParameters.searchInAssetsFolder;
|
||||
dontSearchInSourceAssets = searchParameters.dontSearchInSourceAssets;
|
||||
searchInProjectSettings = searchParameters.searchInProjectSettings;
|
||||
searchUnusedMaterialProperties = searchParameters.searchUnusedMaterialProperties;
|
||||
searchRefactoring = searchParameters.searchRefactoring;
|
||||
lazySceneSearch = searchParameters.lazySceneSearch;
|
||||
#if ASSET_USAGE_ADDRESSABLES
|
||||
addressablesSupport = searchParameters.addressablesSupport;
|
||||
#endif
|
||||
calculateUnusedObjects = searchParameters.calculateUnusedObjects;
|
||||
hideDuplicateRows = searchParameters.hideDuplicateRows;
|
||||
hideRedundantPrefabReferencesInAssets = searchParameters.hideRedundantPrefabReferencesInAssets;
|
||||
hideRedundantPrefabReferencesInScenes = searchParameters.hideRedundantPrefabReferencesInScenes;
|
||||
noAssetDatabaseChanges = searchParameters.noAssetDatabaseChanges;
|
||||
showDetailedProgressBar = searchParameters.showDetailedProgressBar;
|
||||
|
||||
searchInAssetsSubset.Clear();
|
||||
if( searchParameters.searchInAssetsSubset != null )
|
||||
{
|
||||
foreach( Object obj in searchParameters.searchInAssetsSubset )
|
||||
searchInAssetsSubset.Add( obj );
|
||||
}
|
||||
|
||||
excludedAssets.Clear();
|
||||
if( searchParameters.excludedAssetsFromSearch != null )
|
||||
{
|
||||
foreach( Object obj in searchParameters.excludedAssetsFromSearch )
|
||||
excludedAssets.Add( obj );
|
||||
}
|
||||
|
||||
excludedScenes.Clear();
|
||||
if( searchParameters.excludedScenesFromSearch != null )
|
||||
{
|
||||
foreach( Object obj in searchParameters.excludedScenesFromSearch )
|
||||
excludedScenes.Add( obj );
|
||||
}
|
||||
}
|
||||
|
||||
InitiateSearch();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
LoadPrefs();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if( currentPhase == Phase.Complete && AssetUsageDetectorSettings.ShowCustomTooltip )
|
||||
wantsMouseMove = wantsMouseEnterLeaveWindow = true; // These values aren't preserved during domain reload on Unity 2020.3.0f1
|
||||
|
||||
PrefabStage.prefabStageClosing += ReplacePrefabStageObjectsWithAssets;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
PrefabStage.prefabStageClosing -= ReplacePrefabStageObjectsWithAssets;
|
||||
SearchResultTooltip.Hide();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if( core != null )
|
||||
core.SaveCache();
|
||||
|
||||
SavePrefs();
|
||||
|
||||
if( searchResult != null && currentPhase == Phase.Complete )
|
||||
searchResult.RestoreInitialSceneSetup();
|
||||
}
|
||||
|
||||
private void SavePrefs()
|
||||
{
|
||||
EditorPrefs.SetInt( PREFS_SEARCH_SCENES, (int) GetSceneSearchMode( false ) );
|
||||
EditorPrefs.SetBool( PREFS_SEARCH_SCENE_LIGHTING_SETTINGS, searchInSceneLightingSettings );
|
||||
EditorPrefs.SetBool( PREFS_SEARCH_ASSETS, searchInAssetsFolder );
|
||||
EditorPrefs.SetBool( PREFS_DONT_SEARCH_SOURCE_ASSETS, dontSearchInSourceAssets );
|
||||
EditorPrefs.SetBool( PREFS_SEARCH_PROJECT_SETTINGS, searchInProjectSettings );
|
||||
EditorPrefs.SetBool( PREFS_SEARCH_UNUSED_MATERIAL_PROPERTIES, searchUnusedMaterialProperties );
|
||||
EditorPrefs.SetBool( PREFS_LAZY_SCENE_SEARCH, lazySceneSearch );
|
||||
#if ASSET_USAGE_ADDRESSABLES
|
||||
EditorPrefs.SetBool( PREFS_ADDRESSABLES_SUPPORT, addressablesSupport );
|
||||
#endif
|
||||
EditorPrefs.SetBool( PREFS_CALCULATE_UNUSED_OBJECTS, calculateUnusedObjects );
|
||||
EditorPrefs.SetBool( PREFS_HIDE_DUPLICATE_ROWS, hideDuplicateRows );
|
||||
EditorPrefs.SetBool( PREFS_HIDE_REDUNDANT_PREFAB_REFERENCES_IN_ASSETS, hideRedundantPrefabReferencesInAssets );
|
||||
EditorPrefs.SetBool( PREFS_HIDE_REDUNDANT_PREFAB_REFERENCES_IN_SCENES, hideRedundantPrefabReferencesInScenes );
|
||||
EditorPrefs.SetBool( PREFS_SHOW_PROGRESS, showDetailedProgressBar );
|
||||
}
|
||||
|
||||
private void LoadPrefs()
|
||||
{
|
||||
ParseSceneSearchMode( (SceneSearchMode) EditorPrefs.GetInt( PREFS_SEARCH_SCENES, (int) ( SceneSearchMode.OpenScenes | SceneSearchMode.ScenesInBuildSettingsTickedOnly | SceneSearchMode.AllScenes ) ) );
|
||||
searchInSceneLightingSettings = EditorPrefs.GetBool( PREFS_SEARCH_SCENE_LIGHTING_SETTINGS, true );
|
||||
searchInAssetsFolder = EditorPrefs.GetBool( PREFS_SEARCH_ASSETS, true );
|
||||
dontSearchInSourceAssets = EditorPrefs.GetBool( PREFS_DONT_SEARCH_SOURCE_ASSETS, true );
|
||||
searchInProjectSettings = EditorPrefs.GetBool( PREFS_SEARCH_PROJECT_SETTINGS, true );
|
||||
searchUnusedMaterialProperties = EditorPrefs.GetBool( PREFS_SEARCH_UNUSED_MATERIAL_PROPERTIES, true );
|
||||
lazySceneSearch = EditorPrefs.GetBool( PREFS_LAZY_SCENE_SEARCH, true );
|
||||
#if ASSET_USAGE_ADDRESSABLES
|
||||
addressablesSupport = EditorPrefs.GetBool( PREFS_ADDRESSABLES_SUPPORT, false );
|
||||
#endif
|
||||
calculateUnusedObjects = EditorPrefs.GetBool( PREFS_CALCULATE_UNUSED_OBJECTS, false );
|
||||
hideDuplicateRows = EditorPrefs.GetBool( PREFS_HIDE_DUPLICATE_ROWS, true );
|
||||
hideRedundantPrefabReferencesInAssets = EditorPrefs.GetBool( PREFS_HIDE_REDUNDANT_PREFAB_REFERENCES_IN_ASSETS, hideRedundantPrefabReferencesInAssets );
|
||||
hideRedundantPrefabReferencesInScenes = EditorPrefs.GetBool( PREFS_HIDE_REDUNDANT_PREFAB_REFERENCES_IN_SCENES, hideRedundantPrefabReferencesInScenes );
|
||||
showDetailedProgressBar = EditorPrefs.GetBool( PREFS_SHOW_PROGRESS, true );
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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();
|
||||
|
||||
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) . . ." );
|
||||
|
||||
if( GUILayout.Button( "RETURN", Utilities.GL_HEIGHT_30 ) )
|
||||
{
|
||||
ReturnToSetupPhase();
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
}
|
||||
else if( currentPhase == Phase.Setup )
|
||||
{
|
||||
DrawObjectsToSearchSection();
|
||||
|
||||
GUILayout.Space( 10f );
|
||||
|
||||
Utilities.DrawHeader( "<b>SEARCH IN</b>" );
|
||||
|
||||
searchInAssetsFolder = WordWrappingToggleLeft( "Project window (Assets folder)", searchInAssetsFolder );
|
||||
|
||||
if( searchInAssetsFolder )
|
||||
{
|
||||
BeginIndentedGUI();
|
||||
searchInAssetsSubsetDrawer.Draw( searchInAssetsSubset );
|
||||
excludedAssetsDrawer.Draw( excludedAssets );
|
||||
EndIndentedGUI();
|
||||
}
|
||||
|
||||
GUILayout.Space( 5f );
|
||||
|
||||
dontSearchInSourceAssets = WordWrappingToggleLeft( "Don't search \"SEARCHED OBJECTS\" themselves for references", dontSearchInSourceAssets );
|
||||
searchUnusedMaterialProperties = WordWrappingToggleLeft( "Search unused material properties (e.g. normal map of a material that no longer uses normal mapping)", searchUnusedMaterialProperties );
|
||||
|
||||
Utilities.DrawSeparatorLine();
|
||||
|
||||
if( searchInAllScenes && !EditorApplication.isPlaying )
|
||||
GUI.enabled = false;
|
||||
|
||||
searchInOpenScenes = WordWrappingToggleLeft( "Currently open (loaded) scene(s)", searchInOpenScenes );
|
||||
|
||||
if( !EditorApplication.isPlaying )
|
||||
{
|
||||
searchInScenesInBuild = WordWrappingToggleLeft( "Scenes in Build Settings", searchInScenesInBuild );
|
||||
|
||||
if( searchInScenesInBuild )
|
||||
{
|
||||
BeginIndentedGUI( false );
|
||||
searchInScenesInBuildTickedOnly = EditorGUILayout.ToggleLeft( "Ticked only", searchInScenesInBuildTickedOnly, Utilities.GL_WIDTH_100 );
|
||||
searchInScenesInBuildTickedOnly = !EditorGUILayout.ToggleLeft( "All", !searchInScenesInBuildTickedOnly, Utilities.GL_WIDTH_100 );
|
||||
EndIndentedGUI( false );
|
||||
}
|
||||
|
||||
GUI.enabled = true;
|
||||
|
||||
searchInAllScenes = WordWrappingToggleLeft( "All scenes in the project", searchInAllScenes );
|
||||
}
|
||||
|
||||
BeginIndentedGUI();
|
||||
excludedScenesDrawer.Draw( excludedScenes );
|
||||
EndIndentedGUI();
|
||||
|
||||
EditorGUI.BeginDisabledGroup( !searchInOpenScenes && !searchInScenesInBuild && !searchInAllScenes );
|
||||
searchInSceneLightingSettings = WordWrappingToggleLeft( "Scene Lighting Settings (WARNING: This may change the active scene during search)", searchInSceneLightingSettings );
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
Utilities.DrawSeparatorLine();
|
||||
|
||||
searchInProjectSettings = WordWrappingToggleLeft( "Project Settings (Player Settings, Graphics Settings etc.)", searchInProjectSettings );
|
||||
|
||||
GUILayout.Space( 10f );
|
||||
|
||||
Utilities.DrawHeader( "<b>SETTINGS</b>" );
|
||||
|
||||
#if ASSET_USAGE_ADDRESSABLES
|
||||
EditorGUI.BeginDisabledGroup( addressablesSupport );
|
||||
#endif
|
||||
lazySceneSearch = WordWrappingToggleLeft( "Lazy scene search: scenes are searched in detail only when they are manually refreshed (faster search)", lazySceneSearch );
|
||||
#if ASSET_USAGE_ADDRESSABLES
|
||||
EditorGUI.EndDisabledGroup();
|
||||
addressablesSupport = WordWrappingToggleLeft( "Addressables support (Experimental) (WARNING: 'Lazy scene search' will be disabled) (slower search)", addressablesSupport );
|
||||
#endif
|
||||
calculateUnusedObjects = WordWrappingToggleLeft( "Calculate unused objects", calculateUnusedObjects );
|
||||
hideDuplicateRows = WordWrappingToggleLeft( "Hide duplicate rows in search results", hideDuplicateRows );
|
||||
hideRedundantPrefabReferencesInAssets = WordWrappingToggleLeft( hideRedundantPrefabReferencesInAssetsLabel, hideRedundantPrefabReferencesInAssets );
|
||||
hideRedundantPrefabReferencesInScenes = WordWrappingToggleLeft( hideRedundantPrefabReferencesInScenesLabel, hideRedundantPrefabReferencesInScenes );
|
||||
noAssetDatabaseChanges = WordWrappingToggleLeft( "I haven't modified any assets/scenes since the last search (faster search)", noAssetDatabaseChanges );
|
||||
showDetailedProgressBar = WordWrappingToggleLeft( "Update search progress bar more often (cancelable search) (slower search)", showDetailedProgressBar );
|
||||
|
||||
GUILayout.Space( 10f );
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
GUILayout.Space( 5f );
|
||||
}
|
||||
else if( currentPhase == Phase.Complete )
|
||||
{
|
||||
// Draw the results of the search
|
||||
GUI.enabled = false;
|
||||
|
||||
DrawObjectsToSearchSection();
|
||||
|
||||
if( drawObjectsToSearchSection )
|
||||
GUILayout.Space( 10f );
|
||||
|
||||
GUI.enabled = true;
|
||||
|
||||
if( GUILayout.Button( "Reset Search", Utilities.GL_HEIGHT_30 ) )
|
||||
{
|
||||
ReturnToSetupPhase();
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
if( searchResult.NumberOfGroups == 0 )
|
||||
{
|
||||
GUILayout.Space( 10f );
|
||||
GUILayout.Box( "No references found...", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH );
|
||||
}
|
||||
else
|
||||
{
|
||||
noAssetDatabaseChanges = WordWrappingToggleLeft( "I haven't modified any assets/scenes since the last search (faster Refresh)", noAssetDatabaseChanges );
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
scrollPosition.y = searchResult.DrawOnGUI( this, scrollPosition.y, noAssetDatabaseChanges );
|
||||
}
|
||||
}
|
||||
|
||||
if( Event.current.type == EventType.MouseLeaveWindow )
|
||||
{
|
||||
SearchResultTooltip.Hide();
|
||||
|
||||
if( searchResult != null )
|
||||
searchResult.CancelDelayedTreeViewTooltip();
|
||||
}
|
||||
|
||||
GUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void DrawObjectsToSearchSection()
|
||||
{
|
||||
Utilities.DrawHeader( "<b>SEARCHED OBJECTS</b>" );
|
||||
|
||||
Rect searchedObjectsHeaderRect = GUILayoutUtility.GetLastRect();
|
||||
searchedObjectsHeaderRect.x += 5f;
|
||||
searchedObjectsHeaderRect.yMin += ( searchedObjectsHeaderRect.height - EditorGUIUtility.singleLineHeight ) * 0.5f;
|
||||
searchedObjectsHeaderRect.height = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
drawObjectsToSearchSection = EditorGUI.Foldout( searchedObjectsHeaderRect, drawObjectsToSearchSection, GUIContent.none, true );
|
||||
|
||||
if( drawObjectsToSearchSection )
|
||||
objectsToSearchDrawer.Draw( objectsToSearch );
|
||||
}
|
||||
|
||||
private void BeginIndentedGUI( bool isVertical = true )
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space( 35f );
|
||||
|
||||
if( isVertical )
|
||||
GUILayout.BeginVertical();
|
||||
}
|
||||
|
||||
private void EndIndentedGUI( bool isVertical = true )
|
||||
{
|
||||
if( isVertical )
|
||||
GUILayout.EndVertical();
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public static bool WordWrappingToggleLeft( string label, bool value )
|
||||
{
|
||||
sharedGUIContent.text = label;
|
||||
sharedGUIContent.tooltip = null;
|
||||
return WordWrappingToggleLeft( sharedGUIContent, value );
|
||||
}
|
||||
|
||||
public static bool WordWrappingToggleLeft( GUIContent label, bool value )
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
bool result = EditorGUILayout.ToggleLeft( GUIContent.none, value, GL_WIDTH_12 );
|
||||
if( GUILayout.Button( label, EditorStyles.wordWrappedLabel ) )
|
||||
{
|
||||
GUI.FocusControl( null );
|
||||
result = !value;
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void InitiateSearch()
|
||||
{
|
||||
currentPhase = Phase.Processing;
|
||||
|
||||
SavePrefs();
|
||||
ReplacePrefabStageObjectsWithAssets( PrefabStageUtility.GetCurrentPrefabStage() );
|
||||
|
||||
// Start searching
|
||||
searchResult = core.Run( new AssetUsageDetector.Parameters()
|
||||
{
|
||||
objectsToSearch = !objectsToSearch.IsEmpty() ? new ObjectToSearchEnumerator( objectsToSearch ).ToArray() : null,
|
||||
searchInScenes = GetSceneSearchMode( true ),
|
||||
searchInSceneLightingSettings = searchInSceneLightingSettings,
|
||||
searchInAssetsFolder = searchInAssetsFolder,
|
||||
searchInAssetsSubset = !searchInAssetsSubset.IsEmpty() ? searchInAssetsSubset.ToArray() : null,
|
||||
excludedAssetsFromSearch = !excludedAssets.IsEmpty() ? excludedAssets.ToArray() : null,
|
||||
dontSearchInSourceAssets = dontSearchInSourceAssets,
|
||||
excludedScenesFromSearch = !excludedScenes.IsEmpty() ? excludedScenes.ToArray() : null,
|
||||
searchInProjectSettings = searchInProjectSettings,
|
||||
searchUnusedMaterialProperties = searchUnusedMaterialProperties,
|
||||
searchRefactoring = searchRefactoring,
|
||||
#if ASSET_USAGE_ADDRESSABLES
|
||||
lazySceneSearch = lazySceneSearch && !addressablesSupport,
|
||||
addressablesSupport = addressablesSupport,
|
||||
#else
|
||||
lazySceneSearch = lazySceneSearch,
|
||||
#endif
|
||||
calculateUnusedObjects = calculateUnusedObjects,
|
||||
hideDuplicateRows = hideDuplicateRows,
|
||||
hideRedundantPrefabReferencesInAssets = hideRedundantPrefabReferencesInAssets,
|
||||
hideRedundantPrefabReferencesInScenes = hideRedundantPrefabReferencesInScenes && searchInAssetsFolder,
|
||||
noAssetDatabaseChanges = noAssetDatabaseChanges,
|
||||
showDetailedProgressBar = showDetailedProgressBar
|
||||
} );
|
||||
|
||||
currentPhase = Phase.Complete;
|
||||
|
||||
// We really don't want SearchRefactoring to affect next searches unless the search is initiated via ShowAndSearch again
|
||||
searchRefactoring = null;
|
||||
|
||||
if( AssetUsageDetectorSettings.ShowCustomTooltip )
|
||||
wantsMouseMove = wantsMouseEnterLeaveWindow = true;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
GameObject prefabAsset = AssetDatabase.LoadAssetAtPath<GameObject>( prefabStage.assetPath );
|
||||
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<ObjectToSearch.SubAsset> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool ReturnToSetupPhase()
|
||||
{
|
||||
if( searchResult != null && !EditorApplication.isPlaying && !searchResult.RestoreInitialSceneSetup() )
|
||||
return false;
|
||||
|
||||
searchResult = null;
|
||||
currentPhase = Phase.Setup;
|
||||
wantsMouseMove = wantsMouseEnterLeaveWindow = false;
|
||||
|
||||
SearchResultTooltip.Hide();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void OnSettingsChanged( bool highlightedSearchTextColorChanged = false, bool tooltipDescriptionsColorChanged = false )
|
||||
{
|
||||
if( searchResult == null )
|
||||
return;
|
||||
|
||||
wantsMouseMove = wantsMouseEnterLeaveWindow = AssetUsageDetectorSettings.ShowCustomTooltip;
|
||||
|
||||
for( int i = searchResult.NumberOfGroups - 1; i >= 0; i-- )
|
||||
{
|
||||
if( searchResult[i].treeView != null )
|
||||
{
|
||||
searchResult[i].treeView.rowHeight = EditorGUIUtility.singleLineHeight + AssetUsageDetectorSettings.ExtraRowHeight;
|
||||
searchResult[i].treeView.OnSettingsChanged( highlightedSearchTextColorChanged, tooltipDescriptionsColorChanged );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 271a22c69c3d96c4dbdd04cca415a840
|
||||
timeCreated: 1520032279
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/AssetUsageDetectorWindow.cs
|
||||
uploadId: 894428
|
||||
130
Assets/Plugins/AssetUsageDetector/Editor/Enumerators.cs
Normal file
130
Assets/Plugins/AssetUsageDetector/Editor/Enumerators.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AssetUsageDetectorNamespace
|
||||
{
|
||||
public class EmptyEnumerator<T> : IEnumerable<T>, IEnumerator<T>
|
||||
{
|
||||
public T Current { get { return default( T ); } }
|
||||
object IEnumerator.Current { get { return Current; } }
|
||||
|
||||
public void Dispose() { }
|
||||
public void Reset() { }
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public class ObjectToSearchEnumerator : IEnumerable<Object>
|
||||
{
|
||||
public class Enumerator : IEnumerator<Object>
|
||||
{
|
||||
public Object Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if( subAssetIndex < 0 )
|
||||
return source[index].obj;
|
||||
|
||||
return source[index].subAssets[subAssetIndex].subAsset;
|
||||
}
|
||||
}
|
||||
|
||||
object IEnumerator.Current { get { return Current; } }
|
||||
|
||||
private List<ObjectToSearch> source;
|
||||
private int index;
|
||||
private int subAssetIndex;
|
||||
|
||||
public Enumerator( List<ObjectToSearch> source )
|
||||
{
|
||||
this.source = source;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
source = null;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if( subAssetIndex < -1 )
|
||||
{
|
||||
subAssetIndex = -1;
|
||||
|
||||
if( ++index >= source.Count )
|
||||
return false;
|
||||
|
||||
// Skip folder assets in the enumeration, AssetUsageDetector expands encountered folders automatically
|
||||
// and we don't want that to happen as source[index].subAssets already contains the folder's contents
|
||||
if( !source[index].obj.IsFolder() )
|
||||
return true;
|
||||
}
|
||||
|
||||
List<ObjectToSearch.SubAsset> subAssets = source[index].subAssets;
|
||||
if( subAssets != null )
|
||||
{
|
||||
while( ++subAssetIndex < subAssets.Count && !subAssets[subAssetIndex].shouldSearch )
|
||||
continue;
|
||||
|
||||
if( subAssetIndex < subAssets.Count )
|
||||
return true;
|
||||
}
|
||||
|
||||
subAssetIndex = -2;
|
||||
return MoveNext();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
index = -1;
|
||||
subAssetIndex = -2;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<ObjectToSearch> source;
|
||||
|
||||
public ObjectToSearchEnumerator( List<ObjectToSearch> source )
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public IEnumerator<Object> GetEnumerator()
|
||||
{
|
||||
return new Enumerator( source );
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public Object[] ToArray()
|
||||
{
|
||||
int count = 0;
|
||||
foreach( Object obj in this )
|
||||
count++;
|
||||
|
||||
Object[] result = new Object[count];
|
||||
int index = 0;
|
||||
foreach( Object obj in this )
|
||||
result[index++] = obj;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Assets/Plugins/AssetUsageDetector/Editor/Enumerators.cs.meta
Normal file
19
Assets/Plugins/AssetUsageDetector/Editor/Enumerators.cs.meta
Normal file
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 894047c47ce45cf40939dae24afcc72b
|
||||
timeCreated: 1562079461
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/Enumerators.cs
|
||||
uploadId: 894428
|
||||
252
Assets/Plugins/AssetUsageDetector/Editor/ListDrawer.cs
Normal file
252
Assets/Plugins/AssetUsageDetector/Editor/ListDrawer.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AssetUsageDetectorNamespace
|
||||
{
|
||||
public abstract class ListDrawer<T>
|
||||
{
|
||||
private readonly string label;
|
||||
private readonly bool acceptSceneObjects;
|
||||
|
||||
protected ListDrawer( string label, bool acceptSceneObjects )
|
||||
{
|
||||
this.label = label;
|
||||
this.acceptSceneObjects = acceptSceneObjects;
|
||||
}
|
||||
|
||||
// Exposes a list on GUI
|
||||
public bool Draw( List<T> list )
|
||||
{
|
||||
bool hasChanged = false;
|
||||
bool guiEnabled = GUI.enabled;
|
||||
|
||||
Event ev = Event.current;
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
GUILayout.Label( label );
|
||||
|
||||
if( guiEnabled )
|
||||
{
|
||||
// Handle drag & drop references to array
|
||||
// Credit: https://answers.unity.com/answers/657877/view.html
|
||||
if( ( ev.type == EventType.DragPerform || ev.type == EventType.DragUpdated ) && GUILayoutUtility.GetLastRect().Contains( ev.mousePosition ) )
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||||
if( ev.type == EventType.DragPerform )
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
|
||||
Object[] draggedObjects = DragAndDrop.objectReferences;
|
||||
if( draggedObjects.Length > 0 )
|
||||
{
|
||||
for( int i = 0; i < draggedObjects.Length; i++ )
|
||||
{
|
||||
if( draggedObjects[i] != null && !draggedObjects[i].Equals( null ) )
|
||||
{
|
||||
bool replacedNullElement = false;
|
||||
for( int j = 0; j < list.Count; j++ )
|
||||
{
|
||||
if( IsElementNull( list[j] ) )
|
||||
{
|
||||
list[j] = CreateElement( draggedObjects[i] );
|
||||
|
||||
replacedNullElement = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !replacedNullElement )
|
||||
list.Add( CreateElement( draggedObjects[i] ) );
|
||||
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ev.Use();
|
||||
}
|
||||
else if( ev.type == EventType.ContextClick && GUILayoutUtility.GetLastRect().Contains( ev.mousePosition ) )
|
||||
{
|
||||
GenericMenu contextMenu = new GenericMenu();
|
||||
contextMenu.AddItem( new GUIContent( "Clear" ), false, () =>
|
||||
{
|
||||
list.Clear();
|
||||
list.Add( CreateElement( null ) );
|
||||
} );
|
||||
contextMenu.ShowAsContext();
|
||||
|
||||
ev.Use();
|
||||
}
|
||||
|
||||
if( GUILayout.Button( "+", Utilities.GL_WIDTH_25 ) )
|
||||
list.Insert( 0, CreateElement( null ) );
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
for( int i = 0; i < list.Count; i++ )
|
||||
{
|
||||
T element = list[i];
|
||||
|
||||
GUI.changed = false;
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
Object prevObject = GetObjectFromElement( element );
|
||||
Object newObject = EditorGUILayout.ObjectField( "", prevObject, typeof( Object ), acceptSceneObjects );
|
||||
|
||||
if( GUI.changed )
|
||||
{
|
||||
hasChanged = true;
|
||||
SetObjectOfElement( list, i, newObject );
|
||||
}
|
||||
|
||||
if( guiEnabled )
|
||||
{
|
||||
if( GUILayout.Button( "+", Utilities.GL_WIDTH_25 ) )
|
||||
list.Insert( i + 1, CreateElement( null ) );
|
||||
|
||||
if( GUILayout.Button( "-", Utilities.GL_WIDTH_25 ) )
|
||||
{
|
||||
if( element != null && !element.Equals( null ) )
|
||||
hasChanged = true;
|
||||
|
||||
// Lists with no elements look ugly, always keep a dummy null variable
|
||||
if( list.Count > 1 )
|
||||
list.RemoveAt( i-- );
|
||||
else
|
||||
list[0] = CreateElement( null );
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
PostElementDrawer( element );
|
||||
}
|
||||
|
||||
return hasChanged;
|
||||
}
|
||||
|
||||
protected abstract T CreateElement( Object source );
|
||||
protected abstract Object GetObjectFromElement( T element );
|
||||
protected abstract void SetObjectOfElement( List<T> list, int index, Object value );
|
||||
protected abstract bool IsElementNull( T element );
|
||||
protected abstract void PostElementDrawer( T element );
|
||||
}
|
||||
|
||||
public class ObjectListDrawer : ListDrawer<Object>
|
||||
{
|
||||
public ObjectListDrawer( string label, bool acceptSceneObjects ) : base( label, acceptSceneObjects )
|
||||
{
|
||||
}
|
||||
|
||||
protected override Object CreateElement( Object source )
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
protected override Object GetObjectFromElement( Object element )
|
||||
{
|
||||
return element;
|
||||
}
|
||||
|
||||
protected override void SetObjectOfElement( List<Object> list, int index, Object value )
|
||||
{
|
||||
list[index] = value;
|
||||
}
|
||||
|
||||
protected override bool IsElementNull( Object element )
|
||||
{
|
||||
return element == null || element.Equals( null );
|
||||
}
|
||||
|
||||
protected override void PostElementDrawer( Object element )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class ObjectToSearchListDrawer : ListDrawer<ObjectToSearch>
|
||||
{
|
||||
public ObjectToSearchListDrawer() : base( "Find references of:", true )
|
||||
{
|
||||
}
|
||||
|
||||
protected override ObjectToSearch CreateElement( Object source )
|
||||
{
|
||||
return new ObjectToSearch( source );
|
||||
}
|
||||
|
||||
protected override Object GetObjectFromElement( ObjectToSearch element )
|
||||
{
|
||||
return element.obj;
|
||||
}
|
||||
|
||||
protected override void SetObjectOfElement( List<ObjectToSearch> list, int index, Object value )
|
||||
{
|
||||
list[index].obj = value;
|
||||
list[index].RefreshSubAssets();
|
||||
}
|
||||
|
||||
protected override bool IsElementNull( ObjectToSearch element )
|
||||
{
|
||||
return element == null || element.obj == null || element.obj.Equals( null );
|
||||
}
|
||||
|
||||
protected override void PostElementDrawer( ObjectToSearch element )
|
||||
{
|
||||
List<ObjectToSearch.SubAsset> subAssetsToSearch = element.subAssets;
|
||||
if( subAssetsToSearch.Count > 0 )
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
// 0-> all toggles off, 1-> mixed, 2-> all toggles on
|
||||
bool toggleAllSubAssets = subAssetsToSearch[0].shouldSearch;
|
||||
bool mixedToggle = false;
|
||||
for( int j = 1; j < subAssetsToSearch.Count; j++ )
|
||||
{
|
||||
if( subAssetsToSearch[j].shouldSearch != toggleAllSubAssets )
|
||||
{
|
||||
mixedToggle = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( mixedToggle )
|
||||
EditorGUI.showMixedValue = true;
|
||||
|
||||
GUI.changed = false;
|
||||
toggleAllSubAssets = EditorGUILayout.Toggle( toggleAllSubAssets, Utilities.GL_WIDTH_25 );
|
||||
if( GUI.changed )
|
||||
{
|
||||
for( int j = 0; j < subAssetsToSearch.Count; j++ )
|
||||
subAssetsToSearch[j].shouldSearch = toggleAllSubAssets;
|
||||
}
|
||||
|
||||
EditorGUI.showMixedValue = false;
|
||||
|
||||
element.showSubAssetsFoldout = EditorGUILayout.Foldout( element.showSubAssetsFoldout, "Include sub-assets in search:", true );
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if( element.showSubAssetsFoldout )
|
||||
{
|
||||
for( int j = 0; j < subAssetsToSearch.Count; j++ )
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
subAssetsToSearch[j].shouldSearch = EditorGUILayout.Toggle( subAssetsToSearch[j].shouldSearch, Utilities.GL_WIDTH_25 );
|
||||
|
||||
bool guiEnabled = GUI.enabled;
|
||||
GUI.enabled = false;
|
||||
EditorGUILayout.ObjectField( string.Empty, subAssetsToSearch[j].subAsset, typeof( Object ), true );
|
||||
GUI.enabled = guiEnabled;
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Assets/Plugins/AssetUsageDetector/Editor/ListDrawer.cs.meta
Normal file
19
Assets/Plugins/AssetUsageDetector/Editor/ListDrawer.cs.meta
Normal file
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88a4a4e861026b2498a437ce1e12b054
|
||||
timeCreated: 1568758673
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/ListDrawer.cs
|
||||
uploadId: 894428
|
||||
133
Assets/Plugins/AssetUsageDetector/Editor/ObjectToSearch.cs
Normal file
133
Assets/Plugins/AssetUsageDetector/Editor/ObjectToSearch.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.U2D;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace AssetUsageDetectorNamespace
|
||||
{
|
||||
[Serializable]
|
||||
public class ObjectToSearch
|
||||
{
|
||||
[Serializable]
|
||||
public class SubAsset
|
||||
{
|
||||
public Object subAsset;
|
||||
public bool shouldSearch;
|
||||
|
||||
public SubAsset( Object subAsset, bool shouldSearch )
|
||||
{
|
||||
this.subAsset = subAsset;
|
||||
this.shouldSearch = shouldSearch;
|
||||
}
|
||||
}
|
||||
|
||||
public Object obj;
|
||||
public List<SubAsset> subAssets;
|
||||
public bool showSubAssetsFoldout;
|
||||
|
||||
private static HashSet<Object> currentSubAssets;
|
||||
|
||||
public ObjectToSearch( Object obj, bool? shouldSearchChildren = null )
|
||||
{
|
||||
this.obj = obj;
|
||||
RefreshSubAssets( shouldSearchChildren );
|
||||
}
|
||||
|
||||
public void RefreshSubAssets( bool? shouldSearchChildren = null )
|
||||
{
|
||||
if( subAssets == null )
|
||||
subAssets = new List<SubAsset>();
|
||||
else
|
||||
subAssets.Clear();
|
||||
|
||||
if( currentSubAssets == null )
|
||||
currentSubAssets = new HashSet<Object>();
|
||||
else
|
||||
currentSubAssets.Clear();
|
||||
|
||||
AddSubAssets( obj, false, shouldSearchChildren );
|
||||
currentSubAssets.Clear();
|
||||
}
|
||||
|
||||
private void AddSubAssets( Object target, bool includeTarget, bool? shouldSearchChildren )
|
||||
{
|
||||
if( target == null || target.Equals( null ) )
|
||||
return;
|
||||
|
||||
if( !target.IsAsset() )
|
||||
{
|
||||
GameObject go = target as GameObject;
|
||||
if( !go || !go.scene.IsValid() )
|
||||
return;
|
||||
|
||||
// If this is a scene object, add its child objects to the sub-assets list
|
||||
// but don't include them in the search by default
|
||||
Transform goTransform = go.transform;
|
||||
Transform[] children = go.GetComponentsInChildren<Transform>( true );
|
||||
for( int i = 0; i < children.Length; i++ )
|
||||
{
|
||||
if( ReferenceEquals( children[i], goTransform ) )
|
||||
continue;
|
||||
|
||||
subAssets.Add( new SubAsset( children[i].gameObject, shouldSearchChildren ?? false ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !AssetDatabase.IsMainAsset( target ) || target is SceneAsset )
|
||||
return;
|
||||
|
||||
if( includeTarget )
|
||||
{
|
||||
if( currentSubAssets.Add( target ) )
|
||||
subAssets.Add( new SubAsset( target, shouldSearchChildren ?? true ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// If asset is a directory, add all of its contents as sub-assets recursively
|
||||
if( target.IsFolder() )
|
||||
{
|
||||
foreach( string filePath in Utilities.EnumerateFolderContents( target ) )
|
||||
AddSubAssets( AssetDatabase.LoadAssetAtPath<Object>( filePath ), true, shouldSearchChildren );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add Sprites of SpriteAtlases to the sub-assets list
|
||||
if( target is SpriteAtlas spriteAtlas )
|
||||
{
|
||||
Sprite[] packedSprites = AssetUsageDetector.spriteAtlasPackedSpritesGetter( spriteAtlas );
|
||||
if( packedSprites != null )
|
||||
{
|
||||
for( int i = 0; i < packedSprites.Length; i++ )
|
||||
{
|
||||
if( packedSprites[i] != null && currentSubAssets.Add( packedSprites[i] ) )
|
||||
subAssets.Add( new SubAsset( packedSprites[i], shouldSearchChildren ?? true ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find sub-asset(s) of the asset (if any)
|
||||
Object[] assets = AssetDatabase.LoadAllAssetsAtPath( AssetDatabase.GetAssetPath( target ) );
|
||||
for( int i = 0; i < assets.Length; i++ )
|
||||
{
|
||||
Object asset = assets[i];
|
||||
if( asset == null || asset.Equals( null ) || asset is Component || asset == target )
|
||||
continue;
|
||||
|
||||
// Nested prefabs in prefab assets add an additional native object of type 'UnityEngine.PrefabInstance' to the prefab. Managed type of that native type
|
||||
// is UnityEngine.Object (i.e. GetType() returns UnityEngine.Object, not UnityEngine.PrefabInstance). There are no possible references to these native
|
||||
// objects so skip them (we're checking for UnityEngine.Prefab because it includes other native types like UnityEngine.PrefabCreation, as well)
|
||||
if( target is GameObject && asset.GetType() == typeof( Object ) && asset.ToString().Contains( "(UnityEngine.Prefab" ) )
|
||||
continue;
|
||||
|
||||
if( currentSubAssets.Add( asset ) )
|
||||
subAssets.Add( new SubAsset( asset, shouldSearchChildren ?? true ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66d5a144a723fea40945afc069d4231d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/ObjectToSearch.cs
|
||||
uploadId: 894428
|
||||
375
Assets/Plugins/AssetUsageDetector/Editor/SearchRefactoring.cs
Normal file
375
Assets/Plugins/AssetUsageDetector/Editor/SearchRefactoring.cs
Normal file
@@ -0,0 +1,375 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace AssetUsageDetectorNamespace
|
||||
{
|
||||
public delegate void SearchRefactoring( SearchMatch match );
|
||||
|
||||
public abstract class SearchMatch
|
||||
{
|
||||
public readonly object Source;
|
||||
public readonly Object Context; // Almost always equal to Source. This is the Object that needs to be dirtied (if not null) to notify Unity of changes to Value
|
||||
public Object Value { get; private set; }
|
||||
|
||||
protected SearchMatch( object source, Object value )
|
||||
{
|
||||
Source = source;
|
||||
Context = source as Object;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
protected SearchMatch( object source, Object value, Object context ) : this( source, value )
|
||||
{
|
||||
Context = context;
|
||||
}
|
||||
|
||||
public void ChangeValue( Object newValue )
|
||||
{
|
||||
if( newValue == Value )
|
||||
return;
|
||||
|
||||
if( Context && ( Context.hideFlags & HideFlags.NotEditable ) == HideFlags.NotEditable )
|
||||
{
|
||||
Debug.LogWarning( "Can't change value of read-only Object: " + Context, Context );
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bool setContextDirty;
|
||||
if( ChangeValue( newValue, out setContextDirty ) )
|
||||
OnValueChanged( newValue, setContextDirty );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
Debug.LogException( e );
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract bool ChangeValue( Object newValue, out bool setContextDirty );
|
||||
|
||||
public void OnValueChanged( Object newValue, bool setContextDirty = true )
|
||||
{
|
||||
Value = newValue;
|
||||
|
||||
if( setContextDirty )
|
||||
{
|
||||
if( Context )
|
||||
{
|
||||
if( AssetDatabase.Contains( Context ) )
|
||||
EditorUtility.SetDirty( Context );
|
||||
else if( !EditorApplication.isPlaying )
|
||||
{
|
||||
EditorUtility.SetDirty( Context );
|
||||
|
||||
if( Context is Component )
|
||||
EditorSceneManager.MarkSceneDirty( ( (Component) Context ).gameObject.scene );
|
||||
else if( Context is GameObject )
|
||||
EditorSceneManager.MarkSceneDirty( ( (GameObject) Context ).scene );
|
||||
else
|
||||
EditorSceneManager.MarkAllScenesDirty();
|
||||
}
|
||||
}
|
||||
else if( !EditorApplication.isPlaying )
|
||||
EditorSceneManager.MarkAllScenesDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class GenericSearchMatch : SearchMatch
|
||||
{
|
||||
public delegate void SetterFunction( Object newValue );
|
||||
|
||||
public readonly SetterFunction Setter;
|
||||
|
||||
internal GenericSearchMatch( object source, Object value, SetterFunction setter ) : base( source, value ) { Setter = setter; }
|
||||
internal GenericSearchMatch( object source, Object value, Object context, SetterFunction setter ) : base( source, value, context ) { Setter = setter; }
|
||||
|
||||
protected override bool ChangeValue( Object newValue, out bool setContextDirty )
|
||||
{
|
||||
Setter( newValue );
|
||||
|
||||
setContextDirty = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ReadOnlySearchMatch : SearchMatch
|
||||
{
|
||||
internal ReadOnlySearchMatch( object source, Object value ) : base( source, value ) { }
|
||||
|
||||
protected override bool ChangeValue( Object newValue, out bool setContextDirty )
|
||||
{
|
||||
Debug.LogWarning( "Can't change value of " + GetType().Name );
|
||||
|
||||
setContextDirty = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// - Source: Object whose SerializedProperty points to Value
|
||||
/// - Value: Referenced object
|
||||
/// - SerializedProperty: The SerializedProperty that points to Value
|
||||
/// </summary>
|
||||
public class SerializedPropertyMatch : SearchMatch
|
||||
{
|
||||
public readonly SerializedProperty SerializedProperty; // Next or NextVisible mustn't be called with this SerializedProperty
|
||||
|
||||
internal SerializedPropertyMatch( Object source, Object value, SerializedProperty property ) : base( source, value ) { SerializedProperty = property; }
|
||||
|
||||
protected override bool ChangeValue( Object newValue, out bool setContextDirty )
|
||||
{
|
||||
setContextDirty = true;
|
||||
|
||||
switch( SerializedProperty.propertyType )
|
||||
{
|
||||
case SerializedPropertyType.ObjectReference:
|
||||
SerializedProperty.objectReferenceValue = newValue;
|
||||
if( SerializedProperty.objectReferenceValue != newValue )
|
||||
{
|
||||
Debug.LogWarning( "Couldn't cast " + newValue.GetType() + " to " + SerializedProperty.type );
|
||||
SerializedProperty.objectReferenceValue = Value;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
case SerializedPropertyType.ExposedReference:
|
||||
SerializedProperty.exposedReferenceValue = newValue;
|
||||
if( SerializedProperty.exposedReferenceValue != newValue )
|
||||
{
|
||||
Debug.LogWarning( "Couldn't cast " + newValue.GetType() + " to " + SerializedProperty.type );
|
||||
SerializedProperty.exposedReferenceValue = Value;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
case SerializedPropertyType.ManagedReference: SerializedProperty.managedReferenceValue = newValue; break;
|
||||
}
|
||||
|
||||
SerializedProperty.serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// - Source: Object whose variable points to Value
|
||||
/// - Value: Referenced object
|
||||
/// - Variable: FieldInfo, PropertyInfo or IEnumerable (ChangeValue may not work for all IEnumerables)
|
||||
/// </summary>
|
||||
public class ReflectionMatch : SearchMatch
|
||||
{
|
||||
public readonly object Variable;
|
||||
|
||||
internal ReflectionMatch( object source, Object value, object variable ) : base( source, value ) { Variable = variable; }
|
||||
|
||||
protected override bool ChangeValue( Object newValue, out bool setContextDirty )
|
||||
{
|
||||
setContextDirty = true;
|
||||
|
||||
if( Variable is FieldInfo )
|
||||
( (FieldInfo) Variable ).SetValue( Source, newValue );
|
||||
else if( Variable is PropertyInfo )
|
||||
{
|
||||
PropertyInfo property = (PropertyInfo) Variable;
|
||||
if( !property.CanWrite )
|
||||
{
|
||||
Debug.LogWarning( "Property is read-only: " + property.DeclaringType.FullName + "." + property.Name );
|
||||
return false;
|
||||
}
|
||||
|
||||
property.SetValue( Source, newValue, null );
|
||||
}
|
||||
else if( Variable is IList )
|
||||
{
|
||||
IList list = (IList) Variable;
|
||||
for( int i = list.Count - 1; i >= 0; i-- )
|
||||
{
|
||||
if( ReferenceEquals( list[i], Value ) )
|
||||
list[i] = newValue;
|
||||
}
|
||||
}
|
||||
else if( Variable is IDictionary )
|
||||
{
|
||||
IDictionary dictionary = (IDictionary) Variable;
|
||||
bool dictionaryModified;
|
||||
do
|
||||
{
|
||||
dictionaryModified = false;
|
||||
foreach( object dictKey in dictionary.Keys )
|
||||
{
|
||||
object dictValue = dictionary[dictKey];
|
||||
if( ReferenceEquals( dictKey, Value ) )
|
||||
{
|
||||
dictionary.Remove( dictKey );
|
||||
if( newValue )
|
||||
dictionary[newValue] = dictValue;
|
||||
|
||||
dictionaryModified = true;
|
||||
break;
|
||||
}
|
||||
else if( ReferenceEquals( dictValue, Value ) )
|
||||
{
|
||||
dictionary[dictKey] = newValue;
|
||||
dictionaryModified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while( dictionaryModified );
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning( "Can't change value of " + Variable.GetType().Name );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// - Source: MonoImporter (for scripts) or ShaderImporter
|
||||
/// - Value: Default value assigned to Source's specified variable in the Inspector
|
||||
/// - Variable: The variable of Source that Value is assigned to as default value
|
||||
/// - MonoScriptAllVariables: All variables of Source script if it's MonoImporter
|
||||
/// </summary>
|
||||
public class AssetImporterDefaultValueMatch : SearchMatch
|
||||
{
|
||||
public readonly string Variable;
|
||||
public readonly VariableGetterHolder[] MonoScriptAllVariables;
|
||||
|
||||
internal AssetImporterDefaultValueMatch( Object source, Object value, string variable, VariableGetterHolder[] monoScriptAllVariables ) : base( source, value )
|
||||
{
|
||||
Variable = variable;
|
||||
MonoScriptAllVariables = monoScriptAllVariables;
|
||||
}
|
||||
|
||||
protected override bool ChangeValue( Object newValue, out bool setContextDirty )
|
||||
{
|
||||
setContextDirty = false;
|
||||
|
||||
if( Source is MonoImporter )
|
||||
{
|
||||
MonoImporter monoImporter = (MonoImporter) Source;
|
||||
|
||||
List<string> variableNames = new List<string>( 8 );
|
||||
List<Object> variableValues = new List<Object>( 8 );
|
||||
|
||||
for( int i = 0; i < MonoScriptAllVariables.Length; i++ )
|
||||
{
|
||||
if (!MonoScriptAllVariables[i].IsProperty)
|
||||
{
|
||||
Object variableDefaultValue = monoImporter.GetDefaultReference( MonoScriptAllVariables[i].Name );
|
||||
if( variableDefaultValue == Value && MonoScriptAllVariables[i].Name == Variable )
|
||||
variableDefaultValue = newValue;
|
||||
|
||||
variableNames.Add( MonoScriptAllVariables[i].Name );
|
||||
variableValues.Add( variableDefaultValue );
|
||||
}
|
||||
}
|
||||
|
||||
monoImporter.SetDefaultReferences( variableNames.ToArray(), variableValues.ToArray() );
|
||||
EditorApplication.delayCall += () => AssetDatabase.ImportAsset( monoImporter.assetPath ); // If code recompiles during search, it will break the search. Give it a 1 frame delay
|
||||
}
|
||||
else if( Source is ShaderImporter )
|
||||
{
|
||||
ShaderImporter shaderImporter = (ShaderImporter) Source;
|
||||
Shader shader = shaderImporter.GetShader();
|
||||
|
||||
List<string> textureNames = new List<string>( 16 );
|
||||
List<Texture> textureValues = new List<Texture>( 16 );
|
||||
List<string> nonModifiableTextureNames = new List<string>( 16 );
|
||||
List<Texture> nonModifiableTextureValues = new List<Texture>( 16 );
|
||||
|
||||
int shaderPropertyCount = shader.GetPropertyCount();
|
||||
for( int i = 0; i < shaderPropertyCount; i++ )
|
||||
{
|
||||
if (shader.GetPropertyType(i) != ShaderPropertyType.Texture)
|
||||
continue;
|
||||
|
||||
string propertyName = shader.GetPropertyName(i);
|
||||
if ((shader.GetPropertyFlags(i) & ShaderPropertyFlags.NonModifiableTextureData) != 0)
|
||||
{
|
||||
Texture propertyDefaultValue = shaderImporter.GetNonModifiableTexture( propertyName );
|
||||
if( propertyDefaultValue == Value && propertyName == Variable )
|
||||
propertyDefaultValue = (Texture) newValue;
|
||||
|
||||
nonModifiableTextureNames.Add( propertyName );
|
||||
nonModifiableTextureValues.Add( propertyDefaultValue );
|
||||
}
|
||||
else
|
||||
{
|
||||
Texture propertyDefaultValue = shaderImporter.GetDefaultTexture( propertyName );
|
||||
if( propertyDefaultValue == Value && propertyName == Variable )
|
||||
propertyDefaultValue = (Texture) newValue;
|
||||
|
||||
textureNames.Add( propertyName );
|
||||
textureValues.Add( propertyDefaultValue );
|
||||
}
|
||||
}
|
||||
|
||||
shaderImporter.SetDefaultTextures( textureNames.ToArray(), textureValues.ToArray() );
|
||||
shaderImporter.SetNonModifiableTextures( nonModifiableTextureNames.ToArray(), nonModifiableTextureValues.ToArray() );
|
||||
AssetDatabase.ImportAsset( shaderImporter.assetPath );
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning( "Can't change default value of: " + Source.GetType() );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// - Source: Animation, Animator, AnimatorStateMachine, AnimatorState, AnimatorControllerLayer, BlendTree, PlayableDirector* or AnimationClip*
|
||||
/// - Context: If Source is AnimatorControllerLayer, then its RuntimeAnimatorController. Otherwise, equal to Source
|
||||
/// - Value: AnimationClip, AnimatorController or AvatarMask used in Source (*for PlayableDirector and AnimationClip, it can be any Object value)
|
||||
/// </summary>
|
||||
public class AnimationSystemMatch : GenericSearchMatch
|
||||
{
|
||||
internal AnimationSystemMatch( object source, Object value, SetterFunction setter ) : base( source, value, setter ) { }
|
||||
internal AnimationSystemMatch( object source, Object value, Object context, SetterFunction setter ) : base( source, value, context, setter ) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// - Source: GameObject, AnimatorStateMachine or AnimatorState
|
||||
/// - Value: The attached behaviour's source script (C# script or DLL, i.e. MonoScript)
|
||||
/// - Behaviour: The attached behaviour (MonoBehaviour or StateMachineBehaviour)
|
||||
/// </summary>
|
||||
public class BehaviourUsageMatch : ReadOnlySearchMatch
|
||||
{
|
||||
public readonly Object Behaviour;
|
||||
|
||||
internal BehaviourUsageMatch( Object source, MonoScript value, Object behaviour ) : base( source, value ) { Behaviour = behaviour; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// - Source: GameObject Instance
|
||||
/// - Value: Prefab of that GameObject
|
||||
/// </summary>
|
||||
public class PrefabMatch : ReadOnlySearchMatch
|
||||
{
|
||||
internal PrefabMatch( Object source, Object value ) : base( source, value ) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// - Source: Object that references Value
|
||||
/// - Value: Matched object
|
||||
/// </summary>
|
||||
public class OtherSearchMatch : GenericSearchMatch
|
||||
{
|
||||
internal OtherSearchMatch( object source, Object value, SetterFunction setter ) : base( source, value, setter ) { }
|
||||
internal OtherSearchMatch( object source, Object value, Object context, SetterFunction setter ) : base( source, value, context, setter ) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d15ef8bd8f7c7c4e8d228c99713b7eb
|
||||
timeCreated: 1641132238
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/SearchRefactoring.cs
|
||||
uploadId: 894428
|
||||
1421
Assets/Plugins/AssetUsageDetector/Editor/SearchResult.cs
Normal file
1421
Assets/Plugins/AssetUsageDetector/Editor/SearchResult.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca236e4f3c5a9f447be89f0e61e485fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/SearchResult.cs
|
||||
uploadId: 894428
|
||||
@@ -0,0 +1,91 @@
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AssetUsageDetectorNamespace
|
||||
{
|
||||
public class SearchResultTooltip : EditorWindow
|
||||
{
|
||||
private static SearchResultTooltip mainWindow;
|
||||
private static string tooltip;
|
||||
|
||||
private static GUIStyle m_style;
|
||||
internal static GUIStyle Style
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_style == null )
|
||||
{
|
||||
m_style = (GUIStyle) typeof( EditorStyles ).GetProperty( "tooltip", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ).GetValue( null, null );
|
||||
m_style.richText = true;
|
||||
}
|
||||
|
||||
return m_style;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Show( Rect sourcePosition, string tooltip )
|
||||
{
|
||||
// Don't lose focus to the previous window
|
||||
EditorWindow prevFocusedWindow = focusedWindow;
|
||||
|
||||
if (!mainWindow)
|
||||
{
|
||||
mainWindow = CreateInstance<SearchResultTooltip>();
|
||||
mainWindow.ShowPopup();
|
||||
}
|
||||
|
||||
Vector2 preferredSize = Style.CalcSize( new GUIContent( tooltip ) ) + Style.contentOffset + new Vector2( Style.padding.horizontal + Style.margin.horizontal, Style.padding.vertical + Style.margin.vertical );
|
||||
Rect preferredPosition;
|
||||
|
||||
Rect positionLeft = new Rect( sourcePosition.position - new Vector2( preferredSize.x, 0f ), preferredSize );
|
||||
Rect screenFittedPositionLeft = Utilities.GetScreenFittedRect(positionLeft, mainWindow);
|
||||
|
||||
Vector2 positionOffset = positionLeft.position - screenFittedPositionLeft.position;
|
||||
Vector2 sizeOffset = positionLeft.size - screenFittedPositionLeft.size;
|
||||
if( positionOffset.sqrMagnitude <= 400f && sizeOffset.sqrMagnitude <= 400f )
|
||||
preferredPosition = screenFittedPositionLeft;
|
||||
else
|
||||
{
|
||||
Rect positionRight = new Rect( sourcePosition.position + new Vector2( sourcePosition.width, 0f ), preferredSize );
|
||||
Rect screenFittedPositionRight = Utilities.GetScreenFittedRect(positionRight, mainWindow);
|
||||
|
||||
Vector2 positionOffset2 = positionRight.position - screenFittedPositionRight.position;
|
||||
Vector2 sizeOffset2 = positionRight.size - screenFittedPositionRight.size;
|
||||
if( positionOffset2.magnitude + sizeOffset2.magnitude < positionOffset.magnitude + sizeOffset.magnitude )
|
||||
preferredPosition = screenFittedPositionRight;
|
||||
else
|
||||
preferredPosition = screenFittedPositionLeft;
|
||||
}
|
||||
|
||||
SearchResultTooltip.tooltip = tooltip;
|
||||
mainWindow.minSize = preferredPosition.size;
|
||||
mainWindow.position = preferredPosition;
|
||||
mainWindow.Repaint();
|
||||
|
||||
if( prevFocusedWindow )
|
||||
prevFocusedWindow.Focus();
|
||||
}
|
||||
|
||||
public static void Hide()
|
||||
{
|
||||
if( mainWindow )
|
||||
{
|
||||
mainWindow.Close();
|
||||
mainWindow = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
// If somehow the tooltip isn't automatically closed, allow closing it by clicking on it
|
||||
if( Event.current.type == EventType.MouseDown )
|
||||
{
|
||||
Hide();
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
|
||||
GUI.Label( new Rect( Vector2.zero, position.size ), tooltip, Style );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8bd4351b5024324ca5974ebcad1dde3
|
||||
timeCreated: 1639247551
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/SearchResultTooltip.cs
|
||||
uploadId: 894428
|
||||
1322
Assets/Plugins/AssetUsageDetector/Editor/SearchResultTreeView.cs
Normal file
1322
Assets/Plugins/AssetUsageDetector/Editor/SearchResultTreeView.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3903f1d3149e9b4990e49206f8255c8
|
||||
timeCreated: 1638690728
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/SearchResultTreeView.cs
|
||||
uploadId: 894428
|
||||
568
Assets/Plugins/AssetUsageDetector/Editor/Utilities.cs
Normal file
568
Assets/Plugins/AssetUsageDetector/Editor/Utilities.cs
Normal file
@@ -0,0 +1,568 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Unity.Collections;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace AssetUsageDetectorNamespace
|
||||
{
|
||||
public static class Utilities
|
||||
{
|
||||
// A set of commonly used Unity types
|
||||
private static readonly HashSet<Type> primitiveUnityTypes = new HashSet<Type>()
|
||||
{
|
||||
typeof( string ), typeof( Vector4 ), typeof( Vector3 ), typeof( Vector2 ), typeof( Rect ),
|
||||
typeof( Quaternion ), typeof( Color ), typeof( Color32 ), typeof( LayerMask ), typeof( Bounds ),
|
||||
typeof( Matrix4x4 ), typeof( AnimationCurve ), typeof( Gradient ), typeof( RectOffset ),
|
||||
typeof( bool[] ), typeof( byte[] ), typeof( sbyte[] ), typeof( char[] ), typeof( decimal[] ),
|
||||
typeof( double[] ), typeof( float[] ), typeof( int[] ), typeof( uint[] ), typeof( long[] ),
|
||||
typeof( ulong[] ), typeof( short[] ), typeof( ushort[] ), typeof( string[] ),
|
||||
typeof( Vector4[] ), typeof( Vector3[] ), typeof( Vector2[] ), typeof( Rect[] ),
|
||||
typeof( Quaternion[] ), typeof( Color[] ), typeof( Color32[] ), typeof( LayerMask[] ), typeof( Bounds[] ),
|
||||
typeof( Matrix4x4[] ), typeof( AnimationCurve[] ), typeof( Gradient[] ), typeof( RectOffset[] ),
|
||||
typeof( List<bool> ), typeof( List<byte> ), typeof( List<sbyte> ), typeof( List<char> ), typeof( List<decimal> ),
|
||||
typeof( List<double> ), typeof( List<float> ), typeof( List<int> ), typeof( List<uint> ), typeof( List<long> ),
|
||||
typeof( List<ulong> ), typeof( List<short> ), typeof( List<ushort> ), typeof( List<string> ),
|
||||
typeof( List<Vector4> ), typeof( List<Vector3> ), typeof( List<Vector2> ), typeof( List<Rect> ),
|
||||
typeof( List<Quaternion> ), typeof( List<Color> ), typeof( List<Color32> ), typeof( List<LayerMask> ), typeof( List<Bounds> ),
|
||||
typeof( List<Matrix4x4> ), typeof( List<AnimationCurve> ), typeof( List<Gradient> ), typeof( List<RectOffset> ),
|
||||
typeof( Vector3Int ), typeof( Vector2Int ), typeof( RectInt ), typeof( BoundsInt ),
|
||||
typeof( Vector3Int[] ), typeof( Vector2Int[] ), typeof( RectInt[] ), typeof( BoundsInt[] ),
|
||||
typeof( List<Vector3Int> ), typeof( List<Vector2Int> ), typeof( List<RectInt> ), typeof( List<BoundsInt> )
|
||||
};
|
||||
|
||||
private static readonly string reflectionNamespace = typeof( Assembly ).Namespace;
|
||||
private static readonly string nativeCollectionsNamespace = typeof( NativeArray<int> ).Namespace;
|
||||
|
||||
private static MethodInfo screenFittedRectGetter;
|
||||
private static FieldInfo editorWindowHostViewGetter;
|
||||
private static PropertyInfo hostViewContainerWindowGetter;
|
||||
|
||||
private static readonly Func<Object, bool, bool> prefabHasAnyOverridesGetter = (Func<Object, bool, bool>) Delegate.CreateDelegate( typeof( Func<Object, bool, bool> ), typeof( PrefabUtility ).GetMethod( "HasObjectOverride", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ) );
|
||||
|
||||
private static readonly HashSet<string> folderContentsSet = new HashSet<string>();
|
||||
|
||||
internal static readonly StringBuilder stringBuilder = new StringBuilder( 400 );
|
||||
|
||||
public static readonly GUILayoutOption GL_EXPAND_WIDTH = GUILayout.ExpandWidth( true );
|
||||
public static readonly GUILayoutOption GL_EXPAND_HEIGHT = GUILayout.ExpandHeight( true );
|
||||
public static readonly GUILayoutOption GL_WIDTH_25 = GUILayout.Width( 25 );
|
||||
public static readonly GUILayoutOption GL_WIDTH_100 = GUILayout.Width( 100 );
|
||||
public static readonly GUILayoutOption GL_WIDTH_250 = GUILayout.Width( 250 );
|
||||
public static readonly GUILayoutOption GL_HEIGHT_0 = GUILayout.Height( 0 );
|
||||
public static readonly GUILayoutOption GL_HEIGHT_2 = GUILayout.Height( 2 );
|
||||
public static readonly GUILayoutOption GL_HEIGHT_30 = GUILayout.Height( 30 );
|
||||
public static readonly GUILayoutOption GL_HEIGHT_35 = GUILayout.Height( 35 );
|
||||
public static readonly GUILayoutOption GL_HEIGHT_40 = GUILayout.Height( 40 );
|
||||
|
||||
private static GUIStyle m_boxGUIStyle; // GUIStyle used to draw the results of the search
|
||||
public static GUIStyle BoxGUIStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_boxGUIStyle == null )
|
||||
{
|
||||
m_boxGUIStyle = new GUIStyle( EditorStyles.helpBox )
|
||||
{
|
||||
alignment = TextAnchor.MiddleCenter,
|
||||
font = EditorStyles.label.font,
|
||||
richText = true
|
||||
};
|
||||
|
||||
Color textColor = GUI.skin.button.normal.textColor;
|
||||
m_boxGUIStyle.normal.textColor = textColor;
|
||||
m_boxGUIStyle.hover.textColor = textColor;
|
||||
m_boxGUIStyle.focused.textColor = textColor;
|
||||
m_boxGUIStyle.active.textColor = textColor;
|
||||
m_boxGUIStyle.fontSize = ( m_boxGUIStyle.fontSize + GUI.skin.button.fontSize ) / 2;
|
||||
}
|
||||
|
||||
return m_boxGUIStyle;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if object is an asset or a Scene object
|
||||
public static bool IsAsset( this object obj )
|
||||
{
|
||||
return obj is Object && AssetDatabase.Contains( (Object) obj );
|
||||
}
|
||||
|
||||
public static bool IsAsset( this Object obj )
|
||||
{
|
||||
return AssetDatabase.Contains( obj );
|
||||
}
|
||||
|
||||
// Check if object is a folder asset
|
||||
public static bool IsFolder( this Object obj )
|
||||
{
|
||||
return obj is DefaultAsset && AssetDatabase.IsValidFolder( AssetDatabase.GetAssetPath( obj ) );
|
||||
}
|
||||
|
||||
public static T GetPrefabParent<T>( this T obj ) where T : Object
|
||||
{
|
||||
return PrefabUtility.GetCorrespondingObjectFromSource( obj );
|
||||
}
|
||||
|
||||
public static bool HasAnyPrefabOverrides( this Object obj )
|
||||
{
|
||||
if( obj.GetPrefabParent() == null )
|
||||
return false;
|
||||
|
||||
return prefabHasAnyOverridesGetter( obj, false );
|
||||
}
|
||||
|
||||
public static bool HasAnyPrefabOverrides( this GameObject gameObject )
|
||||
{
|
||||
if( gameObject.GetPrefabParent() == null )
|
||||
return false;
|
||||
|
||||
if( !PrefabUtility.HasPrefabInstanceAnyOverrides( gameObject, false ) )
|
||||
return false;
|
||||
|
||||
Transform rootTransform = gameObject.transform;
|
||||
List<Component> components = new List<Component>( 8 );
|
||||
Stack<Transform> stack = new Stack<Transform>( 8 );
|
||||
stack.Push( rootTransform );
|
||||
|
||||
while( stack.Count > 0 )
|
||||
{
|
||||
Transform transform = stack.Pop();
|
||||
Transform prefab = transform.GetPrefabParent();
|
||||
if( prefab == null ) // GameObject is added as override
|
||||
return true;
|
||||
|
||||
if( transform.childCount != prefab.childCount ) // Has some added/destroyed children as override
|
||||
return true;
|
||||
|
||||
bool isRootTransform = ReferenceEquals( transform, rootTransform );
|
||||
if( !isRootTransform && ( transform.gameObject as Object ).HasAnyPrefabOverrides() ) // GameObject's properties are modified (e.g. name or tag) (excluding root GameObject)
|
||||
return true;
|
||||
|
||||
components.Clear();
|
||||
transform.GetComponents( components );
|
||||
foreach( Component component in components )
|
||||
{
|
||||
if( component == null )
|
||||
continue;
|
||||
|
||||
if( component.GetPrefabParent() == null ) // Component is added as override
|
||||
return true;
|
||||
|
||||
if( ( !isRootTransform || !( component is Transform ) ) && component.HasAnyPrefabOverrides() ) // Component is modified (excluding root Transform)
|
||||
return true;
|
||||
}
|
||||
|
||||
int componentCount = components.Count;
|
||||
components.Clear();
|
||||
prefab.GetComponents( components );
|
||||
if( components.Count != componentCount ) // Has some destroyed components as override
|
||||
return true;
|
||||
|
||||
for( int i = 0, childCount = transform.childCount; i < childCount; i++ )
|
||||
{
|
||||
Transform child = transform.GetChild( i );
|
||||
if( child != null )
|
||||
stack.Push( child );
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if UNITY_6000_3_OR_NEWER
|
||||
public static EntityId GetEntityId(this Object obj)
|
||||
{
|
||||
return obj.GetEntityId();
|
||||
}
|
||||
|
||||
public static Object EntityIdToObject(EntityId entityId)
|
||||
{
|
||||
return EditorUtility.EntityIdToObject(entityId);
|
||||
}
|
||||
#else
|
||||
public static int GetEntityId(this Object obj)
|
||||
{
|
||||
return obj.GetInstanceID();
|
||||
}
|
||||
|
||||
public static Object EntityIdToObject(int instanceID)
|
||||
{
|
||||
return EditorUtility.InstanceIDToObject(instanceID);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Returns an enumerator to iterate through all asset paths in the folder
|
||||
public static IEnumerable<string> EnumerateFolderContents( Object folderAsset )
|
||||
{
|
||||
string[] folderContents = AssetDatabase.FindAssets( "", new string[] { AssetDatabase.GetAssetPath( folderAsset ) } );
|
||||
if( folderContents == null )
|
||||
return new EmptyEnumerator<string>();
|
||||
|
||||
folderContentsSet.Clear();
|
||||
for( int i = 0; i < folderContents.Length; i++ )
|
||||
{
|
||||
string filePath = AssetDatabase.GUIDToAssetPath( folderContents[i] );
|
||||
if( !string.IsNullOrEmpty( filePath ) && !AssetDatabase.IsValidFolder( filePath ) )
|
||||
folderContentsSet.Add( filePath );
|
||||
}
|
||||
|
||||
return folderContentsSet;
|
||||
}
|
||||
|
||||
public static void GetObjectsToSelectAndPing( this Object obj, out Object selection, out Object pingTarget )
|
||||
{
|
||||
if( obj == null || obj.Equals( null ) )
|
||||
{
|
||||
selection = pingTarget = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if( obj is Component )
|
||||
obj = ( (Component) obj ).gameObject;
|
||||
|
||||
selection = pingTarget = obj;
|
||||
|
||||
if( obj.IsAsset() )
|
||||
{
|
||||
if( obj is GameObject )
|
||||
{
|
||||
// Pinging a prefab only works if the pinged object is the root of the prefab or a direct child of it. Pinging any grandchildren
|
||||
// of the prefab doesn't work; in which case, traverse the parent hierarchy until a pingable parent is reached
|
||||
Transform objTR = ( (GameObject) obj ).transform.root;
|
||||
|
||||
PrefabAssetType prefabAssetType = PrefabUtility.GetPrefabAssetType( objTR.gameObject );
|
||||
if( prefabAssetType == PrefabAssetType.Regular || prefabAssetType == PrefabAssetType.Variant )
|
||||
{
|
||||
string assetPath = AssetDatabase.GetAssetPath( objTR.gameObject );
|
||||
PrefabStage openPrefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
if( openPrefabStage != null && openPrefabStage.stageHandle.IsValid() && assetPath == openPrefabStage.assetPath )
|
||||
{
|
||||
GameObject prefabStageGO = FollowSymmetricHierarchy( (GameObject) obj, ( (GameObject) obj ).transform.root.gameObject, openPrefabStage.prefabContentsRoot );
|
||||
if( prefabStageGO != null )
|
||||
{
|
||||
objTR = prefabStageGO.transform;
|
||||
selection = objTR.gameObject;
|
||||
}
|
||||
}
|
||||
else if( obj != objTR.gameObject )
|
||||
selection = objTR.gameObject;
|
||||
}
|
||||
else if( prefabAssetType == PrefabAssetType.Model )
|
||||
{
|
||||
objTR = ( (GameObject) obj ).transform;
|
||||
while( objTR.parent != null && objTR.parent.parent != null )
|
||||
objTR = objTR.parent;
|
||||
}
|
||||
|
||||
pingTarget = objTR.gameObject;
|
||||
}
|
||||
else if( ( obj.hideFlags & ( HideFlags.HideInInspector | HideFlags.HideInHierarchy ) ) != HideFlags.None )
|
||||
{
|
||||
// Can't ping assets that are hidden from Project window (e.g. animator states of AnimatorController), ping the main asset at that path instead
|
||||
pingTarget = AssetDatabase.LoadMainAssetAtPath( AssetDatabase.GetAssetPath( obj ) );
|
||||
}
|
||||
else if( !AssetDatabase.IsMainAsset( obj ) && Array.IndexOf( AssetDatabase.LoadAllAssetRepresentationsAtPath( AssetDatabase.GetAssetPath( obj ) ), obj ) < 0 )
|
||||
{
|
||||
// VFX Graph assets' nodes are serialized as part of the graph but they are invisible in the Project window even though their hideFlags is None (I don't know how)
|
||||
pingTarget = AssetDatabase.LoadMainAssetAtPath( AssetDatabase.GetAssetPath( obj ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We are passing "go"s root Transform to thisRoot parameter. If we use go.transform.root instead, when we are in prefab mode on
|
||||
// newer Unity versions, it points to the preview scene at the root of the prefab stage instead of pointing to the actual root of "go"
|
||||
public static GameObject FollowSymmetricHierarchy( this GameObject go, GameObject thisRoot, GameObject symmetricRoot )
|
||||
{
|
||||
Transform target = go.transform;
|
||||
Transform root1 = thisRoot.transform;
|
||||
Transform root2 = symmetricRoot.transform;
|
||||
while( root1 != target )
|
||||
{
|
||||
Transform temp = target;
|
||||
while( temp.parent != root1 )
|
||||
temp = temp.parent;
|
||||
|
||||
Transform newRoot2;
|
||||
int siblingIndex = temp.GetSiblingIndex();
|
||||
if( siblingIndex < root2.childCount )
|
||||
{
|
||||
newRoot2 = root2.GetChild( siblingIndex );
|
||||
if( newRoot2.name != temp.name )
|
||||
newRoot2 = root2.Find( temp.name );
|
||||
}
|
||||
else
|
||||
newRoot2 = root2.Find( temp.name );
|
||||
|
||||
if( newRoot2 == null )
|
||||
return null;
|
||||
|
||||
root2 = newRoot2;
|
||||
root1 = temp;
|
||||
}
|
||||
|
||||
return root2.gameObject;
|
||||
}
|
||||
|
||||
// Returns -1 if t1 is above t2 in Hierarchy, 1 if t1 is below t2 in Hierarchy and 0 if they are the same object
|
||||
public static int CompareHierarchySiblingIndices( Transform t1, Transform t2 )
|
||||
{
|
||||
Transform parent1 = t1.parent;
|
||||
Transform parent2 = t2.parent;
|
||||
|
||||
if( parent1 == parent2 )
|
||||
return t1.GetSiblingIndex() - t2.GetSiblingIndex();
|
||||
|
||||
int deltaHierarchyDepth = 0;
|
||||
for( ; parent1; parent1 = parent1.parent )
|
||||
deltaHierarchyDepth++;
|
||||
for( ; parent2; parent2 = parent2.parent )
|
||||
deltaHierarchyDepth--;
|
||||
|
||||
for( ; deltaHierarchyDepth > 0; deltaHierarchyDepth-- )
|
||||
{
|
||||
t1 = t1.parent;
|
||||
if( t1 == t2 )
|
||||
return 1;
|
||||
}
|
||||
for( ; deltaHierarchyDepth < 0; deltaHierarchyDepth++ )
|
||||
{
|
||||
t2 = t2.parent;
|
||||
if( t1 == t2 )
|
||||
return -1;
|
||||
}
|
||||
|
||||
while( t1.parent != t2.parent )
|
||||
{
|
||||
t1 = t1.parent;
|
||||
t2 = t2.parent;
|
||||
}
|
||||
|
||||
return t1.GetSiblingIndex() - t2.GetSiblingIndex();
|
||||
}
|
||||
|
||||
// Check if instances of this type should be searched for references
|
||||
public static bool IsIgnoredUnityType( this Type type )
|
||||
{
|
||||
if( type.IsPrimitive || primitiveUnityTypes.Contains( type ) || type.IsEnum )
|
||||
return true;
|
||||
|
||||
// Searching NativeArrays for reference can throw InvalidOperationException if the collection is disposed
|
||||
if( type.Namespace == nativeCollectionsNamespace )
|
||||
return true;
|
||||
|
||||
// Searching assembly variables for reference throws InvalidCastException on .NET 4.0 runtime
|
||||
if( typeof( Type ).IsAssignableFrom( type ) || type.Namespace == reflectionNamespace )
|
||||
return true;
|
||||
|
||||
// Searching Tasks for reference may freeze Unity since Task.Result freezes the active thread if Task hasn't completed yet
|
||||
if (typeof(Task).IsAssignableFrom(type))
|
||||
return true;
|
||||
|
||||
// Searching pointers, ref variables or ref structs for reference throws ArgumentException
|
||||
if (type.IsPointer || type.IsByRef || type.IsByRefLike)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get <get> function for a field
|
||||
public static VariableGetVal CreateGetter( this FieldInfo fieldInfo, Type type )
|
||||
{
|
||||
// Commented the IL generator code below because it might actually be slower than simply using reflection
|
||||
// Credit: https://www.codeproject.com/Articles/14560/Fast-Dynamic-Property-Field-Accessors
|
||||
//DynamicMethod dm = new DynamicMethod( "Get" + fieldInfo.Name, fieldInfo.FieldType, new Type[] { typeof( object ) }, type );
|
||||
//ILGenerator il = dm.GetILGenerator();
|
||||
//// Load the instance of the object (argument 0) onto the stack
|
||||
//il.Emit( OpCodes.Ldarg_0 );
|
||||
//// Load the value of the object's field (fi) onto the stack
|
||||
//il.Emit( OpCodes.Ldfld, fieldInfo );
|
||||
//// return the value on the top of the stack
|
||||
//il.Emit( OpCodes.Ret );
|
||||
|
||||
//return (VariableGetVal) dm.CreateDelegate( typeof( VariableGetVal ) );
|
||||
|
||||
return fieldInfo.GetValue;
|
||||
}
|
||||
|
||||
// Get <get> function for a property
|
||||
public static VariableGetVal CreateGetter( this PropertyInfo propertyInfo )
|
||||
{
|
||||
// Can't use PropertyWrapper (which uses CreateDelegate) for property getters of structs
|
||||
if( propertyInfo.DeclaringType.IsValueType )
|
||||
{
|
||||
return !propertyInfo.CanRead ? (VariableGetVal) null : ( obj ) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return propertyInfo.GetValue( obj, null );
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Property getters may return various kinds of exceptions if their backing fields are not initialized (yet)
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Type GenType = typeof( PropertyWrapper<,> ).MakeGenericType( propertyInfo.DeclaringType, propertyInfo.PropertyType );
|
||||
return ( (IPropertyAccessor) Activator.CreateInstance( GenType, propertyInfo.GetGetMethod( true ) ) ).GetValue;
|
||||
}
|
||||
|
||||
// Check if all open scenes are saved (not dirty)
|
||||
public static bool AreScenesSaved()
|
||||
{
|
||||
for( int i = 0; i < SceneManager.sceneCount; i++ )
|
||||
{
|
||||
Scene scene = SceneManager.GetSceneAt( i );
|
||||
if( scene.isDirty || string.IsNullOrEmpty( scene.path ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns file extension in lowercase (period not included)
|
||||
public static string GetFileExtension( string path )
|
||||
{
|
||||
int extensionIndex = path.LastIndexOf( '.' );
|
||||
if( extensionIndex < 0 || extensionIndex >= path.Length - 1 )
|
||||
return "";
|
||||
|
||||
stringBuilder.Length = 0;
|
||||
for( extensionIndex++; extensionIndex < path.Length; extensionIndex++ )
|
||||
{
|
||||
char ch = path[extensionIndex];
|
||||
if( ch >= 65 && ch <= 90 ) // A-Z
|
||||
ch += (char) 32; // Converted to a-z
|
||||
|
||||
stringBuilder.Append( ch );
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
// Draw header inside OnGUI
|
||||
public static void DrawHeader( string label )
|
||||
{
|
||||
Color c = GUI.backgroundColor;
|
||||
GUI.backgroundColor = AssetUsageDetectorSettings.SettingsHeaderColor;
|
||||
GUILayout.Box( label, BoxGUIStyle, GL_EXPAND_WIDTH );
|
||||
GUI.backgroundColor = c;
|
||||
}
|
||||
|
||||
// Draw horizontal line inside OnGUI
|
||||
public static void DrawSeparatorLine()
|
||||
{
|
||||
GUILayout.Space( 4f );
|
||||
GUILayout.Box( "", GL_HEIGHT_2, GL_EXPAND_WIDTH );
|
||||
GUILayout.Space( 4f );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restricts the given Rect within the screen's bounds.
|
||||
/// </summary>
|
||||
public static Rect GetScreenFittedRect(Rect originalRect, EditorWindow editorWindow)
|
||||
{
|
||||
screenFittedRectGetter ??= typeof(EditorWindow).Assembly.GetType("UnityEditor.ContainerWindow").GetMethod("FitRectToScreen", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
|
||||
if (screenFittedRectGetter.GetParameters().Length == 3)
|
||||
return (Rect)screenFittedRectGetter.Invoke(null, new object[] { originalRect, true, true });
|
||||
else
|
||||
{
|
||||
// New version introduced in Unity 2022.3.62f1, Unity 6.0.49f1 and Unity 6.1.0f1.
|
||||
// Usage example: https://github.com/Unity-Technologies/UnityCsReference/blob/10f8718268a7e34844ba7d59792117c28d75a99b/Editor/Mono/EditorWindow.cs#L1264
|
||||
editorWindowHostViewGetter ??= typeof(EditorWindow).GetField("m_Parent", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
hostViewContainerWindowGetter ??= typeof(EditorWindow).Assembly.GetType("UnityEditor.HostView").GetProperty("window", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
return (Rect)screenFittedRectGetter.Invoke(null, new object[] { originalRect, originalRect.center, true, hostViewContainerWindowGetter.GetValue(editorWindowHostViewGetter.GetValue(editorWindow), null) });
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all the objects inside the list are null
|
||||
public static bool IsEmpty( this List<ObjectToSearch> objectsToSearch )
|
||||
{
|
||||
if( objectsToSearch == null )
|
||||
return true;
|
||||
|
||||
for( int i = 0; i < objectsToSearch.Count; i++ )
|
||||
{
|
||||
if( objectsToSearch[i].obj != null )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if all the objects inside the list are null
|
||||
public static bool IsEmpty<T>( this List<T> objects ) where T : Object
|
||||
{
|
||||
if( objects == null )
|
||||
return true;
|
||||
|
||||
for( int i = 0; i < objects.Count; i++ )
|
||||
{
|
||||
if( objects[i] != null )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true is str starts with prefix
|
||||
public static bool StartsWithFast( this string str, string prefix )
|
||||
{
|
||||
int aLen = str.Length;
|
||||
int bLen = prefix.Length;
|
||||
int ap = 0; int bp = 0;
|
||||
while( ap < aLen && bp < bLen && str[ap] == prefix[bp] )
|
||||
{
|
||||
ap++;
|
||||
bp++;
|
||||
}
|
||||
|
||||
return bp == bLen;
|
||||
}
|
||||
|
||||
// Returns true is str ends with postfix
|
||||
public static bool EndsWithFast( this string str, string postfix )
|
||||
{
|
||||
int ap = str.Length - 1;
|
||||
int bp = postfix.Length - 1;
|
||||
while( ap >= 0 && bp >= 0 && str[ap] == postfix[bp] )
|
||||
{
|
||||
ap--;
|
||||
bp--;
|
||||
}
|
||||
|
||||
return bp < 0;
|
||||
}
|
||||
|
||||
public static bool ContainsFast<T>( this List<T> list, T element )
|
||||
{
|
||||
if( !( element is ValueType ) )
|
||||
{
|
||||
for( int i = list.Count - 1; i >= 0; i-- )
|
||||
{
|
||||
if( ReferenceEquals( list[i], element ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int i = list.Count - 1; i >= 0; i-- )
|
||||
{
|
||||
if( element.Equals( list[i] ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Assets/Plugins/AssetUsageDetector/Editor/Utilities.cs.meta
Normal file
18
Assets/Plugins/AssetUsageDetector/Editor/Utilities.cs.meta
Normal file
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52b272b7591fb90499916205261524e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/Utilities.cs
|
||||
uploadId: 894428
|
||||
80
Assets/Plugins/AssetUsageDetector/Editor/VariableGetter.cs
Normal file
80
Assets/Plugins/AssetUsageDetector/Editor/VariableGetter.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AssetUsageDetectorNamespace
|
||||
{
|
||||
// Delegate to get the value of a variable (either field or property)
|
||||
public delegate object VariableGetVal( object obj );
|
||||
|
||||
// Custom struct to hold a variable, its important properties and its getter function
|
||||
public readonly struct VariableGetterHolder
|
||||
{
|
||||
public readonly MemberInfo variable;
|
||||
private readonly VariableGetVal getter;
|
||||
|
||||
public readonly string Name { get { return variable.Name; } }
|
||||
public readonly bool IsProperty { get { return variable is PropertyInfo; } }
|
||||
|
||||
public VariableGetterHolder(FieldInfo fieldInfo, VariableGetVal getter)
|
||||
{
|
||||
this.variable = fieldInfo;
|
||||
this.getter = getter;
|
||||
}
|
||||
|
||||
public VariableGetterHolder(PropertyInfo propertyInfo, VariableGetVal getter)
|
||||
{
|
||||
this.variable = propertyInfo;
|
||||
this.getter = getter;
|
||||
}
|
||||
|
||||
public readonly object Get( object obj )
|
||||
{
|
||||
try
|
||||
{
|
||||
return getter( obj );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
StringBuilder sb = Utilities.stringBuilder;
|
||||
sb.Length = 0;
|
||||
sb.Append( "Error while getting the value of (" ).Append( IsProperty ? ( (PropertyInfo) variable ).PropertyType : ( (FieldInfo) variable ).FieldType ).Append( ") " )
|
||||
.Append( variable.DeclaringType ).Append( "." ).Append( Name ).Append( ": " ).Append( e );
|
||||
|
||||
Debug.LogError( sb.ToString() );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Credit: http://stackoverflow.com/questions/724143/how-do-i-create-a-delegate-for-a-net-property
|
||||
public interface IPropertyAccessor
|
||||
{
|
||||
object GetValue( object source );
|
||||
}
|
||||
|
||||
// A wrapper class for properties to get their values more efficiently
|
||||
public class PropertyWrapper<TObject, TValue> : IPropertyAccessor where TObject : class
|
||||
{
|
||||
private readonly Func<TObject, TValue> getter;
|
||||
|
||||
public PropertyWrapper( MethodInfo getterMethod )
|
||||
{
|
||||
getter = (Func<TObject, TValue>) Delegate.CreateDelegate( typeof( Func<TObject, TValue> ), getterMethod );
|
||||
}
|
||||
|
||||
public object GetValue( object obj )
|
||||
{
|
||||
try
|
||||
{
|
||||
return getter( (TObject) obj );
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Property getters may return various kinds of exceptions if their backing fields are not initialized (yet)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e94c83e8b850514ca0217aeff1491a6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/Editor/VariableGetter.cs
|
||||
uploadId: 894428
|
||||
4
Assets/Plugins/AssetUsageDetector/README.txt
Normal file
4
Assets/Plugins/AssetUsageDetector/README.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
= Asset Usage Detector (v2.6.2) =
|
||||
|
||||
Documentation: https://github.com/yasirkula/UnityAssetUsageDetector
|
||||
E-mail: yasirkula@gmail.com
|
||||
15
Assets/Plugins/AssetUsageDetector/README.txt.meta
Normal file
15
Assets/Plugins/AssetUsageDetector/README.txt.meta
Normal file
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac528f1751f33a647a45caeff6a9344b
|
||||
timeCreated: 1520032521
|
||||
licenseType: Store
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 112837
|
||||
packageName: Asset Usage Detector
|
||||
packageVersion: 2.6.2
|
||||
assetPath: Assets/Plugins/AssetUsageDetector/README.txt
|
||||
uploadId: 894428
|
||||
Reference in New Issue
Block a user