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

Filter Array of Scriptable Objects for Spawner

Discussion in 'Scripting' started by Zefrus, Nov 4, 2021.

  1. Zefrus

    Zefrus

    Joined:
    Dec 3, 2019
    Posts:
    14
    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]
    Code (CSharp):
    1. public class EnemyFoundation : ScriptableObject
    2. {
    3.     public TrainableSkill skillType;
    4. }
    5.  
    6. public enum TrainableSkill
    7. {
    8.     None,
    9.     Mining,
    10.     Combat
    11. }


    Spawner Code
    Code (CSharp):
    1. public class SpawnObjects : MonoBehaviour
    2. {
    3.     [SerializeField] GameObject[] spawnObjectArray = null;
    4.  
    5.     //spawner location, change to screen width + ?
    6.     float xSpawnCoordinate = 8f;
    7.     float ySpawnCoordinate = -0.1f;
    8.  
    9.     GameObject[] enemyObject = null;
    10.  
    11.     //spawner settings
    12.     int enemyLimit = 1;
    13.     int enemyCount = 0;
    14.  
    15.  
    16.     void Update()
    17.     {
    18.         //check for if object should be spawned
    19.         CanObjectSpawn();
    20.  
    21.         //counts how many objects with tag "enemy" are currently spawned
    22.         EnemyObjectCount();
    23.  
    24.     }
    25.  
    26.     void CanObjectSpawn()
    27.     {
    28.         //criteria needed for option to spawn
    29.         if (enemyCount < enemyLimit)
    30.         {
    31.             Spawn();
    32.         }
    33.     }
    34.  
    35.     void Spawn()
    36.     {
    37.         //choose random object from array list. Only test prefab is in array.
    38.         GameObject spawnedObject = spawnObjectArray[Random.Range(0, spawnObjectArray.Length)];
    39.  
    40.         //spawn chosen item
    41.         Instantiate(spawnedObject, new Vector3(xSpawnCoordinate, ySpawnCoordinate, 0), Quaternion.identity);
    42.  
    43.     }
    44.  
    45.     void EnemyObjectCount()
    46.     {
    47.         enemyObject = GameObject.FindGameObjectsWithTag("Enemy");
    48.         enemyCount = enemyObject.Length;
    49.  
    50.     }
    51. }


    EnemyController [condensed]
    Code (CSharp):
    1. void Start()
    2.     {
    3.         spriteRenderer = GetComponent<SpriteRenderer>();
    4.  
    5.         //Find possible enemy.
    6.         CreateEnemyArray();
    7.         SetEnemyStats();
    8.  
    9.         //Set Enemy health to it's default.
    10.         currentEnemyHealth = objectHP;
    11.     }
    12.  
    13.     void Update()
    14.     {
    15.         BeginObjectSpawn(); //accompanying void not shown, works as intended. Just a bool trigger.
    16.     }
    17.  
    18.     void GrabCorrectEnemy()
    19.     {
    20.         //currently does nothing except set the skill. Will substitute hardcoded == 'mining' with logic
    21.         GrabSelectedSkill();
    22.  
    23.         //CURRENT - works as intended. To be replaced with TB2 once it works
    24.         selectedEnemy = totalEnemyArray[Random.Range(0, totalEnemyArray.Length)];
    25.         //change to filteredEnemyarray when it works
    26.         //Debug.Log("List total = " + filteredEnemyList.Count);
    27.  
    28.         //TB2 - BROKEN: obj. ref not set to an instance of an object
    29.         //selectedEnemy = filteredEnemyList[Random.Range(0, filteredEnemyList.Count)];
    30.         //currently given error NullReferenceException
    31.         //Debug.Log("Selected object's name: " + selectedEnemy.name);
    32.     }
    33.  
    34.     void GrabSelectedSkill()
    35.     {
    36.         playerSkill = TrainableSkill.Mining;
    37.     }
    38.  
    39.     void SetEnemyStats()
    40.     {
    41.         GrabCorrectEnemy();
    42.  
    43.         skillType = selectedEnemy.skillType;
    44.     }
    45.  
    46.     void CreateEnemyArray()
    47.     {
    48.         //CURRENT
    49.         totalEnemyArray = Resources.LoadAll<EnemyFoundation>("");
    50.         //Debug.Log("Enemy Array total = " + totalEnemyArray.Length);
    51.  
    52.         //Move EnemyArray to List
    53.         totalEnemyList = totalEnemyArray.ToList<EnemyFoundation>();
    54.         Debug.Log("Enemy List Instances = " + totalEnemyList.Count);
    55.  
    56.         //TB2 - BROKEN: not adding enemies to filtered list
    57.         foreach (EnemyFoundation enemy in totalEnemyList)
    58.         {
    59.             if (skillType == TrainableSkill.Mining)
    60.             {
    61.                 filteredEnemyList.Add(enemy);
    62.                 Debug.Log("Enemy " + enemy.name + " added to list. Total: " + filteredEnemyList.Count);
    63.             }
    64.         }
    65.         //foreach is currently not being called
    66.     }

    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!
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,762
    My eye is drawn to this line in your final code blob above:

    Shouldn't that be:

    Code (csharp):
    1. if (enemy.skillType == TrainableSkill.Mining)
    ??
     
  3. Zefrus

    Zefrus

    Joined:
    Dec 3, 2019
    Posts:
    14
    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
     
  4. Zefrus

    Zefrus

    Joined:
    Dec 3, 2019
    Posts:
    14
    I think I fixed it! Thank you Kurt! Much appreciated.
     
    Kurt-Dekker likes this.