Wave Spawning in Unity: Lessons from the Trenches

Scott Sourile
4 min readOct 26, 2023

--

Mission Accomplished. For now.

Recap

In our previous discussions (yes, those are two seperate links), we talked about different strategies for spawning enemies in waves as well as got into the methods I deployed. For clarity in our examples, we opted for multiple coroutines — one for each wave. Also, just to be clear, this is not the most efficient approach; using a single coroutine and a loop is. However, I really wanted to see the wave system work, and then work through it later, so this is what we got.

The Multi-Coroutine Approach

To put it simply, I created a coroutine for each wave. Each coroutine would:

  1. Increment the wave counter.
  2. Spawn enemies for that wave.
  3. Wait between waves.
  4. Start the next wave.

The code looked loosely like this:

IEnumerator SpawnWave1()
{
// spawn logic for Wave 1
StartCoroutine(WaitBetweenWaves());
}

IEnumerator WaitBetweenWaves()
{
// waiting logic
StartCoroutine(SpawnWave2());
}

IEnumerator SpawnWave2()
{
// spawn logic for Wave 2
// ... and so on
}

See What it Looks Like on “Paper”

Challenges Faced

  1. Complexity: This word was the underlying theme in every attempt to get this done. With each wave having its own coroutine, the code becomes longer and harder to manage as the number of waves increases.
  2. Wave Number Mismatch: Initially, there was an issue where the wave number counter was not behaving as expected. This was due to multiple increments within a single wave’s coroutine. This happens, again, due to the complexity. There are bound to be repeat code in inefficient code such as this, causing bugs and other issues.

Addressing the Challenges

  1. Organized Code: By maintaining a consistent structure in each coroutine, I was able to ensure clarity despite the longer code. Comments, clear variable names, and regular refactoring helped.
  2. Wave Number Counter: By placing the wave increment logic in the right spots (at the start of each wave and at the end of the waiting period), the wave number was correctly maintained.

Alternative (and Better) Approaches

While the multi-coroutine method worked, it cannot be stressed enough here that there are more efficient ways to implement wave spawning. Here are a couple:

  1. Single Coroutine with Loops: Use one coroutine with loops to iterate through waves and enemy spawning.
  2. Scriptable Objects: Use Unity’s Scriptable Objects to define waves. This way, each wave can have its properties (number of enemies, types, spawn rate, etc.), and you can handle spawning through a unified system.
  3. Wave Manager: Create a wave manager class that manages the spawning of each wave. The wave data can be stored in arrays or lists, and the manager can control the flow between waves.

Game development is as much about the journey as it is about the final product. My experience with wave spawning in Unity taught me that sometimes, it’s okay to go with a less efficient method initially to understand the system’s mechanics. Once the foundation is solid, optimization becomes a clearer path.

Always remember: Your first solution might not be the best one, but it’s a start. Refine, learn, and iterate.

With this out the way, I can finally move on to the homing missile and boss as I’ve laid the foundation to create a boss with multiple layers (and coroutines).

--

--