Unity 分享載入場景跳出載入中的畫面


建立時間: 2023年10月8日 16:46
更新時間: 2023年10月8日 17:42

說明

有時候在載入場景時因為遊戲物件太多,導致要等很多時間,若使用同步的載入方式畫面會卡住直到場景載入為止,造成使用者體驗不佳,所以這次將分享一套簡易的非同步載入場景方法給大家。

畫面

首先我們必須實作載入中的畫面,礙於我的能力有限,我想到的做法就是將整個 Canvas 做成預製,這樣就可以設定 Canvas Sort Order,確保載入中的畫面要跳出在最上面,然後將預製放在 Resources 資料夾,使用 Resources 方式載入預製。

Loading Page

這裡我只簡單地告訴大家大致上需要哪些畫面,實作細節還再自行依照自己的需求調整。

LoadingCanvas

一般的畫布,用來設定 Sort Order,重點只有新增 LoadingCanvas 組件,存取 LoadingPanel 遊戲物件

LoadingCanvas Component

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

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() 方法,然後寫上場景名稱即可。

Button Click Event

LoadScene() 是同步載入的方式,使用方式同上,只是方便給按鈕點擊事件使用,不用另外建立按鈕腳本來實作功能。

觀看次數: 698
canvasloadingpageunity載入中
按讚追蹤 Enjoy 軟體 Facebook 粉絲專頁
每週分享資訊技術

一杯咖啡的力量,勝過千言萬語的感謝。

支持我一杯咖啡,讓我繼續創作優質內容,與您分享更多知識與樂趣!