Trying to use lists to check for available spawn points

I’m trying to use lists to check for available spawn points and to select the spawn point to use. To do this I made a list called EnemySpotsAvailable and a list called EnemySpotsHeld. These lists hold the values for the spawn points on the X and Z axis.

public class EnemySpotsAvailable : MonoBehaviour
{
    public int pointX;
    public int pointZ   ;

    public EnemySpotsAvailable(int newPointX, int newPointZ)
    {
        pointX = newPointX;
        pointZ = newPointZ;
    }
}

public class EnemySpotsHeld : MonoBehaviour
{
    public int pointX;
    public int pointZ;

    public EnemySpotsHeld(int newPointX, int newPointZ)
    {
        pointX = newPointX;
        pointZ = newPointZ;
    }
}

In my EnemySpawn class I’m trying to use these lists to decide on where the enemy can spawn. I’m trying to use Random.Range() to choose an index number from the EnemySpotsAvailable list. When it has chosen an index number I want to copy the value in pointX to spawnX and copy the value in pointY to spawnY. After I’ve done this I want to remove this index from EnemySpotsAvailabe and move it to EnemySpotsHeld. I feel like I’m overlooking something really simple but I’m just unable to figure it out.

public class EnemySpawn : MonoBehaviour
{
    public GameObject Enemy;
    int[] spawnRange = new int[] { -4, 0, 4 };
    int spawnX = 0;
    int spawnZ = 0;
    List<EnemySpotsAvailable> spotsAvailable = new List<EnemySpotsAvailable>();
    List<EnemySpotsHeld> spotsHeld = new List<EnemySpotsHeld>();
    void Start()
    {
        foreach (int x in spawnRange)
        {
            foreach (int z in spawnRange)
            {
                spotsAvailable.Add(new EnemySpotsAvailable(x, z));
            }
        }
        foreach(EnemySpotsAvailable spot in spotsAvailable)
        {
            print(spot.pointX + " + " + spot.pointZ);
        }
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            SpawnEnemy();
        }
    }
    public void SpawnEnemy()
    {
        ChooseSpawn();

        Instantiate(Enemy, new Vector3(spawnX, 5, spawnZ), Quaternion.identity);
        Debug.Log("X= " + spawnX + " Z= " + spawnZ);

    }
    private void ChooseSpawn()
    {
        int randomIndex = Random.Range(0, spotsAvailable.Count);
        //Beyond this point I'm unsure on what to do
        spawnX = 0;
        spawnZ = 0;
    }
}

We have a few things to look down…

1-Classes which inherit from MonoBehaviour shouldn’t have constructor methods, i’m pretty shure you want to remove this inheriterance in both classes "EnemySpotsAvailable " and “EnemySpotsHeld”;

2-Those classes "EnemySpotsAvailable " and “EnemySpotsHeld” are redundant, I would suggest you to switch that for two Vector3Int lists;

3-To spawn with those points, it would be like that:

List<Vector3Int> spotsAvailable = new List<Vector3Int>();
List<Vector3Int> spotsHeld = new List<Vector3Int>();
     
    private Vector3Int ChooseSpawn()
    {
         int randomIndex = Random.Range(0, spotsAvailable.Count);
         Vector3Int spot = spotsAvailable[randomIndex ];

         spotsAvailable.RemoveAt(randomIndex);
         spotsHeld.Add(spot);

         return spot;
    }

     public void SpawnEnemy()
     {
         Vector3Int spawnPoint = ChooseSpawn();
 
         Instantiate(Enemy, new Vector3(spawnPoint .x, 5, spawnPoint.z), Quaternion.identity);
         Debug.Log("X= " + spawnX + " Z= " + spawnZ);
 
     }

Hey,

basically first thing to do would be to remove the differentiation between EnemySpotsAvailable and EnemySpotsHeld.

You keep the same data in there so give it a name that fits it’s purpose: To keep data that defines a spawnpoint.

So do somethig like:

public class SpawnPoint : MonoBehaviour // check if this really is needed to be a Monobehaviour here
 {
     public int pointX;
     public int pointZ;
 
     public SpawnPoint(int newPointX, int newPointZ)
     {
         pointX = newPointX;
         pointZ = newPointZ;
     }
 }

At this point please check if you really need this to be a Monobehaviour. If you do not have a need to attach this script to a gameobject and want to create new objects of this type using new then you have to remove the Monobehaviour inheritance.

Apart from that you can then just write:

private SpawnPoint ChooseSpawn()
     {
         int randomIndex = Random.Range(0, spotsAvailable.Count);
        //get the chosen spawn from the list:
         var chosenSpawn = availableSpawns[randomIndex];
        //remove it from availableSpawns:
         availableSpawns.Remove(chosenSpawn);
        //add it to the "held" spawns:
         heldSpawns.Add(chosenSpawn);
         //return the spawn:
         return chosenSpawn;
     }

And change your Spawn function to:

public void SpawnEnemy()
     {
         var newSpawn = ChooseSpawn();
 
         Instantiate(Enemy, new Vector3(newSpawn.newPointX, 5, newSpawn.newPointY), Quaternion.identity);
         //.....
     }

Be aware that this will currently not free any spawns again. For this it might be good to give your enemy a reference to the spawnpoint so that if it moves away from the spawn it can call a function on your spawnmanager where it gives the point it spawned on as a reference from which you can move it to the correct list again.