Building a 3D Tower Defense Game — Wave Spawner
In this article, we’ll be looking at creating a simple wave spawning system. The first thing I want to do is to create an empty GameObject called “GameManager” and create a C# script for it called “WaveSpawner”.
Wave Spawner
We need to get a reference to the enemy prefab, the spawn position, time between waves, wave count, and how many waves we need to spawn.
[SerializeField] private GameObject _enemyPrefab;
[SerializeField] private GameObject _spawnPos;
[SerializeField] private float _timeBetweenWaves = 5f;
[SerializeField] private float _countdown = 2f;
private int _waveIndex = 0;
private int _maxWave = 5;
I want it to spawn only 5 waves. The first wave should be spawned after 2 seconds, and each subsequent wave should be spawned after 5 seconds. Since we need to execute the spawning after each interval, this would be good use case for a Coroutine.
void Update()
{
if (_countdown <= 0f)
{
StartCoroutine(SpawnWave());
_countdown = _timeBetweenWaves;
}
if (_waveIndex < _maxWave)
{
_countdown -= Time.deltaTime;
}
}
Now let’s create the logic for the SpawnWave fucntion.
IEnumerator SpawnWave()
{
_waveIndex++;
Instantiate(_enemyPrefab, _spawnPos.transform.position, Quaternion.identity);
yield return null;
}
So each time the SpawnWave function is executed, the _waveIndex variable is incremented by 1 and a new enemy will be spawned using the Instantiate method.
Wave UI
I also created a canvas GameObject for the UI, which will inform the player about the current wave they are on and display the countdown between waves.
Let’s create a C# script for the canvas.
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class GamePlayUI : MonoBehaviour
{
[SerializeField] TMP_Text _waveText;
[SerializeField] TMP_Text _timeText;
private int _maxWave = 5;
public void UpdateWaveText(int waveIndex)
{
_waveText.text = "Wave " + waveIndex + "/" + _maxWave;
}
public void UpdateTimeText(float time)
{
_timeText.text = "Incoming Wave In " + string.Format("{0:0.00}", time);
}
}
And here’s the full code for the Wave Spawner with the UI elements incorporated:
using UnityEngine;
using System.Collections;
public class WaveSpawner : MonoBehaviour
{
[SerializeField] private GameObject _enemyPrefab;
[SerializeField] private GameObject _spawnPos;
[SerializeField] private float _timeBetweenWaves = 5f;
[SerializeField] private float _countdown = 2f;
private int _waveIndex = 0;
private int _maxWave = 5;
[SerializeField] private GamePlayUI _gamePlayUI;
void Start()
{
_gamePlayUI.UpdateWaveText(_waveIndex + 1);
}
void Update()
{
if (_countdown <= 0f)
{
StartCoroutine(SpawnWave());
_countdown = _timeBetweenWaves;
}
if (_waveIndex < _maxWave)
{
_countdown -= Time.deltaTime;
_gamePlayUI.UpdateTimeText(_countdown);
}
}
IEnumerator SpawnWave()
{
_waveIndex++;
if (_waveIndex > 1)
{
_gamePlayUI.UpdateWaveText(_waveIndex);
}
Instantiate(_enemyPrefab, _spawnPos.transform.position, Quaternion.identity);
yield return null;
}
}
And here’s the result: