- Home /
Nested Coroutines: last coroutine quits early
Greetings,
I am trying to nest Coroutines. It seems to work OK, except that the last nested coroutine exits early.
In a nutshell, here's what I'm trying to do (in pseudocode)
// start coroutine "add to scene"
// start coroutine "fade to black", yield
// does some simple logic
// start coroutine "fade in from black", yield
As you can see, I want my "add to scene" Coroutine
to be "wrapped" in the complete execution of two other Coroutine
s, fading a Unity.UI
Panel
in and out (using Color.Lerp
and adjusting the alpha channel of the start color to determine the end color).
The first Coroutine
"fade to black" works totally fine; it fades out as expected. Then, the simple logic is performed, as expected.
However, the final Coroutine
"fade in from black" seems to yield only one time, even though I am using the same type of yield return
statement on it.
What am I doing wrong here?
Here's some mildly redacted example code:
private IEnumerator TransitionToScene(string sceneName) {
Debug.Log("Transitioning. Starting fade out...");
//Start a fade-out
yield return StartCoroutine(Fade(FadeStyle.FadeOut));
// OTHER STUFF IN HERE
Debug.Log("Loaded! Fading in, now...");
//Start a fade-in
yield return StartCoroutine(Fade(FadeStyle.FadeIn));
Debug.Log("Fade in complete");
}
Here's the actual "Fade" Coroutine, also:
//Create a Coroutine which will fade in or out
private IEnumerator Fade( FadeStyle style, float fadeTime = .25f ) {
//Early exit for invalid fade style
if(style == FadeStyle.Invalid) { Debug.Log("INVALID FADE: Early-exit."); yield break; }
Debug.Log("Fade coroutine started...");
//Keep track of our fade progress out here for the coroutine...
float fadeProgress = 0;
//Grab the start color
Color start = fadeImage.color;
//Create a target color based on the start color...
Color end = start;
//If we're fading in, the target alpha is 0. If we're fading out, it's 1.
end.a = style == FadeStyle.FadeIn ? 0 : 1;
Debug.Log("Fading from " + start + " to " + end);
//While we have not faded, yet...
while(fadeProgress < fadeTime) {
string inOrOut = style == FadeStyle.FadeIn ? "In" : "Out";
Debug.Log("Fade " + inOrOut + ": " + fadeProgress + " / " + fadeTime);
//Calculate how far along we should be in the color transitions...
float lerpVal = fadeProgress / fadeTime;
Color newColor = Color.Lerp(start, end, lerpVal);
//Apply it
fadeImage.color = newColor;
//Increment time
fadeProgress += Time.deltaTime;
//Continue next frame
yield return null;
}
Debug.Log("Fade coroutine complete... (" + fadeProgress + " / " + fadeTime + ")");
}
And, finally, some debug output:
Fade coroutine started...
UnityEngine.Debug:Log(Object)
<Fade>c__Iterator0:MoveNext() (at Assets/SceneManager.cs:75)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
<TransitionToScene>c__Iterator1:MoveNext() (at Assets/SceneManager.cs:160)
Fading from RGBA(0.000, 0.000, 0.000, 0.998) to RGBA(0.000, 0.000, 0.000, 0.000)
UnityEngine.Debug:Log(Object)
<Fade>c__Iterator0:MoveNext() (at Assets/SceneManager.cs:87)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
<TransitionToScene>c__Iterator1:MoveNext() (at Assets/SceneManager.cs:160)
Fade In: 0 / 0.25
UnityEngine.Debug:Log(Object)
<Fade>c__Iterator0:MoveNext() (at Assets/SceneManager.cs:93)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
<TransitionToScene>c__Iterator1:MoveNext() (at Assets/SceneManager.cs:160)
Fade In: 0.001157403 / 0.25
UnityEngine.Debug:Log(Object)
<Fade>c__Iterator0:MoveNext() (at Assets/SceneManager.cs:93)
Fade In: 0.008806617 / 0.25
UnityEngine.Debug:Log(Object)
<Fade>c__Iterator0:MoveNext() (at Assets/SceneManager.cs:93)
Fade coroutine complete... (0.2785531 / 0.25)
UnityEngine.Debug:Log(Object)
<Fade>c__Iterator0:MoveNext() (at Assets/SceneManager.cs:109)
FYI I have commented out the Destroy(...)
call having read that destroying an object which started a Coroutine
can cause it to exit early. No avail.
Looks like there is a spike in Time.deltaTime which causes the coroutine to complete super early.
I agree with tigertrussell - I'd suspect that if the thing you're fading out to cover up is the spawning of a load of gameobjects, some of the scripts in those gameobjects are waking up and causing a spike in the time deltas. $$anonymous$$aybe if you delayed a bit before fading in, it would let those gameobjects settle before the fade begins.
@gfoot hehe I'm OP ;) but you're absolutely right! that was the cause. I have temporarily solved the issue by doing a yield return new WaitForSeconds(1);
but was wondering if there is any way to force it to go to the next frame? It seems like new WaitForEndOfFrame()
waits until the end of the current frame; is there a way to wait until the beginning of the next frame?
Haha ok :)
Simply yielding null or 0 or anything that Unity doesn't recognize will resume you on the next frame during the Update phase.
Answer by tigertrussell · Mar 01, 2015 at 09:51 PM
The culprit is how "progress" is measured within the Coroutine itself.
Take this code:
//Increment time
fadeProgress += Time.deltaTime;
Because Time.deltaTime can be very large, it is possible for the while
-loop to quit execution very early. This can be handled with special cases or you can hack it and use something like yield return new WaitForSeconds();
Another thing I've thought about doing (but is not especially safe) is to do:
if(Time.deltaTime < skipThreshold)
around the processing of the Coroutine to avoid incrementing the timer more than we want to.