How to Create Disappearing Platforms for a 2D Platformer in Unity

Published

Access the latest demo over here

This article is a part of our 2D Platformer Tutorial Serie. Be sure to check the other articles out!

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!

Disappearing Tiles
The End Result

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! :)

You can get more details about Coroutines compared to threads, multiprocessing etc here: https://stackoverflow.com/questions/1934715/difference-between-a-coroutine-and-a-thread

Creating the Disappearing Platform Logic

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:

    IEnumerator StartDisappear() {
        // Get the Sprite Color
        Color col = _mSpriteRenderer.color;
        bool colliderIsDisabled = false;

        // While we haven't reached the target time for the disappearance animation
        for (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 collider
            if (col.a < mThresholdRigid && !colliderIsDisabled) {
                _mBoxCollider.enabled = false;
                colliderIsDisabled = true;
            }

            // give control back and execute nothing until mTimeBetweenCalls has passed
            yield return new WaitForSeconds(mTimeBetweenCalls);
        }

        // ensure the sprite is fully transparent
        col.a = 0.0f;
        _mSpriteRenderer.color = col;

        // give control back and end the coroutine
        yield return null;
    }

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:

    IEnumerator StartIdleBeforeDisappear() {
        for (float elapsedTime = 0f; elapsedTime < mIdleTime; elapsedTime += mTimeBetweenCalls) {
            yield return new WaitForSeconds(mTimeBetweenCalls);
        }

        yield return StartCoroutine("StartDisappear");
    }

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.

Anyway, that’s all on this topic for now, you can check out the other articles in the main project page here!

You can also get the code and the assets from this Github Repository.