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

How to get list of assets at asset path?

Discussion in 'Scripting' started by Ryuuguu, Mar 9, 2009.

  1. Ryuuguu

    Ryuuguu

    Joined:
    Apr 14, 2007
    Posts:
    391
    In the editor I want to load all the assets in a given folder into an array. Once I have the asset's name I can use AssetDatabase.LoadAssetAtPath . But how do I find out what assets are in the folder?

    i.e.

    folder struct


    /myprojocted
    -----/editorthingys
    --------/thingy1
    --------/thingy2
    --------/thingy3

    I know I want to load all assets in /myproject/eeditorthingys but I don't know their name. how do I find out their names so I can lad them?

    cheers,
    Grant[/code]
     
  2. Kcm

    Kcm

    Joined:
    Sep 18, 2008
    Posts:
    18
    I have exactly the same request...

    No one can help us?
     
  3. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    1. You know it
    2. You have an asset build script that puts in a text file into the root that lists all files in as it has written them when adding to the bundle
    then you can get those information from that text file.
     
  4. MikeHergaarden

    MikeHergaarden

    Joined:
    Mar 9, 2008
    Posts:
    1,027
    Besides the unity editor functions, you can probably also use .NET functions to read the folders contents.
     
  5. jonas-echterhoff

    jonas-echterhoff

    Unity Technologies

    Joined:
    Aug 18, 2005
    Posts:
    1,666
    Here's a function to return an Array of all Objects of type T for all Assets in a folder, using the .NET File APIs:

    Code (csharp):
    1.  
    2.     public static T[] GetAtPath<T> (string path) {
    3.        
    4.         ArrayList al = new ArrayList();
    5.         string [] fileEntries = Directory.GetFiles(Application.dataPath+"/"+path);
    6.         foreach(string fileName in fileEntries)
    7.         {
    8.             int index = fileName.LastIndexOf("/");
    9.             string localPath = "Assets/" + path;
    10.            
    11.             if (index > 0)
    12.                 localPath += fileName.Substring(index);
    13.                
    14.             Object t = Resources.LoadAssetAtPath(localPath, typeof(T));
    15.  
    16.             if(t != null)
    17.                 al.Add(t);
    18.         }
    19.         T[] result = new T[al.Count];
    20.         for(int i=0;i<al.Count;i++)
    21.             result[i] = (T)al[i];
    22.            
    23.         return result;
    24.     }
    25.  
     
  6. Ryuuguu

    Ryuuguu

    Joined:
    Apr 14, 2007
    Posts:
    391
    I have looked at my orginal question and see I misunderstood how loadall works. so the answer to my orignal question (untest) be to
    Code (csharp):
    1.  var textures : Object[] = Resources.LoadAll("Prefabs", GameObjects);
    and loop over Object[] getting the names out.
     
    hhoffren likes this.
  7. Meltdown

    Meltdown

    Joined:
    Oct 13, 2010
    Posts:
    5,816
    Hi Jonas,

    I'm trying to get a list of AudioClips in an asset folder using your method, although the ArrayList is always empty.
    I'm calling it as..

    Code (csharp):
    1. Object[] objects = Utils.GetAtPath<AudioClip>("/Sound/Music");
    Thanks
     
  8. thallippoli

    thallippoli

    Joined:
    Nov 1, 2012
    Posts:
    2
    The approach that uses .NET's System.IO is not guaranteed to work on devices. The 'Resources' folder e.g. can become one big wad and filesystem calls will fail at that point.
    The Resources.LoadAll approach is not great either because of the large memory load it can cause.
    It's kind of disappointing that Unity does not support a simple operation like listing your assets without side effects. This can be especially useful when you are trying to get something done quick and dirty.
    Lacking this, the best approach seems to be @Dreamora's approach of caching a list at build time.
     
  9. scottm361

    scottm361

    Joined:
    Sep 22, 2013
    Posts:
    2
    small edit to Jonas's function that worked better for me

    Code (CSharp):
    1.     public static T[] GetAtPath<T>(string path)
    2.     {
    3.  
    4.         ArrayList al = new ArrayList();
    5.         string[] fileEntries = Directory.GetFiles(Application.dataPath + "/" + path);
    6.  
    7.         foreach (string fileName in fileEntries)
    8.         {
    9.             int assetPathIndex = fileName.IndexOf("Assets");
    10.             string localPath = fileName.Substring(assetPathIndex);
    11.  
    12.             Object t = Resources.LoadAssetAtPath(localPath, typeof(T));
    13.  
    14.             if (t != null)
    15.                 al.Add(t);
    16.         }
    17.         T[] result = new T[al.Count];
    18.         for (int i = 0; i < al.Count; i++)
    19.             result[i] = (T)al[i];
    20.  
    21.         return result;
    22.     }
     
    hhoffren and laurG like this.
  10. Sbizz

    Sbizz

    Joined:
    Oct 2, 2014
    Posts:
    250
    Maybe I'm missing something, but why don't you use Resources.LoadAll<T>(string path) ? Which does exactly what you want.. You just have to put the assets you want to import in the Resources folder.
     
  11. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,422
    Because that "loads the asset" imagine having 5000 models in there and you want to iterate over them (loading only 1 or a screenful at a time)... Resources.LoadAll() is no good here.
     
    Last edited: Jan 29, 2017
  12. CarlosBCV

    CarlosBCV

    Joined:
    Jun 21, 2013
    Posts:
    4
    Fantilyre and MaxEden like this.
  13. Cobo3

    Cobo3

    Joined:
    Jun 16, 2013
    Posts:
    66
    Added a couple modifications to be able to load assets outside the Resources folder in the Editor.
    Code (csharp):
    1.  
    2. private T[] GetAtPath<T>(string path)
    3.     {
    4.         ArrayList al = new ArrayList();
    5.         string[] fileEntries = Directory.GetFiles(Application.dataPath + "/" + path);
    6.  
    7.         foreach (string fileName in fileEntries)
    8.         {
    9.             string temp = fileName.Replace("\\", "/");
    10.             int index = temp.LastIndexOf("/");
    11.             string localPath = "Assets/" + path;
    12.  
    13.             if (index > 0)
    14.                 localPath += temp.Substring(index);
    15.  
    16.             Object t = AssetDatabase.LoadAssetAtPath(localPath, typeof(T));
    17.  
    18.             if (t != null)
    19.                 al.Add(t);
    20.         }
    21.  
    22.         T[] result = new T[al.Count];
    23.  
    24.         for (int i = 0; i < al.Count; i++)
    25.             result[i] = (T) al[i];
    26.  
    27.         return result;
    28.     }
    29.  
     
  14. Deckweiss

    Deckweiss

    Joined:
    Nov 22, 2017
    Posts:
    5

    For my usecase, I have implimented this as a static method, that returns a List.
    Also, I dislike for loops, so I used Select for clarity, which applies a function to a whole set.

    Code (csharp):
    1.  
    2. public static List<T> GetAssetList<T>(string path) where T : class
    3. {
    4.     string[] fileEntries = Directory.GetFiles(Application.dataPath + "/" + path);
    5.    
    6.     return fileEntries.Select(fileName =>
    7.     {
    8.         string temp = fileName.Replace("\\", "/");
    9.         int index = temp.LastIndexOf("/");
    10.         string localPath = "Assets/" + path;
    11.  
    12.         if (index > 0)
    13.             localPath += temp.Substring(index);
    14.  
    15.         return AssetDatabase.LoadAssetAtPath(localPath, typeof(T));
    16.     })
    17.         //Filtering null values, the Where statement does not work for all types T
    18.         .OfType<T>()    //.Where(asset => asset != null)
    19.         .ToList();
    20. }
    21.  
     
    lclemens and chrismarch like this.
  15. zach_peoplefun

    zach_peoplefun

    Joined:
    May 16, 2019
    Posts:
    2
    I modified Deckweiss's code to remove the extension before trying to get the files from the assetdatabase

    Code (CSharp):
    1. public static List<T> GetAssetList<T>(string path) where T : class
    2.         {
    3.             string[] fileEntries = Directory.GetFiles( path);
    4.  
    5.             return fileEntries.Select(fileName =>
    6.                 {
    7.                     string assetPath = fileName.Substring(fileName.IndexOf("Assets"));
    8.                     assetPath = Path.ChangeExtension(assetPath, null);
    9.                     return UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath, typeof(T));
    10.                 })
    11.                 .OfType<T>()
    12.                 .ToList();
    13.         }
     
    OgnjenSelver, lclemens, f4bo and 3 others like this.
  16. Luxalpa

    Luxalpa

    Joined:
    May 13, 2014
    Posts:
    8
    Use AssetDatabase.FindAssets combined with AssetDatabase.GUIDToAssetPath in order to get your assets.

    Code (CSharp):
    1. var assets = AssetDatabase.FindAssets("t:AnimationClip", new[] {"Assets/Animations"});
    2.     foreach (var guid in assets) {
    3.       var clip = AssetDatabase.LoadAssetAtPath<AnimationClip>(AssetDatabase.GUIDToAssetPath(guid));
    4.       Debug.Log(clip);
    5.     }
    This is safer than working with Directory and IndexOf("Assets") because those can break randomly (for example when your project ends up in a subdir that has Assets in its name).
     
    petey, andreiagmu, fnnbrr and 18 others like this.
  17. RVDH

    RVDH

    Joined:
    Aug 4, 2019
    Posts:
    7
  18. fbessette

    fbessette

    Joined:
    Mar 10, 2021
    Posts:
    3
    Worth noting: AssetDatabase.FindAssets can be very slow if you have lots of assets in the project. In my project, using the Directory.GetFiles + AssetDatabase.AssetPathToGUID was about 50x faster. (~40 ms down to 0.8ms)
     
    Last edited: Jun 2, 2021
    Novack, andreiagmu and Cobo3 like this.
  19. olmstedm

    olmstedm

    Joined:
    Feb 17, 2022
    Posts:
    5
    Might be a stupid question but how do I call that?

    private T[] GetAtPath<T>(string path) - what does the call look like and what do I have at that point? I am looking for a way to get every mesh under assets and turn off the mesh setting of read|write in order to save some memory durring gameplay.
     
  20. Jumeuan

    Jumeuan

    Joined:
    Mar 14, 2017
    Posts:
    39
    Best solution on editor!
     
    andreiagmu likes this.
  21. Oneiros90

    Oneiros90

    Joined:
    Apr 29, 2014
    Posts:
    78
    Generic implementation:

    Code (CSharp):
    1. public static IEnumerable<T> GetAssetsAtPath<T>(string path) where T : Object
    2. {
    3.     var assets = AssetDatabase.FindAssets($"t:{typeof(T).Name}", new[] { path });
    4.     foreach (var guid in assets)
    5.     {
    6.         yield return AssetDatabase.LoadAssetAtPath<T>(AssetDatabase.GUIDToAssetPath(guid));
    7.     }
    8. }
     
    Jamisco, jolyn_w and adamgryu like this.
  22. Jamisco

    Jamisco

    Joined:
    May 21, 2022
    Posts:
    4
    Code (CSharp):
    1.             public static T[] GetAssetsAtPath<T>(string path) where T : Object
    2.             {
    3.                 var assets = AssetDatabase.FindAssets($"t:{typeof(T).Name}", new[] { path });
    4.                 List<T> foundAssets = new List<T>();
    5.  
    6.                 foreach (var guid in assets)
    7.                 {
    8.                     foundAssets.Add(AssetDatabase.LoadAssetAtPath<T>(AssetDatabase.GUIDToAssetPath(guid)));
    9.                 }
    10.  
    11.                 // if you want to skip the convertion to array, simply change method return type
    12.                 return foundAssets.ToArray();
    13.             }
    Bypassed The IEnumerable entirely for those who want more simplicity.
     
  23. Oneiros90

    Oneiros90

    Joined:
    Apr 29, 2014
    Posts:
    78
    You can use my method like
    Code (CSharp):
    1. GetAssetsAtPath<AssetType>(path).ToArray()
    if you want an array, without the need to allocate a list first