Mike Xiao

Software Engineer & Game Developer

Create a loading scene with a "real" loading bar in Unity

Transition between scenes in Unity is easy, but how to create an intermediate "loading" scene with a loading bar which displays the current progress? In this article I will talk about how to create a loading bar which actually shows loaded percentage for the upcoming scene.

(Cover Photo Credit: sgprolab)

Things You Should Know Already

Before we get started, let's look at how to switch between scenes in Unity:

using UnityEngine;
/*...some other includes...*/
using UnityEngine.SceneManagement;

public class ChangeScene : MonoBehaviour {
    public void ChangeScene(string sceneName) {
        SceneManager.LoadScene(sceneName);
    }
}

When ChangeScene() gets called, it will trigger SceneManager to load the scene with the specific sceneName.

Well, this might work for most of the cases, but the game will stuck for a bit while the other scene is getting loaded in, especially if it is a large one, it may takes a half to a second to finish loading, which makes your game looks craggy. Also, it is nice to have a loading bar to actually tell the player the game is doing loading operation.

"Talk is cheap, show me the code"

Okay...okay...well, the basic idea is, while we loading a scene, we want to display the loading UI and let the scene to be loaded in the background, then switch to the scene when the loading process is completed.

Fortunately, Unity has a powerful scripting API library which makes possible for us to finish this job by just few lines of code, you can load your scene asynchronously using SceneManager.LoadSceneAsync().

Let's see how can we get this done: to make a "real" loading progress bar, we need to know the data of loading progress to get it shown on our UI (in here I used a Slider to display the current loading progress). In Unity, we can do:

AsyncOperation sceneAO;

And assign it in a Coroutine called LoadingSceneRealProgress():

IEnumerator LoadingSceneRealProgress(string sceneName) {
    ...
    sceneAO = SceneManager.LoadSceneAsync(sceneName);
    ...
}

This creates a AsyncOperation called sceneAO which will perform the loading operation in the background while we call ChangeScene() to start the Coroutine, notice here we use LoadSceneAsync() instead of LoadScene() to initialize a Asynchronous Operation.

Then, if you want to do something after the scene is fully loaded but before it brings you to the new scene, you can turn off the scene activation:

sceneAO.allowSceneActivation = false;

And turn it back on when you are finished:

sceneAO.allowSceneActivation = true;

To get the loading progress shown on the slider, we can simply assign the loading progress data to the value of the slider:

while(!sceneAO.isDone) {
    loadingProgbar.value = sceneAO.progress;
    // do something else
}

This will keep assigning the progress data to the value of the progress bar, while the Asynchronous Operation is not complete, which means, before the scene gets fully loaded, the bar will keep updating the loading progress for the upcoming scene, which is exactly what we want.

This is an example in my game Objective: Extraction:

public class ChangeSceneAsync : MonoBehaviour {

    AsyncOperation sceneAO;
    [SerializeField] GameObject loadingUI;
    [SerializeField] Slider loadingProgbar;
    [SerializeField] Text loadingText;

    // the actual percentage while scene is fully loaded
    private const float LOAD_READY_PERCENTAGE = 0.9f;

    public void ChangeScene(string sceneName){
        loadingUI.SetActive(true);
        loadingText.text = "LOADING...";
        StartCoroutine(LoadingSceneRealProgress(sceneName));
    }

    IEnumerator LoadingSceneRealProgress(string sceneName) {
        yield return new WaitForSeconds(1);
        sceneAO = SceneManager.LoadSceneAsync(sceneName);

        // disable scene activation while loading to prevent auto load
        sceneAO.allowSceneActivation = false;

        while (!sceneAO.isDone) {
            loadingProgbar.value = sceneAO.progress;

            if (sceneAO.progress >= LOAD_READY_PERCENTAGE) {
                loadingProgbar.value = 1f;
                loadingText.text = "PRESS SPACE TO CONTINUE";
                if (Input.GetKeyDown(KeyCode.Space)) {
                    sceneAO.allowSceneActivation = true;
                }
            }
            Debug.Log(sceneAO.progress);
            yield return null;
        }
    }
}

This creates a loading bar which will pause the scene while its fully loaded and ask for user a confirm input, if user confirms to navigate to that scene, the script will direct the player to the target scene. Notice that I have a function

yield return new WaitForSeconds(1);

which will stop everything else for a second to give the bar UI have enough time to load (or it will show 100% immediately since the upcoming scene is small).

Another place to pay attention is I set LOAD_READY_PERCENTAGE = 0.9f, the reason I set this value to 0.9 is sceneAO.progress == 0.9 when the upcoming scene is fully loaded (yes, it is 90%, not 100%).

I will leave this code for you to think about, feel free to take this code as a reference while developing your own game, and please leave the comment below if you have any questions.