unity scene 无缝切换(缓存)

来源:互联网 发布:网络流行歌曲大全 编辑:程序博客网 时间:2024/06/08 17:29

场景切换的时候经常会造成卡顿,导致用户体验并不好。
如何能做到不卡呢,有一种方式是做成prefab,然后通过启用禁用来模拟切场景,但是现在遇到的情况是希望烘焙出的lightmap也能够进行切换。而在unity5中动态的去切lightmap有点麻烦,没有去深入研究,于是采用了下面缓存场景的做法。

假设场景切换顺序:Main->A->B->C->Main
从Main场景切到A,A加载完后希望在A运行的同时能够缓存B场景。这样待会从B切到C时B因为已经缓存过了,加载耗时会比较短,B加载同时又缓存C场景,缓存C的过程不会造成B的卡顿,之后会有解释。

异步切换场景的核心代码如下

LoadAsyncApi(ref _curResult,_curSceneName);yield return StartCoroutine(LoadInProgress(_curResult));yield return StartCoroutine(LoadAfterProgress(_curResult, _curSceneName));    private void LoadAsyncApi(ref AsyncOperation result,string sceneName)    {        result = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);    }    //0-0.9    private IEnumerator LoadInProgress(AsyncOperation result)    {        result.allowSceneActivation = false;        while (result.progress < 0.9f)        {            yield return null;        }    }    //0.9-1(start awake ,not include coroutine)    private IEnumerator LoadAfterProgress(AsyncOperation result,string sceneName )    {        result.allowSceneActivation = true;        while (!SceneManager.GetSceneByName(sceneName).isLoaded)        {            yield return null;        }    }

调用LoadSceneAsyc异步加载,然后将之后的加载分为两步。
allowSceneActivation很多帖子写的都很懵逼,今天我来讲清楚。。。
1. 0-0.9(阶段1)
allowSceneActivation设为false的时候,阻止Scene的完全激活,这时进度会一直卡在0.9。这个阶段会做一些磁盘相关的IO操作,比如载入资源等,这些操作不会阻塞当前主线程。当你的场景中引用了比较多的material啊,texture啊,那很多时间会花费在这里。
2. 0.9-1(阶段2)
将allowSceneActivation设为true,这个过程做的事情包括所有的awake,start函数的执行以及setactive的时间,应该也包括所有组件如collider,animator内部初始化相关的操作。通过实验发现,直接在场景中加入成千上万个立方体,最后场景加载的时间基本都花在这个阶段。你手动启用这些物体也是会造成卡顿的。这一步会造成主进程阻塞,会使得unity难以避免的卡顿,这一步暂无办法去优化。

了解了这两步之后,实现这个需求就很清楚了。
Main切A加载B:
执行A的阶段1,阶段2,unload掉Main,再执行B的阶段1,让B卡在progress为0.9的位置。真正加载B的时候再执行B的阶段2。如果说场景中没有大量物体,而引用的资源又大又多,是可以做到秒切场景的。
提供了两个函数,EnterScene需要提供切换到的场景和需要缓存的场景。当不需要走这套机制,直接从场景中跳出到别的场景时,直接将precachescene设为null或者直接调用EnterOutScene退出即可。

完整代码:
DialogSceneMgr

