Filter Array of Scriptable Objects for Spawner

Hey All,

I ran into a challenge I’m struggling to overcome. Any help or assistance is much appreciated. This is my first attempt at using scriptable objects so it’s possible I’m just trying to do something that isn’t possible. I’ll explain to the best of my ability.

High Level Summary

  • I have a “Spawner” game object in my scene that holds an array of enemy prefab GameObjects. Inside this array sits an enemy Prefab called “Test_Enemy” who is tagged ‘enemy.’ If there is no spawned object in my scene with the tag ‘enemy,’ the “Spawner” will spawn “Test_Enemy.”

  • Test_Enemy” is simply a prefab game object who has an “EnemyController” script that sets it’s sprite, stats, and overall attributes which is inherited by a chosen scriptable object who houses that enemy data.

  • EnemyController” is a script that handles the logic to choose the correct enemy to spawn. Currently, the “EnemyController” chooses a random enemy from an array of enemy scriptable objects.

  • Up until this point, my code works as intended. When the game starts, the “Spawner” will spawn the “Test_Enemy” prefab who inherits its attributes from a random chosen enemy scriptable object.

  • This is where I get stuck. I want to augment my code to allow filtering in my array of scriptable objects that gets passed to the enemy selection. E.g. only put scriptable objects in array of possible enemy spawns if those scriptable objects contain a specific value.

  • I’ve attempted to do this by taking my 'totalEnemyArray’ and converting it to a list. Then creating a list called ‘filteredEnemyList’ who tries to take all the scriptable objects in the 'totalEnemyList’ and pass only those who meet criteria into the filtered list.

  • Based on my debugging, those scriptable object enemies are not being passed into the filtered list.

Scriptable Object code [condensed]

public class EnemyFoundation : ScriptableObject
{
    public TrainableSkill skillType;
}

public enum TrainableSkill
{
    None,
    Mining,
    Combat
}

Spawner Code

public class SpawnObjects : MonoBehaviour
{
    [SerializeField] GameObject[] spawnObjectArray = null;

    //spawner location, change to screen width + ?
    float xSpawnCoordinate = 8f;
    float ySpawnCoordinate = -0.1f;

    GameObject[] enemyObject = null;

    //spawner settings
    int enemyLimit = 1;
    int enemyCount = 0;


    void Update()
    {
        //check for if object should be spawned
        CanObjectSpawn();

        //counts how many objects with tag "enemy" are currently spawned
        EnemyObjectCount();

    }

    void CanObjectSpawn()
    {
        //criteria needed for option to spawn
        if (enemyCount < enemyLimit)
        {
            Spawn();
        }
    }

    void Spawn()
    {
        //choose random object from array list. Only test prefab is in array.
        GameObject spawnedObject = spawnObjectArray[Random.Range(0, spawnObjectArray.Length)];

        //spawn chosen item
        Instantiate(spawnedObject, new Vector3(xSpawnCoordinate, ySpawnCoordinate, 0), Quaternion.identity);

    }

    void EnemyObjectCount()
    {
        enemyObject = GameObject.FindGameObjectsWithTag("Enemy");
        enemyCount = enemyObject.Length;

    }
}

EnemyController [condensed]

void Start()
    {
        spriteRenderer = GetComponent<SpriteRenderer>();

        //Find possible enemy.
        CreateEnemyArray();
        SetEnemyStats();

        //Set Enemy health to it's default.
        currentEnemyHealth = objectHP;
    }

    void Update()
    {
        BeginObjectSpawn(); //accompanying void not shown, works as intended. Just a bool trigger.
    }

    void GrabCorrectEnemy()
    {
        //currently does nothing except set the skill. Will substitute hardcoded == 'mining' with logic
        GrabSelectedSkill();

        //CURRENT - works as intended. To be replaced with TB2 once it works
        selectedEnemy = totalEnemyArray[Random.Range(0, totalEnemyArray.Length)];
        //change to filteredEnemyarray when it works
        //Debug.Log("List total = " + filteredEnemyList.Count);

        //TB2 - BROKEN: obj. ref not set to an instance of an object
        //selectedEnemy = filteredEnemyList[Random.Range(0, filteredEnemyList.Count)];
        //currently given error NullReferenceException
        //Debug.Log("Selected object's name: " + selectedEnemy.name);
    }

    void GrabSelectedSkill()
    {
        playerSkill = TrainableSkill.Mining;
    }

    void SetEnemyStats()
    {
        GrabCorrectEnemy();

        skillType = selectedEnemy.skillType;
    }

    void CreateEnemyArray()
    {
        //CURRENT
        totalEnemyArray = Resources.LoadAll<EnemyFoundation>("");
        //Debug.Log("Enemy Array total = " + totalEnemyArray.Length);

        //Move EnemyArray to List
        totalEnemyList = totalEnemyArray.ToList<EnemyFoundation>();
        Debug.Log("Enemy List Instances = " + totalEnemyList.Count);

        //TB2 - BROKEN: not adding enemies to filtered list
        foreach (EnemyFoundation enemy in totalEnemyList)
        {
            if (skillType == TrainableSkill.Mining)
            {
                filteredEnemyList.Add(enemy);
                Debug.Log("Enemy " + enemy.name + " added to list. Total: " + filteredEnemyList.Count);
            }
        }
        //foreach is currently not being called
    }

I’ve eliminated parts of the code that I thought were irrelevant. If anyone needs additional context I can provide it.

I appreciate any pointers or assistance!

My eye is drawn to this line in your final code blob above:

Shouldn’t that be:

if (enemy.skillType == TrainableSkill.Mining)

??

Well, that’s one obvious error. Lol, thank you!

We are getting somewhere. The foreach loop in now being recognized, I’m just hitting a null ref. exception… obj. ref. not set to an instance of an object on the:

filteredEnemyList.Add(enemy);

I’m going to tinker around and try to fix it. I’ve fixed this error in other context’s before, so hopefully I can figure this one out too. Haha

I think I fixed it! Thank you Kurt! Much appreciated.

1 Like