Unity WaitForSeconds waits more than expected - c#

When I complete the level, it must show 'Level Completed!" text and wait for 5 seconds then start next level.
private void showSuccess() {
levelCompleted.SetActive (true);
StartCoroutine("waitForNextLevel");
}
IEnumerator waitForNextLevel() {
Debug.Log ("Start time: " + Time.time);
yield return new WaitForSeconds (5);
Debug.Log ("End time: " + Time.time);
prepareTheLevel ();
}
However, the text appears successfully, the game waits for 5 seconds, and the text dissappears. Then it waits another 5 seconds and start the next level.
I want it wait only 5 seconds without hiding the text.
Any ideas?

It sounds like you have something along the lines of:
if (playerScore > winScore)
showSuccess();
on a game object somewhere.
If this is the case, showSuccess is being called every frame, and each time it creates a new coroutine that starts the next level in 5 seconds. When the first one finishes, the level is destroyed (which removes the text) and the next level starts loading - but you have 5 seconds worth of coroutines stacked up, so roughly every frame it's calling prepareTheLevel again until you run out of active coroutines.
Your solution to guard it with a bool is pretty close to what you should do, but misses the underlying problem of stacking up 5 seconds worth of coroutines - I'd recommend guarding the call to StartCoroutine instead - you could even use the active flag on the levelCompleted text, i.e:
private void showSuccess() {
if (levelCompleted.IsActive() == false) {
levelCompleted.SetActive (true);
StartCoroutine("waitForNextLevel");
}
}
This will make it so the second call to showSuccess will see that levelCompleted is active, and do nothing, leaving you with only one waitForNextLevel coroutine like you want.

Okay I've solved my issue as always. However it is not satisfying for me. I saw Unity executes the code below the WaitForLevel method over and over for 5 seconds. Why does it do this like that? :S
The code below solved my issue
private bool isWaited = false;
IEnumerator waitForNextLevel() {
Debug.Log ("Start time: " + Time.time);
yield return new WaitForSeconds (5);
Debug.Log ("End time: " + Time.time);
if (!isWaited) {
prepareTheLevel ();
isWaited = true;
}
}

Related

How to wait a certain amount of time without freezing code in C#/Unity?

I'm making a practice game to get used to coding where you have to shoot a bird. When you run out of bullets, you need to press the 'r' key to reload your bullets. I want there to be a delay between when the button is pressed and when the bullets reload, but so far what I found is code that freezes everything (shown below). Is there a way to keep the code from freezing everything?
Summary: The code below freezes everything (the whole game) when pressing the 'r' button. Is there code I can use that won't freeze everything and will only wait 2 seconds before running the next action?
IEnumerator TimerRoutine()
{
if (Input.GetKeyDown(KeyCode.R))
{
yield return new WaitForSeconds(2); //Fix this, freezes everything
activeBullets = 0;
}
}
Use Coroutines To set This delay
if (Input.GetKeyDown(KeyCode.R) && isDelayDone) // defined isDelayDone as private bool = true;
{
// When you press the Key
isDelayDone = false;
StartCoroutine(Delay());
IEnumerator Delay()
{
yield return new WaitForSeconds(2);
isDelayDone = true;
activeBullets = 0;
}
}
Your problem is that you are waiting 2 seconds after the key press but not waiting for the actual key event it.
Here is a modified version of your method doing what you want.
IEnumerator TimerRoutine()
{
while(activeBullets == 0) // Use the bullets value to check if its been reloaded
{
if (Input.GetKeyDown(KeyCode.R)) // Key event check each frame
{
// Key event fired so wait 2 seconds before reloading the bullets and exiting the Coroutine
yield return new WaitForSeconds(2);
activeBullets = reloadBulletsAmount;
break;
}
yield return null; // Use null for the time value so it waits each frame independant of how long it is
}
}
(I know this has an accepted answer I just feel this method would be better)

while conditional inside coroutine does not loop, hangs

I have a coroutine that I want to contain a 2 second pause. I use a while loop that checks the diff between initialTime and now to check how long it's been:
IEnumerator MyCoroutine(){
Debug.Log("Before the counter");
//`Time.time` returns the number of seconds since app launch (i.e., `72.33448`)
float initialTime = Time.time;
Debug.Log("initialTime = " + initialTime);
float now = initialTime;
while(now - initialTime < 2.0f){
yield return null;
now = Time.time;
Debug.Log("now = " + now);
}
Debug.Log("After the counter");
//...Stuff that happens after delay
}
For some reason, about 1/5 times I run it, it will not exactly hang in the loop, but will fail to execute the entire coroutine: in the console I see that Debug.Log("now = " + now); only executes once and Debug.Log("After the counter"); never happens— I'd expect a proper while loop hang to print Debug.Log("now = " + now); infinitely.
What could be wrong with the logic around this timer that could cause this behavior?
Edit: I'd prefer to stick to Unity's norms of using StartCoroutine() and StopCoroutine() rather than System.Threading if possible.
The solution is fairly simple.
First off, you NEED an IEnumerator like you had, not an IEnumerable as was suggested in the comments. Very different things.
Second, Unity has a built in function that pauses your coroutine for how long you specify. Use either WaitForSeconds(2f) (2 being the amount of seconds) or WaitForSecondsRealtime(2f)
WaitForSeconds "waits" for 2 seconds while taking framerate into account.
WaitForSecondsRealTIme "waits" for 2 seconds without taking framerate into account.
IEnumerator MyCoroutine(){
Debug.Log("Before the counter");
yield return new WaitForSeconds(2f);
Debug.Log("After the counter");
//...Stuff that happens after delay
}

