Here, we are back to another day of Unity development! Today on day 23, we’re going to learn how to spawn enemy waves.
Currently, the game isn’t challenging, we only have one enemy!
Today, we’re going to fix this by spawning waves of enemies to defeat, making the game a lot tougher to survive!
It’s going to be a big change requiring multiple days, so let’s get right to it!
Creating an Enemy Wave Spawning Point
If we were to recall from the Unity Survival Shooter tutorial, to spawn enemies, we need to create a SpawnManager
class that creates new instances of the enemy.
In our Spawn Manager, the primary thing we need to give it, among many other things that we’ll want to add, are the:
- location of where we would spawn the enemies
- enemies that we want to spawn
However, as a challenge, as opposed to the Survival shooter, where the game would continue for it, we’re going to have a limited amount of enemy spawn so we can win.
There was a lot of work involved in the wave system and I borrowed a lot of ideas from Unity’s Enemy Spawner example.
Creating the Initial Game Objects
The first thing we want to do is create a new Empty Game Object
that we’ll call EnemyManager
. We’re going to make this a child of our GameManager
.
Next, we’ll create a new script for our new game object that we’ll call EnemyManager
.
We’re going to do a couple of things with our manager:
- Keep track of what wave we’re in
- Keep track of how many enemies we defeated in a wave
- Keep track of how many enemies per wave
By keeping track of the number of enemies and waves, we can tell when to move to the next wave and if we win.
Here’s our initial code for EnemyManager.cs:
using System.Collections;
using UnityEngine;
[System.Serializable]
public class Wave
{
public int EnemiesPerWave;
public GameObject Enemy;
}
public class SpawnManager : MonoBehaviour
{
public Wave[] Waves;
public Transform[] SpawnPoints;
public float TimeBetweenEnemies = 2f;
private int _totalEnemiesInCurrentWave;
private int _enemiesInWaveLeft;
private int _spawnedEnemies;
private int _currentWave;
private int _totalWaves;
void Start ()
{
_currentWave = -1;
_totalWaves = Waves.Length - 1;
StartNextWave();
}
void StartNextWave()
{
_currentWave++;
if (_currentWave > _totalWaves)
{
return;
}
_totalEnemiesInCurrentWave = Waves[_currentWave].EnemiesPerWave;
_enemiesInWaveLeft = 0;
_spawnedEnemies = 0;
StartCoroutine(SpawnEnemies());
}
IEnumerator SpawnEnemies()
{
GameObject enemy = Waves[_currentWave].Enemy;
while (_spawnedEnemies < _totalEnemiesInCurrentWave)
{
_spawnedEnemies++;
_enemiesInWaveLeft++;
int spawnPointIndex = Random.Range(0, SpawnPoints.Length);
Instantiate(enemy, SpawnPoints[spawnPointIndex].position,
SpawnPoints[spawnPointIndex].rotation);
yield return new WaitForSeconds(TimeBetweenEnemies);
}
yield return null;
}
public void EnemyDefeated()
{
_enemiesInWaveLeft--;
if (_enemiesInWaveLeft == 0 && _spawnedEnemies == _totalEnemiesInCurrentWave)
{
StartNextWave();
}
}
}
Now this is a lot to take in, which is why I added comments, however, here’s the run through of the code.
About the Wave Class
Before we talk about our variables, I want to introduce the Wave
class.
Wave
is a container for us to hold the data for each wave that we’re going to face.
If you remember from the Space Shooter tutorial, we did something similar. We created a new class to hold information and we made it Serializable
so that Unity knows how to show it in the editor.
Originally, I was going to just pass each of its content to our SpawnManager
, but that’s prone to us causing mix-ups of how many enemies to spawn per wave and which enemies.
About the Variables
For our public
variable, we have:
Waves
– An array of the Wave
class that we created for an easy way for us to access data for each wave SpawnPoints
– An array of locations that we’re going to instantiate our enemies TimeBetweenEnemies
– The delay we wait before we spawn our next enemy
For our private
variables to keep track of the enemies, we have:
_totalEnemiesInCurrentWave
– Self explanatory _enemiesInWaveLeft
– The number of enemies that are still alive in the wave _spawnedEnemies
– Self explanatory
We also keep track of what wave we’re in:
_currentWave
– Self explanatory _totalWaves
– Self explanatory
The Code Flow
Now that we know the variable we’re using, we can walk through the rest of the code.
- In
Start()
, we initialize all our variables to 0
. Note that we set _currentWave
to be -1
and our _totalWaves
is the length of our array – 1
. For those who aren’t familiar, all of this is, because we’re working in a 0-based indexing for arrays (meaning everything starts from 0 instead of 1). - From
Start()
,
we also call StartNextWave()
this code handles what happens when we clear our wave. We increment our _currentWave
and assuming we haven’t finished the game, we’ll setup the enemies we need to encounter for the next wave and call SpawnEnemies()
SpawnEnemies()
is a coroutine that we use to create our enemies. The function will spawn the enemy for the wave based from one of the random spawn points we set. The code will wait 2 seconds before we spawn the next enemy so we don’t flood the players with all the enemies in a wave at once. - Finally, we have
public
EnemyDefeated()
which means this function is called from something else. In this case, our enemy will call this when they’re killed. We will then decrement our enemy counter and if we have defeated all the enemies in a wave, we’ll call StartNextWave()
That’s the basic summary of the code. That wasn’t so bad, now was it? RIGHT?!
Setting Up Our SpawnManager Script
Now that we have our script up and running, the last thing we need to do is to setup our Wave
and create our Spawn Points
.
Setting Up the Wave
For now, let’s just make 2 waves for our SpawnManager
that will create our knight enemy.
There are 2 things we need to provide for our Wave
class:
- The knight game object
- How many enemies to spawn for the wave
Let’s first set the knight game object.
We could just drag and drop our knight from our hierarchy into the spot and everything would be fine. However, the correct way to do this is to first create a prefab
of our knight.
We can think of a prefab
is a template of a game object that we can drag and share to different scenes and games. In our case, we can also instantiate them like what we’re doing here.
The main benefit is that, if you were to change prefab, anytime we need to change something, we can just change the prefab and anything that instantiates it from code will also get the changes.
The first thing we’re going to do is:
- Create a folder in our Assets folder called Prefabs
- Create our prefab, by dragging our
Knight
game object into the Prefabs
Note: In this example, our Knight
is already a prefab, but that’s okay, we’ve already changed some things about it, so let’s make another prefab from it.
On a side note: we can delete
the Knight
game object from our hierarchy. He’s served us well, but we won’t be needing his services anymore.
Now that we have everything we need, we can get started in Creating our waves.
Back in our SpawnManger
script, expand Waves
and set Size
to be 2 so we can create new waves.
Here’s what it’ll look like:
Setting Up the Spawn Point
Now that we have the wave system setup, we need to create our spawn points.
Exactly like the survival shooter, we need to add some game object that we’ll use as the location that we’re going to spawn the enemies.
Let’s get to it!
Create 3 Empty
game objects: I call them SpawnPoint1
– SpawnPoint3
. I made them the child of the SpawnManager
, however, it doesn’t really matter where we put them.
Right now, we can’t see the position of our SpawnPoints
in the game, however we can add a Label
to them to see where the objects are.
We just want to set the Spawn
points to be in 3 of the houses:
Here are my Position
values, yours might be different:
SpawnPoint1
: 98
, 0
, 118
SpawnPoint2
: 72
, 0
, 89
SpawnPoint3
: 106
, 0
, 80
Finally, go back to our SpawnManager
and add our SpawnPoints
in.
Expand Spawn Points
and then for the Size
change it to 3
, and then drag our SpawnPoints
into the empty game spots.
With all of this in place, if we play the game, we should now have multiple enemies coming at us.
Conclusion
We’re not quite done with our Spawning System, however we laid the ground works for spawning enemies.
We still need to implement the logic for generating new enemies and finally creating our victory condition. We’ll start implementing those tomorrow on Day 24!
With that being said, have a good night!
Day 22 | 100 Days of VR | Day 24
Home
CodeProject
The post Day 23 of 100 Days of VR: Creating a Spawning System for Enemies in Unity appeared first on Coding Chronicles.