Search Unity

How to cancel and restart a coroutine

Discussion in 'Scripting' started by graviton, Oct 9, 2016.

  1. graviton

    graviton

    Joined:
    Jan 11, 2013
    Posts:
    75
    I'm trying and start it up again from the beginning

    e.g.

    print 1

    wait for seconds

    print 2

    wait for seconds

    print 3

    I know how to start and stop coroutines, but when I stop at, say 2, and I start again, it just resumes and continues to 3, instead of going back to 1.

    If I want it to start at 1 again, have to wait for the whole coroutine to finish before starting it again.

    Why is it even called "StopCoroutine()" when it essentially just puts the coroutine on pause

    How do I cancel a coroutine and restart it from the beginning?

    it seems like something that should be simple to do
     
    aeffoa and Bakanovskiy95 like this.
  2. Martinez-Vargas

    Martinez-Vargas

    Joined:
    Jun 24, 2016
    Posts:
    39
    Hello, you have to use the following function to stop a corroutina.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class CorroutineExamples : MonoBehaviour
    5. {
    6.     public bool startCou;
    7.  
    8.     IEnumerator CoroutineExample()
    9.     {
    10.         yield return new WaitForSeconds (1);
    11.         print ("1");
    12.         yield return new WaitForSeconds (1);
    13.         print ("2");
    14.         yield return new WaitForSeconds (1);
    15.         print ("3");
    16.         StartCoroutine("CoroutineExample");
    17.     }
    18.  
    19.     void Update()
    20.     {
    21.         if (Input.GetButtonDown ("Fire1"))
    22.         {
    23.             if(startCou)
    24.             {
    25.                 StartCoroutine("CoroutineExample");
    26.             }
    27.             else
    28.             {
    29.                 StopCoroutine("CoroutineExample");
    30.             }
    31.             startCou = ! startCou;
    32.         }
    33.     }
    34. }
     
  3. Martinez-Vargas

    Martinez-Vargas

    Joined:
    Jun 24, 2016
    Posts:
    39
    if (startCoup) is positive, the corroutina begins, if (startCoup) is negative, the corroutina stops, if (startCou) is positive again, the corroutina start again from the beginning.
     
    Bakanovskiy95 and graviton like this.
  4. graviton

    graviton

    Joined:
    Jan 11, 2013
    Posts:
    75
    @Martinez-Vargas
    I wonder why calling the coroutine as a string makes it stop and start again from the beginning.
    There are 3 methods of calling coroutines that I know of.

    Calling a Coroutine using a string.
    Calling StopCoroutine and StartCoroutine as a string seems to stop and start the coroutine back at the beginning, as is in your example.

    Calling a Coroutine as a variable.
    (I was using this method)
    StopCoroutine just pauses the coroutine, and StartCoroutine just resumes it.
    Once it reaches the end of the coroutine, it doesn't loop back to beginning, and StartCoroutine doesn't start it back up again. This method is a one shot, can start just once and never again unless the scene is restarted or "yield return null" is used to loop the coroutine back to the beginning.
    Code (CSharp):
    1.  
    2.  
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. public class CorroutineExamples02 : MonoBehaviour
    7. {
    8.     private IEnumerator CoroutineExample_Stoppable;
    9.     public bool startCou;
    10.  
    11.     void Start ()
    12.     {
    13.         CoroutineExample_Stoppable = CoroutineExample();
    14.     }
    15.  
    16.     IEnumerator CoroutineExample()
    17.     {
    18.         yield return new WaitForSeconds (1);
    19.         print ("1");
    20.  
    21.         yield return new WaitForSeconds (1);
    22.         print ("2");
    23.  
    24.         yield return new WaitForSeconds (1);
    25.         print ("3");
    26.  
    27.         StartCoroutine(CoroutineExample_Stoppable);
    28.     }
    29.  
    30.     void Update()
    31.     {
    32.         if (Input.GetButtonDown ("Fire1"))
    33.         {
    34.             if(startCou)
    35.             {
    36.                 StartCoroutine(CoroutineExample_Stoppable);
    37.             }
    38.             else
    39.             {
    40.                 StopCoroutine(CoroutineExample_Stoppable);
    41.             }
    42.             startCou = ! startCou;
    43.         }
    44.     }
    45. }
    46.  
    47.  

    Calling a Coroutine directly.
    StopCoroutine doesn't seem to work in this method.
    Code (CSharp):
    1.  
    2.  
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. public class CorroutineExamples03 : MonoBehaviour
    7. {
    8.     public bool startCou;
    9.  
    10.     IEnumerator CoroutineExample()
    11.     {
    12.         yield return new WaitForSeconds (1);
    13.         print ("1");
    14.  
    15.         yield return new WaitForSeconds (1);
    16.         print ("2");
    17.  
    18.         yield return new WaitForSeconds (1);
    19.         print ("3");
    20.  
    21.         StartCoroutine(CoroutineExample());
    22.     }
    23.  
    24.     void Update()
    25.     {
    26.         if (Input.GetButtonDown ("Fire1"))
    27.         {
    28.             if(startCou)
    29.             {
    30.                 StartCoroutine(CoroutineExample());
    31.             }
    32.             else
    33.             {
    34.                 StopCoroutine(CoroutineExample());
    35.             }
    36.             startCou = ! startCou;
    37.         }
    38.     }
    39.  
    40. }
     
    Last edited: Oct 13, 2016
  5. Martinez-Vargas

    Martinez-Vargas

    Joined:
    Jun 24, 2016
    Posts:
    39
    if it works as long as you write in brackets: StopCoroutine ("CoroutineExample");
     
    Bakanovskiy95 likes this.
  6. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    I can't test your version right now, but it might be that you're not doing it correctly. Here is the code I use for stopping a coroutine:

    Code (CSharp):
    1. Coroutine m_MyCoroutineReference;
    2.  
    3. void Start()
    4. {
    5.     m_MyCoroutineReference = StartCoroutine(MyRoutine());
    6.  
    7.     // Later to stop it:
    8.     StopCoroutine(m_MyCoroutineReference);
    9. }
    10.  
    11. IEnumerator MyRoutine()
    12. {
    13.     yield return null; // etc.
    14. }
    Pay attention that I'm storing and stopping the coroutine via a reference of type "Coroutine", not IEnumerator. The problem usually is to resume a coroutine, where you picked up. There are plugins to achieve pausable coroutines etc, so the default behaviour is to start at the beginning.
     
    Last edited: Nov 24, 2020
  7. merkaba48

    merkaba48

    Joined:
    Dec 17, 2013
    Posts:
    6
    I am looking at this myself now. I think I understand why there are differences in the results user graviton posted.

    When you assign an IEnumerator to a variable, and pass that to the StartCoroutine, it is indeed a one-shot instance. However, re-assigning the IEnumerator to the same variable does reset it, as it creates a new instance, so that would be a solution to the original question. (There is an IEnumerator method 'Reset', which I thought might let it be re-used, but trying this results in a not-supported error message.)

    When you assign as a string, I suspect Unity is doing some work under the hood to create a new instance each time it is called, effectively calling a new one-shot each time it is used. It also keeps track of all instances created with this string, and StopCoroutine method with a string shuts down all coroutines started with that string. It doesn't seem possible to stop a coroutine started with an IEnumerable argument by using a StopCoroutine with a string argument, supporting this theory.

    You can't pause a string-started coroutine, because each time you call Start, it's creating a new instance.

    The last example graviton provides, where an anonymous IEnumerator is provided, you are unable to stop the coroutine because you don't have a handle to the coroutine to do so; calling the same IEnumerable method in StopCoroutine will return a new instance of IEnumerable rather than locate the one already started.

    In summary,
    Code (CSharp):
    1. coroutine = MyCoroutine();
    2. StartCoroutine(coroutine);
    Starts or resumes a single coroutine instance, which will run to completion and then become 'dead', but can be paused with:
    Code (CSharp):
    1. StopCoroutine(coroutine);
    The variable 'coroutine' can be 'reset' by reassigning it to MyCoroutine().

    ====

    Code (CSharp):
    1. StartCoroutine("MyCoroutine");
    This will create a new coroutine instance each time it is called, and Unity will keep track of all of them, so they can all be stopped at once with:
    Code (CSharp):
    1. StopCoroutine("MyCoroutine");
    ====

    Code (CSharp):
    1. StartCoroutine(MyCoroutine());
    This creates a new coroutine instance - but you will not be able to stop it with StopCoroutine. Because,
    Code (CSharp):
    1. StopCoroutine(MyCoroutine());
    This also creates a new coroutine instance, and so is not the same instance created with the above StartCoroutine. Doing this is effectively useless.
     
    Last edited: Apr 23, 2017
  8. P1505C

    P1505C

    Joined:
    Oct 12, 2017
    Posts:
    14
    Sorry to hijack this thread. But I'm struggling with coroutines in the same way. I need to be able to start, and stop/reset them. Here's my code:

    Code (CSharp):
    1. private IEnumerator waypointEnumerator;
    2.  
    3. waypointEnumerator = waypointTimerEvent ();
    4.  
    5. public void PointerEnter() {
    6.         Debug.Log("I entered.");
    7.         StartCoroutine(waypointEnumerator);
    8.     }
    9.  
    10.     public void PointerExit() {
    11.         Debug.Log("I exited.");
    12.         StopCoroutine(waypointEnumerator);
    13.     }
    14.  
    15.     IEnumerator waypointTimerEvent() {
    16.         if(isWaypointTimerEventExecuting)
    17.             yield break;
    18.  
    19.         isWaypointTimerEventExecuting = true;
    20.  
    21.         yield return new WaitForSeconds(gazeTime);
    22.  
    23.         //code to execute here
    24.         Debug.Log("Delayed event.");
    25.         //move the camera
    26.         playerCam.transform.position = new Vector3 (waypointPosition.x, waypointPosition.y + playerCamPosition.y, waypointPosition.z);
    27.  
    28.         isWaypointTimerEventExecuting = false;
    29.  
    30.     }
     
  9. Martinez-Vargas

    Martinez-Vargas

    Joined:
    Jun 24, 2016
    Posts:
    39
    In order to activate and stop a corrutina, you have to enshrine the name of the corroutina in quotes.
    StopCoroutine("MyCoroutine");
     
  10. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    itscarol and ghostmode like this.
  11. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    What issue are you experiencing? It looks like you have waypointEnumerator = waypointTimerEvent (); outside any methods. Put it in Start and see if that helps.
     
  12. P1505C

    P1505C

    Joined:
    Oct 12, 2017
    Posts:
    14
    Here is the full code, should be easier to see where I messed up :)

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Gaze_Waypoints : MonoBehaviour {
    6.  
    7.     public int gazeTime = 3; //the amount of time needed to look to teleport
    8.     public GameObject playerCam; //the players camera
    9.     private Vector3 waypointPosition; //the position of the current waypoint
    10.     private Vector3 playerCamPosition; //the current position of the players camera used for height information
    11.     private IEnumerator waypointEnumerator; //my co-routine
    12.     private bool startCoRoutine; //is the co-routine running
    13.  
    14.     void Start () {
    15.         waypointPosition = transform.position;  //get the position of the owners waypoint
    16.         waypointEnumerator = waypointTimerEvent (); //set the ienumerator to the relevant function below
    17.         startCoRoutine = true; //testing this out as true or false
    18.     }
    19.    
    20.     void Update () {
    21.         playerCamPosition = playerCam.transform.position; //keep track of the players camera, mainly for height info
    22.     }
    23.  
    24.     // when I gaze a waypoint
    25.     public void PointerEnter() {
    26.         Debug.Log("I entered.");
    27.         if (startCoRoutine) {
    28.             StartCoroutine (waypointEnumerator);
    29.         } else {
    30.             StopCoroutine (waypointEnumerator);
    31.         }
    32.         startCoRoutine = !startCoRoutine;
    33.     }
    34.  
    35.     // when I look away
    36.     public void PointerExit() {
    37.         Debug.Log("I exited.");
    38.         StopCoroutine (waypointEnumerator);
    39.     }
    40.  
    41.     // if I look for 3 seconds teleport the user, if I look away reset the timer
    42.     IEnumerator waypointTimerEvent() {
    43.         yield return new WaitForSeconds (gazeTime);
    44.         playerCam.transform.position = new Vector3 (waypointPosition.x, waypointPosition.y + playerCamPosition.y, waypointPosition.z);
    45.         StartCoroutine(waypointEnumerator);
    46.     }
    47. }
     
  13. P1505C

    P1505C

    Joined:
    Oct 12, 2017
    Posts:
    14
    Added complete code above. When I enter the scene for the first time, if I look at a waypoint it'll work. If I look at one then away, then back at it it'll work. If I look at one, then away, then at a second one it wont work.
     
  14. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    I wrote an Extensions class a couple years ago that I always use to reset coroutines....

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5.     public static class CoroutineExtensions
    6.     {  
    7.         /// <summary>
    8.         /// Tries to stop a coroutine based on a Coroutine Handle.
    9.         /// will only stop the Coroutine if the handle is not null
    10.         /// </summary>
    11.         /// <returns>the Monobehaviour script running the coroutine, allowing chained commands</returns>
    12.         /// <param name="handle">Handle.</param>
    13.         public static MonoBehaviour TryStopCoroutine(this MonoBehaviour script, ref Coroutine handle)
    14.         {
    15.             if (!script) return null;
    16.             if (handle != null) script.StopCoroutine (handle);
    17.             handle = null;
    18.             return script;
    19.         }
    20.      
    21.         /// <summary>
    22.         /// Starts the coroutine and sets the routine to a Coroutine handle.
    23.         /// </summary>
    24.         /// <returns>the Monobehaviour script running the coroutine, allowing chained commands</returns>
    25.         /// <param name="routine">Routine.</param>
    26.         /// <param name="handle">Handle.</param>
    27.         public static MonoBehaviour StartCoroutine(this MonoBehaviour script,IEnumerator routine, ref Coroutine handle)
    28.         {
    29.             if(!script)
    30.             {
    31.                 #if UNITY_EDITOR
    32.                 Debug.LogWarning("A coroutine cannot run while it is null or being destroyed");
    33.                 #endif
    34.                 return null;
    35.             }
    36.  
    37.             if(!script.enabled || !script.gameObject.activeInHierarchy)
    38.             {
    39.                 #if UNITY_EDITOR
    40.                 Debug.LogWarningFormat (script, "The Script {0} is currently disabled and cannot start coroutines", script);
    41.                 #endif
    42.                 return script;
    43.             }
    44.          
    45.             handle = script.StartCoroutine (routine);
    46.          
    47.             return script;
    48.         }
    49.      
    50.      
    51.         /// <summary>
    52.         /// Stops any possible coroutine running on the specified handle and runs a new routine in its place
    53.         /// </summary>
    54.         /// <returns>the Monobehaviour script running the coroutine, allowing chained commands</returns>
    55.         /// <param name="script">Script.</param>
    56.         /// <param name="routine">Routine.</param>
    57.         /// <param name="handle">Handle.</param>
    58.         public static MonoBehaviour RestartCoroutine(this MonoBehaviour script, IEnumerator routine, ref Coroutine handle)
    59.         {
    60.             return script.TryStopCoroutine (ref handle)
    61.                 .StartCoroutine (routine, ref handle);
    62.         }
    63.     }

    ...Then all your script needs to do is store a coroutine Handle instead of an IEnumerator:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class Gaze_Waypoints : MonoBehaviour
    5. {
    6.     public int gazeTime = 3; //the amount of time needed to look to teleport
    7.     public GameObject playerCam; //the players camera
    8.     private Vector3 waypointPosition; //the position of the current waypoint
    9.     private Vector3 playerCamPosition; //the current position of the players camera used for height information
    10.     private Coroutine waypointHandle; //my co-routine
    11.  
    12.     void Start ()
    13.     {
    14.         waypointPosition = transform.position;  //get the position of the owners waypoint
    15.     }
    16.  
    17.     void Update ()
    18.     {
    19.         playerCamPosition = playerCam.transform.position; //keep track of the players camera, mainly for height info
    20.     }
    21.  
    22.     // when I gaze a waypoint
    23.     public void PointerEnter()
    24.     {
    25.         Debug.Log("I entered.");
    26.         this.RestartCoroutine(waypointTimerEvent(),ref waypointHandle);
    27.     }
    28.  
    29.     // when I look away
    30.     public void PointerExit()
    31.     {
    32.         Debug.Log("I exited.");
    33.         this.TryStopCoroutine(ref waypointHandle);
    34.     }
    35.  
    36.     // if I look for 3 seconds teleport the user, if I look away reset the timer
    37.     IEnumerator waypointTimerEvent()
    38.     {
    39.         yield return new WaitForSeconds (gazeTime);
    40.         playerCam.transform.position = waypointPosition + Vector3.up * playerCamPosition.y;
    41.     }
    42. }
    part of the cause with your troubles is that you were starting the coroutine on start, not when you look at the target.

    Technically, your coroutine started the moment you called waypointTimerEvent() it just won't handle the next yield instruction till you wrap it in a Startcoroutine call. this means that when the scene started up it was immeadiately counting down the gaze timer even before you start looking at it. say if you don't look at it for 1 minute, the very frame that you do look at it you'll be teleported instantly cause the gaze timer had already counted down.

    and finally, internally you were starting up another gaze coroutine which was unneeded.

    also when possible (which is pretty much always) avoid starting and stopping a coroutine by string name. use a Coroutine variable as a handle to stop a coroutine. not only is the string version slower, its also more tedious to trace bugs through.
     
  15. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    Is there a reason you are starting your coroutine over within the IEnumerator?

    What object is this script on?
     
  16. P1505C

    P1505C

    Joined:
    Oct 12, 2017
    Posts:
    14
    Wow! Thank you. Not only did you post code that helped but you did other stuff to improve it. Thank you. I need to study what you did. The Vector3.up * etc etc especially. Thanks again.
     
  17. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    the Vector3 up was just code sugar it does the exact same thing yours did before ( well technically its more math internally...), it just more concise.
     
  18. OmarLG

    OmarLG

    Joined:
    Jun 17, 2018
    Posts:
    1
    That is exactly what i need for my problem. Restart a cooldown for my powerups. Thanks so a lot! thank you!!
     
  19. naviln

    naviln

    Joined:
    Oct 18, 2016
    Posts:
    32
    Awesome! thanks so much for this
     
  20. Summit_Peak

    Summit_Peak

    Joined:
    Nov 15, 2014
    Posts:
    3
    Try this to restart a coroutine:

    Code (CSharp):
    1.   public void RestartCoroutine(IEnumerator coroutine)
    2.   {
    3.     StopCoroutine(coroutine);
    4.     StartCoroutine(coroutine);
    5.   }
    6.  
    If this is your coroutine:

    Code (CSharp):
    1.   public IEnumerator PropagateWaves_Coroutine()
    2.   {
    3.  ...
    4.   }
    5.  
    Call it like this:

    Code (CSharp):
    1.   public void Propagate_Waves()
    2.   {
    3.     RestartCoroutine(PropagateWaves_Coroutine());
    4.   }
    5.  
    If the coroutine is not running, it will start. If the coroutine is currently running, it will stop and restart.
     
    Crazymonk101 likes this.
  21. MounirDev

    MounirDev

    Joined:
    Feb 24, 2019
    Posts:
    1
    Thanks dude :D
     
  22. jnlulejian

    jnlulejian

    Joined:
    Sep 9, 2018
    Posts:
    2
    JoshuaMcKenzie's version was the only solution in this thread that worked for, thanks! Since I didn't see it above I placed the "CoroutineExtensions" code in a separate file, wrapped the class declaration in "namespace CoroutineExtensionMethods {...}" and then used it in my file as "using CoroutineExtensionMethods;" to get it to work.

    For my case I had coroutine that waited before expiring a powerup for a player in my game. But I wanted to make it so that if a player collected the same powerup before the expiring coroutine finished it would reset the coroutine so the player would keep the powerup. I only needed to use "RestartCoroutine(...)" each time

     
  23. mitchmunro

    mitchmunro

    Joined:
    Jun 20, 2019
    Posts:
    15
    Yo, since I just spent some time understand and testing this, I thought I would post this. This is pretty much exactly what merkaba48 said but with more examples.

    Here are the personal notes I wrote after learning from this thread. You can test this by making UI buttons and linking them to StartButton, and StopButton.

    You can then start a Coroutine in a number of ways.

    String Method

    Code (CSharp):
    1. public void StartButton()
    2.     {
    3.         StartCoroutine("Count");
    4.     }
    5.  
    6.     public void StopButton()
    7.     {
    8.         StopCoroutine("Count");
    9.     }
    10.  
    11.     public IEnumerator Count()
    12.     {
    13.         Debug.Log("1");
    14.         yield return new WaitForSeconds(1);
    15.         Debug.Log("2");
    16.         yield return new WaitForSeconds(1);
    17.         Debug.Log("3");
    18.     }
    This will create a new instance of `Count` as a coroutine each time `StartCoroutine("Count")` is called. Unity keeps track of them, and then all the instances of `Count` are stopped when `StopCoroutine("Count")` is called.

    You do not have a reference to each individual instance of `Count` so cannot stop individual ones.

    IEnumerator Variable Method

    Code (CSharp):
    1. private IEnumerator testCoroutine;
    2.  
    3. private void Awake()
    4.     {
    5.         testCoroutine = Count();
    6.     }
    7.  
    8.     public void StartButton()
    9.     {
    10.         StartCoroutine(testCoroutine);
    11.     }
    12.  
    13.     public void StopButton()
    14.     {
    15.         StopCoroutine(testCoroutine);
    16.     }
    17.  
    18.     public IEnumerator Count()
    19.     {
    20.         Debug.Log("1");
    21.         yield return new WaitForSeconds(1);
    22.         Debug.Log("2");
    23.         yield return new WaitForSeconds(1);
    24.         Debug.Log("3");
    25.     }
    This method assigns a new instance of `Count` to the variable in `Awake` and then starts and stops that instance. It doesn’t create a new coroutine each time.

    HOWEVER! This method does not fully stop the coroutine, but rather pauses it. In the example above, if you stopped the coroutine after ‘2’ was printed, and then started it again, ‘3’ would be the next number to be printed, not ‘1’ as you might expect.

    Coroutine Variable Method (Best!)

    Code (CSharp):
    1.     Coroutine testCoroutine;
    2.  
    3.     public void StartButton()
    4.     {
    5.         testCoroutine = StartCoroutine(Count());
    6.     }
    7.  
    8.     public void StopButton()
    9.     {
    10.         StopCoroutine(testCoroutine);
    11.     }
    12.  
    13.     public IEnumerator Count()
    14.     {
    15.         Debug.Log("1");
    16.         yield return new WaitForSeconds(1);
    17.         Debug.Log("2");
    18.         yield return new WaitForSeconds(1);
    19.         Debug.Log("3");
    20.     }
    This method stores the whole coroutine started in a variable, rather than just the `IEnumerator` used to created the coroutine. This method will allow for proper starting and stopping of the coroutine, rather than just pausing.
     
    Tonymotion and NelsonChristensen like this.
  24. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,744
    nori0515 likes this.