Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Stopping specific Coroutine

Discussion in 'Scripting' started by Feretro, Nov 28, 2019.

  1. Feretro

    Feretro

    Joined:
    Apr 18, 2017
    Posts:
    57
    Hello everyone!
    Sorry, I did not understand how to stop specific coroutines ... I tried to read and watch tutorials but I still can't figure it out.

    I thought it was a simple task to delay my enemy turning left or right! :-O

    As I read I can't just:
    StartCoroutine(MyCoroutine(2f));
    and:
    StopCoroutine(MyCoroutine(2f));

    I did like this:

    Code (CSharp):
    1. ...
    2. ...
    3. ...
    4.  
    5. public IEnumerator turnEnemyCoroutine;
    6.  
    7. private void Start() {
    8.         turnEnemyCoroutine = TurnEnemy(2f);
    9. }
    10.  
    11.  
    And then ... when needed (Player position is > or < than enemy position ...)

    Code (CSharp):
    1.  if (playerPosition.x > enemyPosition.x)
    2.         {
    3.             tempEnemyScale.x = Mathf.Abs(tempEnemyScale.x);
    4.             justTurned = false;
    5.             faceRight = true;
    6.             StartCoroutine(turnEnemyCoroutine);
    7.         }
    And this was (in my mind) a simple coroutine:

    Code (CSharp):
    1. public IEnumerator TurnEnemy(float waitForTurning) {
    2.         Debug.Log("Start");
    3.         justTurned = true;
    4.         yield return new WaitForSeconds(waitForTurning);
    5.         transform.localScale = tempEnemyScale;
    6.         Debug.Log("Stop");
    7.         StopCoroutine(turnEnemyCoroutine);
    8.         yield break;
    I also randomly (after reading something around) added that "yield break" but watching at that Debug Start and Stop keeps running and ... of course my trick does not work!

    My idea was to wait for 2 seconds then turn enemy stopping this routine waiting for the next time!

    what is the right way to stop a coroutine?

    Thanks!
     
  2. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,315
    Code (CSharp):
    1.  
    2. // field declaration
    3. private Coroutine turnEnemyCo;
    4.  
    5. // when you start the coroutine
    6. turnEnemyCo = StartCoroutine( TurnEnemy( 2f ) );
    7.  
    8. // elsewhere when you want to stop the coroutine
    9. if( turnEnemyCo != null ) StopCoroutine( turnEnemyCo );
     
  3. Feretro

    Feretro

    Joined:
    Apr 18, 2017
    Posts:
    57
    Thank you Madgvox!
    I changed my script as you suggested but It does not stop! On starting it works ... enemy waits two seconds then he turns, but ... then it turns as soon I change my position (left or right of him)

    Declaration:

    Code (CSharp):
    1.     private Coroutine turnEnemyCoroutine;
    2.  
    Piece of Method

    Code (CSharp):
    1. if (playerPosition.x > enemyPosition.x)
    2.         {
    3.             tempEnemyScale.x = Mathf.Abs(tempEnemyScale.x);
    4.             justTurned = false;
    5.             faceRight = true;
    6.             turnEnemyCoroutine = StartCoroutine(TurnEnemy (2f)); // Suggested Code
    7.         }
    Coroutine itself:

    Code (CSharp):
    1.  public IEnumerator TurnEnemy(float waitForTurning) {
    2.         Debug.Log("Start");
    3.         justTurned = true;
    4.         yield return new WaitForSeconds(2);
    5.         transform.localScale = tempEnemyScale;
    6.         Debug.Log("Stop");
    7.         if (turnEnemyCoroutine != null) { StopCoroutine(turnEnemyCoroutine);  // Suggested Code}
    Logs con console still keep increasing!
     
  4. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,745
    Sometimes it might be handy to have an ability to perform stop actions from inside of coroutine. For example, you might want stop some sfx/vfx created inside that coroutine before finishing it. You cant use try...finally within coroutine. Here's another way to stop coroutines

    Code (CSharp):
    1. private bool exitRoutine = false;
    2.  
    3. private IEnumerator RunnigRoutine(){
    4.  
    5.   exitRoutine = false;
    6.  
    7.   while(true) {
    8.      routineActions();
    9.      yield return routineWait;
    10.  
    11.     if(stopRoutine) {
    12.       stopRoutineActions();
    13.       break;
    14.    }
    15.   }
    16. }
     
  5. Feretro

    Feretro

    Joined:
    Apr 18, 2017
    Posts:
    57
    Sorry Palex ... I did not understand! I still can't stop that coroutine! I can't understand why it does not stop if I say stop!
     
  6. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,745
    It stops if you say stop. But! If you start playing sound or animation in that coroutine, that sound or animation will not stop when you call StopCoroutine. The code I posted allows you to stop something else when coroutine is about to exit.
     
  7. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,580
    I suggest avoiding coroutines when possible. They will give more headache to new developers than it is worth.
    Coroutines are not magic tools. Most things can be achieved without them. Don't fall into trap, of spamming coroutines, whenever possible.
     
  8. pzoghbi

    pzoghbi

    Joined:
    Oct 8, 2019
    Posts:
    7
    @Antypodish Your suggestion isn't quite a good one. You suggest "avoid coroutines when possible" without giving any details, reasoning, proper evidence, or at least suggesting an alternative. Even three years later, your reply brings no value for many people that have googled to this point, including myself.
     
    GUIVASCONCELOS likes this.
  9. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,196
    So why did you necro the post to complain, bringing the bad advice to the front page, instead of just letting the bad advice be bad alone forever?
     
  10. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,531
    Well, how do you know if the suggestion is a good one or not without understanding the reason? ^^. Yes, he could have explained why, but than this post would be a lot longer because it's not as simple as it may seem.

    Don't get me wrong, I do like coroutines a lot and I use them a lot, so I don't think the general advice to avoid them at all cost as some people put is a good advice. Though he specifically said for "new developers" and I partially agree here. It's very easy to use coroutines the wrong way if you don't understand how they work. In this example here the OP most likely starts a new coroutine every frame as long as the condition is true. Manually stopping a coroutine at the end of the coroutine is also completely pointless.

    If you want to know more about how coroutines work, I can suggest you read through my coroutine crash course. Reading it carefully should tell you almost everything there is to know about coroutines.

    The reasons why beginners may want to avoid using coroutines is:
    • They may not understand the basic concept of the coroutine scheduler. Some think they have to call StartCoroutine every frame to keep it going, this is not the case. Whenever you call StartCoroutine you start a new coroutine.
    • Many do not understand the relationship between the concept of a coroutine and the IEnumerator statemachine. You can not simply re-use an IEnumerator instance as it represents a statemachine. Passing the same IEnumerator instance two or more times to StartCoroutine would result in pure chaos as the scheduler would advance the statemachine two times seperately from each other.
    • Generator methods (methods with a yield statement in them) do allocate memory as they actually create a statemachine object. That's why it's not a good idea to call StartCoroutine every frame or quite often in general. Coroutines are most memory friendly if they just keep running.
    • Coroutines seem like they work like independent threads of execution, but they are not. Coroutines run synchronously but can be paused at the yield statements and resumed at a later point in time. This later point is determined by the coroutine scheduler based on the value that was yielded.
    So the code provided by the OP clearly shows he didn't know anything about coroutines at all and therefore got confused how to use them. Though from the OP it's not even clear what this coroutine should do, so it's quite difficult to suggest an "alternative" when the actual question is not clear. This is again kind of an XY problem which appears quite often here on the forum.

    He talks about delaying the turning of his enemy. It's not clear what he actually means by that and based on what event that turning should happen. Since the coroutine sets the localScale and doesn't seem to have anything to do with actual turning in the sense or rotation, I would guess he talks about a 2d game and he wants to mirror the character so its facing in the opposite direction. To me that sounds more like he wants the enemy to only evaluate a new direction to move every couple of seconds so when it went past the player it does not immediately follow the player. Though this is all just guesswork based on the fragments we see. The current approach of checking if the enemy went past the player every frame and somehow delaying the reaction is just a bad approach to begin with. What if that state changes within the timeframe? Maybe even a couple of times. Fos such mechanics it makes more sense to simply evaluate where to go in a fix timeinterval. This would not only reduce the amount of checks you have to do each frame, but also gets rid of all those issues. This is essentially quite similar how the AI of minecraft zombies work. They choose a target and move towards it and it takes a bit of time until they change direction when the target moved away.

    It's still possible to use a coroutine for this. As I mentioned above, an endless running coroutine that is started once in Start. Though it's also possible to use InvokeRepeating or just an ordinary timer (float variable that is incremented or decremented each frame by deltaTime).

    I hope this has cleared up a bit of the confusion and why the best answer is often: Well, it's complicated.
     
    ProductiveHippo, Bocci and howler123 like this.