简体   繁体   中英

Unity - WaitForSeconds() does not work

I am developing some flight simulator programme. Right now I am trying to get the level to restart after a delay of 4 seconds, but instead it is waiting forever and not responding. This is my code (in C#) right now:

void Update () {

    //other irrelevant code in front

    float TerrainHeightLocation = Terrain.activeTerrain.SampleHeight (transform.position);

    if (TerrainHeightLocation > transform.position.y) { //the plane crashed

        StartCoroutine(Wait(TerrainHeightLocation));


    }
}

IEnumerator Wait( float TerrainHeightLocation) {
    transform.position = new Vector3 (transform.position.x,
                                      TerrainHeightLocation,
                                      transform.position.z);
    Instantiate(explosion, transform.position, transform.rotation);
    Destroy(gameObject);

    Debug.Log ("Waiting");
    yield return new WaitForSeconds(4.0f); // waits x seconds

    Debug.Log ("Waited for x seconds");

    Application.LoadLevel(Application.loadedLevel);
    Debug.Log ("Reloaded level");

}

It would be a great help if anyone could find a problem in the code, because I have been trying all day and I cannot fix it.

Thanks!

Typically when WaitForSeconds doesn't work it's one of two things:

  1. Time.timeScale is set to 0.
  2. The object is being destroyed or made inactive before the time is up.

In your case you are destroying the very game object that runs the Coroutine before you call yield WaitForSeconds. Destroy is deferred until the end of the frame so the code up until the yield will execute, but then the GameObject & MonoBehaviour will be destroyed and the Coroutine along with it.

Solution: use a Coroutine on an object that persists between the level ending and restarting. To make an object persist even when a level is loaded (as you are doing in your code) you need to call DontDestroyOnLoad .

The problem with your code is you are destroying the very gameobject from which this coroutine is called. So when you call destroy on your game object that will stop running coroutine (at least that should be happening here). The solution to this problem is instead of destroying the gameobject you can always disable its renderer and colliders. Then re enable them when your new level is loaded. So you will not need to destroy the object.

In the case of your scene it looks like it has been predefined .If your scene has initialised objects from default then just hide the object which needs to be destroyed. I think that should solve your problem. The load level will destroy all the objects in scene and reload them.

     IEnumerator Wait( float TerrainHeightLocation) {
        transform.position = new Vector3 (transform.position.x,
                                          TerrainHeightLocation,
                                          transform.position.z);
        Instantiate(explosion, transform.position, transform.rotation);
        //Hide the game object renderer and colliders here instead of destroying it
        //as this is the script which controls your coroutine execution.
        gameObject.renderer.enabled = false;
        gameObject.collider.enabled = false;

        //Make sure your game object has required renderer and collider or else the           
        //above code will give errors as it wont be able to find them

        Debug.Log ("Waiting");
        yield return new WaitForSeconds(4.0f); // waits x seconds

        Debug.Log ("Waited for x seconds");

        Application.LoadLevel(Application.loadedLevel);
        Debug.Log ("Reloaded level");

    }

Similer Question

I still don't understand why would people still consider using co routine in a simple cases. I would suggest only to use co routine as if you're checking something on server. Making co routine calls with other functions.

But please refrain from using co routine calls when you just want to "WAIT" <- that is the key word. Wait not co routine on the function.

If your goal is to restart the game by using Application.Load then use this.

public void MyRestart(){

   Application.LoadLevel("MyLevel");
   // Put your codes where ever in this method.
}

Then just use Invoke Method

Invoke ("MyRestart", 4);

//This will call MyRestart Function after 4 seconds.

bool isDead = false;

void Update () //Update calls your function every frame. So we should call IEnumerator for one time. I will use bool check for this.
{

    //other irrelevant code in front

    float TerrainHeightLocation = Terrain.activeTerrain.SampleHeight (transform.position);

    if ( (TerrainHeightLocation > transform.position.y) && !isDead ) { //the plane crashed

        isDead = true;
        InstantiateFunc( TerrainHeightLocation );
        StartCoroutine(Death);        


    }
}

void InstantiateFunc( float TerrainHeightLocation) {
    transform.position = new Vector3 (transform.position.x,
                                      TerrainHeightLocation,
                                      transform.position.z);
    Instantiate(explosion, transform.position, transform.rotation);
    Destroy(gameObject);

    Debug.Log ("Waiting");

}

IEnumerator Death{
yield return new WaitForSeconds(4.0f); // waits x seconds

        Debug.Log ("Waited for x seconds");

        Application.LoadLevel(Application.loadedLevel);
        Debug.Log ("Reloaded level");
}

I have not tested, I hope it works

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM