一谓媒、簡(jiǎn)述
? ? ? ? 在我們實(shí)際的項(xiàng)目開(kāi)發(fā)中蚯斯,項(xiàng)目中通常有一個(gè)以上的場(chǎng)景Scene,尤以MOBA和RGP有大量的Scene牢贸。場(chǎng)景的加載為何用異步加載竹观,最為主要的是用戶體驗(yàn)。當(dāng)Scene較大時(shí)十减,使用同步加載時(shí)栈幸,直接在內(nèi)存中映射加載,會(huì)造成許久的卡頓后才正常顯示帮辟,且用戶體驗(yàn)很差速址,所以選擇異步加載且用一個(gè)過(guò)渡的UI界面來(lái)顯示場(chǎng)景的加載進(jìn)步。
二由驹、基本思路
????????在當(dāng)前場(chǎng)景中開(kāi)啟一個(gè)協(xié)程Coroutines芍锚,并使用UnityEngine.SceneManagement.SceneManager中的異步加載方法LoadSceneAsync(string sceneName)。詳細(xì)代碼請(qǐng)見(jiàn)下方蔓榄。
private string strNextSceneName = null;//下一需要加載的場(chǎng)景
private void LoadScene(string sceneName)
{
strNextSceneName = sceneName;
StartCoroutine(StartLoadScene());//開(kāi)啟協(xié)程
}
private IEnumerator StartLoadScene() {
AsyncOperation async = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(strNextSceneName);//使用異步加載
if (null == async)
{
yield break;//關(guān)閉協(xié)程
}
}
三并炮、項(xiàng)目開(kāi)發(fā)思路
? ? ? ? 在實(shí)際項(xiàng)目中,我們可能會(huì)返回上一個(gè)場(chǎng)景甥郑,用一個(gè)UI界面來(lái)顯示場(chǎng)景加載的進(jìn)度逃魄,且在加載完成后回調(diào)。那么新建場(chǎng)景管理類來(lái)統(tǒng)一調(diào)度場(chǎng)景澜搅。
using System;
using System.Collections;
using UnityEngine;
/// /// 場(chǎng)景管理類///
public class SceneManager : MonoBehaviour{
private static SceneManager _mInstance;//私有單例
?private Action onSceneLoaded = null;//場(chǎng)景加載完成回調(diào)
private string strNextSceneName = null;//將要加載的場(chǎng)景名
private string strCurSceneName = null;//當(dāng)前場(chǎng)景名伍俘,如若沒(méi)有場(chǎng)景,則默認(rèn)返回Login
private string strPreSceneName = null;//上一個(gè)場(chǎng)景名
private bool bLoading = false; //是否正在加載中
private bool bDestroyAuto = true;//自動(dòng)刪除loading背景
private const string _strLoadSceneName = "LoadingScene"; //加載場(chǎng)景名字
/// /// 獲取當(dāng)前場(chǎng)景名 ///
public static string strLoadedSceneName => _mInstance.strCurSceneName;
public static void CreateInstance(GameObject go) {
if (null != _mInstance) return;
{
_mInstance = go.AddComponent();
DontDestroyOnLoad(_mInstance);
_mInstance.strCurSceneName = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name; }
}
/// /// 加載前一個(gè)場(chǎng)景 ///
public static void LoadPreScene() {
if (string.IsNullOrEmpty(_mInstance.strPreSceneName)) return;
{
LoadScene(_mInstance.strPreSceneName); }
}
public static void LoadScene(string strLevelName) {
_mInstance.LoadLevel(strLevelName,null);
}
public static void LoadScene(string strLevelName, Action onSecenLoaded) {
_mInstance.LoadLevel(strLevelName, onSecenLoaded);
}
//加載場(chǎng)景 bDestroyAuto 自動(dòng)刪除loading背景
void LoadLevel(string strLevelName, Action onSecenLoaded, bool isDestroyAuto = true) {
//是否可加載
if (bLoading) { return; }
if (strCurSceneName == strLevelName) {
//return;
}
bLoading = true; //鎖屏
//開(kāi)始加載 onSceneLoaded = onSecenLoaded;
strNextSceneName = strLevelName;
strPreSceneName = strCurSceneName;
strCurSceneName = _strLoadSceneName;
bDestroyAuto = isDestroyAuto;
//加載進(jìn)度UI界面
InitLoadingUI();
StartCoroutine(StartLoadSceneOnEditor(_strLoadSceneName, OnLoadingSceneLoaded, null)); //StartCoroutine(StartLoadSceneOnEditor(strNextSceneName, OnNextSceneLoaded, OnNextSceneProgress));
}
//加載顯示進(jìn)度的UI界面
private void InitLoadingUI() {
UILoadingView.Show();//UI加載進(jìn)度界面
}
//當(dāng)加載過(guò)度場(chǎng)景加載完成
void OnLoadingSceneLoaded() {
StartCoroutine(StartLoadSceneOnEditor(strNextSceneName, OnNextSceneLoaded, OnNextSceneProgress));
}
//場(chǎng)景加載進(jìn)度變化
void OnNextSceneProgress(float fProgress) {
UILoadingView.Instance.UpdateProgress(fProgress);
}
//加載下一場(chǎng)景王城回調(diào)
void OnNextSceneLoaded() {
bLoading = false;
OnNextSceneProgress(1);
strCurSceneName = strNextSceneName;
strNextSceneName = null;
onSceneLoaded?.Invoke();
}
//開(kāi)始加載
private IEnumerator StartLoadSceneOnEditor(string strLevelName, Action OnSecenLoaded, Action OnSceneProgress) {
AsyncOperation async = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(strLevelName);
if (null == async) {
yield break;
}
while (!async.isDone) {//是否加載完成
//若未加載完成勉躺,則回調(diào)進(jìn)度Action
OnSceneProgress?.Invoke(async.progress);
yield return null;
}
OnSecenLoaded?.Invoke();
}
}
注:
? ? ? ? 如果考慮到代碼性能的話癌瘾,進(jìn)度回調(diào)和加載完成回調(diào)使用delegate委托,去除System.dll的引用饵溅;
????????協(xié)程的yield return 0應(yīng)該使用yield return null妨退,減少GC,因?yàn)?=>nul中有個(gè)裝箱過(guò)程,會(huì)產(chǎn)生不必要的GC咬荷;
? ? ? ? 協(xié)程的開(kāi)啟需要在MonoBehaviour中實(shí)現(xiàn)冠句,則需要SceneManager的實(shí)例,且掛在場(chǎng)景中萍丐,保證場(chǎng)景在加載中不被摧毀轩端;