Necesito esperar hasta que una escena esté completamente cargada para mover un gameObject desde la escena DontDestroyOnLoad . Si lo hago demasiado pronto (justo después de llamar a SceneManager.LoadScene() ), el gameObject desaparece. Basado en esta publicación , implementé una clase de carga de escena para resolver este problema:
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); } } Estoy llamando a dos eventos ( LoadScene y UnloadScene ). Sin embargo LoadLevel(newSceneName) no funciona, simplemente no carga una escena. ¿Qué estoy haciendo mal aquí?
EDITAR:
Ahora estoy pasando la referencia MonoBehavior del script que llama OnLoadScene de esta manera:
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); }Ahora la escena se carga, pero cuando verifico qué escena está cargada actualmente, devuelve el nombre de la escena anterior.
EDITAR 2:
Para ser más precisos, reemplacé Debug.Log(SceneManager.GetActiveScene().name); con Debug.Log(SceneManager.GetSceneByName(newSceneName).isLoaded); y devuelve False .
Tienes que ejecutar coroutienes usando StartCoroutine .
Debería pasar una referencia de un MonoBehaviour que ejecutará la rutina o simplemente hacer de su clase un Singleton que nunca se destruye
Entonces, en realidad, invocará su evento demasiado pronto cuando aún no está cargado, pero acaba de comenzar a cargarlo, así que prefiera hacerlo, por ejemplo.
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)); }Otra forma de hacerlo es con un método asíncrono.
El problema es que para esperar una AsyncOperation necesita una clase de espera personalizada. Hay algunas bibliotecas que lo hacen posible, y mi favorita es 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); } }