Two things to remember:
Unity C# scripts like Update or coroutine methods run synchronously, on the main thread.
They're not running in parallel/in the background while the scene is drawn and redrawn each frame. Everything else the engine wants to do - all the other update scripts and all the rendering - has to wait for its turn, after we exit the method in one of three ways:
- We reach the final curly brace
}
at the end of the method
- We hit a
return
statement
- In a coroutine, we hit a
yield
statement that passes control back to the engine
Until one of these three things happens, we stay in the method, and nothing else has a chance to update or draw. So if you have a loop in your method (without a yield
inside), then that loop has to run all the way to completion before the frame can be drawn.
If you have a loop that never exits (an infinite loop), then the game will hang/crash, because it can never finish drawing the current frame or move on to the next one.
You can check the behaviour of your code - including spotting infinite loops - by walking through it one instruction at a time, just the same way the processor would when it executes your code.
So, let's walk through this line by line:
for (i = 0; i <= 2; i++) {
We initialize the variable i
to zero.
Since zero is less than or equal to two, we pass the loop condition and proceed inside.
platform.SetActive (false);
We disable/hide the platform.
if (i >= 1 ) {
i
is still zero, so we fail this test and skip to the end of the inner if
block.
}
We've reached the end of the loop, so we cycle back up to the top.
for (i = 0; i <= 2; i++) {
We increment i
by 1 (i++
), so now it holds a value of one.
One is still less than or equal to two, so we pass the loop condition and proceed into the body of the loop again.
platform.SetActive (false);
We disable the platform again. It was already disabled though, so this is redundant.
if (i >= 1 ) {
This time, i
holds a value of one, which is greater than or equal to one. So we step inside the if
block.
i--;
We decrement i
by one, so it's back to zero again.
platform.SetActive (true);
We activate the platform.
}}
We reach the end of the if
block, and the end of the loop, so we return again to the top.
for (i = 0; i <= 2; i++) {
We increment i
by 1, so now it holds a value of one again.
Uh-oh... we've been here before. This is exactly the state we were in at step 5!
So now we can clearly see that this loop continues repeating steps 5-10 forever: incrementing i
to one, then decrementing it back down to zero, so it never reaches a value of three that lets it exit the loop.
The whole game is stuck on hold while this infinite loop runs.
So, how can we alternate a platform off and on, while letting the engine actually keep running other updates and drawing the frame? Something like this would work:
public class ActiveAlternator : MonoBehaviour {
[Tooltip("Target object to turn on/off (NOT this object or its parents!")]
public GameObject target;
[Tooltip("Seconds to wait between activating / deactivating the object")]
public float interval = 1f;
// IEnumerator makes this run as a coroutine we can suspend & resume with yield.
IEnumerator Start() {
if(target == gameObject)
Debug.LogError("ActiveAlternator cannot target itself and still re-activate");
// Infinite loop! But it's OK, because we'll tell it to take breaks.
while(true) {
// This passes control back to the engine so it can draw the frame,
// and asks to resume this method on the next line after some time.
yield return new WaitForSeconds(interval);
// If the object was active, set it inactive, and vice versa.
target.SetActive(!target.activeSelf);
// Then loop to wait, and alternate again, forever.
}
}
}