Unity 分享載入場景跳出載入中的畫面
分類
說明
有時候在載入場景時因為遊戲物件太多,導致要等很多時間,若使用同步的載入方式畫面會卡住直到場景載入為止,造成使用者體驗不佳,所以這次將分享一套簡易的非同步載入場景方法給大家。
畫面
首先我們必須實作載入中的畫面,礙於我的能力有限,我想到的做法就是將整個 Canvas 做成預製,這樣就可以設定 Canvas Sort Order,確保載入中的畫面要跳出在最上面,然後將預製放在 Resources 資料夾,使用 Resources 方式載入預製。
 
 
這裡我只簡單地告訴大家大致上需要哪些畫面,實作細節還再自行依照自己的需求調整。
LoadingCanvas
一般的畫布,用來設定 Sort Order,重點只有新增 LoadingCanvas 組件,存取 LoadingPanel 遊戲物件

LoadingCanvas.cs
using UnityEngine;
/// <summary>
/// 載入中畫布
/// </summary>
public class LoadingCanvas : MonoBehaviour
{
    /// <summary>
    /// 載入中面板
    /// </summary>
    [SerializeField]
    private LoadingPanel loadingPanel;
    /// <summary>
    /// 載入中面板
    /// </summary>
    public LoadingPanel LoadingPanel => loadingPanel;
}
上面有一個 LoadingPanel 組件,一樣等等會實作 LoadingPanel 組件。
LoadingPanel
UI Panel 用來覆蓋整個畫面的背景,重點是新增 LoadingPanel 組件,用來存取 ProgressBar。
LoadingPanel.cs
using UnityEngine;
/// <summary>
/// 載入中面板
/// </summary>
public class LoadingPanel : MonoBehaviour
{
    /// <summary>
    /// 進度條
    /// </summary>
    [SerializeField]
    private ProgressBar progressBar;
    /// <summary>
    /// 進度條
    /// </summary>
    public ProgressBar ProgressBar => progressBar;
}
LoadingText
單純顯示文字。
Pickaxe 1
Unity Asset Store 上的動畫,可免費下載,直接將下載的 Prefab 動畫放上去而已。
下載連結: https://assetstore.unity.com/packages/2d/gui/icons/animated-loading-icons-47844
ProgressBar
進度條比較複雜一點,關於進度條畫面實作可以參考我之前寫的 Unity 簡易血條製作。
簡單介紹如下

ProgressBar
它是一個 Slider,重點在我自訂的 ProgressBar 組件,以及 OnValueChanged 事件調用 ProgressBar.OnValueChanged,用來修改 ProgressText 的進度文字。
- Fill Rect: 進度填充的畫面,這裡綁定 Status,Status 是 Images UI,就是一條長方形的條狀物圖片。
ProgressBar.cs
using TMPro;
using UnityEngine.UI;
using UnityEngine;
/// <summary>
/// 進度條 UI
/// </summary>
public class ProgressBar : MonoBehaviour
{
    /// <summary>
    /// 進度滑桿
    /// </summary>
    private Slider slider;
    /// <summary>
    /// 進度文字
    /// </summary>
    [SerializeField]
    private TMP_Text progressText;
    private void Awake()
    {
        slider = GetComponent<Slider>();
        progressText.text = slider.value.ToString("P0");
    }
    /// <summary>
    /// 進度變更
    /// </summary>
    /// <param name="value">進度0~1</param>
    public void OnValueChanged(float value)
    {
        progressText.text = value.ToString("P0");
    }
    /// <summary>
    /// 設定進度
    /// </summary>
    /// <param name="value">進度0~1</param>
    public void SetProgress(float value)
    {
        slider.value = value;
    }
}
SetProgress() 用給給外部腳本設定進度的,當 slider.value 改變時會觸發 OnValueChanged(),接著改變進度條文字。
StatusBar
進度條的外框圖片。
Status
進度條框內的填充圖片。
ProgressText
進度文字。
載入中腳本
SceneLoader.cs
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// 場景載入器,用來給 UI 按鈕調用
/// </summary>
public class SceneLoader : MonoBehaviour
{
    /// <summary>
    /// 載入中畫布
    /// </summary>
    private static LoadingCanvas loadingCanvas;
    private void Awake()
    {
        // 初始化載入中畫布,確保不會重複生成
        if (loadingCanvas == null)
        {
            loadingCanvas = FindFirstObjectByType<LoadingCanvas>();
            // 第一次載入載入中畫布
            if (loadingCanvas == null)
            {
                GameObject LoadingCanvasPrefab =
                    Resources.Load<GameObject>("Prefabs/UIs/LoadingCanvas");
                GameObject loadingCanvasGameObject = Instantiate(LoadingCanvasPrefab, null);
                loadingCanvasGameObject.SetActive(false);
                DontDestroyOnLoad(loadingCanvasGameObject);
                loadingCanvas = loadingCanvasGameObject.GetComponent<LoadingCanvas>();
                SceneManager.sceneLoaded += OnSceneLoaded;
            }
        }
    }
    /// <summary>
    /// 載入場景時顯示載入中畫面
    /// </summary>
    /// <param name="sceneName">場景名稱</param>
    /// <returns></returns>
    private IEnumerator LoadSceneWithLoadingPage(string sceneName)
    {
        AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneName);
        while (!asyncOperation.isDone)
        {
            loadingCanvas.LoadingPanel.ProgressBar.SetProgress(asyncOperation.progress);
            if (asyncOperation.progress >= 0.9f)
            {
                loadingCanvas.LoadingPanel.ProgressBar.SetProgress(1f);
            }
            yield return null;
        }
    }
    /// <summary>
    /// 當場景載入後調用
    /// </summary>
    /// <param name="scene">場景名稱</param>
    /// <param name="mode">載入模式</param>
    private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        loadingCanvas.gameObject.SetActive(false);
    }
    /// <summary>
    /// 按鈕點擊場入場景,並顯示載入中畫面
    /// </summary>
    /// <param name="sceneName">場景名稱</param>
    public void ClickLoadSceneWithLoadingPage(string sceneName)
    {
        loadingCanvas.gameObject.SetActive(true);
        StartCoroutine(LoadSceneWithLoadingPage(sceneName));
    }
    /// <summary>
    /// 載入場景,給按鈕使用
    /// </summary>
    /// <param name="sceneName">場景名稱</param>
    public void LoadScene(string sceneName)
    {
        SceneManager.LoadScene(sceneName);
    }
}
上述腳本 loadingCanvas 使用類似單例的設計方式,避免發生重複生成 LoadingCanvasPrefab 遊戲物件。
將腳本添加在按鈕上,在點擊事件調用 ClickLoadSceneWithLoadingPage() 方法,然後寫上場景名稱即可。
 
 
LoadScene() 是同步載入的方式,使用方式同上,只是方便給按鈕點擊事件使用,不用另外建立按鈕腳本來實作功能。
一杯咖啡的力量,勝過千言萬語的感謝。
支持我一杯咖啡,讓我繼續創作優質內容,與您分享更多知識與樂趣!