Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Asset Usage Detector - Find references to an asset/object [Open Source]

Discussion in 'Immediate Mode GUI (IMGUI)' started by yasirkula, May 31, 2016.

  1. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Hello all!

    This tool helps you find usages of the selected asset(s) and/or scene object(s), i.e. lists the objects that refer to them. It is possible to search for references in the Assets folder (Project view) and/or in the scene(s) of your project. You can also search for references while in Play mode!

    Asset Store: https://assetstore.unity.com/packages/tools/utilities/asset-usage-detector-112837
    Also available at: https://github.com/yasirkula/UnityAssetUsageDetector
    Discord: https://discord.gg/UJJt549AaV
    GitHub Sponsors ☕

    Settings.png

    SearchResults1Dark.png

    SearchResults2Light.png
     
    Last edited: May 31, 2023
  2. arturmandas

    arturmandas

    Joined:
    Sep 29, 2012
    Posts:
    240
    Well, no one said "thank you" yet. Thank you very much, great work!
     
  3. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Much appreciated :)
     
  4. flashframe

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    789
    Looks great! Will give it a try. Thanks for what was clearly a lot of work.
     
    yasirkula likes this.
  5. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Update: now search functionality works in Play mode, too!
     
  6. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Yet another update: more information is provided where necessary in the results page; like the referencing variable's name, material name, shader property name (for textures) etc.
     
  7. n1ntendo

    n1ntendo

    Joined:
    Feb 9, 2013
    Posts:
    2
    This is similar to something I've been writing on the side for our small team for a little bit now, definitely going to check this out, thanks!
     
  8. Jojo-Batista

    Jojo-Batista

    Joined:
    Dec 5, 2012
    Posts:
    32
    Awesome Work!!! Thanks a lot!
     
    yasirkula likes this.
  9. Gstewart

    Gstewart

    Joined:
    Jul 23, 2013
    Posts:
    11
    Great asset. does exactly what it says on the tin.
    I know it's extra work, but do you think you will be able to expand it so that it can give you a list of assets that are NOT being used. I'd happily pay a few dollars for that, even if it can;t search a few types.
     
    Elmundo likes this.
  10. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Thank you for your comment and the suggestion.

    Here is a script that collects the dependencies of Scenes In Build (ticked) and finds assets that are not in that list. Scripts are excluded from the list as they are not correctly collected by Unity. Also, no special action is taken against Resources or StreamingAssets folders (contents of these folders are always included in build, regardless of whether they are actually used or not).

    Also know that there are some paid solutions on Asset Store that use Unity's build log to generate the list of unused assets. They are probably more accurate than my solution.

    But anyways, here is my code snippet (put it in Editor folder):

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections.Generic;
    4. using System.IO;
    5.  
    6. public class UnusedAssetDetector : EditorWindow
    7. {
    8.     private struct AssetHolder
    9.     {
    10.         public string name;
    11.         public int instanceId;
    12.  
    13.         public AssetHolder( string path, int instanceId )
    14.         {
    15.             name = Path.GetFileName( path );
    16.             this.instanceId = instanceId;
    17.         }
    18.     }
    19.  
    20.     private const string META_EXTENSION = ".meta";
    21.  
    22.     private List<AssetHolder> unusedAssets = null;
    23.  
    24.     private static GUIStyle m_boxGUIStyle; // GUIStyle used to draw the results of the search
    25.     public static GUIStyle boxGUIStyle
    26.     {
    27.         get
    28.         {
    29.             if( m_boxGUIStyle == null )
    30.             {
    31.                 m_boxGUIStyle = new GUIStyle( EditorStyles.helpBox );
    32.                 m_boxGUIStyle.alignment = TextAnchor.MiddleCenter;
    33.                 m_boxGUIStyle.font = EditorStyles.label.font;
    34.             }
    35.  
    36.             return m_boxGUIStyle;
    37.         }
    38.     }
    39.  
    40.     private Vector2 scrollPosition = Vector2.zero;
    41.  
    42.     [MenuItem( "Util/Unused Asset Detector" )]
    43.     static void Init()
    44.     {
    45.         UnusedAssetDetector window = GetWindow<UnusedAssetDetector>();
    46.         window.titleContent = new GUIContent( "Unused Asset Detector" );
    47.         window.Show();
    48.     }
    49.  
    50.     void OnGUI()
    51.     {
    52.         if( unusedAssets == null )
    53.         {
    54.             GUILayout.Box( "Only 'Scenes In Build' in Build Settings are searched for dependencies!", GUILayout.ExpandWidth( true ) );
    55.  
    56.             if( GUILayout.Button( "Find unused assets", GUILayout.Height( 25 ) ) )
    57.             {
    58.                 FindUnusedAssets();
    59.             }
    60.         }
    61.         else
    62.         {
    63.             GUILayout.BeginVertical();
    64.  
    65.             GUILayout.Box( unusedAssets.Count + " possibly unused asset(s) found", GUILayout.ExpandWidth( true ) );
    66.  
    67.             if( GUILayout.Button( "Search Again", GUILayout.Height( 25 ) ) )
    68.             {
    69.                 FindUnusedAssets();
    70.             }
    71.  
    72.             GUILayout.Space( 10 );
    73.  
    74.             scrollPosition = GUILayout.BeginScrollView( scrollPosition );
    75.  
    76.             for( int i = 0; i < unusedAssets.Count; i++ )
    77.             {
    78.                 if( GUILayout.Button( unusedAssets[i].name, boxGUIStyle ) )
    79.                 {
    80.                     Selection.activeInstanceID = unusedAssets[i].instanceId;
    81.                     EditorGUIUtility.PingObject( unusedAssets[i].instanceId );
    82.                 }
    83.             }
    84.  
    85.             GUILayout.EndScrollView();
    86.  
    87.             GUILayout.EndVertical();
    88.         }
    89.     }
    90.  
    91.     void OnDestroy()
    92.     {
    93.         unusedAssets = null;
    94.     }
    95.  
    96.     void FindUnusedAssets()
    97.     {
    98.         if( unusedAssets == null )
    99.             unusedAssets = new List<AssetHolder>( 128 );
    100.         else
    101.             unusedAssets.Clear();
    102.  
    103.         // Get all scenes in build settings (ticked)
    104.         EditorBuildSettingsScene[] scenesTemp = EditorBuildSettings.scenes;
    105.         int targetSceneCount = 0;
    106.         for( int i = 0; i < scenesTemp.Length; i++ )
    107.         {
    108.             if( scenesTemp[i].enabled )
    109.                 targetSceneCount++;
    110.         }
    111.  
    112.         if( targetSceneCount == 0 )
    113.             return;
    114.  
    115.         Object[] targetScenes = new Object[targetSceneCount];
    116.         for( int i = 0, j = 0; i < scenesTemp.Length; i++ )
    117.         {
    118.             if( scenesTemp[i].enabled )
    119.                 targetScenes[j++] = AssetDatabase.LoadAssetAtPath<SceneAsset>( scenesTemp[i].path );
    120.         }
    121.  
    122.         Object[] dependencies = EditorUtility.CollectDependencies( targetScenes );
    123.         HashSet<string> usedAssets = new HashSet<string>();
    124.  
    125.         for( int i = 0; i < dependencies.Length; i++ )
    126.         {
    127.             usedAssets.Add( AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( dependencies[i] ) ) );
    128.         }
    129.  
    130.         for( int i = 0; i < targetSceneCount; i++ )
    131.         {
    132.             usedAssets.Add( AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( targetScenes[i] ) ) );
    133.         }
    134.  
    135.         string projectDir = Directory.GetParent( Application.dataPath ).FullName;
    136.         if( projectDir[projectDir.Length - 1] != '/' && projectDir[projectDir.Length - 1] != '\\' )
    137.             projectDir += '/';
    138.  
    139.         int substrIndex = projectDir.Length;
    140.  
    141.         string[] files = Directory.GetFiles( Application.dataPath, "*", SearchOption.AllDirectories );
    142.         for( int i = 0; i < files.Length; i++ )
    143.         {
    144.             if( !files[i].EndsWith( META_EXTENSION ) )
    145.             {
    146.                 string relativePath = files[i].Substring( substrIndex );
    147.                 if( !usedAssets.Contains( AssetDatabase.AssetPathToGUID( relativePath ) ) )
    148.                     TryAddUnusedAsset( relativePath );
    149.             }
    150.         }
    151.     }
    152.  
    153.     void TryAddUnusedAsset( string path )
    154.     {
    155.         Object asset = AssetDatabase.LoadAssetAtPath<Object>( path );
    156.         if( asset == null )
    157.             return;
    158.  
    159.         if( !IsTypeDerivedFrom( asset.GetType(), typeof( MonoScript ) ) )
    160.         {
    161.             int instanceId = asset.GetInstanceID();
    162.             unusedAssets.Add( new AssetHolder( path, instanceId ) );
    163.         }
    164.     }
    165.  
    166.     // Check if "child" is a subclass of "parent" (or if their types match)
    167.     bool IsTypeDerivedFrom( System.Type child, System.Type parent )
    168.     {
    169.         if( child.IsSubclassOf( parent ) || child == parent )
    170.             return true;
    171.  
    172.         return false;
    173.     }
    174. }
    You can access it from the menu bar: "Util-Unused Asset Detector".
     
  11. ekt

    ekt

    Joined:
    Jul 9, 2012
    Posts:
    28
    thanks for sharing this, yasirkula! Great work!
     
  12. monremobile

    monremobile

    Joined:
    Sep 16, 2015
    Posts:
    10
    WOooOW! This extension is simply amazing!
    :eek::eek::eek::eek:!!!

    Thanks a lot for sharing it!
    :rolleyes:
     
  13. Weendie-Games

    Weendie-Games

    Joined:
    Feb 17, 2015
    Posts:
    75
    Thank you for sharing!! Saved me a lot of time :D
     
  14. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    You are welcome :)

    This asset has recently grabbed so much attention that I'm currently working on an update that will improve it in many ways. Stay tuned!
     
    Last edited: Aug 17, 2017
    Weendie-Games likes this.
  15. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Major update! Thank you all for your support!

    - Now a better tool in almost any way: usability, accuracy and speed (most of the time)
    - Can now search custom classes and structs (non UnityEngine.Object deriven)
    - Ability to see the complete paths to the references in the fresh new node-based results page
    - Added option to search private properties
    - Added option to include sub-assets in search as well (if any), like animation clips or the mesh data of an imported model
    - Now searches ScriptableObject's in the project, too
     
    monremobile and Weendie-Games like this.
  16. metroidsnes

    metroidsnes

    Joined:
    Jan 5, 2014
    Posts:
    67
    Invaluable tool, thank you!
     
  17. Trisibo

    Trisibo

    Joined:
    Nov 1, 2010
    Posts:
    245
    Thanks a lot!
     
  18. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,307
    Very nice, this saved me a lot of headache.
     
  19. Gillissie

    Gillissie

    Joined:
    May 16, 2011
    Posts:
    305
    Hey, thanks for this tool.
    I found a problem with it though... it wasn't finding a reference to a texture I was using on a SpriteRenderer component.
    https://screencast.com/t/YYmPuDDs
    For whatever reason, the same texture on an Image component is found.
     
  20. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    You need to include public properties in your search as "Sprite" is a property of "Sprite Renderer". I've now tested it with default settings + public properties selected on a Sprite Renderer and it worked for me. Let me know if the problem persists.

    PS, also make sure that "Include sub-assets in search" is selected.
     
  21. Bodin

    Bodin

    Joined:
    Apr 19, 2017
    Posts:
    37
    Thank a lot! This one is simply the best.
     
    yasirkula likes this.
  22. DungDajHjep

    DungDajHjep

    Joined:
    Mar 25, 2015
    Posts:
    201
    Awesome, thanks you very much, can i right click a gameobject and have a menu find ?

    i make a cheat

    Code (CSharp):
    1.        [MenuItem("GameObject/FindReferenceInOpenScene",false,0)]
    2.         public static void FastInit(MenuCommand menuCommand)
    3.         {
    4.             AssetUsageDetector window = GetWindow<AssetUsageDetector>();
    5.             window.titleContent = new GUIContent("Asset Usage Detector");
    6.  
    7.             window.Show();
    8.  
    9.             window.assetToSearch = menuCommand.context as GameObject;
    10.  
    11.             if (window.assetToSearch == null)
    12.             {
    13.                 window.errorMessage = "SELECT AN ASSET FIRST!";
    14.             }
    15.             else if (!EditorApplication.isPlaying && !window.AreScenesSaved())
    16.             {
    17.                 // Don't start the search if at least one scene is currently dirty (not saved)
    18.                 window.errorMessage = "SAVE OPEN SCENES FIRST!";
    19.             }
    20.             else
    21.             {
    22.                 window.errorMessage = string.Empty;
    23.                 window.currentPhase = Phase.Processing;
    24.  
    25.                 if (!EditorApplication.isPlaying)
    26.                     window.initialSceneSetup = EditorSceneManager.GetSceneManagerSetup(); // Get the scenes that are open right now
    27.                 else
    28.                     window.initialSceneSetup = null;
    29.  
    30.                 // Start searching
    31.                 window.ExecuteQuery();
    32.             }
    33.         }
     
    Last edited: Dec 24, 2017
    yasirkula likes this.
  23. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Do you mean like right clicking a prefab and selecting "Find References" from the context menu?
     
    DungDajHjep likes this.
  24. DungDajHjep

    DungDajHjep

    Joined:
    Mar 25, 2015
    Posts:
    201
    right click a gameobject in scene and click FindSceRef in context menu, yes , i adding some code and it's working. Thanks again !
     
  25. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Didn't see the code, sorry :D Good job.
     
    DungDajHjep likes this.
  26. hammertime2014

    hammertime2014

    Joined:
    Jan 16, 2014
    Posts:
    6
    Another thanks from me @yasirkula! I've been meaning to write something like this as the "Find Reference ..." is basically useless in my project, and this does exactly what I want it to.
     
    yasirkula likes this.
  27. liphttam1

    liphttam1

    Joined:
    Nov 11, 2014
    Posts:
    9
    This is beautiful thanks!
     
    yasirkula likes this.
  28. BoaNeo

    BoaNeo

    Joined:
    Feb 21, 2013
    Posts:
    56
    Fantastic. Only thing I don't understand is why this isn't build into Unity from the start.

    Well done!
     
  29. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Lol, thanks!
     
  30. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Now available on Asset Store!
     
    NEVER-SETTLE and mons00n like this.
  31. s_guy

    s_guy

    Joined:
    Feb 27, 2013
    Posts:
    102
    @yasirkula Very nice tool. Thanks for this contribution!
     
    yasirkula likes this.
  32. dohaiha930

    dohaiha930

    Joined:
    Mar 27, 2018
    Posts:
    55
    Thank you sir, you save my time when ineed to find out where my asset use.
     
    yasirkula likes this.
  33. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,366
    What?! Free!? Let me hit that download Asset Usage Detector now before you change your mind!

    One thing I think I will try to add, and you're welcome to add also, is a referential rule enforcement scheme. I have folders for "THIS GAME" and "SHARED BETWEEN MY GAMES" and "OFFICIAL ACQUIRED" assets. Things in my SHARED folder are allowed to have references to OFFICIAL. Things in THIS GAME can refer to either one. But the other direction indicates bad practices. I don't think I want to try to do this through AssetPostprocessor to enforce it 100% full time, but I do want to audit my projects on a regular basis.
     
    yasirkula likes this.
  34. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    I think this use-case won't apply to most projects, so I'll skip it for now. But thank you for the suggestion!
     
  35. marcz88

    marcz88

    Joined:
    Mar 12, 2017
    Posts:
    41
    What a great asset! Amazing this isn't built in. Thank you!
     
    yasirkula likes this.
  36. xred13

    xred13

    Joined:
    Jul 14, 2018
    Posts:
    74
    I love you right now.
     
  37. nnobinelo98

    nnobinelo98

    Joined:
    May 16, 2018
    Posts:
    6
    Thanks :)
     
    yasirkula likes this.
  38. f0ff886f

    f0ff886f

    Joined:
    Nov 1, 2015
    Posts:
    201
    This is awesome, thank you! I have something similar implemented in python scripts, but its not very portable for the rest of our team.

    Question, this asset seems to use a ton of memory, even when searching for one asset at depth 1. I measured up to 14GB of memory used to search my scenes in the project (there are maybe 30) for one asset (depth 1).

    Is there any chance of reducing that memory usage?
     
  39. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    That's mainly caused by EditorUtility.CollectDependencies which loads all the objects in those scenes to the memory because it returns an Object[]. I think the only way to reduce the memory usage would be to cache these objects' GUID's on the first search and then reuse the cached data, as long as the scene doesn't change.
     
  40. KOKOStern

    KOKOStern

    Joined:
    Dec 22, 2013
    Posts:
    13
    As my current project grows and my use of ScriptableObjects grows I knew I'll eventually need something like this and I'm super happy this already exists.

    Fantastic awesome work!

    Now I just need to figure out how to make this more friendly with ScriptableObjects when they are found inside my Behavior Designer trees (a great behavior tree plugin). You can see it of course but it doesn't exactly tell you on which task the object is found.

    In any case - invaluable stuff! Thank you!
     
    yasirkula likes this.
  41. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    I haven't used Behavior Designer myself, so I can't really tell how to accomplish this, sorry :/
     
  42. Altaf-LSG

    Altaf-LSG

    Joined:
    Apr 3, 2018
    Posts:
    2
    This is awesome, Thank you for writing this tool. Saves a lot of time
     
    yasirkula likes this.
  43. AyoubGharbi

    AyoubGharbi

    Joined:
    Dec 14, 2017
    Posts:
    11
    Thank you :)
     
    yasirkula likes this.
  44. Desoxi

    Desoxi

    Joined:
    Apr 12, 2015
    Posts:
    195
    Thank you very much, this saved me a lot of time!
     
    yasirkula likes this.
  45. Ender_7

    Ender_7

    Joined:
    Nov 22, 2015
    Posts:
    11
    This script has saved me so much hassle when trying to find dependencies for my asset bundles.
    Thanks very much!
     
    yasirkula likes this.
  46. Thanolion

    Thanolion

    Joined:
    Jul 25, 2017
    Posts:
    1
    Thank you very much! This is awesome!
     
    yasirkula likes this.
  47. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Updated the plugin on GitHub and Asset Store.

    NOTE: If your project uses an older version of AssetUsageDetector, delete the older version before updating the plugin.

    A part of the search algorithm has changed to reduce the memory usage and allow for faster searches with caching support (previously, EditorUtility.CollectDependencies was used; now AssetDatabase.GetDependencies is used). Although unlikely, this might result in some references being skipped in the search. If you find any issues, please let me know.

    Here's a summary of the changelog:

    - Caching asset dependencies for faster calculations
    - Dependencies are checked via their paths, so loading the asset is not necessary and much less memory will be used
    - Each searched asset now has its own toggleable sub-assets
    - Adding folders to searched assets is now possible
    - Searching references in a subset of the Project view (Assets folder) is now possible

    - Simplified the output nodes:
    --- multiple links from same node to same node are combined into a single node
    --- root nodes that are part of another node's siblings are omitted
    - Added Timeline, Sprite Atlas and Tilemap support
    - Added better support for the new prefab system
    - Bugfix for structs' property getters throwing exception
    - Separated AssetUsageDetector.cs into smaller classes
    - Made AssetUsageDetector a standalone editor script, so you can now use it in your own editor scripts to calculate some objects' references
     
    Last edited: Jul 23, 2019
  48. Vicomte

    Vicomte

    Joined:
    Sep 18, 2013
    Posts:
    5
    Hi,

    I'm trying to use this extension, but I get a stack overflow on the recursive "AssetHasAnyReferenceTo" @ line 1015

    Any ideas on that?

    Thanks!!
     
  49. BadViking

    BadViking

    Joined:
    Jul 16, 2015
    Posts:
    15
    Same problem for me. Unity 2018.3.8
     
  50. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,865
    Thanks for reporting this issue. I was able to reproduce and resolve it (and one other issue that I've spotted). I will update the plugin on GitHub today.

    P.S. The update is now live @BadViking @Vicomte
     
    Last edited: Jul 26, 2019