We have done a lot of nice stuff in other articles: importing and using tilemaps for level creation, creating simple ennemies, coding a player controller…
Now we’ll add some elements dependent on time, with disappearing platforms! And to do that we’ll use Coroutine to make our code encapsulated and easy to understand. This will be a quick, easy and extremely useful article so let’s start!
What’s a Coroutine?
When creating a game, most of the logic will be handled in the Update method of our MonoBehaviour Game Objects. This means that if you don’t take precautions, they have a tendency to become monolithic, aka really big really fast.
This is especially true when you are incorporating elements which do not need to be called every frame. For example, let’s say you want to create a fade out for your game using a black Canvas image, where you increase the opacity over time.
One way to handle this is to create a boolean property to track whether the screen should be fading out and, while it is true, increase the opacity of the image in the Update method. That’s simple enough. But, if you need a fade out, you would also need a fade in, which needs it own logic. You should see where I am going with this: we are adding properties that are unused most of the time, checks which are executed every frame and increasing the size and complexity of our Update method. Maybe there is a better way: using Coroutines.
A coroutine is an execution stack, with the capability of defining and using its own variables, while also being able to interact with class properties. It is called a coroutine because it is run on the main thread, which is why it has access to variables in the parent scope, and is executed at a seperate time from the rest of the logic (C# is single-threaded) so you don’t have to worry too much about race conditions. This allows us to easily set execution time by using “yield return new WaitForSeconds”. I’ll show you it works below which commented code, do not worry! :)
So why is this useful? With the use of coroutines, we unlock the possibility of reactive programming: while previously we were waiting for a condition to be true, now we can easily start the coroutine on an event happening.
In the case of a disappearing platform, we’ll create a MonoBehaviour Class, which will take as public property the desired length of time for the disappearance (mTimePerCycle in the code below), a reference to the BoxCollider2D of our platform and a transparency threshold below which we’ll switch the collider off, allowing our player to go through. We’ll also specify a frequency for the Coroutine execution: there is little point in changing the Opacity of our every frame
And now we can specify the logic of the Coroutine:
Get the current colour of the sprite we want to make disappear
Start looping on elapsed time, incrementing it by a given time step at each iteration
Compute new opacity and set it to the Sprite
Check if the opacity threshold has been reached, and disable the collider if it has
Wait for next iteration
Here’s the associated call:
IEnumeratorStartDisappear() {// Get the Sprite ColorColor col = _mSpriteRenderer.color;bool colliderIsDisabled =false;// While we haven't reached the target time for the disappearance animationfor (float elapsedTime =0f; elapsedTime < mTimePerCycle; elapsedTime += mTimeBetweenCalls) {// we set the opacity of the sprite's color to the ratio of time elapsed to target col.a =1- elapsedTime / mTimePerCycle;// set the new opacity to the sprite component _mSpriteRenderer.color = col;// if the opacity is below the target threshold, deactivate the colliderif (col.a < mThresholdRigid &&!colliderIsDisabled) { _mBoxCollider.enabled =false; colliderIsDisabled =true; }// give control back and execute nothing until mTimeBetweenCalls has passedyieldreturnnewWaitForSeconds(mTimeBetweenCalls); }// ensure the sprite is fully transparent col.a =0.0f; _mSpriteRenderer.color = col;// give control back and end the coroutineyieldreturnnull; }
Congrats, you now have a disappearing Platform!
Round And Round The Coroutine Goes
Right, I have my invisible platform, but what do we do with it? Well, since we have the logic for a disappearing platform, we can create another coroutine to make it appear.
We can also create another coroutine that would wait for some time before calling another Coroutine very easily:
It now becomes possible to chain Coroutine calls, so that once the disappearing Coroutine has ended, we can have a coroutine wait before starting the reappearance of the platform. And again and again… Very simple but extremely powerful. You can find the code for this at the end of the article.
conclusion
Coroutines are great tools which I recommend using when you have logic that should not be executed every frame, or if you need to schedule function calls, for example waiting for a animation to finish before executing another action.
I have one last recommendation: If you have many of these GameObjects which follow a logic that executes constantly independently of your global state, it is usually better to go through a specific manager, some kind of pool; this will be much more performant than spawning a lot of Coroutines.