Day 39 — Improvements: wave system
Hey and Welcome!
Ok so we’ve got enemies spawning in indefinitely which is fun and all but it doesn’t feel like there’s much progress happening or a goal in mind so let’s fix that by adding in a wave system. Now the way I’ve implemented this is fine for the purposes I have for my game since it’s mostly a short test more than anything and it’s only really me that’s working on it but I’ll talk about ways to improve it after we get it working.
So let’s just get straight into it by redoing the SpawnEnemy() method we have in our Spawner script:
private int _wave = 1;
private int _enemyCount = 0;
private int _enemiesToDestroy = 5;switch (_wave)
{
case 1:
yield return new WaitForSeconds(2.0f);
if (_enemyCount < 5)
{
Vector3 position = new Vector3(Random.Range(-10.7f, 10.7f), 6.2f, 0);
GameObject enemy = Instantiate(_enemy, position, Quaternion.identity);
enemy.transform.parent = _enemyContainer.transform;
_enemyCount += 1;
yield return new WaitForSeconds(5.0f);
}
else
{
yield return new WaitUntil(() => _enemiesToDestroy <= 0);
_enemyCount = 0;
_enemiesToDestroy = 6;
_uiManager.UpdateEnemiesLeft(_enemiesToDestroy);
_wave += 1;
_uiManager.UpdateWave(_wave);
}
break;
default:
Debug.Log("Something has gone wrong with spawning");
break;
}
Quite a lot to go over so let’s start by looking at our variables. To start we’ve got the _wave which is what we’re using to pass into our switch statement so it knows which wave to be on. Then we have _enemyCount which we use to determine how many enemies we want to spawn each wave by setting it to only instantiate an enemy if it’s below a certain number and then _enemyCount is incremented by 1 after each enemy spawns in.
Then we _enemiesToDestroy which is used to determine when the next wave should start when it has the value of 0.
Not too bad so let’s have a look inside the case 1 in the switch statement, you’ll see that it starts off with yield return new WaitForSeconds(2.0f) which is in place because I have this _uiManager.UpdateWave(_wave) added in the Start() method which I’ll go over later but the important thing to note is that it displays “Wave 1” text on the screen for a short time so we want to wait until that’s over with before we start spawning enemies which is the reason for the yield. Which you can see in action in the gif above.
Inside the if (_enemyCount < 5) you’ll notice that our old code for spawning enemies is in here, the only thing new is the fixed yield time and _enemyCount += 1. So given that this code will run 5 times since it will be less than 5 until then that means that 5 enemies overall are going to be spawning in this wave which is why our _enemiesToDestroy was preemptively set to 5.
Now next up is my new favourite bit of code I found which is yield return new WaitUntil(() => _enemiesToDestroy <= 0), basically this is used to pause the execution of the code until a certain condition is true, which in this case is when our enemies to destroy are less than or equal to 0. Without this the code would go straight into the code for the else statement when the enemy count is no longer less than 5, which leads to having leftover enemies into the next wave.
When the enemies to destroy are equal to 0 the code resumes execution and the value of _wave is increased by 1, prompting it to move onto the next wave.
UI Updates
In that else statement you may have noticed two new calls to the UI Manager for the wave number as well as the enemies left, go ahead and create the text objects for those and add the following code to the UIManager script:
[SerializeField]
private Text _waveText;
[SerializeField]
private Text _enemiesRemaining;public void UpdateWave(int currentwave)
{
_waveText.text = "Wave " + currentWave;
StartCoroutine(WaveRoutine());
}public void UpdateEnemiesLeft(int enemies)
{
_enemiesRemaining.text = "Enemies Left: " + enemies;}IEnumerator WaveRoutine()
{
_waveText.gameObject.SetActive(true);
yield return new WaitForSeconds(2.0f);
_waveText.gameObject.SetActive(false);
}
The coroutine is there simply so that we can control how long the wave text will show on the screen for, everything else should be pretty familiar.
Looking back at the code done here I’ve learned a bit more since then, ideally if you’re working on a larger game with say 10 waves or so you don’t want to be manually hard coding each one in so it will be better to create a list or array that contains the wave data that you can easily make a call to using just the one loop most likely.