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:
public static T[] GetAllInstances<T>() where T : ScriptableObject
{
// FindAssets uses tags check documentation for more info
string[] guids = AssetDatabase.FindAssets("t:" + typeof(T).Name);
// I'm using this section of the method to debug the one I'm having a problem with.
if(typeof(T) == typeof(ScenarioEnvironment))
{
Debug.Log(typeof(T).Name);
}
T[] a = new T[guids.Length];
// Probably could get optimized
for (int i = 0; i < guids.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
a[i] = AssetDatabase.LoadAssetAtPath<T>(path);
}
return a;
}
The classes for each are quite long, but here’s the abstract class used by each:
namespace TacHaz.Scenarios
{
using UnityEngine;
/// <summary>
/// This is the base class of any component that can be used to generate a scenario.
/// </summary>
public abstract class ScenarioComponent : ScriptableObject, IEditorReadable
{
[Tooltip("This is auto generated by the Scenarios View Window. Viewable for convenience.")]
[SerializeField, ReadOnly] ConnectionID id;
[SerializeField] private string commonName;
/// <summary>
/// A string indicating the name of this <see cref="ScenarioComponent"/>.
/// </summary>
public string Name
{
get
{
return commonName;
}
}
/// <summary>
/// <see cref="ConnectionID"/> indicating the unique id of this scenario component, and it's linked id of the <see cref="Object"/>.
/// </summary>
public ConnectionID ConnectionID
{
get
{
return id;
}
}
/// <summary>
/// Returns an object indicating the instance of this <see cref="ScenarioComponent"/>. Not a property to avoid self referencing loops on converting to json.
/// </summary>
/// <returns>Returns an object indicating the instance of this <see cref="ScenarioComponent"/>.</returns>
public Object Selectable()
{
return this;
}
}
}
Well I’ve cut it down to something REALLY REALLY wierd… This works:
namespace TacHaz.Scenarios
{
using UnityEngine;
using System.Collections;
using TacticalHazmat.Utilities;
[CreateAssetMenu(fileName = "TestEnv", menuName = NamingConvention.FrameWorkName + NamingConvention.Scenarios + "TestEnv")]
public class TestEnv : ScenarioComponent
{
}
}
This does not:
namespace TacHaz.Scenarios
{
using UnityEngine;
using System.Collections;
using TacticalHazmat.Utilities;
[CreateAssetMenu(fileName = "Env", menuName = NamingConvention.FrameWorkName + NamingConvention.Scenarios + "Env")]
public class Env : ScenarioComponent
{
}
}
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.
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.
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.
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.
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.
Thanks for the info @Peter77 . I just saw that in the 2019.3 beta there is a new experimental Asset Database Version 2. The issue is still present in this version currently though (2019.3.0b1).
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…
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.FindAssetsJust Doesn’t Work with Component types, then so be it. ¯_(ツ)_/¯
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.
@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.
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:
/// <summary>
/// In 2020, the AssetDatabase API's only way to find the asset-path for a class is this
/// ridiculous dance of taking Unity's API and loading all the (incorrect!) matches then filtering them to
/// find the match that matches the item you asked for!
///
/// (obviously, if you have an INSTANCE of the class, you can use the magic MonoScript,
/// ... but the MonoScript class was never fully implemented, and only works for objects not classes)
/// </summary>
/// <returns></returns>
private static string _PathToAssetForClass<T>() where T : Object
{
var fuzzyMatchesUnityBadSearch = AssetDatabase.FindAssets( typeof(T).Name );
foreach( var fuzzyMatch in fuzzyMatchesUnityBadSearch )
{
var path = AssetDatabase.GUIDToAssetPath( fuzzyMatch );
var matched = AssetDatabase.LoadAssetAtPath<MonoScript>( path );
if( matched.GetClass().IsAssignableFrom( typeof(T) ) )
return path;
}
return null;
}