WaitForSeconds in C#

I just started using Unity and I am stuck on trying to use WaitForSeconds in C#.

I have searched and have found a ton of examples on how to do this. I can’t get any of them to work. I have tried this, this and this. There is no way this is that difficult so I must be doing some little thing wrong.

Here is my latest attempt:

    private void executeWait()
    {
        Debug.Log("wait start");
        StartCoroutine(Wait(20.0f));
        Debug.Log("wait end");
    }

    private IEnumerator Wait(float seconds)
    {
        Debug.Log("start waiting");
        yield return new WaitForSeconds(seconds);
        Debug.Log("waiting");
    }

Any suggestions are appreciated, Thanks.

It shouldn’t matter that I’m calling executeWait() from a OnControllerColliderHit() method should it? All the examples are starting a coroutine from within the Start() method. No matter what I have tried, it does not wait and it never calls the line Debug.Log(“waiting”);

here : an example

http://forum.unity3d.com/threads/59826-CodeSample-Countdown-Timer

Thanks for the example, I did manage to get WaitForSeconds to work for the first time using your example, but I am still confused on a few things. For example, I wrote this and attached it to an empty game object. It prints the button fine, but when I press it I get back immediate results for all the Debug.Log calls. It does not wait at all, not like I was expecting anyway.

using UnityEngine;
using System.Collections;

public class Countdown : MonoBehaviour
{
    void OnGUI()
    {
        GUILayout.BeginArea(new Rect(0, 0, Screen.width, Screen.height));

        if (GUILayout.Button((Texture2D)Resources.Load("blank", typeof(Texture2D)),
                GUILayout.Width(100)))
        {
            Debug.Log("Button Pressed");

            StartWait();
            Debug.Log("5 seconds are up");

            StartWait();
            Debug.Log("10 seconds are up");
        }

        GUILayout.EndArea();
    }

    IEnumerator StartWait()
    {
        yield return StartCoroutine(Wait());
    }

    IEnumerator Wait()
    {
        Debug.Log("waiting");
        yield return new WaitForSeconds(5);       
    }
}

Any idea why everything executes immediately and Debug.Log(“waiting”) never gets called?

Thanks

Hi, I think you are misunderstanding the concept a bit.

In your first example your code should look like :

 private void executeWait()
    {
        Debug.Log("wait start");
        StartCoroutine(Wait(20.0f));
        Debug.Log("This gets called after starting the Coroutine");
       
    }

    private IEnumerator Wait(float seconds)
    {
        Debug.Log("waiting");
        yield return new WaitForSeconds(seconds);
        Debug.Log("wait end");
    }

Hope this helps.


oxl

Not sure if it should be happening, but in VIsual Studio I’m somewhat coherced by the little red squiggly lines to do this:

void Start()
{
      StartCoroutine(DoStuff());
}

private IEnumerator<WaitForSeconds> DoStuff()
{
      yield return new WaitForSeconds();
      // do some stuff
}

No idea why, but VS wants that type parameter to be there… if you feed it your yield return type it gets happy and doesn’t bother you… but it doesn’t look right… especially since this is not in the docs :stuck_out_tongue:

@OP: if all else fails, you can try doing this… maybe it works :slight_smile:

Cheers

Oxl Thank you for the explanation, I think I understand how that works now.

As far as my seconed example though, I am not sure why it doesn’t work the way I think it should. I took the last example from the Unity Scripting reference under “Coroutines Yield” and changed it a littel to more fit my second example. This is what I came up with:

public class Countdown : MonoBehaviour
{
    void OnGUI()
    {
        GUILayout.BeginArea(new Rect(0, 0, Screen.width, Screen.height));

        if (GUILayout.Button((Texture2D)Resources.Load("blank", typeof(Texture2D)),
                GUILayout.Width(100)))
        {
            Debug.Log("Button Pressed");

            StartDo();
        }
        GUILayout.EndArea();
    }

    IEnumerator Do()
    {
        print("Do now");
        yield return new WaitForSeconds(2);
        print("Do 2 seconds later");
    }
    IEnumerator StartDo()
    {
        yield return StartCoroutine("Do");
        print("Also after 2 seconds");

        yield return StartCoroutine("Do");
        print("after 4 seconds");

        print("This is after the Do coroutine has finished execution");
    }
}

This example returns “Button Pressed” in the console, but nothing else. I have only been using Unity for about a week, so I’m guessing there is some concept I am just missing. Any idea why StartDo() doesn’t seem to execute at all?

Thanks for the suggestion, at this point I’m trying everything I can think of. It didn’t seem to help though.

Hmm, I had trouble too getting coroutines to start other coroutines… didn’t know about that yield return StartCoroutine() thing…

Try instead of passing the string name of the function, passing the function call itself… like this:

// code ommited

IEnumerator StartDo()
{
    yield return StartCoroutine( Do() ); 

    // more code ommited
}

Although the StartCoroutine function does have an overload that takes a string for the function to call, it’s never worked for me… what did work was passing the function call as the startcoroutine parameter.

Hope this helps

