Laser Defender - Number of Enemies appears to be tied to Number of Waypoints

I’m working on the Unity 2d course and I’m on Laser Defender. I’ve created the coroutine to spawn all of the enemies of a wave. The coroutine that created the enemy and moved it along the waypoints worked correctly.

Once I added the for loop, however, I’m finding issues. The additional enemies of the wave are created and do move along the waypoints, as expected.

However, it appears that the script will fail once the number of enemies spawned reaches the number of points in the path for the wave. As long as the number of enemies does not exceed the number of waypoints in the path. I’ve tested this by creating new waves, new paths, linking and unlinking new and existing paths and waves. I’ve verified variables, indexes, and I can’t find how they’re linked.

Any help would be appreciated, because I’m stumped.

EnemyPathing

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyPathing : MonoBehaviour
{
    [SerializeField]
    private WaveConfig waveConfig;

    private List<Transform> waypoints;

    [SerializeField]
    private float moveSpeed = 1.0f;

    private int waypointIndex = 0;

    // Start is called before the first frame update
    void Start()
    {
        waypoints = waveConfig.GetWavePoints();
        transform.position = waypoints[waypointIndex].transform.position;
    }

    // Update is called once per frame
    void Update()
    {
        MoveToWaypoints();
    }

    private void MoveToWaypoints()
    {
        if (waypointIndex < waypoints.Count)
        {
            var targetPosition = waypoints[waypointIndex].transform.position;
            var movementThisFrame = moveSpeed * Time.deltaTime;
            transform.position = Vector2.MoveTowards(transform.position, targetPosition, movementThisFrame);
            if (transform.position == targetPosition)
            {
                waypointIndex++;
            }
        }

        else
        {
            Destroy(gameObject);
        }
    }
}

WaveConfig

using System.Collections;  
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "Enemy Wave Config")]
public class WaveConfig : ScriptableObject
{
    [SerializeField]
    private GameObject enemyPrefab;

    [SerializeField]
    private GameObject pathPrefab;

    [SerializeField]
    private float timeBetweenSpawns = 0.5f;

    [SerializeField]
    private float spawnRandomFactor = 0.3f;

    [SerializeField]
    private int numberOfEnemies;

    [SerializeField]
    private float moveSpeed = 2f;


    public List<Transform> GetWavePoints ()
    {
        // Pull the waypoints out of the wave path prefab and return them
        // so we don't just return the prefab
        var waveWayPoints = new List<Transform>();

        foreach(Transform child in pathPrefab.transform)
        {
            waveWayPoints.Add(child);
        }

        return waveWayPoints;
    }

    public GameObject GetEnemyPrefab() { return enemyPrefab; }
    public float GetTimeBetweenSpawns () { return timeBetweenSpawns; }
    public float GetSpawnRandomFactor () { return spawnRandomFactor; }
    public int GetNumberOfEnemies () { return numberOfEnemies; }
    public float GetMoveSpeed () { return moveSpeed; }

}

EnemySpawner

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemySpawner : MonoBehaviour
{
    [SerializeField]
    private List<WaveConfig> waveConfigs;

    private int startingWave = 0;

    // Start is called before the first frame update
    void Start()
    {
        var currentWave = waveConfigs[startingWave];
        StartCoroutine(SpawnAllEnemiesInWave(currentWave));
    }

    private IEnumerator SpawnAllEnemiesInWave(WaveConfig waveConfig)
    {
        var enemyCount = waveConfig.GetNumberOfEnemies();
        Debug.Log($"Number of Enemies: {enemyCount}");

        for (int enemyIndex = 0; enemyIndex <= enemyCount - 1; enemyIndex++)
        {
            Debug.Log($"Enemy# {enemyIndex}");
            Instantiate(
                waveConfig.GetEnemyPrefab(),
                waveConfig.GetWavePoints()[enemyIndex].transform.position,
                Quaternion.identity
            );
            yield return new WaitForSeconds(waveConfig.GetTimeBetweenSpawns());
        }
    }
}

Thank you in advance for any help.

Hi Robert,

Could you format your scripts properly, please?


See also:

Thanks for the link, Nina. I tried to find out how to format the code, and I couldn’t.

I’ve updated my question to make it more readable.

Thank you. That’s much better. :slight_smile:

What do you mean by “it appears that the script will fail”? What is supposed to happen?

And what does your console say?

As per the video, if the wave had x enemies, then x enemies should be spawned.

However, what happens is that if there are more enemies in the wave than there are waypoints, the script will fail to spawn any more enemies past the number of waypoints.

I am unable to figure out how the number of waypoints is affecting the number of enemies spawned, and how they are causing the script to crash. The enemies that do spawn follow the correct number of waypoints, but there will not be more enemies than there are waypoints, which shouldn’t be related.

The error is as follows:

ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
System.ThrowHelper.ThrowArgumentOutOfRangeException (System.ExceptionArgument argument, System.ExceptionResource resource) (at :0)
System.ThrowHelper.ThrowArgumentOutOfRangeException () (at :0)
System.Collections.Generic.List`1[T].get_Item (System.Int32 index) (at :0)
EnemySpawner+d__3.MoveNext () (at Assets/Scripts/EnemySpawner.cs:28)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17)

Actually, the error message was very helpful. Take a look at the following line:

waveConfig.GetWavePoints()[enemyIndex].transform.position

The program is trying to access GetWavePoints()[enemyIndex]. If enemyIndex is higher than the highest index, you will get an IndexOutOfRangeException. Since you said that you had more enemies than waypoints, the error makes sense.

Thanks, Nina! That did fix it. I can’t believe I missed that.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.