做網(wǎng)站公司哪家好百度競價開戶多少錢
一、異步加載場景的過程
1、異步加載場景用到的API
LoadSceneAsync
2、異步加載的參數(shù)說明
- (1)默認參數(shù):SceneManagement.LoadSceneAsync(“SceneName”);
AsyncOperation task = SceneManager.LoadSceneAsync("SceneName");
- (2)異步加載時的兩種加載模式-Single和Additive
關(guān)于【活動場景】和【非活動場景】有些什么差異,本文不做討論。
//定義加載任務(wù)
AsyncOperation loadTask = SceneManager.LoadSceneAsync("場景2", LoadSceneMode.Additive);
//AsyncOperation loadTask = SceneManager.LoadSceneAsync("場景2", LoadSceneMode.Single);await loadTask; //實際加載過程
- (3)Single異步加載的流程
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync("SceneName", LoadSceneMode.Single);
加載的步驟解釋:
步驟 | 進度值 | 事項描述 |
---|---|---|
第一步 | 0% ~ 90% | 把資源加入到內(nèi)存中 |
第二步 | 初始化新加入的場景,激活新加入的場景,設(shè)置為【活動場景】 | |
第三步 | 新場景激活后,原來的場景會被卸載和清理 | |
第四步 | asyncLoad.isDone設(shè)置為true | 當(dāng) isDone 屬性為 true 時,表示場景切換已完成,新場景已經(jīng)完全加載并激活,舊場景已經(jīng)被卸載。 |
加載過程:
- 1、進度值:0%~90% 加載新場景,當(dāng)?shù)竭M度值達到90%時,代表場景資源已經(jīng)加入到內(nèi)存中,后面10%的時間留給新舊場景的顯示切換、新場景初始化、舊場景資源回收。
- 2、場景要如何切換,【自動顯示】還是【代碼手動控制】它顯示
下面是加載完畢自動顯示:
AsyncOperation task = SceneManager.LoadSceneAsync("SceneName");
await loadTask;
下面是手工控制場景的顯示
//定義加載任務(wù)
AsyncOperation loadTask = SceneManager.LoadSceneAsync("SceneName");
loadTask.allowSceneActivation = false; //加載完畢場景不立即顯示await loadTask; //實際加載過程//此處,用戶要進行其他操作,比如點擊一個按鈕,把數(shù)據(jù)上傳到服務(wù)器
//...loadTask.allowSceneActivation = true; //設(shè)置成true后,系統(tǒng)開始后續(xù)的10%的工作,會把新場景顯示,把老場景卸載
二、大場景切換面臨的問題
1、理想情況下的加載
- (1)、先定義一個Progress的對象(進度報告器),相當(dāng)于一個事件,當(dāng)進度值有變化的時候,就會回調(diào)該方法。
- (2)、異步加載資源,并把Progress對象綁定到加載過程。
// 定義進度報告器
var progress = new Progress<float>(value =>
{ //更新滑動條進度Debug.Log($"加載進度: {value * 100}%");
});//實際加載
await SceneManager.LoadSceneAsync("場景2", LoadSceneMode.Single).ToUniTask(progress: progress);
2、實際加載情況
-
(1)、存在的問題
要加載的是小場景時,進度報告非常絲滑,一點也不卡頓。
但是,讓要加載的是大場景的時候,update主線程卡死,進度不會走,一直在0%,等幾秒鐘之后,加載完畢時,進度突然跳成100%,這就太刺激了。
測試環(huán)境說明:Unity2021.3.40 -
(2)、改進方法
把進度條改成加載前、加載中和加載后,真正的加載放在【加載中】。
三、進度條的設(shè)計
(1)如何劃分進度條
我們把進度條分成三個部分,如下圖的階段一、二、三。
我們把加載的真正過程放在階段一和階段二之間,加載時,進度條暫停不動。
為了看起來絲滑一些,我們把進度條設(shè)置曲線動畫
(2)加載效果
四、如何給進度條配上曲線動畫
如何給滑動條設(shè)置曲線動畫,如下圖所示,在1秒內(nèi),讓滑塊從0%跑到33%,跑的時- 候不勻速,而是先快后慢。
(1)曲線運動速度的設(shè)置
public class JumpScene : MonoBehaviour
{/// <summary>/// 【第一段進度條動畫】的動畫曲線/// </summary>[Header("【第一段進度條動畫】的動畫曲線")]public AnimationCurve startAnim;//......
}
在面板上設(shè)置曲線速率
(2)曲線速率與運動的實現(xiàn):按照給定的曲線速度運動,而不是勻速運動
public async UniTask test4()
{var progress = 0.0f; //當(dāng)前累計耗時var duration = 3f; //給定的動畫時間,3秒var progressCurve = 0f; //曲線進度,按照曲線速度運動,而不是勻速運動while (true){progress += Time.deltaTime;progressCurve = startAnim.Evaluate(progress / duration) / duration; Debug.Log(progressCurve);if (progress > duration) break;await UniTask.Yield();}
}
五、主要代碼
(1)過程說明
大場景加載的時候,異步加載的進度值會卡死,因為update主線程卡死了
(無論是協(xié)程還是用異步,都沒有改觀,都是卡死狀態(tài))。
出現(xiàn)該情況的時候,進度會卡住,直到加載完畢,進度值突然從【0%】調(diào)到【90%】,
90%代表資源加載完畢,余下10%的時間是用來進行場景顯示和資源回收處理的。
1、前面三分之一:初始化…
2、…實際加載…
3、中間三分之一:假裝在加載…
4、后面三分之一:加載后資源處理…實際加載的時候,資源調(diào)入完畢時進度
(2)代碼清單
/// <summary>
/// 場景跳轉(zhuǎn):異步加載一個場景,顯示進度條,進度條滑動動畫
///
/// **************************************************************************
/// 大場景加載的時候,異步加載的進度值會卡死,因為update主線程卡死了
/// (無論是協(xié)程還是用異步,都沒有改觀,都是卡死狀態(tài))。
/// 出現(xiàn)該情況的時候,進度會卡住,直到加載完畢,進度值突然從【0%】調(diào)到【90%】,
/// 90%代表資源加載完畢,余下10%的時間是用來進行場景顯示和資源回收處理的。
/// 1、前面三分之一:初始化.........
/// 2、..............實際加載.......
/// 3、中間三分之一:假裝在加載.....
/// 4、后面三分之一:加載后資源處理...實際加載的時候,資源調(diào)入完畢時進度
///
/// **************************************************************************
///
/// </summary>
/// <param name="onValueChangedAction">加載進度變化時的回調(diào)方法</param>
/// <param name="sceneName">要加載的場景名字</param>
/// <param name="totalSliderTime">加載進度動畫時間</param>
/// <param name="startAnim">開始加載的動畫曲線</param>
/// <param name="midAnim">中間加載的動畫曲線</param>
/// <param name="endAnim">加載后處理的動畫曲線</param>
/// <returns></returns>
public static async UniTask LoadSceneAsyncWithSliderCurve(Action<float,string> onValueChangedAction,string sceneName,float totalSliderTime,AnimationCurve startAnim,AnimationCurve midAnim,AnimationCurve endAnim,CancellationToken ctk)
{ float progress = 0; //每次循環(huán)的累計耗時float duration = 0; //本次循環(huán)給定的用時float progressCurve = 0; //曲線百分比進度 //第一段:Debug.Log("執(zhí)行第一段");progress = 0.0f;duration = totalSliderTime / 3f;while (true){ progress += Time.deltaTime;progressCurve = startAnim.Evaluate(progress / duration)/3f;onValueChangedAction?.Invoke(progressCurve,"開始加載..."); Debug.Log(progressCurve);if (progress > duration) break;await UniTask.Yield(ctk);}//第二段://Debug.Log("執(zhí)行第二段");//實際加載onValueChangedAction?.Invoke(progressCurve, "賣力加載...");await SceneManager.LoadSceneAsync(sceneName);//完畢后補加動畫progress = 0.0f;duration = totalSliderTime / 3f;while (true){progress += Time.deltaTime;progressCurve = 1f/3f + midAnim.Evaluate(progress / duration) / 3f;onValueChangedAction?.Invoke(progressCurve, "賣力加載..."); Debug.Log(progressCurve);if (progress > duration) break;await UniTask.Yield(ctk);}//第三段//Debug.Log("執(zhí)行第三段"); progress = 0.0f;duration = totalSliderTime / 3f;while (true){progress += Time.deltaTime;progressCurve = 2f/3f + endAnim.Evaluate(progress / duration) / 3f;onValueChangedAction?.Invoke(progressCurve, "繼續(xù)加載..."); //Debug.Log(progressCurve);if (progress > duration) break;await UniTask.Yield(ctk);}onValueChangedAction?.Invoke(progressCurve, "加載完畢...");
}
六、測試
(1) 腳本掛載:
(2)測試的關(guān)鍵代碼
點擊按鈕,顯示進度條,加載中進度變化,加載完畢進度條隱藏
//按鈕邏輯——加載場景myButton.onClick.AddListener(async() => {//顯示進度UImySlider.gameObject.SetActive(true);myText.gameObject.SetActive(true);await UniTask.DelayFrame(1, cancellationToken: this.GetCancellationTokenOnDestroy());//加載的過程await LoadSceneAsyncWithSliderCurve(actionOnValueChanged,sceneName, totalSliderTime,startAnim,midAnim,endAnim,this.GetCancellationTokenOnDestroy());//加載完畢隱藏進度的UI...await UniTask.Delay(500, cancellationToken: this.GetCancellationTokenOnDestroy());mySlider.gameObject.SetActive(false);myText.gameObject.SetActive(false);});
七、Code source附錄
本文測試環(huán)境Editor,Unity2021.3.40,Win11
using UnityEngine;
using Cysharp.Threading.Tasks;//github-> UniTask包
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using TMPro;
using System;
using System.Threading;
using UnityEditor;/// <summary>
/// 場景跳轉(zhuǎn)的進度條效果:
/// 1、異步加載場景
/// 2、進度條分成三段:初始階段、加載階段、回復(fù)場景階段
/// 3、各段滑動條的動畫用【動畫曲線】控制
/// </summary>
public class JumpScene : MonoBehaviour
{/// <summary>/// 【第一段進度條動畫】的動畫曲線/// </summary>[Header("【第一段進度條動畫】的動畫曲線")]public AnimationCurve startAnim;/// <summary>/// 【第二段進度條動畫】的動畫曲線/// </summary>[Header("【第二段進度條動畫】的動畫曲線")]public AnimationCurve midAnim;/// <summary>/// 【第三段進度條動畫】的動畫曲線/// </summary>[Header("【第三段進度條動畫】的動畫曲線")]public AnimationCurve endAnim;/// <summary>/// 跳轉(zhuǎn)按鈕/// </summary>[Header("跳轉(zhuǎn)按鈕")]public Button myButton;/// <summary>/// 要跳轉(zhuǎn)的場景名字/// </summary>[Header("要跳轉(zhuǎn)的場景名字")]public string sceneName;/// <summary>/// 進度比值顯示框/// </summary>[Header("進度比值顯示框")]public TMP_Text myText;/// <summary>/// 進度條/// </summary>[Header("進度條")]public Slider mySlider;/// <summary>/// 進度條給定的動畫時間/// </summary>[Header("進度條給定的動畫時間:三個動畫階段平分時間")]public float totalSliderTime = 2f;// Start is called before the first frame updatevoid Start(){DontDestroyOnLoad(this);//加載進度變化的回調(diào)方法,進度值變化的時候調(diào)用// value:進度條的值,從0-1// tips:提示信息,加載中,努力加載中...加載完畢Action<float,string> actionOnValueChanged = (value,tips) => {mySlider.value = value; //進度條更新myText.text = $"{tips} : {(int)(value * 100)}%"; //匹配的提示信息//Debug.Log($"加載進度為:{(int)(value * 100)}%"); };//按鈕邏輯——加載場景myButton.onClick.AddListener(async() => {//顯示進度UImySlider.gameObject.SetActive(true);myText.gameObject.SetActive(true);await UniTask.DelayFrame(1, cancellationToken: this.GetCancellationTokenOnDestroy());//加載的過程await LoadSceneAsyncWithSliderCurve(actionOnValueChanged,sceneName, totalSliderTime,startAnim,midAnim,endAnim,this.GetCancellationTokenOnDestroy());//加載完畢隱藏進度的UI...await UniTask.Delay(500, cancellationToken: this.GetCancellationTokenOnDestroy());mySlider.gameObject.SetActive(false);myText.gameObject.SetActive(false);});}/// <summary>/// 場景跳轉(zhuǎn):異步加載一個場景,顯示進度條,進度條滑動動畫/// /// **************************************************************************/// 大場景加載的時候,異步加載的進度值會卡死,因為update主線程卡死了/// (無論是協(xié)程還是用異步,都沒有改觀,都是卡死狀態(tài))。/// 出現(xiàn)該情況的時候,進度會卡住,直到加載完畢,進度值突然從【0%】調(diào)到【90%】,/// 90%代表資源加載完畢,余下10%的時間是用來進行場景顯示和資源回收處理的。/// 1、前面三分之一:初始化........./// 2、..............實際加載....... /// 3、中間三分之一:假裝在加載...../// 4、后面三分之一:加載后資源處理...實際加載的時候,資源調(diào)入完畢時進度/// /// ************************************************************************** /// /// </summary>/// <param name="onValueChangedAction">加載進度變化時的回調(diào)方法</param>/// <param name="sceneName">要加載的場景名字</param>/// <param name="totalSliderTime">加載進度動畫時間</param>/// <param name="startAnim">開始加載的動畫曲線</param>/// <param name="midAnim">中間加載的動畫曲線</param>/// <param name="endAnim">加載后處理的動畫曲線</param>/// <returns></returns>public static async UniTask LoadSceneAsyncWithSliderCurve(Action<float,string> onValueChangedAction,string sceneName,float totalSliderTime,AnimationCurve startAnim,AnimationCurve midAnim,AnimationCurve endAnim,CancellationToken ctk){ float progress = 0; //每次循環(huán)的累計耗時float duration = 0; //本次循環(huán)給定的用時float progressCurve = 0; //曲線百分比進度 //第一段:Debug.Log("執(zhí)行第一段");progress = 0.0f;duration = totalSliderTime / 3f;while (true){ progress += Time.deltaTime;progressCurve = startAnim.Evaluate(progress / duration)/3f;onValueChangedAction?.Invoke(progressCurve,"開始加載..."); Debug.Log(progressCurve);if (progress > duration) break;await UniTask.Yield(ctk);}//第二段://Debug.Log("執(zhí)行第二段");//實際加載onValueChangedAction?.Invoke(progressCurve, "賣力加載...");await SceneManager.LoadSceneAsync(sceneName);//完畢后補加動畫progress = 0.0f;duration = totalSliderTime / 3f;while (true){progress += Time.deltaTime;progressCurve = 1f/3f + midAnim.Evaluate(progress / duration) / 3f;onValueChangedAction?.Invoke(progressCurve, "賣力加載..."); Debug.Log(progressCurve);if (progress > duration) break;await UniTask.Yield(ctk);}//第三段//Debug.Log("執(zhí)行第三段"); progress = 0.0f;duration = totalSliderTime / 3f;while (true){progress += Time.deltaTime;progressCurve = 2f/3f + endAnim.Evaluate(progress / duration) / 3f;onValueChangedAction?.Invoke(progressCurve, "繼續(xù)加載..."); //Debug.Log(progressCurve);if (progress > duration) break;await UniTask.Yield(ctk);}onValueChangedAction?.Invoke(progressCurve, "加載完畢...");}public async UniTask LoadTest1(){//定義加載任務(wù)AsyncOperation loadTask = SceneManager.LoadSceneAsync("SceneName");loadTask.allowSceneActivation = false; //加載完畢場景不立即顯示await loadTask; //實際加載過程//此處,用戶要進行其他操作,比如點擊一個按鈕,把數(shù)據(jù)上傳到服務(wù)器//...loadTask.allowSceneActivation = true; //設(shè)置成true后,系統(tǒng)開始后續(xù)的10%的工作,會把新場景顯示,把老場景卸載}public async UniTask LoadTest2(){//定義加載任務(wù)AsyncOperation loadTask = SceneManager.LoadSceneAsync("茶樹葉片形態(tài)觀察", LoadSceneMode.Additive);//SceneManager.SetActiveScene(SceneManager.GetSceneByName("新加入的場景名字"));//loadTask.allowSceneActivation = false; //加載完畢場景不立即顯示,處于隱藏狀態(tài)await loadTask; //實際加載過程//await UniTask.Delay(1000); //等待1秒 //loadTask.allowSceneActivation = true; //把新場景顯示出來}[ContextMenu("Additive加載模式測試")]void test2(){LoadTest2().Forget();}void test(){var myScene = SceneManager.GetSceneByName("場景名字");SceneManager.SetActiveScene(myScene);}public async UniTask test3(){// 定義進度報告器var progress = new Progress<float>(value =>{ //更新滑動條進度Debug.Log($"加載進度: {value * 100}%");});//實際加載await SceneManager.LoadSceneAsync("場景2", LoadSceneMode.Single).ToUniTask(progress: progress); }public async UniTask test4(){var progress = 0.0f; //當(dāng)前累計耗時var duration = 3f; //給定的動畫時間,3秒var progressCurve = 0f; //曲線進度,按照曲線速度運動,而不是勻速運動while (true){progress += Time.deltaTime;progressCurve = startAnim.Evaluate(progress / duration) / duration; Debug.Log(progressCurve);if (progress > duration) break;await UniTask.Yield();}}
}