- Home /
What is wrong with this use of WaitForSeconds?
Hello everyone! I try to use WaitForSeconds method, but it dosent work at all and I dont know whats wrong. Now it just freeze the unity editor. My goal is to play animation before destroying and moving other objects.
public void DeleteColumnsRight()
{
for (int x = gridWidth/2; x < gridWidth ; x++)
{
if (IsColumnFull(x))
{
StartCoroutine(PlayCrushAnimation(x));
//DeleteMinoInColumn(x);
//MoveAllToRight(x - 1);
x--;
}
}
}
IEnumerator PlayCrushAnimation(int x)
{
for (int y = 0; y < gridHeight; y++)
{
animator = grid[x, y].gameObject.GetComponent<Animator>();
animator.SetTrigger("Crush");
}
yield return new WaitForSeconds(3);
DeleteMinoInColumn(x);
MoveAllToRight(x - 1);
}
Have you tried to use the step by step debugger of your IDE? Example for Visual Studio
Answer by Bunny83 · Oct 07, 2017 at 09:42 AM
There are several things that could give you problems / not what you want. First of all the for loop inside DeleteColumnsRight runs and completes in one frame as it is not a coroutine. Only a coroutine can wait for something. That means you start "gridWidth/2" coroutines all at the same time. Each will yield in the same frame and therefore all coroutines will continue all at the same time, after 3 sec. I guess that's not what you had in mind.
Next problem is, which is causing your crash is your for loop in your DeleteColumnsRight method. That for loop is equivalent to this loop:
{
int x = gridWidth/2
while (x < gridWidth)
{
if (IsColumnFull(x))
{
StartCoroutine(PlayCrushAnimation(x));
x--;
}
x++
}
}
As you can see if the column "x" is full you start a coroutine and decrement "x" and immediately increment x again. So the next iteration will use the same x again. Since your coroutine does not change the state of your column x before the yield it is still full. So you have an infinite loop here as x will be stuck by the first column that is full.
Finally keep in mind that when you want any kind of animation the game will continue to run during that time. You can't trap the code in an infinite loop as nothing will be shown during that time. All your code need to complete so the current frame can finish and actually update the screen. That means while your animation runs you usually want to block any user input and maybe ans automatic mechanism which runs in parallel.
So you should do two things:
first move all your code you've posted here into your coroutine. That way you can actually wait between two columns.
Maybe use a boolean value which you set to true at the start of your coroutine and back to false when your done. This can be used to prevent unwanted actions by other scripts.
So it might look something like this:
public IEnumerator DeleteColumnsRight()
{
deleteAnimationStarted = true;
int x = gridWidth/2;
while(x < gridWidth)
{
if (IsColumnFull(x))
{
for (int y = 0; y < gridHeight; y++)
{
animator = grid[x, y].gameObject.GetComponent<Animator>();
animator.SetTrigger("Crush");
}
yield return new WaitForSeconds(3);
DeleteMinoInColumn(x);
MoveAllToRight(x - 1);
}
else
x++;
}
deleteAnimationStarted = false;
}
ps: it's in general bad practise to modify a for loop variable manually outside the for loop header. If you needs such functionality it's better to use a while loop where the increment is seperated. This makes it easier to spot such problems. If a programmer sees a for loop he assumes everything in the for loop header is responsible for how often the loop runs.