Enemy Wave Spawner in Unity

Dennisse Pagán Dávila
Geek Culture
Published in
7 min readJul 6, 2022

Today’s core programming challenge will focus on using a coroutine to control how our enemies spawn into the game.

Objective: Create a coroutine that will make it so more and more enemies appear per wave.

Adding Wave Indicator text

We’ll use a set of variables to control the wave spawning, and another set to set the wave indicator text on the screen. For the text, we’ll use TextMeshPro, a more advanced high-quality alternative to the regular UI text.

  1. Add a TextMeshPro and rename it accordingly, E.g. WaveText or WaveIndicator_text.

Note: If you’ve never used TextMeshPro before, you will likely need to import its essentials.

Leave the text empty and deactivate it in the Inspector as its contents will be added through code later.

Make sure to ancho the text location to center. You can add some placeholder text to tweak its position.

2. To use TextMeshPro, you need to add its particular library to your script. Ideally, your script should be dedicated to handling the spawnning specifically.

3. Now that everything is set up, we’ll need a global variable for the text, the serialized variable will be assigned in the inspector.

4. Add global variables to control the spawn. It might seem like a lot, but we’ll go through each one by one.

Note: You can modify the value of the numeric variables as you see fit.

  • waveCount: This will keep track of the wave count, and increase the number when necessary.
  • enemyCount: Takes care of increasing the number of enemies per wave.
  • waveTextTimer: How long the text will be on screen before it fades.
  • spawnRate:
  • timeBetweenWaves: How long it takes for each wave to start.
  • _isWaveActive: This is needed to control the coroutine which we’ll code in the next segment of this article.
  • _stopSpawning: An additional control variable for the coroutine. Your script might not need to use two bool variables, in which case you would be fine only using the _isWaveActive bool only. In my case, this is necessary because there are two instances in which the control booleans need to change their default values: When there is a game over screen, in which the _stopSpawaning must be set to true because it would not make sense to continue spawning waves or power-ups the player has lost. The second instance is whenever a new wave needs to start, in which the _isWaveActive must be set to true. Therefore these two variables need to be independent of one another.

Creating the Enemy Wave Spawner Coroutine

If you’re wondering what a coroutine is, it’s only one of my favorite things I’ve learned from C# so far! You can learn more about it in the link down below. This will teach you what you need to know to get started, I will be focusing on explaining the Wave Spawn Routine in this article.

  1. This is the first chunk of our coroutine, here we randomize the position from which the enemies will spawn from, and we also randomize which enemy types will spawn. My game currently has 6 enemy types, which are set in the inspector via a GameObject array with a SerializedField. Here the _isWaveActive is set to false because our spawn routine hasn’t started yet, this is just the setup.

Note: Since we’ll be spawning a lot of enemies, it might be useful to learn how to spawn objects without cluttering your Hierarchy.

2. Next, we’ll use a for-loop to iterate the wave logic for a given number of times.

  • ActivateWaveText: This is a method that takes care of displaying the wave count text, it was placed on a separate method to keep the coroutine as tidy and understandable as possible.
  • The next lines will make Unity wait for the assigned amount of seconds and then make the text disappear by deactivating it.

Note: Here is the aforementioned method that is being called from the coroutine. This updates the cout displaying the actual number along with wave text. You will see how this is done as we continue with the coroutine.

3. The next set, the highlighted code here, is where we actually instantiate the objects. The spawn rate controls the rate at which the enemies get instantiated into the scene.

4. Right after that set, we have an if-statement that defines what will happen if you reach the final wave. The final wave can be any number you desire, in my case, it’s wave 5.

There’s a lot going on here, let’s break it down!

  • When my game reaches wave 5, a text indicating that a boss fight is incoming will appear for 3 seconds. (I’ll teach you about my boss fight code in a later article!)
  • There is a method here that ends the enemy waves because we can’t have the enemies barraging the Player when transitioning to a boss fight.
  • After the end of the wave, we load up the next scene which is the boss fight.

Note: The two brackets at the end of the code above close the if-statement and then the for-loop. I wanted to set that little marker here since I have been showing the code in chunks. I will show a full view at the end as well, this is just to break it down.

This is the aforementioned method to end the waves.

  • Here we set the _stopSpawning control bool to true, this will stop the coroutine.
  • The next line searches for every object that is tagged as Enemy and destroys it. This is to make sure there aren’t any enemies leftover from the wave still roaming around.

5. After closing that if-statement and for-loop, we have to update everything for the next wave.

  • The spawnRate will decrease so that enemies spawn more rapidly making more and more enemies spawn per wave.
  • enemyCount increases to keep the for-loop condition running
  • Wait the assigned number of seconds before adding to the waveCount which is what causes the wave number to increase.
  • _isWaveActive is set to true again so that the coroutine keeps running now that the setup for the next wave is complete.

Now all that’s left is for you to add the script to an empty game object on your scene and call the coroutine at the start of the script.

Full view of code here

The Result

This article is part of a series of core programming challenges from GameDevHQ! In the next article, I’ll be talking about homing projectiles! The final article of this set of coding challenges will feature the boss fight which was referenced in this article.

Special thanks to Technical Friends for helping me understand the kind of logic I should implement. Dissecting the functionality of this code and breaking everything down was a blast!

--

--

Dennisse Pagán Dávila
Geek Culture

Software Engineer that specialize in Game Development. Currently looking for new opportunities. Portfolio: dennissepagan.com