Fade out / fade in scene w/ loading screen

by artganify   Last Updated April 29, 2016 08:05 AM

I've got a (very simple?) requirement I just can't get to work, even though it's been covered by numerous tutorials and how-to's on the web. Basically, I want to transition between individual scenes with a black fade in/fade out. Here's my setup:

At the beginning of the game, I have some sort of 'kick-off' scene. It's nothing more than an empty scene with (for testing purposes) red background an an 'init' script which looks like this (pseudo):

Awake:
    StartCoroutine(LoadTitleScene());

LoadTitleScene:
    SceneController.Instance.LoadScene("TitleScene");

The SceneController is a singleton service which handles all the scene loading and transition stuff. Here's the code (excerpt):

// SceneController : SingletonMonoBehavior<SceneController>
private ScreenFadeHelper _screenFadeHelper;
private AsyncOperation   _sceneLoadOperation;

public void Awake() {
    _screenFadeHelper = ScreenFadeHelper.Instance;
}

public void LoadScene(string sceneName) {
    StartCoroutine(LoadSceneAsync(sceneName));
}

private IEnumerator LoadSceneAsync(string sceneName) {

    // display loading scene
    {
        _screenFadeHelper.BeginFadeIn(0.5f);
        while(_screenFadeHelper.IsFading)
            yield return new WaitForEndOfFrame();

        SceneManager.LoadScene("LoadingScene");

        _screenFadeHelper.BeginFadeOut(0.5f);
        while(_screenFadeHelper.IsFading)
            yield return new WaitForEndOfFrame();
     }

     // some other stuff..

     // load actual scene
     {
         // clear unused assets
         yield return Resources.UnloadUnusedAssets();

         _sceneLoadOperation = SceneManager.LoadSceneAsync(sceneName);
         _sceneLoadOperation.allowSceneActivation = false;

         while(_sceneLoadOperation.progress < 0.9f)
         {
             // update progress & stuff
             yield return null;
         }

         _screenFadeHelper.BeginFadeIn(0.5f);
         while (_screenFadeHelper.IsFading)
             yield return new WaitForEndOfFrame();

         _sceneLoadOperation.allowSceneActivation = true;

         _screenFadeHelper.BeginFadeOut(0.5f);
         while (_screenFadeHelper.IsFading)
             yield return new WaitForEndOfFrame();
     }
}

And the code for the screen fader (e.g. fade in):

public void BeginFadeIn(float duration)
{
    if (IsFading)
        return;

    IsFading = true;

    StartCoroutine(FadeInAsync(duration));
}

private IEnumerator FadeInAsync(float duration, Color color)
{
    var alpha = 0.0f;
    while (alpha < 1.0f) {
        yield return new WaitForEndOfFrame();
        if (!IsPaused)
            alpha = Mathf.Clamp01(alpha + Time.deltaTime / duration);
        DrawQuad(Color.black, alpha);
     }

    IsFading = false;
}

private static void DrawQuad(Color color, float alpha)
{
    color.a = alpha;
    GL.PushMatrix();
    GL.LoadOrtho();
    GL.Begin(GL.QUADS);
    GL.Color(color);
    GL.Vertex3(0, 0, -1);
    GL.Vertex3(0, 1, -1);
    GL.Vertex3(1, 1, -1);
    GL.Vertex3(1, 0, -1);
    GL.End();
    GL.PopMatrix();
}

The basic idea is this:

1. Fade in a black quad on the whole screen
2. Load the 'loading scene'
3. Fade out the black quad
4. Asynchronously load the actual scene
5. Fade in a black quad on the whole screen
6. Activate the actual scene
7. Fade out the black quad 

Long story short: The whole thing just flickers, and then the actual scene pops up. No fading at all (in build). And in the editor, the same thing happens, except the last fade-in doesn't seem finished (there's a semi-transparent black overlay over the whole screen).

What doesn't work here? Is there a problem with the co-routines?



Answers 1


You seem to be over-complicating things. Here is a sample script that you can attach to a Quad, that will fade it in or out just by calling FadeIn() or FadeOut().

using UnityEngine;
using System.Collections;

public enum FaderState{
    FadeIn,
    FadeOut
}

public class Fader : MonoBehaviour {

    MeshRenderer Quad;

    bool Done;
    FaderState State;

    public const float FADEDELAY = 0.5f;
    public const float FADESPEED = 0.25f;
    float FadeTimer;
    float DelayTimer;

    void Start () {     
        Quad = GetComponent<MeshRenderer> ();
        Quad.sortingOrder = 1000;
        FadeOut ();
    }

    void Reset()
    {
        Done = false;
        FadeTimer = FADESPEED;
        DelayTimer = FADEDELAY;
    }

    public void FadeIn()
    {
        Reset ();
        Quad.materials [0].color = new Color (0, 0, 0, 0);
        State = FaderState.FadeIn;
    }

    public void FadeOut()
    {
        Reset ();
        Quad.materials [0].color = new Color (0, 0, 0, 1);
        State = FaderState.FadeOut;
    }

    void Update () {

        if (!Done) {

            DelayTimer -= Time.deltaTime;

            if (DelayTimer <= 0) {

                FadeTimer -= Time.deltaTime;
                if (FadeTimer <= 0) {
                    FadeTimer = 0;
                    Done = true;
                }

                if (State == FaderState.FadeIn) {
                    Quad.materials [0].color = Color.Lerp (new Color (0, 0, 0, 1), new Color (0, 0, 0, 0), FadeTimer / FADESPEED); 
                } else if (State == FaderState.FadeOut) {
                    Quad.materials [0].color = Color.Lerp (new Color (0, 0, 0, 0), new Color (0, 0, 0, 1), FadeTimer / FADESPEED); 
                }
            }
        } else {

            if (State == FaderState.FadeIn)
                FadeOut ();
            else if (State == FaderState.FadeOut)
                FadeIn ();

        }       
    }
}
Jon
Jon
April 29, 2016 11:35 AM

Related Questions


What is wrong with my Respawn coroutine?

Updated May 01, 2018 23:13 PM

How do I use coroutines to animate properly?

Updated April 27, 2017 06:13 AM


How to await async operations / coroutines?

Updated July 26, 2016 08:05 AM