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.
CodePudding user response:
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!