Cheers

Just tried it, the method still doesn’t seem to execute. Thanks though, keep the ideas coming, because I’m out :slight_smile:

You want this :

            Debug.Log("Button Pressed");

            StartCoroutine( StartDo() );


oxl

Thanks Oxl, that made it work.

I understand how this all works now, except for one thing. In this example everything works how I expect it to work, except for one thing. The “Final Message” statement gets printed immediately. Shouldn’t this wait until the Coroutine finishes? If not, how would I get it to function like that?

public class Countdown : MonoBehaviour
{
    void OnGUI()
    {
        GUILayout.BeginArea(new Rect(0, 0, Screen.width, Screen.height));

        if (GUILayout.Button((Texture2D)Resources.Load("blank", typeof(Texture2D)),
                GUILayout.Width(100)))
        {
            StartCoroutine(StartWait());

            print("Final Message");
        }

        GUILayout.EndArea();
    }

    IEnumerator StartWait()
    {       
        yield return StartCoroutine(Wait(5));
        print("Also after 5 seconds");

        yield return StartCoroutine(Wait(5));
        print("after 10 seconds");

        print("This is after the Do coroutine has finished execution");
    }

    IEnumerator Wait(float seconds)
    {
        yield return new WaitForSeconds(seconds);
    }
}

No, its supposed to work like this. The end of the Coroutine is after the yield return new WaitForSeconds();

Before its getting too crazy, why you are not using InvokeRepeating() to count something down ?

It seems more suitable for what you are trying to achieve.


oxl

I’m actually not trying to count down anything I just want to
-DoSomething()
-Wait two seconds
-DoSomethingElse()

In the example you sent earlier you added “This gets called after starting the Coroutine”, how do I delay that action so it waits until the Coroutine is finished? In Javascript it looks like it is as simple as “yield WaitForSeconds (2);”, however in C# you have to run WaitforSeconds inside a Coroutine, then I would have to have what I wanted to execute after the wait inside the coroutine.

I hope that makes sense. Thanks again for the help.

Look at my first example again. You must insert the code, that should be executed after 2 seconds, into the Wait() function after the yield statemant :slight_smile:


oxl

I think that solution will work, but it’s not a very good solution. It creates ugly code where I have to pass around local variables just to get a pause. I also found this post http://forum.unity3d.com/threads/64874-Don-t-understand-Yield-amp-Coroutines-in-C which may contain a better solution. The final post mentions something about callback functions,but I have not looked into what they are or how they work yet.

TimB, don’t abandon hope. Coroutines take some grappling with to understand but they are hugely powerful and are a good way to increase performance of your code.

Here’s a crucial bit of info:

As far as I can tell, StartCoroutine is a Unity method that handles a stack of Coroutines to iterate through each frame. It’s not magic :slight_smile:

BTW, here’s how I would do it.

public class Countdown : MonoBehaviour
{
    int count = 3;
    void OnGUI()
    {
        GUILayout.BeginArea(new Rect(100, 0, Screen.width, Screen.height));

        if (GUILayout.Button(""+count,
                GUILayout.Width(100)))
        {
           StartCoroutine(DoCountdown());
        }

        GUILayout.EndArea();
    }

    IEnumerator DoCountdown()
    {   
		while (count > 0) {
       	            yield return new WaitForSeconds(5);
		    count--;
		}
		Debug.Log("Countdown done");
		yield return null;
    }

}

You might need to add some logic to prevent multiple button presses after it start counting, but that should be easy.

Also, see here:
http://unity3d.com/support/documentation/Manual/Execution%20Order.html

That was a crucial bit of info, Thanks! That fixed my initial problem.

I am still not really happy with how you have to do a wait in c#, you have to create a separate method that runs the wait:

    IEnumerator Wait()
    {
        yield return new WaitForSeconds(2);
    }

Then you have to call that method with a:

yield return StartCoroutine(Wait());

So, whatever method you run the StartCoroutine from, must have a return value of IEnumerator. This makes it difficult to do a wait inside a method that can’t return and IEnumerator, for example OnGUI. In the following example I am not sure how you would ever get “Print Last” to print after the Coroutine has finished.

public class TestCoroutine : MonoBehaviour
{
    void OnGUI()
    {
        GUILayout.BeginArea(new Rect(0, 0, Screen.width, Screen.height));

        if (GUILayout.Button((Texture2D)Resources.Load("blank", typeof(Texture2D)),
                GUILayout.Width(100)))
        {
            // Do stuff in my Coroutine
            StartCoroutine(StartWait());

            // Print after my Coroutine has finished
            print("Print Last");
        }

        GUILayout.EndArea();
    }

    IEnumerator StartWait()
    {
        yield return StartCoroutine(Wait(5));
        print("Print after 5 seconds");
    }

    IEnumerator Wait(float seconds)
    {
        yield return new WaitForSeconds(seconds);
    }
}

I’m sure I am making this harder than it is, I tend to do that :smile: For now, I think I know what I need, thanks everyone for the help!

Thanks a lot. If we meet in real life some day I owe you a beer.