Second Coroutine isn't working Unity

I am making 2D game and want to cause delay of about 3 sec after player's all lives ran out. I tried to implement Coroutine method before the scene start all over again but it doesn't work.
I have already implemented Coroutine method for each time my player falls of a cliff and respawn back to its position. And it works like a charm.
public void Respawner()
{
StartCoroutine("RespawnCoroutine");
}
// Coroutine Delay of 2 sec for each time player Respawn
public IEnumerator RespawnCoroutine()
{
classobj.gameObject.SetActive(false);
yield return new WaitForSeconds(respawnDelaySec);
classobj.transform.position = classobj.respawnPoint;
classobj.gameObject.SetActive(true);
}
public void ReduceLives()
{
if (lives <= 3 && lives >= 2)
{
lives--;
live_text.text = "Remaining Live " + lives;
}
else
{
StartCoroutine("RestartScene1");
}
}
public IEnumerable RestartScene1()
{
yield return new WaitForSeconds(RestartSceneDelaySec);
SceneManager.LoadScene("demo2");
}
here is no error on console window but SceneManager.LoadScene("demo2"); is never called and the player is respawning each time after i die and after 1 life remaining
The issue with your second coroutine is..
You have mistakenly used "IEnumerable" instead "IEnumerator", make it change to "IEnumerator" and it will work..
You should not call SceneManager.LoadScene("demo2"); after StartCoroutine("RestartScene1");.
StartCoroutine("RestartScene1"); this code you can say is an asynchronous code. It is called, and the execution of program keeps going forward (the execution does not await here). You should call the code you want to delay inside that Coroutine after yielding.
Small exampel:
public void SomeFunction()
{
StartCoroutine("RestartScene1");
// The code here will **not** be delayed
}
public IEnumerable RestartScene1()
{
yield return new WaitForSeconds(RestartSceneDelaySec);
// The code here will be delayed
}

InvokeRepeating didn't work well with Update

I have met a problem about the function InvokeRepeating(), they didn't work in parallel. I use a for loop in Update() to go through an array of 100 float and compare the figures in InvokeRepeating() every 1 second. If the figure given is less than that one in Update(), it will be shown in console.
But eachtime I saw is 99 for the InvokeRepeating() in console. Here is part of my code
void Update () {
foreach(int f in test){
target = f;
Debug.Log (target);
}
}
void AccXFunction(int time, float x){
InvokeRepeating ("AccXR", 0.2f, 0.8F);
}
void AccXR(){
float tempAccX = float.Parse(
GameObject.FindGameObjectWithTag("AccX")
.GetComponent<Text>()
.text);
accEvent.AddListener (accXAction);
if (tempAccX < 99) {
Debug.Log ("Hi, got it, the" + target);
}
else
{
Debug.Log ("Func0 " + tempAccX);
}
}
And here is the results in console: Only 99 but not other numbers from 0 to 98.
Unity only uses a single thread. Update will run from 0 to 99 then once it finishes it will do the pending InvokeRepeating. See the page for Execution Order
Normal coroutine updates are run after the Update function returns. A coroutine is a function that can suspend its execution (yield) until the given YieldInstruction finishes.
If you want to have them run "at the same time" you need to use classes from the System.Threading namespace. However if you are not on the "Unity thread" you can't touch anything that Unity controls and exposes to the UI, you will need to find a unity specific threading library to get your code to run on the UI thread again.

No effects with WaitForSeconds

I want my character to stop when I press a button on my gamepad or keyboard. The character must do a specific animation when I press the button and nothing else, so no movement at all, just the animation.
I'm trying to figure out how WaitForSeconds works, but when I try to use it, it doesn't work. Here the code of the function that calls WaitForSeconds
public IEnumerator Wait()
{
yield return new WaitForSeconds (6);
}
When the bool variable animationTest is true I want the program to wait for 6 seconds
if (animationTest)
{
UnityEngine.Debug.Log ("check1");
StartCoroutine (Wait ());
UnityEngine.Debug.Log ("check2");
animationTest = false;
}
but this doesn't work! check1 and check2 are printed at the same time. I'm missing something. This runs in FixedUpdate().
The Coroutine does not work like this. It starts a new (parallel) execution.
In order to achieve the wait you'd have to do it in the IEnumerator.
public IEnumerator SomethingElse() {
animationTest = false;
Debug.Log("check1");
yield return new WaidForSeconds(6f);
Debug.Log("check2");
yield return true;
}
void FixedUpdate() {
if (animationTest) {
StartCoroutine(SomethingElse());
}
}
Now when you set the animationTest at some point, you should see the two logs with a time gap of 6 seconds in between.

Categories

Resources