Search Unity

Coroutine WaitForSeconds does not work after destroying an object

Discussion in 'Getting Started' started by Michal_the, Aug 8, 2019.

  1. Michal_the

    Michal_the

    Joined:
    Jul 9, 2019
    Posts:
    11
    Hello,

    I am working on a simple Space Invaders clone and when the player gets hit by enemy bullet, I want to wait 3 seconds and then load the Menu scene. I am using coroutine for this and this is how my script for an enemyBullet looks like. The problem is that when the player collides with a bullet, the SceneManager does not wait 3 seconds but loads a menu scene immediately. Why is that? Thank you very much

    Code (CSharp):
    1.   private void OnTriggerEnter2D(Collider2D collision)
    2.  
    3. IEnumerator restartWaiter()
    4.     {
    5.  
    6.         yield return new WaitForSeconds(3.0f);
    7.  
    8.     }
    9.    
    10.  
    11. {
    12.         if (collision.gameObject.tag == "Player")
    13.         {
    14.  
    15.             Destroy(this.gameObject);
    16.             Destroy(collision.gameObject);
    17.             restartLevel();
    18.         }
    19.  
    20. private void restartLevel()
    21.     {
    22.         StartCoroutine(restartWaiter());
    23.         SceneManager.LoadScene("Menu");
    24.     }
    25.  
    26.  
    27.  }
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,205
    It's because a Coroutine belongs to the game object that starts it. If you destroy the object, you destroy the Coroutine.

    This really isn't something your bullets should be doing anyway. Make a GameManager, and have bullets (or the things they hit) inform the GameManager of what's happened, and let that do stuff like load the next scene. Keep all your game-manager-y logic in one place, not spread out among a bunch of unrelated scripts.
     
    Ryiah likes this.
  3. Michal_the

    Michal_the

    Joined:
    Jul 9, 2019
    Posts:
    11
    Thank you, that makes sense now :)
     
  4. Michal_the

    Michal_the

    Joined:
    Jul 9, 2019
    Posts:
    11
    So I tried to do as you said, and still no luck. The SceneManager loads the level instantly and still does not wait 3 seconds before doing so. This is my GameManager script:

    Code (CSharp):
    1.  IEnumerator restartWaiter()
    2.     {
    3.         yield return new WaitForSeconds(3.0f);
    4.     }
    5.  
    6. public void restartLevel()
    7.     {
    8.         StartCoroutine(restartWaiter());
    9.         SceneManager.LoadScene("Menu");
    10. }
    And this is a part of my enemyBullet script:

    Code (CSharp):
    1. public class enemyBullet : MonoBehaviour
    2. {
    3.     public GameManager gameManager;
    4.  
    5.     // Start is called before the first frame update
    6.     void Start()
    7.     {
    8.         gameManager = FindObjectOfType<GameManager>();
    9.     }
    10.  
    11.     private void OnTriggerEnter2D(Collider2D collision)
    12.     {
    13.         if (collision.gameObject.tag == "Player")
    14.         {
    15.  
    16.             Destroy(this.gameObject);
    17.             Destroy(collision.gameObject);
    18.  
    19.             gameManager.restartLevel();
    20.         }
    21.  
    22.     }
    23.  
    24.  
    25. }
    26.  
    27.  
     
  5. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,205
    Code (csharp):
    1.  
    2.        StartCoroutine(restartWaiter());
    3.  
    This line starts a coroutine, which will sort of spawn off and do its own thing, while the code that started it continues right on to the next line:

    Code (csharp):
    1.  
    2.         SceneManager.LoadScene("Menu");
    3.  
    ...which immediately loads the Menu scene. Indeed, since your restartWaiter() method does nothing but wait, it is a useless method that could never have any effect at all.

    If you don't understand Coroutines (and in most cases, even when you do), then I don't recommend using them. You'd probably be better off using Invoke with the delay (time) parameter, which is much easier to understand.

    But if you feel you must use a Coroutine, then your coroutine method needs to both wait and do the LoadScene. And the code that starts the coroutine should start it, and do nothing else.
     
    unity_Es-Wss80ur6d5A and Ryiah like this.
  6. Michal_the

    Michal_the

    Joined:
    Jul 9, 2019
    Posts:
    11
    Thank you, I understand and it works perfectly now. Also I will take a look at invoke.
     
    JoeStrout likes this.
  7. unity_Es-Wss80ur6d5A

    unity_Es-Wss80ur6d5A

    Joined:
    Aug 13, 2019
    Posts:
    4
    solved my issue with non-working coroutine involving an object that called the coroutine after being deleted.

    I used invoke in the player class instead of my other object class (the object that was getting destroyed)
    Thanks