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

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: GitHub - yasirkula/UnityAssetUsageDetector: Find usages of the selected asset(s) and/or Object(s) in your Unity project, i.e. list the objects that refer to them
Discord: yasirkula Unity Assets
GitHub Sponsors :coffee:

50 Likes

Well, no one said “thank you” yet. Thank you very much, great work!

Much appreciated :slight_smile:

Looks great! Will give it a try. Thanks for what was clearly a lot of work.

1 Like

Update: now search functionality works in Play mode, too!

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.

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!

Awesome Work!!! Thanks a lot!

1 Like

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.

1 Like

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):

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;

public class UnusedAssetDetector : EditorWindow
{
    private struct AssetHolder
    {
        public string name;
        public int instanceId;

        public AssetHolder( string path, int instanceId )
        {
            name = Path.GetFileName( path );
            this.instanceId = instanceId;
        }
    }

    private const string META_EXTENSION = ".meta";

    private List<AssetHolder> unusedAssets = null;

    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 );
                m_boxGUIStyle.alignment = TextAnchor.MiddleCenter;
                m_boxGUIStyle.font = EditorStyles.label.font;
            }

            return m_boxGUIStyle;
        }
    }

    private Vector2 scrollPosition = Vector2.zero;

    [MenuItem( "Util/Unused Asset Detector" )]
    static void Init()
    {
        UnusedAssetDetector window = GetWindow<UnusedAssetDetector>();
        window.titleContent = new GUIContent( "Unused Asset Detector" );
        window.Show();
    }

    void OnGUI()
    {
        if( unusedAssets == null )
        {
            GUILayout.Box( "Only 'Scenes In Build' in Build Settings are searched for dependencies!", GUILayout.ExpandWidth( true ) );

            if( GUILayout.Button( "Find unused assets", GUILayout.Height( 25 ) ) )
            {
                FindUnusedAssets();
            }
        }
        else
        {
            GUILayout.BeginVertical();

            GUILayout.Box( unusedAssets.Count + " possibly unused asset(s) found", GUILayout.ExpandWidth( true ) );

            if( GUILayout.Button( "Search Again", GUILayout.Height( 25 ) ) )
            {
                FindUnusedAssets();
            }

            GUILayout.Space( 10 );

            scrollPosition = GUILayout.BeginScrollView( scrollPosition );

            for( int i = 0; i < unusedAssets.Count; i++ )
            {
                if( GUILayout.Button( unusedAssets[i].name, boxGUIStyle ) )
                {
                    Selection.activeInstanceID = unusedAssets[i].instanceId;
                    EditorGUIUtility.PingObject( unusedAssets[i].instanceId );
                }
            }

            GUILayout.EndScrollView();

            GUILayout.EndVertical();
        }
    }

    void OnDestroy()
    {
        unusedAssets = null;
    }

    void FindUnusedAssets()
    {
        if( unusedAssets == null )
            unusedAssets = new List<AssetHolder>( 128 );
        else
            unusedAssets.Clear();

        // Get all scenes in build settings (ticked)
        EditorBuildSettingsScene[] scenesTemp = EditorBuildSettings.scenes;
        int targetSceneCount = 0;
        for( int i = 0; i < scenesTemp.Length; i++ )
        {
            if( scenesTemp[i].enabled )
                targetSceneCount++;
        }

        if( targetSceneCount == 0 )
            return;

        Object[] targetScenes = new Object[targetSceneCount];
        for( int i = 0, j = 0; i < scenesTemp.Length; i++ )
        {
            if( scenesTemp[i].enabled )
                targetScenes[j++] = AssetDatabase.LoadAssetAtPath<SceneAsset>( scenesTemp[i].path );
        }

        Object[] dependencies = EditorUtility.CollectDependencies( targetScenes );
        HashSet<string> usedAssets = new HashSet<string>();

        for( int i = 0; i < dependencies.Length; i++ )
        {
            usedAssets.Add( AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( dependencies[i] ) ) );
        }

        for( int i = 0; i < targetSceneCount; i++ )
        {
            usedAssets.Add( AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( targetScenes[i] ) ) );
        }

        string projectDir = Directory.GetParent( Application.dataPath ).FullName;
        if( projectDir[projectDir.Length - 1] != '/' && projectDir[projectDir.Length - 1] != '\\' )
            projectDir += '/';

        int substrIndex = projectDir.Length;

        string[] files = Directory.GetFiles( Application.dataPath, "*", SearchOption.AllDirectories );
        for( int i = 0; i < files.Length; i++ )
        {
            if( !files[i].EndsWith( META_EXTENSION ) )
            {
                string relativePath = files[i].Substring( substrIndex );
                if( !usedAssets.Contains( AssetDatabase.AssetPathToGUID( relativePath ) ) )
                    TryAddUnusedAsset( relativePath );
            }
        }
    }

    void TryAddUnusedAsset( string path )
    {
        Object asset = AssetDatabase.LoadAssetAtPath<Object>( path );
        if( asset == null )
            return;

        if( !IsTypeDerivedFrom( asset.GetType(), typeof( MonoScript ) ) )
        {
            int instanceId = asset.GetInstanceID();
            unusedAssets.Add( new AssetHolder( path, instanceId ) );
        }
    }

    // Check if "child" is a subclass of "parent" (or if their types match)
    bool IsTypeDerivedFrom( System.Type child, System.Type parent )
    {
        if( child.IsSubclassOf( parent ) || child == parent )
            return true;

        return false;
    }
}

You can access it from the menu bar: “Util-Unused Asset Detector”.

4 Likes

thanks for sharing this, yasirkula! Great work!

WOooOW! This extension is simply amazing!
:hushed::hushed::hushed::hushed:!!!

Thanks a lot for sharing it!
:roll_eyes:

Thank you for sharing!! Saved me a lot of time :smile:

You are welcome :slight_smile:

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!

1 Like

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
2 Likes

Invaluable tool, thank you!

Thanks a lot!

Very nice, this saved me a lot of headache.

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.

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.