SceneManager的加载场景和卸载场景很简单--全用

SceneManager的加载场景和卸载场景很简单--全用

全用 SceneManager

我以前纯用 SceneManager 的加载场景和卸载场景

很简单……为了赶工

对以下测试脚本,调用 LoadHome()

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using Universal;
public class AppController : Singleton<AppController>
{
    private GameUI gameUI;
    
    private void Start()
    {
        DontDestroyOnLoad(gameObject);
    }
    public void LoadHome()
    {
        StartCoroutine(WaitForLoadHome());
    }
    public IEnumerator WaitForLoadHome()
    {
        int oldSceneID = SceneManager.GetActiveScene().buildIndex;
        
        AsyncOperation asyncLoad = SceneManager.LoadSceneAsync("Scenes/LoadingScene", LoadSceneMode.Additive);
        while (!asyncLoad.isDone)
        {
            yield return null;
        }
        
        asyncLoad = SceneManager.UnloadSceneAsync(oldSceneID);
        
        while (!asyncLoad.isDone)
        {
            yield return null;
        }
        
        LoadingController.Instance.BeginLoading();
        
        yield return LoadingController.Instance.SetProgress(100);
        
        asyncLoad = SceneManager.LoadSceneAsync("Scenes/HomeScene", LoadSceneMode.Additive);
        while (!asyncLoad.isDone)
        {
            yield return null;
        }
        
        asyncLoad = SceneManager.UnloadSceneAsync("Scenes/LoadingScene");
        
        while (!asyncLoad.isDone)
        { 
            yield return null;
        }
        
        HomeController.Instance.BeginHome();
    }
    public void LoadGame()
    {
        StartCoroutine(WaitForLoadGame());
    }
    
    private IEnumerator WaitForLoadGame()
    {
        int oldSceneID = SceneManager.GetActiveScene().buildIndex;
        
        AsyncOperation asyncLoad = SceneManager.LoadSceneAsync("Scenes/LoadingScene", LoadSceneMode.Additive);
        while (!asyncLoad.isDone)
        {
            yield return null;
        }
        
        asyncLoad = SceneManager.UnloadSceneAsync(oldSceneID);
        
        while (!asyncLoad.isDone)
        {
            yield return null;
        }
        
        LoadingController.Instance.BeginLoading();
        
        yield return LoadingController.Instance.SetProgress(100);
        
        asyncLoad = SceneManager.LoadSceneAsync("Scenes/GameScene", LoadSceneMode.Additive);
        while (!asyncLoad.isDone)
        {
            yield return null;
        }
        
        asyncLoad = SceneManager.UnloadSceneAsync("Scenes/LoadingScene");
        
        while (!asyncLoad.isDone)
        {
            yield return null;
        }
        
        GameController.Instance.BeginGame();
    }
}
复制代码

谨慎使用 AsyncOperationHandle.WaitForCompletion()

然后换成 Addressables 之后就有问题了……具体的 Debug 代码像这样:

对以下测试脚本,调用 TestLoad()

public string loadingSceneAddress;
private AsyncOperationHandle loadHandle;
private IEnumerator TestLoad()
{
    loadHandle = Addressables.LoadSceneAsync(loadingSceneAddress, LoadSceneMode.Additive);
    loadHandle.Completed += OnSceneLoaded;
        
    Debug.Log("=============    1    ================");
    Debug.Log(loadHandle.Status);
    Debug.Log(loadHandle.Result.Scene.isLoaded);
    Debug.Log(SceneManager.sceneCount);
    
    yield return loadHandle.WaitForCompletion();
    Debug.Log("=============    2    ================");
    Debug.Log(loadHandle.Status);
    Debug.Log(loadHandle.Result.Scene.isLoaded);
    Debug.Log(SceneManager.sceneCount);
    yield return new WaitForEndOfFrame();
    Debug.Log("=============    3    ================");
    Debug.Log(loadHandle.Status);
    Debug.Log(loadHandle.Result.Scene.isLoaded);
    Debug.Log(SceneManager.sceneCount);
    
    yield return new WaitForEndOfFrame();
    Debug.Log("=============    5    ================");
    Debug.Log(loadHandle.Status);
    Debug.Log(loadHandle.Result.Scene.isLoaded);
    Debug.Log(SceneManager.sceneCount);
}
private void OnSceneLoaded(AsyncOperationHandle obj)
{
    Debug.Log("=============    4    ================");
    Debug.Log(loadHandle.Status);
    Debug.Log(loadHandle.Result.Scene.isLoaded);
    Debug.Log(SceneManager.sceneCount);
}
复制代码

