I am trying to detonate a grenade (which is working) and then after a delay, remove the objects that are damaged from the scene, to clear up what the player is looking at. Because the delay needs to begin only once the grenade has exploded, I can’t use a timer set-up in the Update() function, as other solutions on here have suggested (like you can see I’m doing for the detonation countdown).
Invoke() also won’t work because I need to pass in the nearby object colliders as a parameter. I’ve been hunting a solution for a day now, but I just can’t make it work.
My code is here:
public class Grenade : MonoBehaviour
{
public float delay = 3f;
private float countdown;
private bool hasExploded = false;
public GameObject explosionEffect;
public float explosionForce;
public float damageRadius;
public float destroyDelayTime = 3.0f;
void Start()
{
countdown = delay;
}
void Update()
{
// start countdown
countdown -= Time.deltaTime;
// run explode method
if (countdown <= 0 && !hasExploded)
{
Explode();
hasExploded = true;
}
}
private void Explode()
{
// Show explosion effect (cut out to simplify my question, but works fine)
// Get nearby objects
Collider[] colliders = Physics.OverlapSphere(transform.position, damageRadius);
// Apply explosion force to each object (again cut out, but works fine)
// destroy grenade object
Destroy(gameObject);
Debug.Log("Grenade destroyed");
// destroy nearby objects after a delay
StartCoroutine(DelayObjectDestroy(colliders, destroyDelayTime));
}
IEnumerator DelayObjectDestroy(Collider[] colliders, float delay)
{
Debug.Log("DelayObjectDestroy called");
yield return new WaitForSeconds(3.0f); // will replace 3.0f with 'delay' once it is working
Debug.Log("Objects destroyed");
}
}
The Debug.Log("DelayObjectDestroy called"); line gets displayed in the console so I know the coroutine is indeed being called. The Debug.Log("Objects destroyed"); line doesn’t show in the console though, even after waiting a very long time.
There is no superclass other than MonoBehaviour, and no errors appear in the console.
What confuses me even more is that I made a fresh, plain script and attached it to the camera just to see if a coroutine would work, and it worked exactly as it should. Why does this code work but my grenade script doesn’t?
public class camera : MonoBehaviour
{
void Start()
{
StartCoroutine(meh());
}
private IEnumerator meh()
{
Debug.Log("Game started");
yield return new WaitForSeconds(8.0f);
Debug.Log("Game ended");
}
}
At start, "Game started" is displayed in the console. After an 8 second delay, "Game ended" is displayed in the console.
>Solution :
Because you call
Destroy(gameObject);
so your object which is running this coroutine doesn’t exist anymore => The routine executes everything to the first yield instruction.
After that it basically doesn’t exist anymore for the engine.
You either need to wait with destroying the grenade … or in your case I would simply introduce an always existing routine executer like e.g.
public class CoroutineRunner : MonoBehaviour
{
public static CoroutineRunner Instance { get; private set; }
private void Awake()
{
if(Instance && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
}
}
and then do
CoroutineRunner.Instance.StartCoroutine(DelayObjectDestroy(colliders, destroyDelayTime));
Note though: You still may not access any of the built-in properties and methods anymore which would require the grenade GameObject to still exist!