using UnityEngine;using System.Collections;using System.Collections.Generic;using System.Runtime.CompilerServices;using UnityEngine.SceneManagement;public class DialogSceneMgr : Singleton<DialogSceneMgr>{    private string _curSceneName;    private string _precacheSceneName;    private bool _fromOuterScene = false;    private AsyncOperation _curResult;    private AsyncOperation _precacheResult;    void OnGUI()    {        if (GUILayout.Button("BaseLight2 cache BaseLight", GUILayout.Width(200)))        {            EnterScene("BaseLight2", "BaseLight");            //DialogSceneMgr.Instance.InitScenes(new List<string>() { "BaseLight", "BaseLight2" });        }        if (GUILayout.Button("BaseLight cache Context", GUILayout.Width(200)))        {            EnterScene("BaseLight","Context");        }        if (GUILayout.Button("SwitchOut", GUILayout.Width(200)))        {            EnterOuterScene("Context");        }//        if (GUILayout.Button("BaseLight2", GUILayout.Width(200)))//        {//            EnterScene("BaseLight2", null);//        }//        if (GUILayout.Button("BaseLight", GUILayout.Width(200)))//        {//            EnterScene("BaseLight", null);//        }        if (_curResult!=null)            GUILayout.TextArea("CurProgress:" + _curResult.progress);        if (_precacheResult != null)            GUILayout.TextArea("PrecacheProgress:" + _precacheResult.progress);    }    public void EnterScene(string sceneName,string precacheSceneName )    {        _curSceneName = sceneName;        //没有precache的scene说明是第一次进入        _fromOuterScene = string.IsNullOrEmpty(_precacheSceneName);        //缓存的场景和即将进入的场景不一致直接Load场景        if (_curSceneName != _precacheSceneName && !_fromOuterScene)        {            Clear();            SceneManager.LoadScene(_curSceneName);            return;        }        _precacheSceneName = precacheSceneName;        StartCoroutine(LoadSceneCor());    }    private void Clear()    {        _precacheSceneName = null;    }    public void EnterOuterScene(string sceneName)    {        EnterScene(sceneName, null);    }    private IEnumerator LoadSceneCor()    {        //from prev to cur        if (_fromOuterScene)        {            LoadAsyncApi(ref _curResult,_curSceneName);            yield return StartCoroutine(LoadInProgress(_curResult));            yield return StartCoroutine(LoadAfterProgress(_curResult, _curSceneName));        }        else        {            _curResult = _precacheResult;            yield return StartCoroutine(LoadAfterProgress(_curResult, _curSceneName));        }        UnLoadPrevScene();        SetActiveScene(_curSceneName);        //precache next        Debug.Log("precache next");        //没有缓存场景了,不需要缓存        if (string.IsNullOrEmpty(_precacheSceneName))        {            yield break;        }        LoadAsyncApi(ref _precacheResult,_precacheSceneName);        yield return StartCoroutine(LoadInProgress(_precacheResult));    }    private void UnLoadPrevScene()    {        Scene prevScene = SceneManager.GetActiveScene();        SceneManager.UnloadScene(prevScene.name);    }    private void SetActiveScene(string sceneName)    {        SceneManager.SetActiveScene(SceneManager.GetSceneByName(sceneName));    }    //0-0.9    private IEnumerator LoadInProgress(AsyncOperation result)    {        result.allowSceneActivation = false;        while (result.progress < 0.9f)        {            yield return null;        }    }    private void LoadAsyncApi(ref AsyncOperation result,string sceneName)    {        result = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);    }    //0.9-1(start awake ,not include coroutine)    private IEnumerator LoadAfterProgress(AsyncOperation result,string sceneName )    {        result.allowSceneActivation = true;        while (!SceneManager.GetSceneByName(sceneName).isLoaded)        {            yield return null;        }    }}

附singleton

using System;using UnityEngine;using System.Collections;public class Singleton<T> : MonoBehaviour where T : Singleton<T>{    private static T mInstance;    private static bool isQuiting;    public static T Instance    {        get        {            if (mInstance == null && !isQuiting)            {                mInstance = new GameObject("(Singleton) " + typeof (T).Name).AddComponent<T>();            }            return mInstance;        }    }    private void Awake()    {        T instance = this as T;        if (mInstance != null && mInstance != instance)        {            T cacheInstance = mInstance;//因为下一步destroy的时候更改了static的mInstance            DestroyImmediate(this.gameObject);            mInstance = cacheInstance;            return;        }        // 切换场景不要销毁GameObject        DontDestroyOnLoad(gameObject);        mInstance = instance;        OnInit();    }    private void OnDestroy()    {        OnRelease();        mInstance = null;    }    private void OnApplicationQuit()    {        isQuiting = true;    }    /// <summary>    /// 初始化    /// </summary>    protected virtual void OnInit()    {    }    /// <summary>    /// 释放    /// </summary>    protected virtual void OnRelease()    {    }}
0 0
原创粉丝点击