Search Unity

How to properly create an array of all ScriptableObjects in a folder?

Discussion in 'Scripting' started by CannedSmeef, Dec 14, 2019.

  1. CannedSmeef

    CannedSmeef

    Joined:
    Sep 29, 2018
    Posts:
    28
    Hello, I am currently working on a card game in its extreme infancy. I have created a data folder where each of my cards (stored as scriptableobjects) are located. I need to be able to load in every card in this folder as an array, but I've realized as the number of cards I have climbs to the dozens, this is going to be an absurd amount of assignment in the inspector. Do I really have to manually drag each card into a slot in the inspector?

    I've tried using Resources.LoadAll() to create an array at the start of the game for me, but I can't figure out how to cast the object[] as a Card[]. Is there a way to get this to work? If I really am better off just assigning the cards manually, I guess I'll just have to bite the bullet.

    Thank you!
     
  2. Team2Studio

    Team2Studio

    Joined:
    Sep 23, 2015
    Posts:
    98
    this seams to work for me:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Card : ScriptableObject
    4. {
    5.     public string test = "Hello world";
    6. }
    7.  
    8. public class MyScript : MonoBehaviour
    9. {
    10.     private void Start()
    11.     {
    12.         Card[] cardObjects = Resources.LoadAll("Cards") as Card[];
    13.  
    14.         Debug.Log(cardObjects[cardObjects.Length - 1].test);
    15.     }
    16. }
     
  3. CannedSmeef

    CannedSmeef

    Joined:
    Sep 29, 2018
    Posts:
    28
    I doesn't seem to be working, the array seems to throw out NullReferenceException errors and nulls when I try to print anything out. Here is the relevant data, and yes I am sure this is the correct directory.

    Code (CSharp):
    1.     private Card[] allCards;
    2.  
    3.     void Start()
    4.     {
    5.         health = maxHealth;
    6.  
    7.         // compile a catalogue of all cards available in the game
    8.         allCards = Resources.LoadAll("Data/Cards") as Card[];
    9.         Debug.Log(allCards.Length.ToString());
    10.     }
     
  4. Team2Studio

    Team2Studio

    Joined:
    Sep 23, 2015
    Posts:
    98
    I'm sorry, I tested the next bit of code, and it seems to work for me :
    Code (CSharp):
    1. using UnityEngine;
    2. public class Card : ScriptableObject
    3. {
    4.     public string test = "Hello world";
    5. }
    6.  
    7. public class MyScript : MonoBehaviour
    8. {
    9.     private void Start()
    10.     {
    11.         Object[] cardObjects = Resources.LoadAll("Cards");
    12.  
    13.         Card[] cards = new Card[cardObjects.Length];
    14.  
    15.         cardObjects.CopyTo(cards, 0);
    16.  
    17.         Debug.Log(cards[0].test);
    18.     }
    19. }
     
  5. Team2Studio

    Team2Studio

    Joined:
    Sep 23, 2015
    Posts:
    98
    Looks like it can also be done this way, but you can't reference a specific folder with this one :
    Code (CSharp):
    1. using UnityEngine;
    2. public class Card : ScriptableObject
    3. {
    4.     public string test = "Hello world";
    5. }
    6.  
    7. public class MyScript : MonoBehaviour
    8. {
    9.     private void Start()
    10.     {
    11.         Card[] allCards = (Card[])Resources.FindObjectsOfTypeAll(typeof(Card));
    12.  
    13.         Debug.Log(allCards[0].test);
    14.     }
    15. }
     
  6. Cyber-Dog

    Cyber-Dog

    Joined:
    Sep 12, 2018
    Posts:
    352
    Just wanted to point out you can also convert this to a list very easily.
    Like so...
    Code (CSharp):
    1. using System.linq;
    2.  
    3. List<Card> cards = Resources.LoadAll<Card>("").ToList();
    I always disliked arrays. And TBH the efficiency gains is minor.
     
  7. CannedSmeef

    CannedSmeef

    Joined:
    Sep 29, 2018
    Posts:
    28
    Nothing suggested is working, not even switching the type of object loaded doesn't help, it always returns an array/list that contains nothing. Removing folder directories doesn't help either, it's like it refuses to access the resource folders at all.
     
  8. Team2Studio

    Team2Studio

    Joined:
    Sep 23, 2015
    Posts:
    98
    Thats weird, my second example (copying the object array) works for sure, i tested it myself, and was able to access the “test” variable. Are you sure everything is named as it should be? (capital letters and such). @Cyber-Dog i prefer Lists also but if the op is asking for an array solution then i’m not going to push converting to a List.
     
  9. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @CannedSmeef

    Are you sure your files are in folder that is written exactly "Resources" not "resources" or "resource" or something else? You can have many Resources folders and those can be somewhere in your Assets folder.
     
  10. CannedSmeef

    CannedSmeef

    Joined:
    Sep 29, 2018
    Posts:
    28
    When I said resource folders I was referring to the asset folders as a whole--simply searching all assets folders for any kind of object, like my scriptable object or sprites, will create a list/array that contains absolutely nothing. Capitalization or spelling isn't the issue here, I am sure of it.
     
  11. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @CannedSmeef

    You explicitly stated you are using Resources.* to load your files:

    "I've tried using Resources.LoadAll()"


    If you are not placing asset files in Resources folder, you can't load them with Resources.Load commands - AFAIK. Resources.* is meant to also be used runtime in builds.

    "When I said resource folders I was referring to the asset folders as a whole"

    This piece of information should have been included in the original post.

    If you want to access files in any folder of Assets, you will have to use AssetDatabase.* commands that work only in editor.
     
  12. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,331
    You can use AssetDatabase.FindAssets to do this. But like @eses said, it is an editor only method, so you'll need to do the fetching in edit mode in the editor and then serialize the array so that it persists into the build.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. #if UNITY_EDITOR
    4. using UnityEditor;
    5. #endif
    6.  
    7. public class Deck : MonoBehaviour
    8. {
    9.     [SerializeField]
    10.     private Card[] cards = new Card[0];
    11.  
    12.     #if UNITY_EDITOR
    13.     private void OnValidate()
    14.     {
    15.         cards = LoadAllCards()
    16.     }
    17.  
    18.     private static Card[] LoadAllCards()
    19.     {
    20.         string[] guids = AssetDatabase.FindAssets("t:Card", new[] { "Assets/Cards" });
    21.         int count = guids.Length;
    22.         cards = new Card[count];
    23.         for(int n = 0; n < count; n++)
    24.         {
    25.             var path = AssetDatabase.GUIDToAssetPath(guids[n]);
    26.             cards[n] = AssetDatabase.LoadAssetAtPath<Card>(path);
    27.         }
    28.     }
    29.     #endif
    30. }
     
    Last edited: Dec 16, 2019
    eses likes this.
  13. CannedSmeef

    CannedSmeef

    Joined:
    Sep 29, 2018
    Posts:
    28
    I have an announcement to make: I am very, very dumb.

    I was completely unaware that the "resources" and the "assets" folders were two separate entities. I thought they were interchangable terms for one another. So I created a resources folder in my assets folder, moved my card data there, and viola! It works perfectly now. The Scripting API for Resources.Load() methods should probably make the distinction a bit more explicit, but hey you live you learn.

    Thank you for helping me everyone! Sorry to waste your time!
     
  14. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,913
    The good part is that you can have more than one Resources folder, and they don't have to be at the root, unlike the Plugins/Gizmos folders.
    • Assets
      • Items
        • Resources
          • Weapons
            • Weapon1ScriptableObject
            • Weapon2ScriptableObject
     
    SaltwaterAssembly likes this.
  15. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,331
    Be wary when using the Resources folder though. Everything you place inside Resources folders is always included in your builds, even if you have zero references to them in any of your scenes.

    Excessive use of the Resources folder can easily lead to you having a large number of assets in your builds that haven't actually been used anywhere for a long time.

    Unity Manual: Best Practice Guide
     
  16. RamonKing

    RamonKing

    Joined:
    Dec 13, 2019
    Posts:
    10
    You are right,thanks.
     
  17. tadsavage

    tadsavage

    Joined:
    Jan 13, 2018
    Posts:
    2
    Thanks Cyber Dog - this is exactly what I needed!