Mihai Stan
Mihai Stan

Reputation: 153

Coroutine not starting

I'm using a lightning GameObject prefab to have a visual effect when i'm firing my weapon. When I fire, I enable that lightning object and I have a generic component that deactivates it after a certain delay.

The problem is that the "should wait" log is never reached, and instead of waiting the set delay, it waits much longer and doesn't actually deactivate the GameObject.

Here's the code for the DeactivateAfter component

public class DestroyAfter : MonoBehaviour {

    [SerializeField]
    private float delay;
    private bool firstRun = true;

    void OnEnable () {
        if (firstRun == false) {
            StartCoroutine(DestroyMethod());
        }
        firstRun = false;
    }

    public IEnumerator DestroyMethod() {
        Debug.LogFormat("Should wait; Time: {0}", Time.time);
        yield return new WaitForSeconds(delay);
        Debug.LogFormat("Should deactivate; Time: {0}", Time.time);
        gameObject.SetActive(false);
    }
}

Upvotes: 0

Views: 570

Answers (4)

derHugo
derHugo

Reputation: 90580

First Solution - Instantiate and Destroy with delay

If I understood you correctly the lightning GameObject is already a prefab you instantiate when firing and you want to destroy it after a delay.

So instead of a Coroutine solution and enabling or disabling the Object, this could be way simpler done using instantiate and destroy after as your script is actually called:

GameObject obj = GameObject.Instantiate(yourPrefab, position, rotation);    
GameObject.Destroy(obj, delay);

This function can be called from anywhere as long as you have yourGameObject provided to the calling class. So you don't even need an own class for this.

The second float parameter is a delay in seconds (see API)

For example you could have a shooting class like:

public class Shooting : MonoBehavior 
{

    [SerializeField]
    private GameObject lightningPrefab;

    [SerializeField]
    private float delay;

    public void OnShooting(Vector3 position, Quaternion rotation){
        GameObject obj = GameObject.Instantiate(lightningPrefab, position, rotation);

        /* Directly after instantiating the Object
         * you can already "mark" it to be destroyed after x seconds 
         * so you don't have to store it as variable anywhere */
        GameObject.Destroy(obj, delay); 
    }
}

You then also could add some check to not constantly spawn the prefab:

[...]

private GameObject lightningObject;

public void OnShooting(Vector3 position, Quaternion rotation){
    if(lightningObject == null ){
        lightningObject = GameObject.Instantiate(lightningPrefab, position, rotation);            

        GameObject.Destroy(lightningObject, delay);
    }
}

On this way there is always only one of your lightning objects a time.


Second Solution - Timeout

Another way without having to instantiate and destroy the object all the time would be a simple timeout instead of the Coroutine:

public class Shooting : MonoBehavior 
{
    [Tooltip("The lightning Object")]
    [SerializeField]
    private GameObject lightningObject;

    [Tooltip("The time in sec the lightningObject stays visible")]
    [SerializeField]
    private float visibleDelay;

    /* track the time that passed since the last OnShooting call */
    private float timePassed;

    /* Additional bool to not perform Update when not needed */
    private bool isVisible;

    /* Update is called once each frame */
    private void Update(){
        /* If the object is not visible do nothing */
        if (isVisible != true) return;

        /* update the timePassed */
        timePassed += Time.deltaTime;

        /* if delay passed since last OnShooting call 
         * deactiavte the object */
        if (timePassed >= visibleDelay){
            lightningObject.SetActive(false);
        }
    }

    public void OnShooting(){
        /* Activate the Object */
        lightningObject.SetActive(true);

        /* set isVisible */
        isVisible = true;            

        /* reset the timePassed */
        timePassed = 0;
    }
}

Upvotes: 1

joreldraw
joreldraw

Reputation: 1736

The condition never be true, you need to set firstRun condition to true first.

private bool firstRun = **true**;

void OnEnable () {
    if (firstRun == **true**) {
        StartCoroutine(DestroyMethod());
    }
    firstRun = false;
}

And i ever like to set flag first and later do what you want:

private bool firstRun = **true**;

void OnEnable () {
    if (firstRun == **true**) {
        firstRun = false;
        StartCoroutine(DestroyMethod());
    }    
}

Upvotes: 3

nipercop
nipercop

Reputation: 387

Hmm. Your code works fine in my project.

What i'm doing, some object have your script and I deactivate and then activate the gameObject when scene is playing.

Your Destroy Method is public. Check your project, maybe somewhere calls StopAllCoroutines() or StopCoroutine(DestroyMethod());

Upvotes: 0

Armin
Armin

Reputation: 599

I think you should use particle system for your weapon fire. Anyway I think your code is not working because you are deactivating the game object instead of deactivating the component. Activate and deactivate your component using something like this :

gameObject.GetComponent<YourComponent>().enabled = false;

Upvotes: 1

Related Questions