Coroutines in Unity

“A coroutine allows you to spread tasks across several frames. In Unity, a coroutine is a method that can pause execution and return control to Unity but then continue where it left off on the following frame.

In most situations, when you call a method, it runs to completion and then returns control to the calling method, plus any optional return values. This means that any action that takes place within a method must happen within a single frame update.

In situations where you would like to use a method call to contain a procedural animation or a sequence of events over time, you can use a coroutine.

However, it’s important to remember that coroutines aren’t threads. Synchronous operations that run within a coroutine still execute on the main thread.”

We can use coroutines to either spread out events over several frames or over a certain time period.

This is especially useful for things such as animations or choreography

We’ll be using coroutines to handle spawning enemies our greybox prototype.

Do not confuse coroutines with asynchronous execution (multithreading).

Coroutines still execute on the main thread, just with delayed timing.

IEnumerator is what we use with coroutine methods so that Unity can parse through the events as if in a “for each” loop when needed.

Another handy bit of code we will use to make these events occur according to game time, rather than game frames, is WaitForSeconds.

“Suspends the coroutine execution for the given amount of seconds using scaled time.

The real time suspended is equal to the given time divided by Time.timeScale.”

We will also use the Yield keyword.

“When you use the yield contextual keyword in a statement, you indicate that the method, operator, or get accessor in which it appears is an iterator.

Using yield to define an iterator removes the need for an explicit extra class…”

“You use a yield return statement to return each element one at a time.”

Create SpawnManager game object and script

We’ll create an empty game object for our Spawn Manager game object since the only component we need is the script we will be adding.

No meshes, colliders, or materials are wanted here.

Create a script and attach it to the game object.

Copy paste the following code into the script:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class SpawnManager : MonoBehaviour{[SerializeField]private GameObject _enemyGO;[SerializeField]private Vector3 _enemySpawnPosition = new Vector3(0, 8, 0);[SerializeField]private int _enemySpawnInterval = 5;// Start is called before the first frame updatevoid Start(){StartCoroutine(SpawnRoutine(_enemyGO, _enemySpawnPosition, _enemySpawnInterval));}// Update is called once per framevoid Update(){}IEnumerator SpawnRoutine(GameObject spawnedGameObject, Vector3 spawnPosition, int spawnInterval){while (true){Instantiate(spawnedGameObject, spawnPosition, Quaternion.identity);yield return new WaitForSeconds(spawnInterval);}}}

What is our code doing?

So, first of all we have declared 3 private variables for our SpawnManager class.

The enemy game object we wish to spawn, where we want to spawn it, and how often we want to spawn it.

We’ve used [SerializeField] so that Unity will display these private fields in the editor and allow us to play with the values while testing.

Next, we’ve used StartCoroutine in our Start() method to call our IEnumerator method, “SpawnRoutine” and we have passed the 3 values we declared in variables above.

In our SpawnRoutine() method we’ve set an infinite while loop that instantiates the passed game object and then wait for the number of seconds we’ve also passed into this method.

We could remove the Update() method altogether, but we may wish to use it in the future so we’ll leave it be.

Assign the enemy prefab to the script

While we have a placeholder for the enemy game object, “_enemyGO” in our script, we have not assigned the actual game object to it.

In the Unity editor, we will simply drag and drop the Enemy prefab onto the _enemyGO field of our SpawnManager gameobject and script:

Note, we did not assign a randomized spawning position. This is because in our Enemy script of the Enemy prefab game object, we randomize the starting position on creation.

So, whenever we instantiate a new Enemy game object based on the Enemy prefab, it will automatically randomize it’s starting position.

Enemies overload

One thing you might notice if you just let the game run without destroying any enemy game objects it that the Unity Hierarchy window will rapidly fill up duplicate Enemy game objects.

As our game gets more complex, this will really clutter up our view of the Hierarchy window.

To tidy this up, we’ll spawn them as child game objects in an enemies container childed to the SpawnManager game object itself.

Now, let’s go back to our SpawnManager script in Visual Studio.

Add the following to our class variables:

[SerializeField]private GameObject _enemiesContainer;

Back in the Unity Editor, let’s assign our new Enemies gameobject, which we childed to SpawnManager game object, to this variable on our SpawnManager script.

Back on the SpawnManager script in Visual Studio, we want to alter our code to spawn these new enemy game objects as children of the Enemies container we’ve created.

We update our SpawnRoutine() method to create a temporary gameobject that will hold each newly created enemy as we instantiate these game objects.

We then assign the new enemy game objects transform a parent, our _enemiesContainer.transform.

Simple!

Now if we run our game, we see our code is working as intended.

With our Spawn Manager working, we can delete the default Enemy game object we have until this point left in the Hierarchy window.

Please stop attacking me, I’m dead…

Currently, once the player dies, the Spawn Manager will continue to spawn enemies.

We’ll go back to our SpawnManager script in Visual Studio, add a Boolean variable “_isDead” and a public method “PlayerDied” to implement a way to stop spawning enemies when the player has died.

We change our while loop in the SpawnRoutine() method to be conditional on the _isDead Boolean value, and offer a way for code outside of this script to modify that value through our PlayerDied() method.

Open up the Player script, since this is where the player dies event occurs in our code.

We’ll modify our player script as follows:

First we create a SpawnManager variable, then in the Start() method we use GameObject.Find to assign the existing SpawnManager gameobject to it.

In our LoseLife() method, we add a null reference check for the _spawnManager variable we created.

If not null, we trigger the PlayerDied() method of our SpawnManger script which will cease instantiating enemy objects.

That’s it for now, we’ll start making our game look a bit more fancy in the next article!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store