Why, I can't simply run an animation in loop?

I can bet $100 that every beginner Unity programmer approached this problem:

For example, I want to smoothly change scale of cube from 1 to 1.5, so I run this code:

void CubeAnim() {  
   for (int i = 0; i < 50; i++)
   {
      cube.transform.localScale += 
         new Vector3(0.01f, 0.01f, 0.01f);
   }
}

But, I was very suprised when I saw that nothing happened and cube immidiately changed scale to 1.5, Unity why? The answer is simple, when you call a function, it runs until it complete before returning. This means that any code taking place in a function proceed within single frame. So this is the reason why cube changed it scale immidiately. So how can I handle it? Surely, I can try run this code in Update() method:

int steps = 50;

void Update() {  
   if (steps > 0)
   {
      cube.transform.localScale += 
         new Vector3(0.01f, 0.01f, 0.01f);
      steps--;
   }
}

Although, code works, but it isn't good piece of code. Unity provide a convenient solution for this kind of problems. This solution is named Coroutine.

What are coroutines?

Coroutine is a kind of function which has ability to pause it execution and return control to Unity until sometime or certain condition is met. Then coroutine continues from place where it paused before. The C# implementation of cube scale animation should look like this:

private IEnumerator CubeAnim() {  
   for (int i = 0; i < 50; i++)
   {
    cube.transform.localScale +=
       new Vector3(0.01f, 0.01f, 0.01f);
    yield return null;
   }
}

We can notice two differences between above code and first code from this article introduction. Firstly we changed method return type from void to IEnumerator and secondly we added this line:

yield return null;  

This line is the point at which execution will pause and be resumed on the next frame. And finally we can't run coroutine function like normal one. We need to use StartCoroutine() instruction. In this case:

StartCoroutine(CubeAnim());  

When we run the scene, we will see the same effect like earlier when we used Update() method.

How to use coroutines

In previous example we wait one frame before process continued by using yield return null instruction. What if we wish to wait longer than one frame? Fortunalely there are more possibilities to use yield return:

  • yield return null - resumes execution on the next frame,
  • yield return new WaitForEndOfFrame() - resumes execution on the end of frame after all cameras and GUI is rendered, just before displaying the next frame,
  • yield return new WaitForFixedUpdate() - resumes execution after next fixed update fuctions have been ended,
  • yield return new WaitForSeconds(t) - resumes execution after given amount (t) of seconds using scaled time,
  • yield return new WaitForSecondsRealtime(t) - resumes execution after given amount (t) of seconds using unscaled time,
  • yield return new WWW(url) - resumes execution after the web resource was downloaded or failed,
  • yield return new WaitUntil(delegate) - resumes execution after the supplied delegate evaluates to true,
  • and finally yield return new WaitWhile(delegate) - resumes execution after the supplied delegate evaluates to false.

As you see we have wide scope to maneuver. One more useful information about using coroutines: we don't have to wait until coroutine stops - we can stop it manually. Similarly like starting coroutines, there is an instruction StopCoroutine(). The simpliest way to stop our CubeAnim animation is run instruction:

StopCoroutine("CubeAnim");  

It works but this method has a disadvantage: it stops all coroutines named CubeAnim also. When we want to stop only a specific coroutine, we need to cache it to IEnumerator variable before we use it:

IEnumerator coroutine = CubeAnim();  
StartCoroutine(coroutine);  
//Then we can stop only this one coroutine
StopCoroutine(coroutine);  

Coroutines sequenced animations

When should we use coroutines? Generally we use coroutines in situations:

  1. Working with asynchrous stuff like WWW downloading.
  2. When we want split expensive computations between multiple frames.
  3. Working with code animations.

In one of my previous article (Animation scripting in Unity) I have shown how to create a simple scripting animation with coroutine usage. What if we want to create more complex animation chain, for exaple path following animation? We can use sequenced coroutines to run this type animation:

path

Path animation

public Transform[] path;


private void Start() {  
   StartCoroutine(FollowPath());
}

private IEnumerator FollowPath() {  
   foreach (Transform waypoint in path)
   {
      yield return 
         StartCoroutine(Move(waypoint.position, 4));
   }
}

private IEnumerator Move(Vector3 destination, float animSpeed) {  
   while (transform.position != destination)
   {
      transform.position = 
         Vector3.MoveTowards(transform.position, destination, animSpeed * Time.deltaTime);
      yield return null;
   }
}

In this case we use two coroutine functions. In FollowPath method we iterate through path's waypoint array. For each waypoint we run Move coroutine. But we don't run all coroutines at same time. Using yield return allows us to wait until each animation ends. Next path waypoint move will run1 only when previous animation ended.

Conclusion

To sum up, coroutines are special type of function that allow us spread the execution of code over multiple frames using the yield statement. To start coroutine we use StartCoroutine() instruction, to stop StopCoroutine(). You can only start and stop coroutines from a class that inherits from Monobehaviour.
I think that this article will help you to start use coroutines, which are a very handy feature of C# and really helps during work with Unity.

I hope it’s useful for you. If you have any comments or questions, please write them here!

Mateusz Olszewski - Lead Programmer



Show Comments