When using Unity it’s very tempting to do everything at once inside the Update() loop but trying to do too much at once creates a traffic jam as tasks can only be completed one at a time. This is where Coroutines shine, at their core they allow you to effectively multitask any number given tasks over time, shining bright when doing long drawn out operations in the background. Now you can have your fade effect, enemy spawning, and bake a cake all at once. It is important to note that Coroutines still execute sequentially you can learn more about coroutines vs multi-threading here.
So what are coroutines? Let’s take a little look at the Unity Scripting Reference (here) for some hints. “A coroutine is a function that can suspend its execution (yield) until the given YieldInstruction finishes.” Not particularly helpful but does clue us in on what a coroutine does, which is it doesn’t execute until something else (a YieldInstruction) finishes. Let’s take a practical example below to get a better idea.
Built Right: Coroutine Structure
The basic structure of a Coroutine is pretty straight forward it is constructed like any other method but with three key differences. First it needs to be prefaced by IEnumerator (this can still be public, private, or something like protected), second it needs to have some sort of yield return inside of the method telling it when to terminate, and it can only be started with StartCoroutine([Coroutine Name]). Let’s look at the scripting example below for better illustration.
As you can see commented above this is a very very very basic coroutine spawning a single enemy, waiting 5 seconds, and then printing that the enemy has spawned. Now let’s look at a more complex example of a coroutine using a similar system of randomly spawning enemies.
By the Numbers: A Simple Example
Start by creating (or opening an existing) Unity Project. Add a plane so that you can see what’s going on and point the camera down at it. Make sure it’s zeroed out in the position to keep everything square.
Create an empty object in Hierarchy and name it “Spawn Manager.” Make sure it’s zeroed out.
Now let’s create a Cube and name it Enemy, reset it’s transform and adjust as necessary so that you can see it. This will be important when we begin spawning multiples.
Now create a new script named (you guessed it) “SpawnManager” this will be responsible for our newly minted Spawn Manager. Attach it to the manager and then open the script up in your favorite code editor. Now let’s go over the code for the Spawn Manager below.
First we’ll set up a GameObject variable for the Enemy Prefab we made and then we’ll set up two int variables one controlling the current amount of spawns and one controlling the total amount of spawns with the total amount variable visible so we can change it.
Next we’ll put the StartCoroutine() method in Start, this is important because it essentially calls the coroutine to begin. You cannot call a coroutine like a normal method the specifics are discussed in the Unity documentation (here) this is the typical reason for coroutines not working.
Getting to the meat of the script is the SpawnEnemies() coroutine, like all coroutines this requires you preface it with a IEnumerator. This tells Unity that this is a coroutine.
Within the coroutine you have a while loop that will continue to be true as long as the number of current enemies is below the total amount spawned. This is where instantiation occurs, we spice it up by randomizing the spawn locations but make sure to add a increment to the current enemies spawn otherwise the loop would never end. Then we call a yield return that will wait 1 second before continuing the loop. Lastly we have a yield return null outside the while loop that will pause for a frame before ending the loop.
Next we’ll add the enemy prefab to the Spawn Manager in Hierarchy and then we’ll set the total spawn to 10 for example. Finally we’ll delete the enemy that is still in the scene since we no longer need it.
Now let’s see it action!
There you have it. A simplified spawning system using a coroutine. This really illustrates the power a coroutine has, you can now run concurrent events using coroutines.
While coroutines have their own set of downsides such as being difficult to debug when they encounter an error since it isn’t immediately obvious where the error is. An example would be an infinite loop which you wouldn’t discover until it crashes the game but the benefits outweigh the downsides. Until next time, happy coding.