Smooth Scene Loading in Unity

In Unity, if you want to load another scene asynchronously, you can do it without problem but using LoadSceneAsync from SceneManager. The only problem here is that this method won’t give you smooth progress values.

So how can you make a loading screen which will make this loading progress smooth?

Let’s dive into this in this post!

First things first ?

To start, we should first create some simple scenes!

On one scene, we will have a loading bar, and on the other one, we will have a simple scene with assets.

Loading scene ⛓

The first scene should have some simple UI, with a progress bar. We can utilize Unity’s slider template here, we would only need to remove the handler from it, and we almost have what we would need.

I would also recommend removing interaction from the slider component, as we don’t want a user to interact with it. ?

Slider as progress bar.

We can also add a title and maybe background if you wish so.

My loading scene.

Target scene ?

To not overcomplicate things, we can use Unity’s example scene. It’s already created, and it will be enough for us.

We don’t need to mess with it.

Unity’s example scene.

Build settings ?

As we have 2 scenes in the project, we should add both of them to build settings and set their order.

Build settings.

Scripting time! ?‍?

We would only need one script for our smooth loading.

Let’s call it SmoothLoader. This script will need a few properties.

  • A reference to the load operation,
  • A reference to the progress bar,
  • Values for calculating progress for display,
  • And some multiplier to slow down or speed up animation.

In code, this will look like that:

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

/// <summary>
/// Script which animates progress bar in the UI with current loading bar process.
/// At the completion loads next scene.
/// </summary>
public class SmoothLoader : MonoBehaviour
{
    // Reference to the load operation.
    private AsyncOperation loadOperation;

    // Reference to the progress bar in the UI.
    [SerializeField]
    private Slider progressBar;

    // Progress values.
    private float currentValue;
    private float targetValue;

    // Multiplier for progress animation speed.
    [SerializeField]
    [Range(0, 1)]
    private float progressAnimationMultiplier = 0.25f;

}

Start the loading

The next step will be to initialize the whole loading process and assign values to our properties.

We will use MonoBehaviour’s Start method for it. However, before we do that, a quick intro for scene asynchronous load.

In Unity we can scenes in two ways – synchronously with SceneManager.LoadScene() method and asynchronously with SceneManager.LoadSceneAsync() method.

You can also load scenes additively, but that’s a story for another time.

Let’s get back to our LoadSceneAsync() method. By default, the loading process will go from 0 to 1 within a few moments, depending on how big your scene is.

The problem starts when we want to display that progress to the UI. Here we will see that progress value doesn’t interpolate smoothly. It jumps between values. Is it a problem? Depends…

But here, we want to interpolate it so it will look more professional! ?

To do that we will have to set allowSceneActivation flag to false. This will do two things. Firstly, the scene won’t load automatically. Secondly, progress will stop at 0.9f, which we will have to handle in some clever way.

In code, this will look like that:

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

/// <summary>
/// Script which animates progress bar in the UI with current loading bar process.
/// At the completion loads next scene.
/// </summary>
public class SmoothLoader : MonoBehaviour
{
    ...

    /// <summary>
    /// Unity method called once at the start.
    /// Used here to start the loading progress.
    /// </summary>
    private void Start()
    {
        // Set 0 for progress values.
        progressBar.value = currentValue = targetValue = 0;

        // Load the next scene.
        var currentScene = SceneManager.GetActiveScene();
        loadOperation = SceneManager.LoadSceneAsync(currentScene.buildIndex + 1);

        // Don't active the scene when it's fully loaded, let the progress bar finish the animation.
        // With this flag set, progress will stop at 0.9f.
        loadOperation.allowSceneActivation = false;
    }

}

Interpolation time!

To interpolate our values and display them in UI, we have to process them a little.

As I mention a few lines before, our loading operation’s progress will stop at 0.9f (90%). We don’t want to use it like that in UI. We want it to go to 1 (100%).

We can achieve that by simply dividing the progress value by the 0.9f. That will “scale” progress value to be between 0 and 1. That’s our targetValue for interpolation.

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

/// <summary>
/// Script which animates progress bar in the UI with current loading bar process.
/// At the completion loads next scene.
/// </summary>
public class SmoothLoader : MonoBehaviour
{
    ...

    /// <summary>
    /// Unity method called every frame.
    /// Used here to animate progress bar.
    /// </summary>
    private void Update()
    {
        // Assign current load progress, divide by 0.9f to stretch it to values between 0 and 1.
        targetValue = loadOperation.progress/0.9f;

    }
}

The next step is to calculate “current” value to display it. You can use whichever interpolation method you want, I will go with Mathf.MoveTowards().

This method will simply increase currentValue until it will reach targetValue. After that, we can simply assign that value to the progress bar in UI.

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

/// <summary>
/// Script which animates progress bar in the UI with current loading bar process.
/// At the completion loads next scene.
/// </summary>
public class SmoothLoader : MonoBehaviour
{
    ...

    /// <summary>
    /// Unity method called every frame.
    /// Used here to animate progress bar.
    /// </summary>
    private void Update()
    {
        ...

        // Calculate progress value to display.
        currentValue = Mathf.MoveTowards(currentValue, targetValue, progressAnimationMultiplier * Time.deltaTime);
        progressBar.value = currentValue;

    }
}

Now, load the next scene

When currentValue reach 1, we should allow the loading operation to load the next scene. We can do that by simply changing AllowSceneActivation back to true.

Whole Update method should look like that:

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

/// <summary>
/// Script which animates progress bar in the UI with current loading bar process.
/// At the completion loads next scene.
/// </summary>
public class SmoothLoader : MonoBehaviour
{
    ...

    /// <summary>
    /// Unity method called every frame.
    /// Used here to animate progress bar.
    /// </summary>
    private void Update()
    {
        // Assign current load progress, divide by 0.9f to stretch it to values between 0 and 1.
        targetValue = loadOperation.progress/0.9f;

        // Calculate progress value to display.
        currentValue = Mathf.MoveTowards(currentValue, targetValue, progressAnimationMultiplier * Time.deltaTime);
        progressBar.value = currentValue;

        // When the progress reaches 1, allow the process to finish by setting the scene activation flag.
        if (Mathf.Approximately(currentValue, 1))
        {
            loadOperation.allowSceneActivation = true;
        }
    }

The only thing left to do is to set the component somewhere in the scene, and we can try it out!

Smooth Loader component.

Ready to see the final result?

Here it is!

Final result! ?

Summary ?

So that’s how you can make smooth loading in Unity. It’s not that hard when you look at it. ?

Do you like it? Have you done it in a different way? Let me know in the comment section below!

If your friends are making a game with some loading screens, don’t forget to share this post with them! I would really appreciate that! ?

And if you want to be notified about future content on my blog, you can sign up for my newsletter!

The whole project is available at my public repository. ?

Thanks for reading, and see you next time! ?

4.4 13 votes
Article Rating
Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
AFatPigeon
AFatPigeon
1 year ago

I used this advice to get a scene to load rapidly
Wasn’t aware of AsyncOps
Many thanks

1
0
Would love your thoughts, please comment.x
()
x