【问题标题】:How to wait until a scene is fully loaded?如何等到场景完全加载?
【发布时间】:2021-04-20 15:05:20
【问题描述】:

我需要等到场景完全加载后才能将 gameObjectDontDestroyOnLoad 场景移动到它。如果我做得太早(就在调用SceneManager.LoadScene() 之后),那么gameObject 就会消失。基于this post我实现了一个场景加载类来解决这个问题:

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);
    }
}

我从中调用了两个事件(LoadSceneUnloadScene)。但是 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);
    }

现在场景加载了,但是当我检查当前加载的场景时,它会返回之前的场景名称。

编辑 2:

更准确地说,我将Debug.Log(SceneManager.GetActiveScene().name); 替换为Debug.Log(SceneManager.GetSceneByName(newSceneName).isLoaded); 并返回False

【问题讨论】:

  • 你必须使用StartCoroutine(LoadLevel(newSceneName);运行协程
  • @derHugo 这是一个静态类。有没有其他办法?
  • @derHugo 谢谢,这有助于加载场景,但协程似乎无法正常工作。
  • SceneManager 没有SceneLoaded 事件吗?
  • @Immersive 根据我的测试,它在场景完全加载之前被调用,因此将 gameObject 移动到该场景不起作用。

标签: c# unity3d coroutine


【解决方案1】:

您必须使用StartCoroutine 运行协程。

你要么需要传递一个 MonoBehaviour 的引用来执行协程,要么只是让你的类成为一个永远不会被破坏的单例

实际上,当事件尚未加载但您刚刚开始加载它时,您会过早调用它,所以宁愿这样做,例如

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));
    }

【讨论】:

  • 我想保持类静态。我对协程仍有问题,我更新了问题。
  • 您的最新编辑显然是错误的.. 请参阅此答案中的第三句话。 StartCoroutine 确实不会延迟调用方法的执行,因此您在加载新场景之前执行其他两行..您必须将其移动到 IEnumerator 的末尾或传递它在作为回调.. 你可以看到我在例程结束时调用了LoadScene?.Invoke()
【解决方案2】:

另一种方法是使用异步方法。

要注意的是,为了等待 AsyncOperation,您需要一个自定义的 awaiter 类。有几个库可以做到这一点,我最喜欢的是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);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    相关资源
    最近更新 更多