Coroutine's WaitForSeconds not firing

I’m attempting to create a destroy and respawn script for when a gameobject touches a certain collider; however, the WaitForSeconds function doesn’t call in the IEnumerator method. I’ve tried just testing the WaitForSeconds on its own along with debug prints and it works as intended:

    IEnumerator Wait()
    {
        print("Waiting");
        yield return new WaitForSeconds(3f);
        print("Waited");
    }

but if I include other code, even a call to another method:

 IEnumerator Wait()
    {
        // This prints
        print("Waiting");

        // No code after this point is run
        yield return new WaitForSeconds(3f);
        print("Waited");
        DestroyAndRespawn();
    }

nothing after the WaitForSeconds function is run.

Here’s my script; I don’t believe I’m doing anything wrong since I’ve read the Coroutine and WaitForSeconds documentation along with multiple Unity forum posts on this topic, but maybe I have in fact made such a simple mistake that nobody would even think to mention it.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CauldronCrafting : MonoBehaviour
{
    Rigidbody2D objectBody;
    BoxCollider2D cauldronCollider;
    public GameObject ingredientObject;

    void Start()
    {
        objectBody = GetComponent<Rigidbody2D>();
        cauldronCollider = GameObject.Find("CauldronCollision").GetComponent<BoxCollider2D>();
    }

    IEnumerator DestroyAndRespawn()
    {
        // These lines behave as expected
        Destroy(objectBody.gameObject);
        print("Ingredient destroyed");

        // Nothing after this code is run
        yield return new WaitForSeconds(3f);
        // Nothing after this code is run

        GameObject newIngredient = Instantiate(ingredientObject, new Vector3(5, 3, 0), Quaternion.identity);
        newIngredient.GetComponent<BoxCollider2D>().enabled = true;
        newIngredient.GetComponent<DragScript>().enabled = true;
        newIngredient.GetComponent<CauldronCrafting>().enabled = true;
        newIngredient.name = ingredientObject.name;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision == cauldronCollider)
        {
            // This is called when intended
            StartCoroutine(DestroyAndRespawn());
        }
    }

}

Side note: When I put a call to a similar coroutine into the Start method, it ran continuously spawning new GameObjects every 3 seconds as specified – e.g. the WaitForSeconds function worked. :eyes:

You may not create gameobjects outside the main thread

coroutines are on the main thread and you can still create and destroy objects within them

so i see a destroy in the coroutine, is that destroying the same object that started the coroutine? The object that starts a coroutine must be alive and enabled for the lifetime of the coroutine.

This isn’t my problem – I’m perfectly able to instantiate gameobjects within coroutines, but when I add the WaitForSeconds yieldrequest, nothing after the WaitForSeconds call will operate correctly. Thanks for the post, though.

your issue is because you destroy the object running the coroutine

I believe it is, although what started the coroutine was the OnCollisionEnter2D event, I can’t tell if that means the object started it or not. I’ll try your suggestion!

yeah what happens is coroutines are executed by the object that started them, so once you destroy that object nothing the other side of a yield instruction will execute, disabling the object that started it would have the same effect.

you can work around this, but using the StartCoroutine method from a other object

Edit: Sorry, I’ve just now seen your post. I’ll get to work trying to separate the Respawn bit into another game object. Thank you!

I’ve attempted to separate the Destroy and Respawn parts of the coroutine. The Destroy function is now called inside the OnTriggerEnter2D event, and the Respawn coroutine is started afterward. This does not respawn the gameobject, but does destroy it.

 IEnumerator Respawn()
    {
        // Nothing after this code is run
        yield return new WaitForSeconds(3f);
        // Nothing after this code is run

        GameObject newIngredient = Instantiate(ingredientObject, new Vector3(5, 3, 0), Quaternion.identity);
        newIngredient.GetComponent<BoxCollider2D>().enabled = true;
        newIngredient.GetComponent<DragScript>().enabled = true;
        newIngredient.GetComponent<CauldronCrafting>().enabled = true;
        newIngredient.name = ingredientObject.name;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision == cauldronCollider)
        {
            Destroy(objectBody.gameObject);
            print("Ingredient destroyed");
            StartCoroutine(Respawn());
        }
    }

when i say what started the coroutine i mean the thing that has had its StartCoroutine method called, if that object gets disabled or destroyed the coroutine stops. Think you are going to want to move this logic to a other object that manages the destruction and respawn.

It worked, thank you tons. I probably would have figured this out only after weeks of experimenting. Cheers!

Alternatively you can simply move the object to a location in the world that the player can’t access when it is to be removed and then move it back when it is due to be respawned. To stop it from receiving events like Update() you can simply add a “spawned” boolean and an if condition then set it to true/false as needed.