Search Unity

Bug AssetDatabase.FindAssets Not Working

Discussion in 'Asset Database' started by Nigey, Nov 22, 2018.

  1. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Hi Guys,

    I have a strange problem with AssetDatabase.FindAssets. It's working for 3 out of 4 classes. I can't see why one is different and not working. They all inherit from the same abstract class, and all have similar properties. They all share the same namespace.

    Here's my method to use FindAssets:

    Code (CSharp):
    1. public static T[] GetAllInstances<T>() where T : ScriptableObject
    2.         {
    3.             // FindAssets uses tags check documentation for more info
    4.             string[] guids = AssetDatabase.FindAssets("t:" + typeof(T).Name);
    5.  
    6.             // I'm using this section of the method to debug the one I'm having a problem with.
    7.             if(typeof(T) == typeof(ScenarioEnvironment))
    8.             {
    9.                 Debug.Log(typeof(T).Name);
    10.             }
    11.  
    12.             T[] a = new T[guids.Length];
    13.  
    14.             // Probably could get optimized
    15.             for (int i = 0; i < guids.Length; i++)
    16.             {
    17.                 string path = AssetDatabase.GUIDToAssetPath(guids[i]);
    18.                 a[i] = AssetDatabase.LoadAssetAtPath<T>(path);
    19.             }
    20.  
    21.             return a;
    22.         }
    And here's my use case for each:

    Code (CSharp):
    1. MasterFile.environments = GetAllInstances<ScenarioEnvironment>();
    2.             MasterFile.chemicals = GetAllInstances<Chemical>();
    3.             MasterFile.chemicalContainers = GetAllInstances<ChemicalContainer>();
    4.             MasterFile.failureModes = GetAllInstances<FailureMode>();
    The classes for each are quite long, but here's the abstract class used by each:

    Code (CSharp):
    1. namespace TacHaz.Scenarios
    2. {
    3.     using UnityEngine;
    4.  
    5.     /// <summary>
    6.     /// This is the base class of any component that can be used to generate a scenario.
    7.     /// </summary>
    8.     public abstract class ScenarioComponent : ScriptableObject, IEditorReadable
    9.     {
    10.         [Tooltip("This is auto generated by the Scenarios View Window. Viewable for convenience.")]
    11.         [SerializeField, ReadOnly] ConnectionID id;
    12.         [SerializeField] private string commonName;
    13.  
    14.         /// <summary>
    15.         /// A string indicating the name of this <see cref="ScenarioComponent"/>.
    16.         /// </summary>
    17.         public string Name
    18.         {
    19.             get
    20.             {
    21.                 return commonName;
    22.             }
    23.         }
    24.  
    25.         /// <summary>
    26.         /// <see cref="ConnectionID"/> indicating the unique id of this scenario component, and it's linked id of the <see cref="Object"/>.
    27.         /// </summary>
    28.         public ConnectionID ConnectionID
    29.         {
    30.             get
    31.             {
    32.                 return id;
    33.             }
    34.         }
    35.  
    36.         /// <summary>
    37.         /// Returns an object indicating the instance of this <see cref="ScenarioComponent"/>. Not a property to avoid self referencing loops on converting to json.
    38.         /// </summary>
    39.         /// <returns>Returns an object indicating the instance of this <see cref="ScenarioComponent"/>.</returns>
    40.         public Object Selectable()
    41.         {
    42.             return this;
    43.         }
    44.     }
    45. }
    Any ideas massively appreciated!
     
  2. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Well I've cut it down to something REALLY REALLY wierd.... This works:

    Code (CSharp):
    1. namespace TacHaz.Scenarios
    2. {
    3.     using UnityEngine;
    4.     using System.Collections;
    5.     using TacticalHazmat.Utilities;
    6.  
    7.     [CreateAssetMenu(fileName = "TestEnv", menuName = NamingConvention.FrameWorkName + NamingConvention.Scenarios + "TestEnv")]
    8.     public class TestEnv : ScenarioComponent
    9.     {
    10.  
    11.     }
    12. }
    This does not:

    Code (CSharp):
    1. namespace TacHaz.Scenarios
    2. {
    3.     using UnityEngine;
    4.     using System.Collections;
    5.     using TacticalHazmat.Utilities;
    6.  
    7.     [CreateAssetMenu(fileName = "Env", menuName = NamingConvention.FrameWorkName + NamingConvention.Scenarios + "Env")]
    8.     public class Env : ScenarioComponent
    9.     {
    10.  
    11.     }
    12. }
    Apparently beginning the classname with 'Env' causes FindAssets to find nothing!!! Right..? Well. Anyone else who has this issue, change your class names until it works.
     
  3. zledas

    zledas

    Joined:
    Nov 3, 2010
    Posts:
    23
    I stumbled on this quirk as well. Looks like it fails because Unity has other classes in other namespaces with the same name (eg. failures: t:Env, t:AudioManager). You can make it work by adding it to namespace and using full name.
    Code (CSharp):
    1. //<...>
    2. string[] guids = AssetDatabase.FindAssets("t:" + typeof(T).FullName);
    3. //<...>
     
  4. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    This is something along the lines of what I'd figured too. The only issue being that mine doesn't find ANYTHING once I use FullName, instead of Name. I've found that by deleting the same class several times, and re-adding that same class with the same name, it will eventually work. Weird as all hell, but at least it's working.
     
  5. Pelican_7

    Pelican_7

    Joined:
    Nov 25, 2014
    Posts:
    190
    Just to add something here that may be of use to people: If you change the namespace of a ScriptableObject script, AssetDatabase.FindAssets will no longer be able to find the existing asset instances in the project. To resolve this, reimport the ScriptableObject assets (right-click --> Reimport on the folder in the Project inspector). If you're developing a third party plugin, I'm not sure there is a quick and simple way to detect an asset is no longer findable (without monitoring script changes yourself), so hopefully it's something Unity can resolve in the future.

    (Unity version 2019.1.0f2)

    Edit:

    My current workaround is to use Resources.FindObjectsOfTypeAll when I detect that a user may have changed the namespace of a script that has ScriptableObject asset instances. FindObjectsOfTypeAll will find these missing assets that AssetDatabase.FindAssets cannot find. You can then use AssetDatabase.ImportAsset to re-import these assets automatically and resolve the issue.
     
    Last edited: Jul 11, 2019
  6. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Than you a lot. I just clicked to ReimportAll assets in project menu. And unity now able to find assets via AssetDatabase.FindAsset an via ObjectPicker.
     
    Pelican_7 likes this.
  7. Rafael-cmk

    Rafael-cmk

    Joined:
    Jun 28, 2016
    Posts:
    56
    Solved for me too, thank you a lot.
     
    Pelican_7 likes this.
  8. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,609
  9. Pelican_7

    Pelican_7

    Joined:
    Nov 25, 2014
    Posts:
    190
    noio and mdooneymill like this.
  10. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    230
    I also can't find any way to get
    AssetDatabase.FindAssets(t:SomeMonoBehaviour)
    to work at all. With or without
    FullName
    , after a full Reimport, just nothing...
     
    radiantboy likes this.
  11. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,609
    It doesn't work with MonoBehaviour, since MonoBehaviour isn't considered an asset. It's a Component on a GameObject and can be part of a Prefab.
     
  12. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    230
    I think that's a bit too general because you can use
    AssetDatabase.LoadAssetAtPath<SomeMonoBehaviour>
    and it will return a reference to the actual component on the prefab.

    But if
    AssetDatabase.FindAssets
    Just Doesn't Work with Component types, then so be it. ¯\_(ツ)_/¯
     
  13. fherbst

    fherbst

    Joined:
    Jun 24, 2012
    Posts:
    802
    Just stumpled upon this behaviour. @Peter77 do you have more info on why it won't be fixed? The copy-paste "protecting the stability" blah does not really tell anything.
     
  14. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,609
    Last edited: Sep 6, 2020
  15. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Once again encountered this issue...
     
  16. unity_123robertmax123

    unity_123robertmax123

    Joined:
    Mar 6, 2019
    Posts:
    1
  17. fherbst

    fherbst

    Joined:
    Jun 24, 2012
    Posts:
    802
    @LeonhardP if you have a moment would be great if you point the right people towards this. Pretty weird data corruption issue that breaks a lot of tooling.
     
  18. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,932
    2020: AssetDatabase still sucks :). With the help of this thread, I was able to fix the broken "FindAssets" method so that it can ... find the asset for a known class (which, as documented above, fundamentally won't work in Unity's API because it silently fails on MonoBehaviours, is corupt on ScriptableObjects, and uses a "broken by default" fuzzy matching algorithm).

    In case anyone else needs it, I needed to know the path of a class:

    Code (CSharp):
    1. /// <summary>
    2.         /// In 2020, the AssetDatabase API's only way to find the asset-path for a class is this
    3.         /// ridiculous dance of taking Unity's API and loading all the (incorrect!) matches then filtering them to
    4.         /// find the match that matches the item you asked for!
    5.         ///
    6.         /// (obviously, if you have an INSTANCE of the class, you can use the magic MonoScript,
    7.         /// ... but the MonoScript class was never fully implemented, and only works for objects not classes)
    8.         /// </summary>
    9.         /// <returns></returns>
    10.         private static string _PathToAssetForClass<T>() where T : Object
    11.         {
    12.             var fuzzyMatchesUnityBadSearch = AssetDatabase.FindAssets( typeof(T).Name );
    13.             foreach( var fuzzyMatch in fuzzyMatchesUnityBadSearch )
    14.             {
    15.                 var path = AssetDatabase.GUIDToAssetPath( fuzzyMatch );
    16.                 var matched = AssetDatabase.LoadAssetAtPath<MonoScript>( path );
    17.                 if( matched.GetClass().IsAssignableFrom( typeof(T) ) )
    18.                     return path;
    19.             }
    20.  
    21.             return null;
    22.         }
     
  19. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,609
    Do you use Asset Database v1 or v2?
     
  20. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,932
    v2 is exactly the same API as v1, no?

    I haven't seen any API changes so far (and none were mentioned in any of the forum posts or blog posts about v2 that I read).
     
  21. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,609
  22. fherbst

    fherbst

    Joined:
    Jun 24, 2012
    Posts:
    802
    I heard from someone at Unity that the issue described in this thread (FindAssets not working after namespace changes or script renames until Editor restart) should be fixed in 2019.4+.
    Does anyone of you still see it there?
     
  23. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,932
    Most of the problems are at the API level: the API's are missing core method calls, or defined badly, and most of them are largely undocumented. Using Asset DB v2 will have literally no positive effect for most issues.

    (I've logged bugs against this, one appears to have been accepted, the others are in-flight pending review at the moment)
     
    Peter77 likes this.
  24. Trisibo

    Trisibo

    Joined:
    Nov 1, 2010
    Posts:
    245
    I can't find any info about it in Unity's release notes, and the issue tracker still shows "Won't Fix", but I'm unable to reproduce the bug in 2019.4.11, while in 2019.3.3 it happens in the same project. I have tried:
    • Renaming the class and file name from Visual Studio.
    • Renaming the file name from Unity, and then the class.
    • Adding a namespace to the class.
    • Renaming the namespace.
    • Removing the namespace.
    I guess those tests cover the issue, or did I miss something else?
     
  25. fherbst

    fherbst

    Joined:
    Jun 24, 2012
    Posts:
    802
    @LeonhardP could you take a look at the issue linked above and check if it's actually fixed (and update the status as to which version) please? Thank you :)
     
    LeonhardP likes this.
  26. LeonhardP

    LeonhardP

    Unity Technologies

    Joined:
    Jul 4, 2016
    Posts:
    3,135
    This issue has indeed been fixed for ADBv2 as of 2020.2.0a11, with backports landing in 2019.3.14f1 and 2020.1.0b9.

    The issue tracker link will be updated.
     
    Trisibo and fherbst like this.
  27. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Good job. Thanks.
     
  28. LittleGeek

    LittleGeek

    Joined:
    Oct 18, 2013
    Posts:
    7
    I think it's back in 2020.3.9 (at least). If I have an editor window that uses FindAssets to look for assets of a specific type it returns nothing. I can close down and reopen the window and I might then see 1 asset. If I recompile scripts because I changed a file, it then seems to find them. It's a mess.
     
    sean_virtualmarine likes this.
  29. The_MrX_

    The_MrX_

    Joined:
    Aug 25, 2019
    Posts:
    12
    Just had this bug today in 2020.3.22f so its back.
     
  30. Trae_1P

    Trae_1P

    Joined:
    Apr 13, 2021
    Posts:
    3
    I also experienced this bug in 2020.3.25 - there are two workarounds that seemed to work for me that others may try if you are looking for a script solution:

    Use a string (without the type used in an interpolated string):
    "t:Type"

    Note: This seems to work as an alternative for some assets (I've had success here with VideoClip type).

    OR

    Use the GUID (I usually find this pretty easily in *.meta files, but I think there may be other ways to find).
    Note: This works if you are trying to find a specific asset but breaks down if you need to find many assets because each asset will have a unique GUID.

    Disclaimer: These solutions may not be ideal for some, but hopefully this helps others or inspires better workarounds.
     
  31. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,932
    Free 1-shot class you can drop into your project: https://github.com/adamgit/Publishe...WorkaroundUnityMissingAssetDatabaseFindAPI.cs

    (this was written for AssetStore publishers, just one of the reusable fixes I have to make life bearable when working across a large number of projects :) - feel free to use and distribute it, but change the namespace to make sure it doesn't collide with anyone else who's also using it)

    I use it in a lot of projects, should be working fine - if not let me know (or send me a PR!)
     
    Trae_1P likes this.
  32. Trae_1P

    Trae_1P

    Joined:
    Apr 13, 2021
    Posts:
    3
    After looking at the code, it seems like it's doing the same thing I had tried before. My personal issue was that certain assets showed up just fine using an interpolated string
    Code (CSharp):
    1. "t:{typeof(Type)}"
    and others only worked with
    Code (CSharp):
    1. "t:Type"
    and then one asset in particular (an AssetBundle lookup table scriptable object) didn't show up using either (in a C# script or in the asset search bar in the editor). In that case, I could only get the reference via GUID.

    What's even stranger is that my coworkers don't have this problem. I've also tried restarting Unity, reimporting the scriptable object asset, and reimporting the entire asset folder with no success. That said, I will try using the linked code extension to see if it works for me (when time allows), but this is a really great resource for others that stumble upon this thread!

    EDIT: Something that is important to note is that the non-interpolated method was working for a long while until a few days ago.
     
    Last edited: May 19, 2022
  33. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,932
    I'm using typeof(T).Name which doesn't necessarily resolve the same as an implicit .ToString() (which your example would use). Have you compared the .ToString() and the .Name values? Maybe one has an Assembly name prepended, or something like that?
     
  34. AdamBebko

    AdamBebko

    Joined:
    Apr 8, 2016
    Posts:
    168
    I just ran into this issue. using
    $"t:{typeof(T).FullName}" (adding .FullName) fixed it for me. Thanks. 2020.3.26f1
     
    a436t4ataf likes this.
  35. Stickymadness

    Stickymadness

    Joined:
    Aug 9, 2018
    Posts:
    5
    Thanks for this, I have namespaces + assemblies so this fixed the issue for me.

    Weirdly there was no errors, crashes or anything, the window just stopped appearing. Had to remove the direct "t:Something" access and rename my subclass of EditorWindow in order to run the window again - until I renamed the window, Unity did nothing when it should open the window. I killed all processes of Unity and tried again, same result.
     
    a436t4ataf likes this.