Search Unity

  1. Check out the Unite LA keynote for updates on the Visual Effect Editor, the FPS Sample, ECS, Unity for Film and more! Watch it now!
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  4. Want more efficiency in your development work? Sign up to receive weekly tech and creative know-how from Unity experts.
    Dismiss Notice
  5. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  6. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

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

Discussion in 'Extensions & OnGUI' started by yasirkula, May 31, 2016.

  1. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    600
    Hello all!

    I'd like to introduce to you an open source editor extension that helps you find references to asset(s) and/or Object(s) in your project. You can use it to e.g. find materials and renderers that use a specific texture/sprite, find objects with a specific MonoBehaviour, find instances of a specific prefab, etc.

    Asset Store: https://www.assetstore.unity3d.com/en/#!/content/112837
    Also available at: https://github.com/yasirkula/UnityAssetUsageDetector

    (information below is shamelessly copy&pasted from the GitHub page, enjoy! :])


    A. ABOUT

    This editor extension helps you figure out at which places an asset or GameObject is used, i.e. lists the objects that refer to the asset. 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!

    B. HOW TO USE
    • Simply put the AssetUsageDetector.cs script into the Editor folder of your project (if "Editor" folder does not exist, create it manually)
    • Now open Window - Asset Usage Detector window and you are good to go!
    C. FEATURES
    • You can search for references of any object that extends UnityEngine.Object
    • Searches every corner of your project with its reflection based search algorithm (even non-Unity objects, structs and data types like dictionaries are searched)
    • Can search in multiple scenes at once
    • Can show complete paths to the references or only the most relevant parts of the paths (see demonstration below)

    D. KNOWN LIMITATIONS
    • static variables are not searched
    • GUIText materials are not searched
    • Textures in Lens Flare's can not be searched
     
    Last edited: Mar 16, 2018
    El-Tun, xred13, SugoiDev and 25 others like this.
  2. arturmandas

    arturmandas

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

    yasirkula

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

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    339
    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:
    600
    Update: now search functionality works in Play mode, too!
     
  6. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    600
    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:
    10
    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.
     
  10. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    600
    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".
     
    UXeyeway, monremobile and CrazyD0G like this.
  11. ekt

    ekt

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

    monremobile

    Joined:
    Sep 16, 2015
    Posts:
    7
    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:
    52
    Thank you for sharing!! Saved me a lot of time :D
     
  14. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    600
    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:
    600
    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:
    55
    Invaluable tool, thank you!
     
  17. DED-Games

    DED-Games

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

    CDF

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

    Gillissie

    Joined:
    May 16, 2011
    Posts:
    27
    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:
    600
    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:
    5
    Thank a lot! This one is simply the best.
     
    yasirkula likes this.
  22. DungDajHjep

    DungDajHjep

    Joined:
    Mar 25, 2015
    Posts:
    38
    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:
    600
    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:
    38
    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:
    600
    Didn't see the code, sorry :D Good job.
     
    DungDajHjep likes this.
  26. hammertime2014

    hammertime2014

    Joined:
    Jan 16, 2014
    Posts:
    4
    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:
    5
    This is beautiful thanks!
     
    yasirkula likes this.
  28. BoaNeo

    BoaNeo

    Joined:
    Feb 21, 2013
    Posts:
    34
    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:
    600
    Lol, thanks!
     
  30. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    600
    Now available on Asset Store!
     
    self_made and mons00n like this.
  31. s_guy

    s_guy

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

    dohaiha930

    Joined:
    Mar 27, 2018
    Posts:
    29
    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:
    451
    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:
    600
    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:
    61
    I love you right now.
     
  37. nnobinelo98

    nnobinelo98

    Joined:
    May 16, 2018
    Posts:
    5
    Thanks :)
     
    yasirkula likes this.