beginner Utilities

Scene Manager

Async scene loading with loading screen, progress bar, and minimum load time. Clean transitions between levels.

Unity 2022.3+ · 2.5 KB · SceneLoader.cs

How to Use

1

Create a persistent 'SceneLoader' object (DontDestroyOnLoad)

2

Attach SceneLoader and optionally assign a loading screen panel

3

Call SceneLoader.Instance.LoadScene("LevelName") to load

4

Hook OnLoadProgress to a UI Slider for a progress bar

5

Set minimumLoadTime to prevent flash-loading on fast hardware

6

Pair with ScreenFader for polished scene transitions

Source Code

SceneLoader.cs
C#
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Events;
using System.Collections;

/// <summary>
/// Async scene loader with loading screen support.
/// Provides progress callbacks for loading UI.
/// </summary>
public class SceneLoader : MonoBehaviour
{
    public static SceneLoader Instance { get; private set; }

    [Header("Loading Settings")]
    [SerializeField] private float minimumLoadTime = 1f;
    [SerializeField] private GameObject loadingScreen;

    [Header("Events")]
    public UnityEvent<float> OnLoadProgress;
    public UnityEvent OnLoadStarted;
    public UnityEvent OnLoadComplete;

    private bool isLoading;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    /// <summary>
    /// Load a scene by name with loading screen.
    /// </summary>
    public void LoadScene(string sceneName)
    {
        if (isLoading) return;
        StartCoroutine(LoadSceneAsync(sceneName));
    }

    /// <summary>
    /// Load a scene by build index with loading screen.
    /// </summary>
    public void LoadScene(int buildIndex)
    {
        if (isLoading) return;
        string scenePath = UnityEngine.SceneManagement.SceneUtility.GetScenePathByBuildIndex(buildIndex);
        string sceneName = System.IO.Path.GetFileNameWithoutExtension(scenePath);
        StartCoroutine(LoadSceneAsync(sceneName));
    }

    /// <summary>
    /// Reload the current scene.
    /// </summary>
    public void ReloadCurrentScene()
    {
        LoadScene(SceneManager.GetActiveScene().name);
    }

    private IEnumerator LoadSceneAsync(string sceneName)
    {
        isLoading = true;

        OnLoadStarted?.Invoke();

        if (loadingScreen != null)
            loadingScreen.SetActive(true);

        OnLoadProgress?.Invoke(0f);

        float startTime = Time.unscaledTime;

        AsyncOperation operation = SceneManager.LoadSceneAsync(sceneName);
        operation.allowSceneActivation = false;

        while (!operation.isDone)
        {
            // Unity loads to 0.9, then waits for activation
            float progress = Mathf.Clamp01(operation.progress / 0.9f);
            OnLoadProgress?.Invoke(progress);

            if (operation.progress >= 0.9f)
            {
                // Enforce minimum load time
                float elapsed = Time.unscaledTime - startTime;
                if (elapsed < minimumLoadTime)
                {
                    yield return new WaitForSecondsRealtime(minimumLoadTime - elapsed);
                }

                OnLoadProgress?.Invoke(1f);
                yield return new WaitForSecondsRealtime(0.1f);

                operation.allowSceneActivation = true;
            }

            yield return null;
        }

        if (loadingScreen != null)
            loadingScreen.SetActive(false);

        OnLoadComplete?.Invoke();
        isLoading = false;
    }
}