美文网首页
Unity中的场景管理

Unity中的场景管理

作者: JCone | 来源:发表于2018-04-03 15:44 被阅读0次

    一、简述

            在我们实际的项目开发中,项目中通常有一个以上的场景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中的场景管理

          本文链接:https://www.haomeiwen.com/subject/etsqhftx.html