Search code examples
c#unity-game-enginescriptable-object

How do I find all Scriptable Objects in a folder and then load a variable from one?


I have a folder in my unity project under "Resources/ScriptableObjects/Skins". I need to get all the objects in the folder, generate a random number for the index, then assign the sprite to an existing game object that the script is attached to. The current error I'm getting is "NullReferenceException: Object reference not set to an instance of an object" on line 151 but I'm creating an instance of the object on line 149. What gives? Here is my function to assign the sprite from a random scriptableobject in the folder to the game object the script is tied to:

void AssignRandomSkin(){
    // Load all skins into memory
    Object[] skinObjects = Resources.LoadAll("ScriptableObjects/Skins");


    // Get length of list
    int amountOfSkins = skinObjects.Length;

    // Get random index of skin to assign
    int skinToAssignIndex = Random.Range(0, amountOfSkins);

    GameObject thisSkin = Instantiate(skinObjects[skinToAssignIndex]) as GameObject;
    // Assign it to game object
    gameObject.GetComponent<SpriteRenderer>().sprite = thisSkin.GetComponent<Sprite>();

}

Here is the Scriptable Object:

using UnityEngine;

[CreateAssetMenu(fileName = "Skin", menuName = "ScriptableObjects/SkinObject", order = 1)]
public class SkinObject : ScriptableObject
{
    public string spriteName; // Name of sprite

    public Sprite sprite;

    public float xPos;

    public float yPos;

    public float zPos;

    public float xScale;

    public float yScale;

    public float zScale;

    public float fallSpeed; //AKA Weight

    public string tier; //Category that skin can be assigned in
}

Solution

  • So what happens here?

    Your objects are ScriptableObject of type SkinObject! => They are not GameObject prefabs!

    Everything in your code should work until the

    GameObject thisSkin = Instantiate(skinObjects[skinToAssignIndex]) as GameObject;
    

    first of all it is unnecessary to instantiate a ScriptableObject. This would only create a clone of the asset but you don't need this.

    And second you are trying to cast it to GameObject. As said this is a type mismatch and therefore thisSkin will be null!

    And finally Sprite is no component. You are rather trying to access the field .sprite of your SkinObject type.


    I'm pretty sure it should rather be

    // You can directly tell LoadAll to only load assets of the correct type
    // even if there would be other assets in the same folder
    SkinObject[] skinObjects = Resources.LoadAll<SkinObject>("ScriptableObjects/Skins");
    
    var thisSkin = skinObjects[Random.Range(0, skinObjects.Length)];
    // Assign it to game object
    gameObject.GetComponent<SpriteRenderer>().sprite = thisSkin.sprite;
    

    However, as said before, from the Best Practices - Resource Folder Don't use it!

    Why not simply reference these ScriptableObjects the usual way via the Inspector in a field like

    public SkinObject[] availableSkins;