Unity异步场景加载

在游戏中,场景加载是很常用的功能。如果要在加载的时候让当前的场景还能正常运行,或者加载时能有一个包含进度条之类组件的加载界面,就应该采取异步的场景加载方式。

同步加载

在讲异步加载以前,先说同步加载。

当执行同步加载时,正在运行的场景会被阻塞,产生卡顿,这种方法适合小型的、不怎么需要获取读取进度的场景的加载。如在地牢中切换到另一个房间,读取完成之前当前房间中的一切都静止不动,这种情况应该是用同步加载实现的。

Unity使用SceneManager类的静态方法LoadScene实现同步的场景加载。由于是静态方法,不需要对SceneManager类进行实例化,只要使用命名空间using UnityEngine.SceneManagement;就可直接通过SceneManager.LoadSceneAsync访问。

1
2
3
4
5
//方法原型:

public static void LoadScene(int sceneBuildIndex, SceneManagement.LoadSceneMode mode = LoadSceneMode.Single);

public static void LoadScene(string sceneName, SceneManagement.LoadSceneMode mode = LoadSceneMode.Single);

其中,第一个参数可以是场景的编号sceneBuildIndex,也可以是场景的名称sceneName

什么是场景编号?

场景编号是工程中每个场景唯一的int型编号,记录在Unity的File->Build Settings->Scenes in Build中。如果没有看到你想要的场景,可以通过Build Settings中的Add Open Scenes加入场景。

第二个参数所说的LoadSceneMode是一个枚举类,包含AddtiveSingle两种加载方法。前者是在保留当前场景的基础上加载其他场景,如果是临时打开一个全屏的设置界面,就可以使用这种加载方式;而后者在加载完新场景之后就不保留原来的场景,适合进行关卡的切换等。

异步加载

异步加载和同步加载大同小异,但是它通过C#的“协程”使得加载过程看起来是在后台运行。

1
2
3
4
5
6
7
8
9
10
//方法原型:

public static AsyncOperation LoadSceneAsync(string sceneName, SceneManagement.LoadSceneMode mode = LoadSceneMode.Single);

public static AsyncOperation LoadSceneAsync(int sceneBuildIndex, SceneManagement.LoadSceneMode mode = LoadSceneMode.Single);

public static AsyncOperation LoadSceneAsync(string sceneName, SceneManagement.LoadSceneParameters parameters);

public static AsyncOperation LoadSceneAsync(int sceneBuildIndex, SceneManagement.LoadSceneParameters parameters);

方法原型与同步加载非常相似,方法名也只是加了一个“Async”。但最大的不同是,LoadSceneAsync返回的是一个AsyncOperation类对象,用于对后台的加载过程进行监测,其中有一个比较有意思的bool型属性allowSceneActivation,如果将其设置为false,加载进度public float progress会在0.9处暂停,直到将allowSceneActivation设置为true时再继续加载剩下的10%。

需要注意,Unity并不支持多线程,这里使用的协程实际上是一种Unity主线程上的伪线程。在VS的线程调试窗口里也可以看到协程启动后其实仍然在原来的线程里,我认为它大概和传说中的纤程就是一种东西,而后面的几个工作线程应当是C#的CLR开辟的。

纤程?

1
2
3
4
5
6
7
8
9
10
11
12
13

//这里给出一个简单的例子:
IEnumerator AsyncLoading()
{
yield return new WaitForSeconds(3); //加载太快了,先播上3s的加载动画
operation = SceneManager.LoadSceneAsync("StartMenuScene", mode: LoadSceneMode.Single);
while (!operation.isDone)
{
Debug.Log(operation.progress);
yield return null;
}
}

参考资料

  1. Unity - SceneManager.LoadScene
  2. Unity - SceneManager.LoadSceneAsync
  3. 知乎 - Unity 的多线程、协程、纤程
  4. CSDN - Unity多线程、线程池的使用
  5. CSDN - Unity异步加载场景SceneManager.LoadSceneAsync与AsyncOperation的使用

虽然是个很简单的需求,但是涉及的东西不少也不简单,我至今也没完全搞明白……