参考
https://www.bilibili.com/video/BV1Ut4y1m7Zv?p=19
Unity异步加载场景SceneManager.LoadSceneAsync与AsyncOperation的使用
一、同步加载 SceneManager.LoadScene
https://docs.unity3d.com/cn/2019.4/ScriptReference/SceneManagement.SceneManager.LoadScene.html
注意:在大多数情况下,为了避免在加载时出现暂停或性能中断现象, 您应该使用此命令的异步版,即: LoadSceneAsync。
public static void LoadScene (int sceneBuildIndex,
SceneManagement.LoadSceneMode mode= LoadSceneMode.Single);
public static void LoadScene (string sceneName,
SceneManagement.LoadSceneMode mode= LoadSceneMode.Single);
1.名称或路径或索引
- sceneName 要加载的场景的名称或路径。
- sceneBuildIndex Build Settings 中要加载场景的索引。
提供的 sceneName 可以只是场景名称(不包含 .unity 扩展名),也可以是 BuildSettings 窗口中显示的路径(仍然不包含 .unity 扩展名)。如果仅提供场景名称,此方法将加载场景列表中匹配的第一个场景。如果有多个名称相同但路径不同的场景,应该使用完整路径。
2.LoadSceneMode
https://docs.unity3d.com/cn/2019.4/ScriptReference/SceneManagement.LoadSceneMode.html
- Single 关闭所有当前加载的场景 并加载一个场景。
- Additive 将场景添加到当前加载的场景。
用户可以通过LoadSceneMode来指定不同的加载模式。LoadSceneMode.Single在加载之前会卸载其他所有的场景,LoadSceneMode.Additive则是加载的时候不关闭之前的场景。
还有一点很重要的是LoadScene()并不是完全同步的,它只能保证在下一帧开始之前加载完毕。所以在此推荐大家使用LoadSceneAsync()这个异步的加载方法。
使用 SceneManager.LoadScene 时,不会立即加载场景,而是在下一帧加载。这种半异步的行为可能会导致帧卡顿,并可能令人困惑,因为加载无法立即完成。
参考Unity SceneManager.LoadScene之后不能马上SetActiveScene
SceneManager.LoadScene("Scene2", LoadSceneMode.Additive);
var scene = SceneManager.GetSceneByName("Scene2");
Debug.Log(scene.name);
//SceneManager.SetActiveScene(scene);
SceneManager.sceneLoaded += (Scene sc, LoadSceneMode loadSceneMode) =>
{
SceneManager.SetActiveScene(scene);
};
二、异步加载 SceneManager.LoadSceneAsync
https://docs.unity3d.com/cn/2019.4/ScriptReference/SceneManagement.SceneManager.LoadSceneAsync.html
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);
parameters 用于将各种参数(除了名称和索引)收集到单个位置的结构。
1.返回值AsyncOperation
您可以 yield 直到异步操作继续,或手动检查它已完成 (isDone) 还是正在进行 (progress)。
- allowSceneActivation 允许在场景准备就绪后立即激活场景。当我们不允许时,即使加载完成,也不会跳转。
- isDone 操作是否已完成?(只读)。实际上用到比较少,较多的还是用进度。因为这个属性是要加载完毕并且开启跳转后,跳转成功才会变成完成
- priority Priority 允许您调整执行异步操作调用的顺序。
- progress 获取操作进度(只读)。实际上到0.9就已经加载完毕。
2.官方加载示例
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Example : MonoBehaviour
{
void Update()
{
// Press the space key to start coroutine
if (Input.GetKeyDown(KeyCode.Space))
{
// Use a coroutine to load the Scene in the background
StartCoroutine(LoadYourAsyncScene());
}
}
IEnumerator LoadYourAsyncScene()
{
// The Application loads the Scene in the background as the current Scene runs.
// This is particularly good for creating loading screens.
// You could also load the Scene by using sceneBuildIndex. In this case Scene2 has
// a sceneBuildIndex of 1 as shown in Build Settings.
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync("Scene2");
// Wait until the asynchronous scene fully loads
while (!asyncLoad.isDone)
{
yield return null;
}
}
}
三、进度条实例
参考Unity进度条 异步加载SceneManager.LoadSceneAsync
新建LoadingScene,添加一个最大值为100的Slider,和一个Text
然后将脚本挂在GameObject上即可:
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class LoadingSceneScript : MonoBehaviour
{
public Slider slider;
public Text text;//百分制显示进度加载情况
void Start()
{
StartCoroutine(loginMy());
}
IEnumerator loginMy()
{
int displayProgress = 0;
int toProgress = 0;
AsyncOperation op = SceneManager.LoadSceneAsync("Scenes/modelScene");
op.allowSceneActivation = false;
while (op.progress < 0.9f) //此处如果是 <= 0.9f 则会出现死循环所以必须小0.9
{
toProgress = (int)op.progress * 100;
while (displayProgress < toProgress)
{
++displayProgress;
SetLoadingPercentage(displayProgress);
yield return new WaitForEndOfFrame();//ui渲染完成之后
}
}
toProgress = 100;
while (displayProgress < toProgress)
{
++displayProgress;
SetLoadingPercentage(displayProgress);
yield return new WaitForEndOfFrame();
}
op.allowSceneActivation = true;
}
private void SetLoadingPercentage(int displayProgress)
{
slider.value = displayProgress;
text.text = displayProgress.ToString() + "%";
}
}
如果报错,检查一下要加载的场景是否添加到BuildSettings:
四、DontDestroyOnLoad
参考
https://docs.unity3d.com/cn/2019.4/ScriptReference/Object.DontDestroyOnLoad.html
DontDestroyOnLoad的使用
加载新的 Scene 会销毁所有现有的 Scene 对象。调用 Object.DontDestroyOnLoad 可以在关卡加载期间保留 Object。如果目标 Object 是组件或 GameObject,Unity 还会保留 Transform 的所有子项。 Object.DontDestroyOnLoad 不会返回值。
public class ExampleClass :MonoBehaviour{
void Awake() {
DontDestroyOnLoad(transform.gameObject);
}
}
由于使用DontDestroyOnLoad的物体不会被释放掉,假设我们写了上面的代码,而物体所在的游戏场景又可以重复进入的时候,游戏物体就会在每次进入场景的时候创建一次,甚至可以无限创建下去,这样的处理明显不妥。
1.方案1
在每次调用DontDestroyOnLoad的时候,都去判断场景中是否有对应的物体,如果没有再去创建
void Awake() {
if(Gameobject.Find("GlobalController"))
DontDestroyOnLoad(transform.gameObject);
}
2.方案2
把DontDestroyOnLoad的调用写到最初始的场景,并且保证相应的场景中不存在再次进入的可能性
3.方案3
把使用DontDestroyOnLoad的脚本进行静态初始化,在静态初始化的时候进行DontDestroyOnLoad操作
public class Global:MonoBehaviour
{
public static Globalinstance;
static Global()
{
GameObjectgo=newGameObject("Globa");
DontDestroyOnLoad(go);
instance=go.AddComponent();
}
public voidDoSomeThings()
{
Debug.Log("DoSomeThings");
}
voidStart()
{
Debug.Log("Start");
}
}
4.官方示例
该示例中的 scene1 开始播放来自 AudioSource 的背景音乐。在 scene2 加载时,音乐继续播放。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// Object.DontDestroyOnLoad example.
//
// This script example manages the playing audio. The GameObject with the
// "music" tag is the BackgroundMusic GameObject. The AudioSource has the
// audio attached to the AudioClip.
public class DontDestroy : MonoBehaviour
{
void Awake()
{
GameObject[] objs = GameObject.FindGameObjectsWithTag("music");
if (objs.Length > 1)
{
Destroy(this.gameObject);
}
DontDestroyOnLoad(this.gameObject);
}
}