I have a coroutine, which I called from the Update() method on every click.

First, I call AddTrunk() to add to my trunkList, then I use my coroutine to move the animation for 0.3 seconds. After 0.3 seconds, it calls Destroy(), and then it changes transform.position. However, before completing the animation, it runs transform.position; it first my tree down first, and then does the animation.


This is where I call my coroutine:

void Update () 
{
    if (Input.GetMouseButtonDown (0)) 
    {
        AddTrunk ();

        if (!isRunning) 
        {
            StartCoroutine (BranchOutAnimation ());
        }
    }
}

Here is the coroutine BranchOutAnimation():

IEnumerator BranchOutAnimation()
{
    while (true) 
    {
        isRunning = true;
        iTween.MoveTo (trunkList[index], 
            new Vector3 (trunkList[index].transform.position.x 
            - 5f, trunkList[index].transform.position.y, 
            trunkList [index].transform.position.z), 0.3f);
        yield return new WaitForSeconds (0.3f);

        Destroy (trunkList [index]);
        trunkList.RemoveAt (index);
        transform.position = new Vector3 (transform.position.x, transform.position.y 
            - treeHeight, transform.position.z);
        isRunning = false;
        yield break;
    }
}

What am I doing wrong?

  • @Lasse, sounds like you should post that as an answer. – Gnemlock Apr 27 '17 at 10:30
  • 2
    One tip based on your code, just because it's burned me in the past. I would set isRunning = true immediately in your if(!sRunning) statement, but set it false where you do. This will help prevent the very unlikely occurrence of the coroutine initiating twice if called very rapidly. – Jesse Williams Apr 27 '17 at 17:42
  • @JesseWilliams since Unity runs its Update methods single-threaded, the coroutine can't be initiated twice here. The Coroutine runs all the way up to its first yield statement immediately when it's started, before StatCoroutine returns, on the same thread as the update method that started it (ie. the main thread), so there's no opportunity for other main thread code to preempt the setting of this flag. You'll only run into trouble if you put the flag after the first yield, or if you have code you've manually initiated on another thread also mucking with that flag. – DMGregory Oct 9 '17 at 14:51
up vote 2 down vote accepted

Calling yield break breaks the coroutine and the while loop in it won't start over again. The while loop goes through only once after waiting for 0.3 seconds. Use the yield break only when you want to break out from the coroutine. If you want the while loop to go through multiple times, try yield return null; which will just make the coroutine to wait for one frame.

If you actually want to run the whole code block only once, just remove the while loop as it would behave exactly the same.

Based on your code I'm guessing/assuming you want to grow the tree until there are no more trunks in the list, and finally just remove the whole tree. Here's some untested code to help you out:

IEnumerator BranchOutAnimation()
{
    // Make sure we are running only one coroutine
    if(isRunning)
        yield break;

    isRunning = true;

    // Which index did we start from?
    int trunkIndex = index;

    // Just make the animation interval configurable for easier modification later
    const float animationInterval = 0.3f;

    // Loop until instructed otherwise
    while (true) 
    {
        // Do some nice animation
        iTween.MoveTo (trunkList[trunkIndex], new Vector3 (trunkList[trunkIndex].transform.position.x - 5f, trunkList[trunkIndex].transform.position.y, trunkList [trunkIndex].transform.position.z), animationInterval);

        // Make the coroutine wait for a moment
        yield return new WaitForSeconds (animationInterval);

        // Increase index of the animation
        trunkIndex++;

        // Check if we have reached the last index
        if(trunkIndex == trunkList.Count)
        {
            // Break the while loop, NOT the coroutine
            break;
        }
    }

    // Finally just remove the whole tree
    for(int i = trunkList.Count - 1; i >= 0 ; i--)
    {
        Destroy (trunkList [i]);
        trunkList.RemoveAt (i);
        transform.position = new Vector3 (transform.position.x, transform.position.y - treeHeight, transform.position.z);
    }

    isRunning = false;
}

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.