A Simple Wave System to Spawn Enemies with Increased Difficulty (2D Space Shooter)
This Wave Spawner has two core loops to run through; for 5 waves it must create enemies. Six enemies on the first wave, multiply the number of enemies * 2 at the end of each wave. The handlers are pretty obvious, many integers to administer the amount of max and current waves, enemies; a bool that serves as an emergency switch to shut the Coroutine off; and arrays of enemy prefabs to be spawned. When the coroutine starts, the first amount of enemies is defined, the switch is turned on and a three-seconds pause gives room for the player to prepare.
//waves handlers
[SerializeField]
private bool _waveIsActive;
[SerializeField]
private int _currentWave;
[SerializeField]
private int _maxWaves;
[SerializeField]
private int _enemiesToSpawn;
[SerializeField]
private int _enemiesToKill;
[SerializeField]
private GameObject[] _commonEnemies;
[SerializeField]
private GameObject[] _rareEnemies;
private WaitForSeconds _waveBrake = new WaitForSeconds(6);
IEnumerator EnemyWaveSpawner()
{
_enemiesToSpawn = 6;
_waveIsActive = true;
yield return new WaitForSeconds(3.0f);
while (_stopSpawning == false && _waveIsActive)
{
for (int currentWave = 1; currentWave <= _maxWaves; currentWave++)
{
Debug.Log("Current wave: " + currentWave.ToString());
_enemiesToKill = _enemiesToSpawn;
_currentWave = currentWave;
for (int enemiesOn = 0; enemiesOn < _enemiesToSpawn; enemiesOn++)
{
Vector3 posToSpawn = new Vector3(Random.Range(-8.5f, 8.5f), 8, 0);
int randomCommonEnemy = Random.Range(0, _commonEnemies.Length);
GameObject newEnemy = Instantiate(_commonEnemies[randomCommonEnemy], posToSpawn, Quaternion.identity);
newEnemy.transform.parent = _enemyContainer.transform;
yield return new WaitForSeconds(2f);
}
while (_enemiesToKill > 0)
{
yield return new WaitForEndOfFrame();
}
_enemiesToSpawn = _enemiesToSpawn * 2; // line that increments difficulty over waves.
_player.AddMaxAmmo();
yield return _waveBrake;
}
_waveIsActive = false;
}
}
The first For-Loop starts the waves system, it has a maximum amount of five waves for now; it starts by defining the “enemies to kill” as equal to the amount of enemies to spawn in the current wave and then opens the For-Loop that instantiates enemies in a Random X position and makes them child object of an empty enemy container to keep the hierarchy clean. This second, inner For-Loop has a 2 second pause as spawn rate. After all the enemies to spawn have been instantiated, there is a key line of code that while the “enemies to kill” is greater than 0, the code loops until all enemies of the current wave have been killed (to prevent the next wave from starting before the current wave is over).
After the current wave is over, the amount of enemies to spawn is multiplied, a function inside the player script is ran to add max ammo as a reward of accomplishing the wave and a Wave Break timer runs to give the player a moment of peace.
This is the beginning of a very simple Coroutine that serves as a wave spawner that increments difficulty over time. The lines before the wave break leaves room for a scalable script, as through various if statements, each wave could be tweaked to spawn an specific amount of enemies, among other enhancements. For example, an specific power up or upgrade could be given after an specific wave, the wave break could be made longer in case the player needs onboarding on a new weapon or game element.
And of course, essential debugging to check current wave status. Rare wave is a secondary coroutine that runs simultaneously along the common enemies coroutine and has the same enemies to kill check system to go to the next wave simultaneously.
More enhancements will be implemented as a final boss at the end of the last wave.