How to stop a co-routine in C# instantly?

I have been looking around for days now and cant seem to find a way to instantly stop a co-routine.

I have a character that performs several different actions, some can take up to 30 secs to complete. Each are different co-routines. I need to stop the action the character is doing instantly then call the next. At the moment one of two things happen, both co-routines run at the same time causing the character stop working, or I have to wait for the co-routine to stop before I can call the next one.

How to stop a co-routine instantly and then start the next.?

I used this code as a test. Its a simple code that counts to 10 in seconds, printing the seconds. Pressing Q runs StopCoroutine. If you press Q within the 10 seconds you can clearly see the co-routine carries on counting in the consol.

    using UnityEngine;
using System.Collections;

public class TestCoRoutine : MonoBehaviour {

 void Start () {
 StartCoroutine (StartCounting());
 }
 
 // Press Q to run StopCoroutine
 void Update () {
 if (Input.GetKey("q")){
 StopCoroutine ("StartCounting");
 print ("Stopped");
 }
 }
 
 IEnumerator StartCounting(){
 yield return new WaitForSeconds(1);
 print ("1 second");
 yield return new WaitForSeconds(1);
 print ("2 second");
 yield return new WaitForSeconds(1);
 print ("3 second");
 yield return new WaitForSeconds(1);
 print ("4 second");
 yield return new WaitForSeconds(1);
 print ("5 second");
 yield return new WaitForSeconds(1);
 print ("6 second");
 yield return new WaitForSeconds(1);
 print ("7 second");
 yield return new WaitForSeconds(1);
 print ("8 second");
 yield return new WaitForSeconds(1);
 print ("9 second");
 yield return new WaitForSeconds(1);
 print ("10 second");
 }
 
}

Thanks

1 Like

Since the question got bumped already i’l like to add that the mentioned feature here in the comments has finally been implemented ^^. StopCoroutine now comes in 3 variants:

  • StopCoroutine(string);
  • StopCoroutine(IEnumerator);
  • StopCoroutine(Coroutine);

The string version still has the same limitation. So you can only stop a coroutine with the string version of StopCoroutine which has beed started with the string version of StartCoroutine. The string version has more disadvantages as you’re limited with the parameters you can pass and you can only start a coroutine that is actually part of the script.

The IEnumerator version of StopCoroutine has caused a lot of confusion by a lot people. The quirks with the IEnumerator version is that you have to use the same IEnumerator object that you have used to start the coroutine. However each time you invoke your coroutine you will “generate” a new IEnumerator object. So this Doesn’t work:

StartCoroutine(MyCoroutine());
StopCoroutine(MyCoroutine());  // doesn't work. It's a different IEnumerator object

To use the IEnumerator version you have to store the IEnumerator object before you pass it to StartCoroutine:

IEnumerator co;

co = MyCoroutine(); // create an IEnumerator object
StartCoroutine(co); // start the coroutine
StopCoroutine(co); // stop it.

The IEnumerator version is actually cumbersome. The Coroutine version is much better:

Coroutine co;

// start the coroutine the usual way but store the Coroutine object that StartCoroutine returns.
co = StartCoroutine(MyCoroutine());
StopCoroutine(co); // stop the coroutine

So if you need to stop a coroutine either use the Coroutine version of StopCoroutine or the string version if you don’t need any parameters.

As can be read in the documentation, StopCoroutine(string) can only stop coroutines started with StartCoroutine(string). Since you used the other version StartCoroutine(IEnumerator), this does not work.

Two solutions. First use the string version of StartCoroutine:

StartCoroutine("StartCounting");

Second, use a boolean value and check it before any yield statement:

private boolean stopped = false;
IEnumerator StartCounting()
{
    stopped = false;
    yield return new WaitForSeconds(1);
    print ("1 second");

    if (stopped)
    {
        yield break;
    }        
    yield return new WaitForSeconds(1);
    print ("2 second"); 

    if (stopped)
    {
        yield break;
    }
    yield return new WaitForSeconds(1);
    print ("3 second");

    // and so on...
}

// or using a loop
IEnumerator StartCounting()
{
    int i = 0;
    stopped = false;
    do
    {
        yield return new WaitForSeconds(1);
        print (i+" second");
        i++;
    } while(!stopped && i<10);
}

To stop the coroutine, just set the boolean to true.

You can use StopCoroutine for stop any coroutine. If you want to stop all coroutine you can use StopAllCoroutines.

This post is a little old but it helped me and i want to contribute synthesizing what finally worked for me with an IEnumerator:

I declared a “private Coroutine co” , then, when i needed to start the coroutine (including yield return new WaitForSeconds) I replaced “co = StartCoroutine (MyCoroutine());” instead of “StartCoroutine (MyCoroutine());” and when i needed to stop “StopCoroutine (co);”

That was all! Works fine!

I made it so I can’t turn or move my character while attacking but it is possible to cancel the attack at any time, making it possible to turn and move before the attack is finished.

public class Player : Character {

[SerializeField] private AnimationClip attackSpearUp;
private bool anchoredAttackRunning = false;
private IEnumerator anchoredAttack;

void Start ()
    {

    }

void FixedUpdate () {
    
    if (Input.GetMouseButtonDown(0) && anchoredAttackRunning == false)
    {
        anchoredAttack = anchoredAttackRoutine();
        StartCoroutine(anchoredAttack);
    }
    if (Input.GetMouseButtonDown(1) && anchoredAttackRunning == true)
    {
        StopCoroutine(anchoredAttack);
        anchoredAttackRunning = false;
        anim.SetBool("is_attacking", false);
    }
    
    if (anchoredAttackRunning == false)
    {
        Movement();
    }
}

private void Movement()
{
    //.... blah, blah, blah my irrelevant movement code.
}

private IEnumerator anchoredAttackRoutine()
{
    anchoredAttackRunning = true;
    anim.SetBool("is_attacking", true);
    yield return new WaitForSeconds(attackSpearUp.length);
    anchoredAttackRunning = false;
    anim.SetBool("is_attacking", false);
}

}