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.
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”);
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?
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
@OP: if all else fails, you can try doing this… maybe it works
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?
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.
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);
}
}
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.
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
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.
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 For now, I think I know what I need, thanks everyone for the help!