打印结果:

=============    1    ================
None
False
2
=============    2    ================
None
False
2
=============    3    ================
None
False
2
=============    4    ================
Succeeded
True
2
=============    5    ================
Succeeded
True
2
复制代码

可以看到他这个 WaitForCompletion() 是完全没有效果的

同时unity动态加载游戏对象,在 handle 的 WaitForCompletion() 前后,handle 的 Status 也是 None,我看了一下他这个 Status 的赋值,感觉是在引用计数为 0 的时候才赋的 None。那这就说明调用了 Addressables.LoadSceneAsync 之后 Status 是 None游戏动态,可能是因为还不知道是成功或者失败吧

但同时场上却有他这个 Addressables 想要 load 的场景,因为打印出来场景数量是 2unity动态加载游戏对象,但是这个场景却没有 loaded,就很奇怪,但是既然现在他 Completed 是有效的游戏开发素材,那也不管了

改为使用 Addressables

现在就可以实现场景 A 到场景 B 之间的切换有一个加载场景作为缓冲,并且能够显示加载进度条的功能了

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
using Universal;
public class AppController : Singleton<AppController>
{
    public string homeSceneAddress;
    public string loadingSceneAddress;
    public string gameSceneAddress;
    
    private void Start()
    {
        DontDestroyOnLoad(gameObject);
    }
    public void LoadHome()
    {
        TryLoad(homeSceneAddress, () => HomeController.Instance.BeginHome());
    }
    public void LoadGame()
    {
        TryLoad(gameSceneAddress, () => GameController.Instance.BeginGame());
    }
    
    private void TryLoad(string nextSceneAddress, Action callback = null)
    {
        AsyncOperationHandle lastLoadHandle = Addressables.LoadSceneAsync(loadingSceneAddress, LoadSceneMode.Single);
        lastLoadHandle.Completed += (AsyncOperationHandle op) =>
        {
            if(op.Status == AsyncOperationStatus.Succeeded)
            {
                LoadingController.Instance.BeginLoading();
                StartCoroutine(WaitForLoading(lastLoadHandle, nextSceneAddress, callback));
            }
        };
    }
    
    private IEnumerator WaitForLoading(AsyncOperationHandle lastLoadHandle, string nextSceneAddress, Action callback = null)
    {
        AsyncOperationHandle currLoadHandle = Addressables.LoadSceneAsync(nextSceneAddress, LoadSceneMode.Additive);
        // 新场景加载时刷新进度条
        while (currLoadHandle.Status == AsyncOperationStatus.None)
        {
            LoadingController.Instance.SetPercent(currLoadHandle.PercentComplete);
            yield return null;
        }
        // 如果新场景加载完毕
        if(currLoadHandle.Status == AsyncOperationStatus.Succeeded)
        {
            // 进度条缓冲时间,免得 loading 界面一闪而过,给人晃眼的感觉
            LoadingController.Instance.SetPercent(1f);
            yield return new WaitForSeconds(0.5f);
            
            // 卸载 loading 场景
            Addressables.UnloadSceneAsync(lastLoadHandle)
                .Completed += (AsyncOperationHandle op2) =>
            {
                if (op2.Status == AsyncOperationStatus.Succeeded)
                {
                    callback?.Invoke();
                }
            };
        }
        
        yield return null;
    }
}
复制代码

文章来源:https://juejin.cn/post/7225629551601827898