Adding GameController to spawn enemy waves

We have all of the mechanics of our game completed at this point. Now, we need to actually create the game or manage what happens in the game. This game controller would be required to run our game, keep track of and display the game's score, and finally end the game whenever the player dies. Later on, we'll discuss a game state manager, which we can use for larger projects with multiple states, but for the sake of this simple project, we will create a simple game controller, and that's what we'll do now:

  1. First, create an empty game object by going to Game Object | Create Empty. From there, with the object selected, go to Inspector and set its name to Game Controller, and optionally, for neatness sake, set its Position to (0, 0, 0). As this is our main game object, I'm also going to drag it up on the Hierarchy tab so that it is the top object on the list.
  2. Underneath the name, you'll see the Tag property. Change it from Untagged to GameController.
    Adding GameController to spawn enemy waves

    Note

    A Tag is a way to link to one or more game objects in a collected group. For instance, you might use Player and Enemy tags for players and enemies respectively; a Collectable tag could be defined for power-ups or coins that are placed in the scene, and so on. This could also have been used with EnemyBehaviour to check whether something was a bullet or not. One thing to note is the fact that GameObject can only have one tag assigned to it. Tags do nothing to the scene but are a way to identify game objects for scripting purposes.

  3. Next, let's see another way that we can create scripts quickly. From the Inspector tab, select Add Component | New Script, and once you are brought to the next menu, change the language to C#, and set the name of the script to GameController.
  4. Select the newly created script, and move it to the AssetsScripts folder. Go to your IDE of choice by double-clicking on the script file.

    While our game does many things, the most important thing is the spawning of enemies, which is what we'll be adding in first. Let's create a variable to store our enemy.

  5. Inside of the class definition, add the following variable:
    // Our enemy to spawn
    public Transform enemy;
  6. Now, we can set the enemy that we currently have in the scene, but we should instead make the enemy a prefab and use it. To do so, drag the enemy from Hierarchy into your AssetsPrefabs folder. Once we've created the prefab, we can remove the enemy object from our scene by deleting it.
  7. Next, drag and drop the enemy prefab into the Enemy variable in the GameController component.
    Adding GameController to spawn enemy waves
  8. Next, go back into our GameController script by double-clicking it to go into MonoDevelop. Add the following additional variables to the component:
    [Header("Wave Properties")]
    // We want to delay our code at certain times
    public float timeBeforeSpawning = 1.5f;
    public float timeBetweenEnemies = .25f;
    public float timeBeforeWaves = 2.0f;
    
    public  int enemiesPerWave = 10;
    private int currentNumberOfEnemies = 0;
    Adding GameController to spawn enemy waves

    Now, if we save the script and go back into the editor, you'll notice a couple of interesting things. First of all, you'll see that due to the Header that we added to our script, the script has been separated into sections to make it easier to compartmentalize our code. In addition, we also see a warning on the bottom-left of the screen saying that we haven't used our variables yet. You can click on it to open up the Console window to look at it, but with that in mind, let's make it so that we are actually using the variables.

    We now need a function to spawn enemies; let's call it SpawnEnemies. We don't want to spawn all of the enemies at once. What we want is a steady stream of enemies to come to the player over the course of the game. However, in C#, to have a function pause the gameplay without having to stop the entire game, we need to use a coroutine that looks different from all of the code that we've used so far.

  9. Inside the Start method, add the following line:
    StartCoroutine(SpawnEnemies());

    Note

    A coroutine is like a function that has the ability to pause execution and continue where it left off after a period of time. By default, a coroutine is resumed on the frame after we start to yield, but it is also possible to introduce a time delay using the WaitForSeconds function for how long you want to wait before it's called again.

  10. Now that we are already using the function, let's add in the SpawnEnemies function as follows:
    // Coroutine used to spawn enemies
    IEnumerator SpawnEnemies()
    {
      // Give the player time before we start the game
      yield return new WaitForSeconds(timeBeforeSpawning);
    
      // After timeBeforeSpawning has elapsed, we will enter 
      // this loop
      while(true)
      {
        // Don't spawn anything new until all of the previous
        // wave's enemies are dead
        if(currentNumberOfEnemies <= 0) 
        {
    
          //Spawn 10 enemies in a random position
          for (int i = 0; i < enemiesPerWave; i++) 
          {
            // We want the enemies to be off screen
            // (Random.Range gives us a number between the 
            // first and second parameter)
            float randDistance = Random.Range(10, 25);
    
            // Enemies can come from any direction
            Vector2 randDirection =
              Random.insideUnitCircle;
            Vector3 enemyPos =
              this.transform.position;
    
            // Using the distance and direction we set the 
            // position
            enemyPos.x += randDirection.x * randDistance;
            enemyPos.y += randDirection.y * randDistance;
    
            // Spawn the enemy and increment the number of 
            // enemies spawned 
            // (Instantiate Makes a clone of the first 
            // parameter 
            // and places it at the second with a rotation of 
            // the third.)
            Instantiate(enemy, enemyPos, this.transform.rotation);
            currentNumberOfEnemies++;
            yield return new WaitForSeconds(timeBetweenEnemies);
          }
        }
        // How much time to wait before checking if we need to 
        // spawn another wave
        yield return new WaitForSeconds(timeBeforeWaves);
      }
    }

    The ++ operator will take the current value of a number and increment it by 1.

  11. Now, when we destroy an enemy, we want to decrement the number of currentNumberOfEnemies, but it's a private variable, which means that it can only be changed inside the GameController class or one of the methods inside of the class. Simple enough? Now, let's add a new function in our GameController class:
    // Allows classes outside of GameController to say when we 
    // killed an enemy.
    public void KilledEnemy()
    {
      currentNumberOfEnemies--;
    }
  12. Finally, let's go back into our EnemyBehaviour class. Inside the OnCollisionEnter2D function under the Destroy function call, add the following lines:
    GameController controller = GameObject.FindGameObjectWithTag("GameController").GetComponent<GameController>();
    controller.KilledEnemy();

    The preceding line gets the script GameController from the game object that has the GameController tag.

    This will call the KilledEnemy function from GameController, onto which we set the GameController tag in step 2.

  13. With all those changes, save both script files and run the game! Have a look at the following screenshot:
    Adding GameController to spawn enemy waves

We now have waves of enemies that will now move toward the player! When we kill all of the enemies inside a wave, we will spawn the next wave. In such a short period of time, we already have so much going on!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset