Day 23 of 100 Days of VR: Creating a Spawning System for Enemies in Unity

Oct. 10, 2017
protect

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; // class to hold information per wave
    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; // avoid off by 1
	    _totalWaves = Waves.Length - 1; // adjust, because we're using 0 index
	    StartNextWave();
	}
    void StartNextWave()
    {
        _currentWave++;
        // win
        if (_currentWave > _totalWaves)
        {
            return;
        }
        _totalEnemiesInCurrentWave = Waves[_currentWave].EnemiesPerWave;
        _enemiesInWaveLeft = 0;
        _spawnedEnemies = 0;
        StartCoroutine(SpawnEnemies());
    }
    // Coroutine to spawn all of our enemies
    IEnumerator SpawnEnemies()
    {
        GameObject enemy = Waves[_currentWave].Enemy;
        while (_spawnedEnemies < _totalEnemiesInCurrentWave)
        {
            _spawnedEnemies++;
            _enemiesInWaveLeft++;
            int spawnPointIndex = Random.Range(0, SpawnPoints.Length);
            // Create an instance of the enemy prefab at the randomly selected spawn point's position and rotation.
            Instantiate(enemy, SpawnPoints[spawnPointIndex].position, SpawnPoints[spawnPointIndex].rotation);
            yield return new WaitForSeconds(TimeBetweenEnemies);
        }
        yield return null;
    }
    
    // called by an enemy when they're defeated
    public void EnemyDefeated()
    {
        _enemiesInWaveLeft--;
        
        // We start the next wave once we have spawned and defeated them all
        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.

  1. 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).

  2. 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()</

JikGuard.com, a high-tech security service provider focusing on game protection and anti-cheat, is committed to helping game companies solve the problem of cheats and hacks, and providing deeply integrated encryption protection solutions for games.

Read More>>