如何等到场景完全加载?
How to wait until a scene is fully loaded?
我需要等到场景完全加载才能将 gameObject
从 DontDestroyOnLoad
场景移动到它。如果我做得太早(就在调用 SceneManager.LoadScene()
之后),那么 gameObject
就会消失。基于this post我实现了一个场景加载class来解决这个问题:
public static class CustomSceneManager
{
public delegate void SceneChange(string sceneName);
public static event SceneChange LoadScene;
public static event SceneChange UnloadScene;
private static IEnumerator LoadLevel (string sceneName){
var asyncLoadLevel = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single);
while (!asyncLoadLevel.isDone){
Debug.Log("Loading the Scene");
yield return null;
}
}
public static void OnLoadScene(string newSceneName)
{
OnUnloadScene(newSceneName);
LoadLevel(newSceneName);
LoadScene?.Invoke(newSceneName);
}
private static void OnUnloadScene(string newSceneName)
{
UnloadScene?.Invoke(newSceneName);
}
}
我正在从中调用两个事件(LoadScene
和 UnloadScene
)。但是 LoadLevel(newSceneName)
不起作用 - 它根本不加载场景。我在这里做错了什么?
编辑:
现在我正在传递调用 OnLoadScene
方法的脚本的 MonoBehavior
引用,如下所示:
public static void OnLoadScene(MonoBehaviour loader, string newSceneName)
{
UnloadScene?.Invoke(newSceneName);
loader.StartCoroutine(LoadLevel(newSceneName));
Debug.Log(SceneManager.GetActiveScene().name); // this line returns previous scene
LoadScene?.Invoke(newSceneName);
}
现在场景加载了,但是当我检查当前加载的场景时,它 returns 以前的场景名称。
编辑 2:
更准确地说,我用 Debug.Log(SceneManager.GetSceneByName(newSceneName).isLoaded);
替换了 Debug.Log(SceneManager.GetActiveScene().name);
并且 returns False
.
您必须 运行 协程使用 StartCoroutine
。
您要么需要传入将执行协程的 MonoBehaviour
的引用,要么只是让您的 class 成为一个永远不会被销毁的单例
实际上你会在事件尚未加载但你刚刚开始加载它时过早地调用它,所以宁愿这样做,例如
public class CustomSceneManager : MonoBehaviour
{
public delegate void SceneChange(string sceneName);
public static event SceneChange LoadScene;
public static event SceneChange UnloadScene;
private CustomNetworkManager singleton;
private void Awake ()
{
if(singleton && singleton != this)
{
Destroy(gameObject);
}
singleton = this;
DontDestroyOnLoad (gameObject);
}
private static IEnumerator LoadLevel (string sceneName){
var asyncLoadLevel = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single);
while (!asyncLoadLevel.isDone){
Debug.Log("Loading the Scene");
yield return null;
}
LoadScene?.Invoke(newSceneName);
}
public static void OnLoadScene(string newSceneName)
{
if(! singleton)
{
singleton = new GameObject("CustomNetworkManager").AddComponent<CustomNetworkManager>();
}
OnUnloadScene(newSceneName);
singleton.StartCoroutine(LoadLevel(newSceneName));
}
另一种方法是使用异步方法。
要注意的是,为了等待 AsyncOperation,您需要自定义等待程序 class。有几个库使之成为可能,我最喜欢的是 UniTask.
using Cysharp.Threading.Tasks;
using UnityEngine.SceneManagement;
public static class CustomSceneManager
{
public delegate void SceneChange(string sceneName);
public static event SceneChange LoadScene;
public static event SceneChange UnloadScene;
private static async UniTask LoadLevelAsync(string sceneName)
{
await SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single);
}
public static async UniTask OnLoadSceneAsync(string newSceneName)
{
OnUnloadScene(newSceneName);
await LoadLevelAsync(newSceneName);
LoadScene?.Invoke(newSceneName);
}
private static void OnUnloadScene(string newSceneName)
{
UnloadScene?.Invoke(newSceneName);
}
}
我需要等到场景完全加载才能将 gameObject
从 DontDestroyOnLoad
场景移动到它。如果我做得太早(就在调用 SceneManager.LoadScene()
之后),那么 gameObject
就会消失。基于this post我实现了一个场景加载class来解决这个问题:
public static class CustomSceneManager
{
public delegate void SceneChange(string sceneName);
public static event SceneChange LoadScene;
public static event SceneChange UnloadScene;
private static IEnumerator LoadLevel (string sceneName){
var asyncLoadLevel = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single);
while (!asyncLoadLevel.isDone){
Debug.Log("Loading the Scene");
yield return null;
}
}
public static void OnLoadScene(string newSceneName)
{
OnUnloadScene(newSceneName);
LoadLevel(newSceneName);
LoadScene?.Invoke(newSceneName);
}
private static void OnUnloadScene(string newSceneName)
{
UnloadScene?.Invoke(newSceneName);
}
}
我正在从中调用两个事件(LoadScene
和 UnloadScene
)。但是 LoadLevel(newSceneName)
不起作用 - 它根本不加载场景。我在这里做错了什么?
编辑:
现在我正在传递调用 OnLoadScene
方法的脚本的 MonoBehavior
引用,如下所示:
public static void OnLoadScene(MonoBehaviour loader, string newSceneName)
{
UnloadScene?.Invoke(newSceneName);
loader.StartCoroutine(LoadLevel(newSceneName));
Debug.Log(SceneManager.GetActiveScene().name); // this line returns previous scene
LoadScene?.Invoke(newSceneName);
}
现在场景加载了,但是当我检查当前加载的场景时,它 returns 以前的场景名称。
编辑 2:
更准确地说,我用 Debug.Log(SceneManager.GetSceneByName(newSceneName).isLoaded);
替换了 Debug.Log(SceneManager.GetActiveScene().name);
并且 returns False
.
您必须 运行 协程使用 StartCoroutine
。
您要么需要传入将执行协程的 MonoBehaviour
的引用,要么只是让您的 class 成为一个永远不会被销毁的单例
实际上你会在事件尚未加载但你刚刚开始加载它时过早地调用它,所以宁愿这样做,例如
public class CustomSceneManager : MonoBehaviour
{
public delegate void SceneChange(string sceneName);
public static event SceneChange LoadScene;
public static event SceneChange UnloadScene;
private CustomNetworkManager singleton;
private void Awake ()
{
if(singleton && singleton != this)
{
Destroy(gameObject);
}
singleton = this;
DontDestroyOnLoad (gameObject);
}
private static IEnumerator LoadLevel (string sceneName){
var asyncLoadLevel = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single);
while (!asyncLoadLevel.isDone){
Debug.Log("Loading the Scene");
yield return null;
}
LoadScene?.Invoke(newSceneName);
}
public static void OnLoadScene(string newSceneName)
{
if(! singleton)
{
singleton = new GameObject("CustomNetworkManager").AddComponent<CustomNetworkManager>();
}
OnUnloadScene(newSceneName);
singleton.StartCoroutine(LoadLevel(newSceneName));
}
另一种方法是使用异步方法。
要注意的是,为了等待 AsyncOperation,您需要自定义等待程序 class。有几个库使之成为可能,我最喜欢的是 UniTask.
using Cysharp.Threading.Tasks;
using UnityEngine.SceneManagement;
public static class CustomSceneManager
{
public delegate void SceneChange(string sceneName);
public static event SceneChange LoadScene;
public static event SceneChange UnloadScene;
private static async UniTask LoadLevelAsync(string sceneName)
{
await SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single);
}
public static async UniTask OnLoadSceneAsync(string newSceneName)
{
OnUnloadScene(newSceneName);
await LoadLevelAsync(newSceneName);
LoadScene?.Invoke(newSceneName);
}
private static void OnUnloadScene(string newSceneName)
{
UnloadScene?.Invoke(newSceneName);
}
}