Search Unity

Check if coroutine is finished

Discussion in 'Scripting' started by Pedro_R, Dec 15, 2017.

  1. Pedro_R

    Pedro_R

    Joined:
    Dec 7, 2017
    Posts:
    45
    Hello,

    Is there a way to check if a coroutine is finished before continuing the code? I want to:

    - Run coroutine;
    - Wait for it to finish;
    - Destroy gameObject;

    Can anybody help me?

    Thanks!
     
  2. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,714
    Sure, destroy the game object inside the coroutine.. at the end ? :)
     
    johne5 likes this.
  3. johne5

    johne5

    Joined:
    Dec 4, 2011
    Posts:
    1,091
    Do you have the coroutine currently working?

    here is a sample if you don't
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class WaitForSecondsExample : MonoBehaviour
    5. {
    6.     void Start()
    7.     {
    8.         StartCoroutine(Example());
    9.     }
    10.  
    11.     IEnumerator Example()
    12.     {
    13.         print(Time.time);
    14.         yield return new WaitForSeconds(5);
    15.         print(Time.time);
    16.         Destroy(this.gameObject);
    17.     }
    18. }
     
  4. Pedro_R

    Pedro_R

    Joined:
    Dec 7, 2017
    Posts:
    45
    I know I could just destroy the gameObject at the end of the coroutine, but for my case it would be better to check if the started coroutine is already finished and then, if it is, destroy it.

    This is because I have a parent Powerup class that has an abstract Effect() coroutine, and all its children override it to do the desired effect. So instead of writing Destroy(gameObject) in all of the children, I wanted to just check if the coroutine is finished, destroy the gameObject from the parent, which is the one that starts the coroutine as well... The children just overrides the effect, but the parent starts it.

    Here is my code in case it got confusing:

    Parent:
    Code (CSharp):
    1.  
    2.     protected override void OnTriggerStay(Collider collider)
    3.     {
    4.         if (collider.CompareTag("Player") && collider.GetComponent<PlayerController>().isAlive)
    5.         {
    6.             // Some code...
    7.             PlayerController playerController = collider.gameObject.GetComponent<PlayerController>();
    8.             StartCoroutine(Effect(playerController));
    9.             // If (Coroutine is finished)
    10.             //       Destroy(gameObject);
    11.         }
    12.     }
    13.     public abstract IEnumerator Effect(PlayerController player);
    14.  
    Child (One of them):
    Code (CSharp):
    1.  
    2.     [SerializeField] float duration = 7f;
    3.  
    4.     public override IEnumerator Effect(PlayerController player)
    5.     {
    6.         player.isInvincible = true;
    7.         yield return new WaitForSeconds(duration);
    8.         player.isInvincible = false;
    9.         yield break;
    10.     }
    11.  
    Can anybody tell me if there's a better way to do this instead of writing Destroy(gameObject) in every child?

    Thanks!
     
  5. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,714
    In the parent class, you could have another coroutine that yields on the effect coroutine (to finish). From there, you could then destroy the game object?

    Edit: I notice your code example starts a coroutine during ontriggerstay. That will start many coroutines; that is probably not what you want?
     
  6. Pedro_R

    Pedro_R

    Joined:
    Dec 7, 2017
    Posts:
    45
    But the coroutine only starts when the player collides with the powerup, isn't that right?
     
  7. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    4,561
    On trigger stay means what it says. As long as it "stays inside", do something. You want OnTriggerEnter probably. As far as destroying an object from the parent, just pass in a callback method which gets called at the end of the coroutine. Then, when it ends, it can call the method on it's parent, passing itself as a param and says to destroy it.

    But, really, if that is all it does, just destroying in the child is perfectly fine.

    Otherwise, I don't understand why you hesitate to add a line of code to the scripts. Coroutines, at least to my best knowledge, don't have a way to inform you they are done. So while you can implement your own, they will still take extra code.
     
    iamvideep likes this.
  8. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,714
    Let us know if all's making sense/working :)
     
  9. Pedro_R

    Pedro_R

    Joined:
    Dec 7, 2017
    Posts:
    45
    Thanks for all the replies! The callback method thing makes sense, but I ended up just destroying the gameobject at the end of each coroutine. Also the reason I used OnTriggerStay() is because sometimes the player can spawn in a powerup, in which case the powerup should be collected instantly instead of requiring the player to leave the trigger and enter it again.

    Here is my final code if someone is interested:

    Parent:
    Code (CSharp):
    1.    
    2. protected override void OnTriggerStay(Collider collider)
    3.     {
    4.         if (collider.CompareTag("Player") && collider.GetComponent<PlayerController>().isAlive)
    5.         {
    6.             PlayerController playerController = collider.gameObject.GetComponent<PlayerController>();
    7.             // Some code for disabling components such as MeshRenderers, Colliders, etc...
    8.             StartCoroutine(Effect(playerController));
    9.         }
    10.     }
    11.  
    12.     public abstract IEnumerator Effect(PlayerController player);
    13.  
    Child:
    Code (CSharp):
    1.  
    2.     [SerializeField] float duration = 7f;
    3.  
    4.     public override IEnumerator Effect(PlayerController player)
    5.     {
    6.         // Some code to do the effect
    7.         yield return new WaitForSeconds(duration);
    8.         // Some code to undo the effect
    9.         Destroy(gameObject);
    10.         yield break;
    11.     }
    12.