Unity中的场景管理

一、简述

        在我们实际的项目开发中,项目中通常有一个以上的场景Scene,尤以MOBA和RGP有大量的Scene。场景的加载为何用异步加载,最为主要的是用户体验。当Scene较大时,使用同步加载时,直接在内存中映射加载,会造成许久的卡顿后才正常显示,且用户体验很差,所以选择异步加载且用一个过渡的UI界面来显示场景的加载进步。


二、基本思路

        在当前场景中开启一个协程Coroutines,并使用UnityEngine.SceneManagement.SceneManager中的异步加载方法LoadSceneAsync(string sceneName)。详细代码请见下方。

private string strNextSceneName = null;//下一需要加载的场景

private void LoadScene(string sceneName)

{

strNextSceneName = sceneName;

StartCoroutine(StartLoadScene());//开启协程

}

private IEnumerator StartLoadScene() {

AsyncOperation async = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(strNextSceneName);//使用异步加载

if (null == async)

{

yield break;//关闭协程

}

}

三、项目开发思路

        在实际项目中,我们可能会返回上一个场景,用一个UI界面来显示场景加载的进度,且在加载完成后回调。那么新建场景管理类来统一调度场景。

using System;

using System.Collections;

using UnityEngine;

/// /// 场景管理类///

public class SceneManager : MonoBehaviour{

private static SceneManager _mInstance;//私有单例

 private Action onSceneLoaded = null;//场景加载完成回调

private string strNextSceneName = null;//将要加载的场景名

private string strCurSceneName = null;//当前场景名,如若没有场景,则默认返回Login

private string strPreSceneName = null;//上一个场景名

private bool bLoading = false; //是否正在加载中

private bool bDestroyAuto = true;//自动删除loading背景

private const string _strLoadSceneName = "LoadingScene"; //加载场景名字

/// /// 获取当前场景名 ///

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; }

}

/// /// 加载前一个场景 ///

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);

}

//加载场景 bDestroyAuto 自动删除loading背景

void LoadLevel(string strLevelName, Action onSecenLoaded, bool isDestroyAuto = true) {

//是否可加载

if (bLoading) { return; }

if (strCurSceneName == strLevelName) {

//return;

}

bLoading = true; //锁屏

//开始加载 onSceneLoaded = onSecenLoaded;

strNextSceneName = strLevelName;

strPreSceneName = strCurSceneName;

strCurSceneName = _strLoadSceneName;

bDestroyAuto = isDestroyAuto;

//加载进度UI界面

InitLoadingUI();

StartCoroutine(StartLoadSceneOnEditor(_strLoadSceneName, OnLoadingSceneLoaded, null)); //StartCoroutine(StartLoadSceneOnEditor(strNextSceneName, OnNextSceneLoaded, OnNextSceneProgress));

}

//加载显示进度的UI界面

private void InitLoadingUI() {

UILoadingView.Show();//UI加载进度界面

}

//当加载过度场景加载完成

void OnLoadingSceneLoaded() {

StartCoroutine(StartLoadSceneOnEditor(strNextSceneName, OnNextSceneLoaded, OnNextSceneProgress));

}

//场景加载进度变化

void OnNextSceneProgress(float fProgress) {

UILoadingView.Instance.UpdateProgress(fProgress);

}

//加载下一场景王城回调

void OnNextSceneLoaded() {

bLoading = false;

OnNextSceneProgress(1);

strCurSceneName = strNextSceneName;

strNextSceneName = null;

onSceneLoaded?.Invoke();

}

//开始加载

private IEnumerator StartLoadSceneOnEditor(string strLevelName, Action OnSecenLoaded, Action OnSceneProgress) {

AsyncOperation async = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(strLevelName);

if (null == async) {

yield break;

}

while (!async.isDone) {//是否加载完成

//若未加载完成,则回调进度Action

OnSceneProgress?.Invoke(async.progress);

yield return null;

}

OnSecenLoaded?.Invoke();

}

}


注:

        如果考虑到代码性能的话,进度回调和加载完成回调使用delegate委托,去除System.dll的引用;

        协程的yield return 0应该使用yield return null,减少GC,因为0=>nul中有个装箱过程,会产生不必要的GC;

        协程的开启需要在MonoBehaviour中实现,则需要SceneManager的实例,且挂在场景中,保证场景在加载中不被摧毁;

你可能感兴趣的:(Unity中的场景管理)