- 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 Coroutines, 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.
Your answer
 
 
              koobas.hobune.stream
koobas.hobune.stream 
                       
                
                       
			     
			 
                