Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Load all scriptable objects from custom project folder

Discussion in 'Scripting' started by gabrimo, Nov 15, 2021.

  1. gabrimo

    gabrimo

    Joined:
    May 2, 2015
    Posts:
    86
    I've been facing a hard time to find a way to load all scriptable objects from a custom directory of my project. Since Resources.Load isn't a good practice, I would like to know if anyone here knows a way to achieve that.

    All SO's have the same type, my plan is to load all of them and set an unique ID for each instance. If that's not possible, my goal is to check their manually setted ID's to be sure that they're unique and don't break my system later on.

    The classes involved at this system would not be in production, this is something to avoid problems during development...
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,762
    Not sure where this idea comes from, but Resources.Load<T>() is fine as long as you realize the implications of putting things into your project and what will get included.

    Steps to success:

    make a folder such as:

    Resources/MyScriptableWingnuts


    Create as many wingnuts as you want, put them in that folder.

    At runtime, load all your wingnuts up with:

    Code (csharp):
    1. var AllAvailableWingnuts = Resources.LoadAll<ScriptableWingnut>( "MyScriptableWingnuts/");
    Done.

    Not sure what this means... do you instead intend for this all to just be editor tooling? If so then write it like editor tooling, put all the scripts under the
    Editor
    folder and use classes in the
    UnityEditor
    namespace.
     
    Bunny83 likes this.
  3. gabrimo

    gabrimo

    Joined:
    May 2, 2015
    Posts:
    86
    Thanks.

    Well, I'll use the Resources approach for now then. Is there any reason why I can't load an SO outside of it?

    I mean, this seems to be far too arbitrary considering most decisions on Unity seems to prioritize flexibility over simplicity.

    I just said that I'll check these SO's only on the editor. All parts of this check system should be disable on the final build.
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,895
    JoNax97 likes this.
  5. Zephus

    Zephus

    Joined:
    May 25, 2015
    Posts:
    356
    Most of the time you get told to never use Resources.Load, just in general. For example here:
    https://forum.unity.com/threads/when-do-you-use-resources-load.510935/

    The idea has been around for years now that you should absolutely never use Resources.Load anymore and only use AssetDatabase, AssetBundles and Addressables. I remember the first time reading about this on Unity Learn. Take a look at the first sentence in chapter 3.1:
    https://learn.unity.com/tutorial/assets-resources-and-assetbundles#5c7f8528edbc2a002053b5a7

    As far as I know people have universally assumed the opinion that Resources in itself are deprecated. I honestly wouldn't suggest anyone to use them anymore, but not because they don't work. Just because of how the other approaches have been adopted by pretty much everyone by now. It's just an outdated concept that people don't use anymore in new projects.
     
    Last edited: Nov 16, 2021
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,762
    I honestly can't tell if this is a troll post or not.

    Have you ever tried to ask a new Unity user (eg, someone asking a question like "find a way to load all scriptable objects from a custom directory of my project.") to set up addressables in a Unity project?

    Go ahead, try it... I'll wait. That new user is NOT gonna have a good time at it. And even if they get it ALL working, they will be completely unable to reason about the sources, states, development and mocking modes implicitly embedded in the Addressables.

    Don't get me wrong: Addressables are a wonderful massive general purpose content delivery architecture. But as far as I'm concerned, they have ZERO place in tutorials and people just starting out. The analogy would be "Look, driving to the store is dangerous because cars are dangerous and dirty. You should instead take a Space Shuttle to the store because those hardly ever crash."
     
    Raws1 and Socrates like this.
  7. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,895
    Incorrect? They have a whole course to get you started with it: https://learn.unity.com/project/getting-started-with-addressables

    That's where I started with addressables. Now my whole game uses it to load content. I use resources for exactly one thing.

    I can understand using resources for very small projects. But anything beyond that I think you're shooting folks in the by suggesting they use resources.
     
  8. Zephus

    Zephus

    Joined:
    May 25, 2015
    Posts:
    356
    Yes, you're right that new users will have major problems with that. I'm just telling you what the general consensus is. You're pretty much recommending features that even Unity themselves say should not be used anymore.
     
  9. Shreddedcoconut

    Shreddedcoconut

    Joined:
    Jul 30, 2021
    Posts:
    61
    Why does Unity say those features should not be used anymore?
     
    Kurt-Dekker likes this.
  10. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,895
    Shreddedcoconut likes this.
  11. Shreddedcoconut

    Shreddedcoconut

    Joined:
    Jul 30, 2021
    Posts:
    61
  12. gabrimo

    gabrimo

    Joined:
    May 2, 2015
    Posts:
    86
    So I tried to use your suggestion like that:


    Code (CSharp):
    1.  
    2.         var paths = AssetDatabase.FindAssets("", new[] { kitSuppliersPath }); //files path on Assets/ folder
    3.         kitSuppliers = new KitSupplier[paths.Length];
    4.         for (int i = 0; i < paths.Length; i++)
    5.         {
    6.             var path = AssetDatabase.GUIDToAssetPath(paths[i]);
    7.             kitSuppliers[i] = (KitSupplier)AssetDatabase.LoadAssetAtPath(path, typeof(KitSupplier[]));
    8.         }
    But that didn't worked, presenting a NullReferenceException indicating that "kitSuppliers" is null.

    So I tested another approach with this:

    Code (CSharp):
    1. var rootPath = Application.dataPath + kitSuppliersPath;
    2.         var paths = Directory.GetFiles(rootPath, "*.asset");
    3.         kitSuppliers = new KitSupplier[paths.Length];
    4.         for (int i = 0; i < paths.Length; i++)
    5.             kitSuppliers[i] = (KitSupplier)AssetDatabase.LoadAssetAtPath(paths[i], typeof(KitSupplier[]));
    But this also didn't worked, presenting an "Invalid AssetDatabase path" warning, indicating that I must use a path relative to the project folder. Beyond that, my "kitSuppliers" variable still null.

    If change "dataPath" for "persistentDataPath" I get "DirectoryNotFoundException", casue the system tries to look for the files in a empty directory, something at "AppData\LocalLow\DefaultCompany", which is empty in my case.

    I don't get why load a bunch of files from the same type can be this complicated...
     
  13. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,895
    I roll this extension method in one of my utility classes:
    Code (CSharp):
    1.  
    2.     using System.Linq;
    3.     using System.Collections;
    4.     using System.Collections.Generic;
    5.     using UnityEngine;
    6.     using UnityEditor;
    7.  
    8.     public static class ScriptableObjectUtilities
    9.     {
    10.  
    11.         public static List<T> FindAllScriptableObjectsOfType<T>(string filter, string folder = "Assets")
    12.             where T : ScriptableObject
    13.         {
    14.             return AssetDatabase.FindAssets(filter, new[] { folder })
    15.                 .Select(guid => AssetDatabase.LoadAssetAtPath<T>(AssetDatabase.GUIDToAssetPath(guid)))
    16.                 .ToList();
    17.         }
    18.     }
    And can be called like this:
    Code (CSharp):
    1.  
    2. List<YourClassType> listOfStuff = ScriptableObjectUtilities.FindAllScriptableObjectsOfType<YourClassType>(t:YourClassType, "Assets/YourDirectoryhere");
    3.  
    Technically the code doesn't need to be restricted to ScriptableObjects, but I find them the most reliable when trying to find assets with the AssetDatabase. This should get you sorted nonetheless.
     
    EgoJacky and gabrimo like this.
  14. gabrimo

    gabrimo

    Joined:
    May 2, 2015
    Posts:
    86
    Hi again, your suggested method worked great till I build the project. Apparently, AssetDatabase class should be used on the editor only, so I would like to know if somebody has a solution that works both on editor and build...
     
  15. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,762
    Five weeks later, the first response I gave here will still solve this problem 100% every time.

    https://forum.unity.com/threads/loa...m-custom-project-folder.1198330/#post-7658830

    You're welcome to iterate with other newfangled wiggy-whacky advanced solutions if you like, but I told you the solution I use in 100% of my games. Here is me using it in Jetpack Kurt to load all kinds of things:

    Screen Shot 2021-12-21 at 3.54.50 PM.png

    Yes, Jetpack Kurt has built-in Jetpack Golf, so there are Golf Club Configurations.
     
  16. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,895
    Yes AssetDatabase is Editor only (as is anything in the UnityEditor namespace).

    For non-Editor fetching of materials, you've either got Resources, AssetBundles (ew), or Addressables.

    I've softened a bit on my stances on Resources. If you're still learning, I'd say use Resources, but note that you will hit limitations depending on what you're doing. Addressables is more complicated, yes, but able to do, much, much more.

    Addressables is a wrapper around AssetBundles, so I wouldn't worry about AssetBundles. Having a read a bit into the documentation... they sounded like a right pain to use (not that addressables is without pains).

    As mentioned before, I use AssetDatabase to 'pre-load' lists of stuff to avoid having to do so in run time.
     
  17. gabrimo

    gabrimo

    Joined:
    May 2, 2015
    Posts:
    86
    I need to load hundreds of SOs, both on editor and build, I was wrong about need to load stuff only on editor.

    I'll go with Resources for now and try to learn more about Addressables in the near future if Resources' performance hit proves too high for my needs then.

    I'm positive that there's must be a good reason for such limitation, but I still feeling strange about Resources being the only out of the box solution for such a basic stuff, considering that it isn't even recommended by the own Unity guys...

    Thanks for those who answered anyway.
     
  18. TalkingBlah

    TalkingBlah

    Joined:
    Jun 10, 2020
    Posts:
    2
    Hi, I know this is an old post with the issue might have already been solved. I am new to Unity and I really get very limited time to do development after my work. While working on something I also came across a problem where it would have been really useful if I could load a lot of ScriptableObject into a list during runtime. The working solution I came up with was using the Addresables package and using it to load all the ScriptableObjects after giving the folder that contains the ScriptableObjects a label
    Code (CSharp):
    1.   [SerializeField]
    2.     List<Items> itemlist;
    3.     [SerializeField]
    4.     AssetLabelReference assetLabelReference;
    5.     // Start is called before the first frame update
    6.     void Start()
    7.     {
    8.         SetItemList();
    9.     }
    10.  
    11.     // Update is called once per frame
    12.     void Update()
    13.     {
    14.         Debug.Log(itemlist.Count);
    15.     }
    16.     private void SetItemList()
    17.     {
    18.         UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<IList<Items>> asyncOperationHandle = Addressables.LoadAssetsAsync<Items>(assetLabelReference, (items) =>
    19.                    {
    20.                        itemlist.Add(items);
    21.                    });
    22.     }
    .