Let’s see how to use C# coroutines to animate UI canvas elements!
This article is also available on Medium.
What about Unity animations?
When you work on your UI, you might be tempted to use “normal” Unity animations to update the various parameters of your elements. And it’s a valid and user-friendly of doing it.
For example, it allows you to quickly setup a basic fade-to-black scene transition; I’ve talked about this in more detail in a previous tutorial (available in text or video format), if you’re interested in learning more 🙂
Despite being a great way of prototyping UI animations, this technique is not the most efficient.
The problem is that Unity’s animator engine was originally crafted and tuned for character rigs animation – so they sort of expect continuous movement that keep on updating ever so-slightly the positions of a whole bunch of bones in a complex skeleton. And this is pretty different from what you’re doing when you’re just interpolating some colour alpha between 1 and 0, right?
In particular, this implies having a lot of unnecessary computation – because, basically, the animation system will keep on “updating” your UI element, even if it doesn’t change any of its parameters anymore!
That’s why when/if you run into performance issues and you’re using a lot of UI animations, you might need to consider switching to script-based UI animation.
Tweening and interpolation
Designing a UI animation and controlling it via script is actually quite similar to doing one with Unity’s animation, at least in terms of mindset.
Both the animation system and the script-based technique rely on the idea of keyframes: rather than defining the exact value of each of your parameters every frame, you instead set their value at some specific points in time and then interpolate the rest.
Computing these in-between values is often called tweening, and it can be done in various ways. The most common interpolation is the linear one: you simply transition from the start to the end value regularly, with equal updates every frame. On the other hand, you could also transition abruptly and jump from one value to another with a constant interpolator; or have a smoother update with bézier interpolation, for example.
There are of course lots of other interpolation types, and you could even devise some that are a bit crazy.
This is what Unity’s animation timeline does: it allows you to quickly place keyframes where a given parameter has a particular value, and then it lets you choose between several interpolation types to automatically compute the in-between values.
And, of course, it’s absolutely possible to do the same in a C# script, and it’s actually quite straight-forward… so let’s see how we could re-create our scene transition effect using scripts instead of Unity animations! 🙂
A script-based Unity scene transitioner
Running the animation without blocking the game!
Before we dive in, there is something important to note: we need to find a way to run this animation without blocking the rest of the game 😉
By default, when you run a program, all its instructions are executed one after the one, sequentially; and so you need to wait for a task to finish before you can go to the next one. This is called synchronous programming.
The problem is that, in our case, this would mean that while the animation is playing, nothing else can happen in the game. So: no sound, no click, no user interaction – no nothing. Which is pretty bad!
To avoid this issue, the solution is to use asynchronous techniques to have several tasks run in parallel without blocking each other.
But since a computer is inherently sequential, you can’t really have true parallel execution. We can simulate this parallelisation by:
- chopping down those tasks in very very small bits
- having the computer switch very quickly from one task to the other
- and, every time it switches, complete one of these tiny subtask
Given how small the chunks are, it seems to us like they’re done instantly, and so the computer switches between the two tasks fast enough to get a simulated parallelisation.
Using coroutines to run an async process
In Unity, this can be done thanks to coroutines and
IEnumerators are a C# variable type similar to Python’s generators: they are an iterable collection that support custom collection types and don’t require you to compute all elements at once. Instead, you can iterate through your collection and grab elements one by one. You “end” each iteration with a
Unity makes use of those
IEnumerators to handle parallel processing via coroutines: contrary to usual functions that run until completion in one go, coroutines can pause at one point, return control to Unity so it can manage the rest of the code and then start back from where they left off at the next frame.
The reason coroutines are great for UI animations is that you can run them a given number of times, or even until/while a condition is true, and then stop them. Once stopped, you do have to worry about them doing “phantom useless work” anymore: they are not in the game anymore, and they’ll only exist again if you re-start them 🙂
And bonus: they can be easily be “nested” and called from one each other. This allows you to cut down your full process in smaller and more digestible methods!
Applying coroutines to our UI animations
You’ll notice that we have already used coroutines in the previous version of this tutorial, to wait until the animation is completed before switching to the next scene:
What we want to do here is replace the whole transition process by scripts only; so we’ll basically need to rework this
LoadNextScene() into a coroutine that does the following:
- a fade out: the alpha channel of the colour of our black panel goes from 0 to 1
- the scene switch: we use Unity’s
SceneManagementpackage to actually load the next scene
Fading back into the new scene will be handled automatically upon scene load, so we don’t add it to this transition process.
Also, the transition is linear, nothing too fancy or complex… and the nice thing is that Unity actually provides us with a method to lerp between two colours linearly:
The idea of this function is to pass it the start and end points, and then the interpolation ratio (usually called the t-value) you want to “evaluate” the transition at. This t-value is normalised, so it goes from 0 (the start value) to 1 (the end value):
So, for example, let’s say we want to refactor our fade-out animation. Here, our start colour will be the “transparent black” (i.e. a black colour with an alpha of 0) and our end colour will be “opaque black” (a black colour with an alpha of 1).
Then, we can declare these two colours and get the value of the in-between colour for t = 0.5, for example, with the following snippet:
Now, to actually get our animation, we want to make t increase slowly from 0 to 1 in a given amount of seconds (the duration of the animation) and, every time it changes, we recompute the tweened colour and apply it to our panel to really get the visual update.
This can be written with the following piece of code:
yield return null; is a way of calling this coroutine every update frame – this gives a smooth transition with enough intermediary steps for the process to feel continuous. It goes hand to hand with the increment of
Time.deltaTime of our t counter: every time we run a new iteration in this while loop, it lasts just a frame, so the total elapsed time has to be incremented by the duration of this last frame, which is given by this built-in Unity variable.
Note that you can reduce the number of steps a bit for performance, if you want. Because the fact is that your eyes do some interpolation of their own and so you can simulate continuity even if you have a bit less frames. For example, you could compute and update the colour only every 0.01 seconds without much of a difference to our human eye.
But in terms of computation, it reduces the amount of work a bit and avoids your UI animation from taking too much of a toll on the overall game execution!
Just like any performance/optimisation technique, though, it’s a trade-off (of quality versus computation power). So make sure you really need it before diving in this too much! 😉
Refactoring the fade-in animation
The fade-in animation is basically the same code with the start and end positions reversed. So we can factorise this code in a single
Fading() coroutine and abstract the start/end colours one level above in dedicated functions:
At that point, we’ve successfully reconstructed the scene exit visual effect we had before: when we click the left mouse button, the screen fades to black, then we change scenes and we get the new scene.
Having the initial fade-in
However, to get our full smooth transition effect, we also want to reproduce the “Play on Awake” feature of our previous animation – remember that in the first version of the tutorial that used Unity animations, the screen would initially be black and fade out when the game was started, or when we got to a new scene.
Easy! All we need to do is start the
FadingIn() coroutine when we first start our scene, like this 🙂
Re-integrating the sound-fade effect
Finally, if you want to achieve the exact same result as in the first tutorial, you’ll also want to have the background music fade to a volume of 0 when you exit a scene and fade back to a volume of 1 when you enter the next one.
This can be done in our coroutine, just by having the loop iterations modify the audio source volume parameter too. We just need to be careful to catch whether we’re fading in our out to properly ramp the volume up or down:
This time, it’s even simpler because the t value is directly the value of the volume, or its complementary over the 0-1 range! 🙂
Although Unity animations are a quick and easy way to update parameters on your UI elements, they are not optimised and can lead to bad performance. To get a more efficient animation process, you can use C# scripts and coroutines with interpolation.
If you want even more interesting UI effects, you can check out assets from the Unity Asset Store; for example, the Lean Tween package is a really great lib that lets you animate 3D objects or UI canvas elements (translate, rotate, scale…)!
I hope you liked this short Unity tutorial 😉
And as usual, don’t hesitate to leave a comment to tell me if you have any other idea of Unity/C# tutorial I could do…