【unity进阶知识8】unity场景Scene的使用, 如何封装一个场景管理器

发布于:2024-10-09 ⋅ 阅读:(11) ⋅ 点赞:(0)


在这里插入图片描述

一、场景基本操作

1、加载切换场景

载索引为1的场景

SceneManager.LoadScene(1);

加载名为MyScene的场景

SceneManager.LoadScene("MyScene");

加载场景,LoadSceneMode.Single表示:销毁当前场景,加载要需要的场景,默认

SceneManager.LoadScene("MyScene", LoadSceneMode.Single);

叠加式加载场景,AddSceneMode.Additive表示:当前场景不销毁,并加载需要的场景

SceneManager.LoadScene("MyScene", LoadSceneMode.Additive);

2、获取场景信息

根据场景的名称(在此例中为 “Scene2”)来获取对应的场景对象

Scene scene2 = SceneManager.GetSceneByName("Scene2")

根据指定的场景索引获取场景信息

int sceneIndex = 1; // 假设我们要获取索引为 1 的场景
Scene scene = SceneManager.GetSceneByBuildIndex(sceneIndex);

3、激活场景

设置scene2为当前活动场景

SceneManager.SetActiveScene(scene2);

获取当前激活场景

Scene scene = SceneManager.GetActiveScene();

4、场景基本属性获取

场景名称

Debug.Log(scene.name);

场景是否已经加载

Debug.Log(scene.isLoaded);

场景路径

Debug.Log(scene.path);

场景索引

Debug.Log(scene.buildIndex);

获取当前构建设置中场景的数量。具体来说,它返回在 Unity 的构建设置(Build Settings)窗口中列出的所有场景的总数。

int totalScenes = SceneManager.sceneCountInBuildSettings;

5、已加载场景个数

Debug.Log(SceneManager.sceneCount);

6、获取场景中所有物体

隐藏的根游戏对象也会包含在其中,DontDestroyOnLoad的游戏对象不包含在其中。

GameObject[] gos = scene.GetRootGameObjects();
Debug.Log(gos.Length);

7、创建新场景

Scene newScene = SceneManager.CreateScene("newScene");

8、卸载销毁场景

//本方法不会卸载内存中的场景资源,如果要释放资源,应在调用这个方法后,再调用Resources.UnloadUnusedAssets()

SceneManager.UnloadSceneAsync(newScene);

//用于释放未使用的资源,以帮助管理内存。它会清理那些不再被使用的资源

Resources.UnloadUnusedAssets()

二、使用协程方法来异步加载场景

1、AsyncOperation相关的代码应写在一个协同程序中。

# 开始加载场景
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync("Scene1", LoadSceneMode.Single);

2、allowSceneActivation加载完场景是否自动跳转

值为false表示即使加载场景完毕也不会激活该场景,直到用代码再次把这个变量的值改为true,才会激活该场景。

//加载完场景不要自动跳转
asyncOperation.allowSceneActivation = false;

3、progress异步加载的进度

返回float型,范围是0-1。表示异步加载的进度,开始是0,完成时是1
注意:当AsyncOperation型变量.allowSceneActivation的值为false,这个参数的值最多会卡在0.9,直到AsyncOperation型变量.allowSceneActivation的值变为true,这个参数的值才会变为1

asyncOperation.progress

4、isDone异步加载是否完成

返回bool型。表示该异步加载是否完成。如果完成,则值为true,如果未完成,则值为false。
当AsyncOperation型对象.progress的值为1时,此时这个变量的值才为true,但这样就会激活新的新场景,一般很难观测到AsyncOperation型对象.isDone是true

asyncOperation.isDone

5、priority设置异步操作的优先级

返回int型,用于设置异步操作的优先级。
当有多个异步操作排队时,将优先执行更高优先级的异步操作。但如果异步操作在后台线程上启动, 则更改优先级没有任何效果。

asyncOperation.priority

6、completed异步加载完成会执行一次这个事件

这个是一个有一个AsyncOperation型参数的Action事件。该AsyncOperation型参数存储了本次异步加载的信息。
当异步加载完成,也就是AsyncOperation型对象.isDone的值为true时,会执行一次这个事件。

asyncOperation.completed += Completed()

Action<AsyncOperation> Completed(){
    return null;
}

7、简单案例

using UnityEngine;
using UnityEngine.SceneManagement;

public class  AsyncTest:MonoBehaviour
{
	AsyncOperation asyncOperation;
	
	void Start()
	{
		StartCoroutine(loadScene());
	}

	//协程方法用来异步加载场景
	IEnumerator loadScene()
	{
		//异步加载索引为1的场景
		operation = SceneManager.LoadSceneAsync(1);
		//加载完场景不要自动跳转
		operation.allowSceneActivation = false;
		//等待资源加载完毕
		yield return operation;
	}

	float timer = 0;
	
	void Update()
	{
		//输出加载进度 0-0.9,可以按这个值制作加载进度条
		Debug.Log(operation.progress);
		timer += Time.deltaTime;
		//如果到达5秒,再跳转场景
		if(timer > 5){
			operation.allowSceneActivation = true;
		}
	}
}

三、封装场景管理器

1、同步加载当前场景、下一个场景、上一个场景

using UnityEngine;
using UnityEngine.SceneManagement;

/// <summary>
/// 场景切换管理器
/// </summary>
public class LoadSceneManager : SingletonPatternBase<LoadSceneManager>
{
    /// <summary>
    /// 重新切换到当前场景
    /// </summary>
    public void LoadActiveScene()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }

    /// <summary>
    /// 切换到下一个场景
    /// </summary>
    /// <param name="isCyclical">是否循环切换</param>
    public void LoadNextScene(bool isCyclical = false)
    {
        int buildIndex = SceneManager.GetActiveScene().buildIndex + 1;

        if (buildIndex > SceneManager.sceneCountInBuildSettings - 1)
        {
            if (isCyclical)
            {
                buildIndex = 0;
            }
            else
            {
                Debug.LogWarning($"加载场景失败!要加载的场景的索引是{buildIndex}, 越界了!");
                return;
            }
        }

        SceneManager.LoadScene(buildIndex);
    }

    /// <summary>
    /// 切换到上一个场景
    /// </summary>
    /// <param name="isCyclical">是否循环切换</param>
    public void LoadPreviousScene(bool isCyclical = false)
    {
        int buildIndex = SceneManager.GetActiveScene().buildIndex - 1;

        if (buildIndex < 0)
        {
            if (isCyclical)
            {
                buildIndex = SceneManager.sceneCountInBuildSettings - 1;
            }
            else
            {
                Debug.LogWarning($"加载场景失败!要加载的场景的索引是{buildIndex}, 索引不能为负数!");
                return;
            }
        }

        SceneManager.LoadScene(buildIndex);
    }
}

测试调用

LoadSceneManager.Instance.LoadActiveScene();
LoadSceneManager.Instance.LoadNextScene();
LoadSceneManager.Instance.LoadPreviousScene();

2、异步切换场景

/// <summary>
/// 异步加载场景
/// </summary>
public void LoadSceneAsync(string sceneName, UnityAction completed = null, LoadSceneMode mode = LoadSceneMode.Single)
{
    MonoManager.Instance.StartCoroutine(LoadSceneCoroutine(sceneName, completed, mode));
}

private IEnumerator LoadSceneCoroutine(string sceneName, UnityAction completed = null, LoadSceneMode mode = LoadSceneMode.Single)
{
    // 开始加载场景
    AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneName, mode);

    // 等待资源加载完毕
    while (!asyncOperation.isDone)
    {
        yield return null; // 等待下一帧
    }

    // 加载资源完毕后执行的逻辑
    completed?.Invoke();
}

/// <summary>
/// 异步加载场景(通过索引)
/// </summary>
public void LoadSceneAsync(int sceneIndex, UnityAction completed = null, LoadSceneMode mode = LoadSceneMode.Single)
{
    MonoManager.Instance.StartCoroutine(LoadSceneCoroutine(sceneIndex, completed, mode));
}

private IEnumerator LoadSceneCoroutine(int sceneIndex, UnityAction completed = null, LoadSceneMode mode = LoadSceneMode.Single)
{
    // 开始加载场景
    AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneIndex, mode);

    // 等待资源加载完毕
    while (!asyncOperation.isDone)
    {
        yield return null; // 等待下一帧
    }

    // 加载资源完毕后执行的逻辑
    completed?.Invoke();
}

测试调用

LoadSceneManager.Instance.LoadSceneAsync(1);
LoadSceneManager.Instance.LoadSceneAsync("Scene1");

3、异步加载场景获取加载进度条

public void LoadSceneAsync(string sceneName, UnityAction<float> loading = null, UnityAction<AsyncOperation> completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single)
{
    MonoManager.Instance.StartCoroutine(LoadSceneCoroutine(sceneName, loading, completed, setActiveAfterCompleted, mode));
}

private IEnumerator LoadSceneCoroutine(string sceneName, UnityAction<float> loading = null, UnityAction<AsyncOperation> completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single)
{
    // 开始加载资源
    AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneName, mode);
    asyncOperation.allowSceneActivation = false;

    // 等待资源加载完毕
    while (asyncOperation.progress < 0.9f)
    {
        loading?.Invoke(asyncOperation.progress);
        yield return null;
    }

    // 人为将进度设置为1,以便外部进度条的显示
    loading?.Invoke(1);

    // 根据设置决定何时激活场景
    asyncOperation.allowSceneActivation = setActiveAfterCompleted;

    // 加载资源完毕后执行的逻辑
    completed?.Invoke(asyncOperation);
}

测试调用

public class SceneTest : MonoBehaviour
{
    private void Start()
    {
        LoadSceneManager.Instance.LoadSceneAsync(1, Loading, Completed);
    }

    void Loading(float progress)
    {
        Debug.Log($"加载进度是{progress*100}%");
    }

    void Completed(AsyncOperation asyncOperation)
    {
        Debug.Log("场景加载完成");
    }
}

结果
在这里插入图片描述

4、完整代码

using System;
using System.Collections;
using System.Collections.Generic;
using PlasticGui.WorkspaceWindow;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;

namespace XYFrame
{
    /// <summary>
    /// 切换场景的管理器。用于加载场景和切换场景。
    /// </summary>
    public class LoadSceneManager : Singleton<LoadSceneManager>
    {
        #region 同步加载场景
        /// <summary>
        /// <para>同步加载场景(通过索引)</para>
        /// </summary>
        /// <param name="sceneBuildIndex">场景的索引</param>
        /// <param name="mode">加载场景的模式。默认是LoadSceneMode.Single,表示会卸载原来的场景,再切换到新场景。LoadSceneMode.Additive表示会将新场景叠加在原来的场景中。</param>
        public void LoadScene(int sceneBuildIndex, LoadSceneMode mode = LoadSceneMode.Single)
        {
            SceneManager.LoadScene(sceneBuildIndex, mode);
        }

        /// <summary>
        /// <para>同步加载场景(通过名字)</para>
        /// </summary>
        /// <param name="sceneName">场景的名字</param>
        /// <param name="mode">加载场景的模式。默认是LoadSceneMode.Single,表示会卸载原来的场景,再切换到新场景。LoadSceneMode.Additive表示会将新场景叠加在原来的场景中。</param>
        public void LoadScene(string sceneName, LoadSceneMode mode)
        {
            SceneManager.LoadScene(sceneName, mode);
        }

        /// <summary>
        /// <para>同步重新加载当前的活动场景</para>
        /// </summary>
        public void LoadActiveScene()
        {
            SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
        }

        /// <summary>
        /// <para>同步加载下一个场景</para>
        /// </summary>
        /// <param name="isCyclical">是否循环加载场景。</param>
        public void LoadNextScene(bool isCyclical = false)
        {
            int buildIndex = SceneManager.GetActiveScene().buildIndex + 1;

            if (buildIndex > SceneManager.sceneCountInBuildSettings - 1)
            {
                if (isCyclical)
                {
                    buildIndex = 0;
                }
                else
                {
                    Debug.LogWarning($"加载场景失败!要加载的场景的索引是{buildIndex},超过了当前Build Settings窗口中的最大索引。");
                    return;
                }
            }
            SceneManager.LoadScene(buildIndex);
        }

        /// <summary>
        /// <para>同步加载上一个场景</para>
        /// </summary>
        /// <param name="isCyclical">是否循环加载场景。</param>
        public void LoadPreviousScene(bool isCyclical = false)
        {
            int buildIndex = SceneManager.GetActiveScene().buildIndex - 1;

            if (buildIndex < 0)
            {
                if (isCyclical)
                {
                    buildIndex = SceneManager.sceneCountInBuildSettings - 1;
                }
                else
                {
                    Debug.LogWarning($"加载场景失败!要加载的场景的索引是{buildIndex},没有索引为负数的场景。");
                    return;
                }
            }
            SceneManager.LoadScene(buildIndex);
        }
        #endregion

        #region 异步加载场景
        /// <summary>
        /// <para>异步加载场景(通过索引)</para>
        /// </summary>
        /// <param name="sceneBuildIndex">要加载的场景索引。</param>
        /// <param name="loading">加载中的回调。只要在加载中,就会不断地执行这个回调。一般用于进度条的显示。</param>
        /// <param name="completed">加载完毕后的回调。</param>
        /// <param name="setActiveAfterCompleted">加载场景完毕后,是否切换到该场景。</param>
        /// <param name="mode">加载场景的模式。默认是LoadSceneMode.Single,表示会卸载原来的场景,再切换到新场景。LoadSceneMode.Additive表示会将新场景叠加在原来的场景中。</param>
        public void LoadSceneAsync(int sceneBuildIndex, UnityAction<float> loading = null, UnityAction<AsyncOperation> completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single)
        {
            //如果要加载的场景的索引不合法,则返回。
            if (!IsSceneBuildIndexValid(sceneBuildIndex)) return;

            //开启协程进行异步加载。
            MonoManager.Instance.StartCoroutine(LoadSceneAsyncCoroutine(sceneBuildIndex, loading, completed, setActiveAfterCompleted, mode));
        }
        IEnumerator LoadSceneAsyncCoroutine(int sceneBuildIndex, UnityAction<float> loading = null, UnityAction<AsyncOperation> completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single)
        {
            //异步操作对象,记录了异步操作的数据。
            AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneBuildIndex, mode);

            //设置为false,则即使场景加载完毕,也不会切换过去。当场景加载完毕,再次把这个值复制为true,才会切换过去。
            asyncOperation.allowSceneActivation = false;

            //加载场景的过程中,提供给外部执行的回调。一般用于进度条的显示。
            while (asyncOperation.progress < 0.9f)
            {
                loading?.Invoke(asyncOperation.progress);
                yield return CoroutineTool.WaitForFrame();
            }

            //当asyncOperation.allowSceneActivation为false,则asyncOperation.progress最多只能到达0.9,我们人为让它凑成整数1,方便外部进度条的显示。
            loading?.Invoke(1f);

            //加载场景完毕之后,如果把这个变量设置为true,则会切换到该场景。如果为false,则不会切换到该场景。
            asyncOperation.allowSceneActivation = setActiveAfterCompleted;

            //加载场景完毕之后执行的回调。
            completed?.Invoke(asyncOperation);
        }

        /// <summary>
        /// <para>异步加载场景(通过名称)</para>
        /// </summary>
        /// <param name="sceneName">要加载的场景名</param>
        /// <param name="loading">加载中的回调。只要在加载中,就会不断地执行这个回调。一般用于进度条的显示。</param>
        /// <param name="completed">加载完毕后的回调。</param>
        /// <param name="setActiveAfterCompleted">加载场景完毕后,是否切换到该场景。</param>
        /// <param name="mode">加载场景的模式。默认是LoadSceneMode.Single,表示会卸载原来的场景,再切换到新场景。LoadSceneMode.Additive表示会将新场景叠加在原来的场景中。</param>
        public void LoadSceneAsync(string sceneName, UnityAction<float> loading = null, UnityAction<AsyncOperation> completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single)
        {
            //开启协程进行异步加载。
            MonoManager.Instance.StartCoroutine(LoadSceneAsyncCoroutine(sceneName, loading, completed, setActiveAfterCompleted, mode));
        }
        IEnumerator LoadSceneAsyncCoroutine(string sceneName, UnityAction<float> loading = null, UnityAction<AsyncOperation> completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single)
        {
            //异步操作对象,记录了异步操作的数据。
            AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneName, mode);

            //设置为false,则即使场景加载完毕,也不会切换过去。当场景加载完毕,再次把这个值复制为true,才会切换过去。
            asyncOperation.allowSceneActivation = false;

            //加载场景的过程中,提供给外部执行的回调。一般用于进度条的显示。
            while (asyncOperation.progress < 0.9f)
            {
                loading?.Invoke(asyncOperation.progress);
                yield return CoroutineTool.WaitForFrame();
            }

            //当asyncOperation.allowSceneActivation为false,则asyncOperation.progress最多只能到达0.9,我们人为让它凑成整数1,方便外部进度条的显示。
            loading?.Invoke(1f);

            //加载场景完毕之后,如果把这个变量设置为true,则会切换到该场景。如果为false,则不会切换到该场景。
            asyncOperation.allowSceneActivation = setActiveAfterCompleted;

            //加载场景完毕之后执行的回调。
            completed?.Invoke(asyncOperation);
        }

        /// <summary>
        /// <para>异步加载当前活动场景。</para>
        /// </summary>
        /// <param name="loading">加载中的回调。只要在加载中,就会不断地执行这个回调。一般用于进度条的显示。</param>
        /// <param name="completed">加载完毕后的回调。</param>
        /// <param name="setActiveAfterCompleted">加载场景完毕后,是否切换到该场景。</param>
        /// <param name="mode">加载场景的模式。默认是LoadSceneMode.Single,表示会卸载原来的场景,再切换到新场景。LoadSceneMode.Additive表示会将新场景叠加在原来的场景中。</param>
        public void LoadActiveSceneAsync(UnityAction<float> loading = null, UnityAction<AsyncOperation> completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single)
        {
            LoadSceneAsync(SceneManager.GetActiveScene().buildIndex, loading, completed, setActiveAfterCompleted, mode);
        }

        /// <summary>
        /// 异步加载下一个场景。
        /// </summary>
        /// <param name="isCyclical">是否循环加载场景。</param>
        /// <param name="loading">加载中的回调。只要在加载中,就会不断地执行这个回调。一般用于进度条的显示。</param>
        /// <param name="completed">加载完毕后的回调。</param>
        /// <param name="setActiveAfterCompleted">加载场景完毕后,是否切换到该场景。</param>
        /// <param name="mode">加载场景的模式。默认是LoadSceneMode.Single,表示会卸载原来的场景,再切换到新场景。LoadSceneMode.Additive表示会将新场景叠加在原来的场景中。</param>
        public void LoadNextSceneAsync(bool isCyclical = false, UnityAction<float> loading = null, UnityAction<AsyncOperation> completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single)
        {
            //下一个场景的索引。
            int buildIndex = SceneManager.GetActiveScene().buildIndex + 1;

            //当要加载的场景的索引超过了Build Settings窗口中的最大索引,则根据参数来判断是否循环加载场景。
            if (buildIndex > SceneManager.sceneCountInBuildSettings - 1)
            {
                if (isCyclical)
                {
                    buildIndex = 0;
                }
                else
                {
                    Debug.LogWarning($"加载场景失败!要加载的场景的索引是{buildIndex},超过了当前Build Settings窗口中的最大索引。");
                    return;
                }
            }
            //异步加载该场景。
            LoadSceneAsync(buildIndex, loading, completed, setActiveAfterCompleted, mode);
        }

        /// <summary>
        /// 异步加载上一个场景。
        /// </summary>
        /// <param name="isCyclical">是否循环加载场景。</param>
        /// <param name="loading">加载中的回调。只要在加载中,就会不断地执行这个回调。一般用于进度条的显示。</param>
        /// <param name="completed">加载完毕后的回调。</param>
        /// <param name="setActiveAfterCompleted">加载场景完毕后,是否切换到该场景。</param>
        /// <param name="mode">加载场景的模式。默认是LoadSceneMode.Single,表示会卸载原来的场景,再切换到新场景。LoadSceneMode.Additive表示会将新场景叠加在原来的场景中。</param>
        public void LoadPreviousSceneAsync(bool isCyclical = false, UnityAction<float> loading = null, UnityAction<AsyncOperation> completed = null, bool setActiveAfterCompleted = true, LoadSceneMode mode = LoadSceneMode.Single)
        {
            //上一个场景的索引。
            int buildIndex = SceneManager.GetActiveScene().buildIndex - 1;

            //当要加载的场景的索引为负数,则根据参数来判断是否循环加载场景。
            if (buildIndex < 0)
            {
                if (isCyclical)
                {
                    buildIndex = SceneManager.sceneCountInBuildSettings - 1;
                }
                else
                {
                    Debug.LogWarning($"加载场景失败!要加载的场景的索引是{buildIndex},没有索引为负数的场景。");
                    return;
                }
            }
            //异步加载该场景。
            LoadSceneAsync(buildIndex, loading, completed, setActiveAfterCompleted, mode);
        }
        #endregion

        #region 异步销毁场景和这个场景中的所有游戏对象
        /// <summary>
        /// <para>按索引销毁指定的场景和这个场景中的所有游戏对象。</para>
        /// <para>本方法只对加载时用了LoadSceneMode.Additive来加载的场景有效。如果当前游戏中只有一个场景,则本方法无效,且会在控制台报黄色的警告。</para>
        /// <para>本方法不会释放内存中的场景资源,如果要释放该场景资源,应在调用这个方法后,再调用Resources.UnloadUnusedAssets方法。</para>
        /// </summary>
        /// <param name="sceneBuildIndex">要销毁的场景索引。</param>
        /// <param name="callback">销毁完毕后执行的回调。</param>
        /// <param name="options">销毁场景的选项。是一个UnloadSceneOptions型枚举。</param>
        public void DestroySceneAsync(int sceneBuildIndex, UnityAction callback = null, UnloadSceneOptions options = UnloadSceneOptions.None)
        {
            MonoManager.Instance.StartCoroutine(DestroySceneCoroutine(sceneBuildIndex, callback, options));
        }
        IEnumerator DestroySceneCoroutine(int sceneBuildIndex, UnityAction callback = null, UnloadSceneOptions options = UnloadSceneOptions.None)
        {
            //异步操作对象,记录了异步操作的数据。
            AsyncOperation asyncOperation = SceneManager.UnloadSceneAsync(sceneBuildIndex, options);

            if (asyncOperation == null)
            {
                Debug.LogWarning("要销毁的场景不合法,销毁无效!");
                yield break;
            }

            //如果没有卸载完成,则停在这里面等待。
            while (asyncOperation.progress < 0.9f)
                yield return CoroutineTool.WaitForFrame();

            //卸载场景完毕之后执行的回调。
            callback?.Invoke();
        }


        /// <summary>
        /// <para>按名称销毁指定的场景和这个场景中的所有游戏对象。</para>
        /// <para>本方法只对加载时用了LoadSceneMode.Additive来加载的场景有效。如果当前游戏中只有一个场景,则本方法无效,且会在控制台报黄色的警告。</para>
        /// <para>本方法不会释放内存中的场景资源,如果要释放该场景资源,应在调用这个方法后,再调用Resources.UnloadUnusedAssets方法。</para>
        /// </summary>
        /// <param name="sceneName">要销毁的场景名或场景路径。</param>
        /// <param name="callback">销毁完毕后执行的回调。</param>
        /// <param name="options">销毁场景的选项。是一个UnloadSceneOptions型枚举。</param>
        public void DestroySceneAsync(string sceneName, UnityAction callback = null, UnloadSceneOptions options = UnloadSceneOptions.None)
        {
            MonoManager.Instance.StartCoroutine(DestroySceneCoroutine(sceneName, callback, options));
        }
        IEnumerator DestroySceneCoroutine(string sceneName, UnityAction callback = null, UnloadSceneOptions options = UnloadSceneOptions.None)
        {
            //异步操作对象,记录了异步操作的数据。
            AsyncOperation asyncOperation = SceneManager.UnloadSceneAsync(sceneName, options);

            if (asyncOperation == null)
            {
                Debug.LogWarning("要销毁的场景不合法,销毁无效!");
                yield break;
            }

            //如果没有卸载完成,则停在这里面等待。
            while (asyncOperation.progress < 0.9f)
                yield return CoroutineTool.WaitForFrame();

            //卸载场景完毕之后执行的回调。
            callback?.Invoke();
        }

        /// <summary>
        /// <para>按场景对象销毁指定的场景和这个场景中的所有游戏对象。</para>
        /// <para>本方法只对加载时用了LoadSceneMode.Additive来加载的场景有效。如果当前游戏中只有一个场景,则本方法无效,且会在控制台报黄色的警告。</para>
        /// <para>本方法不会释放内存中的场景资源,如果要释放该场景资源,应在调用这个方法后,再调用Resources.UnloadUnusedAssets方法。</para>
        /// </summary>
        /// <param name="scene">要销毁的场景对象。</param>
        /// <param name="callback">销毁完毕后执行的回调。</param>
        /// <param name="options">销毁场景的选项。是一个UnloadSceneOptions型枚举。</param>
        public void DestroySceneAsync(Scene scene, UnityAction callback = null, UnloadSceneOptions options = UnloadSceneOptions.None)
        {
            MonoManager.Instance.StartCoroutine(DestroySceneCoroutine(scene, callback, options));
        }
        IEnumerator DestroySceneCoroutine(Scene scene, UnityAction callback = null, UnloadSceneOptions options = UnloadSceneOptions.None)
        {
            //异步操作对象,记录了异步操作的数据。
            AsyncOperation asyncOperation = SceneManager.UnloadSceneAsync(scene, options);

            if (asyncOperation == null)
            {
                Debug.LogWarning("要销毁的场景不合法,销毁无效!");
                yield break;
            }

            //如果没有卸载完成,则停在这里面等待。
            while (asyncOperation.progress < 0.9f)
                yield return CoroutineTool.WaitForFrame();

            //卸载场景完毕之后执行的回调。
            callback?.Invoke();
        }
        #endregion

        #region 返回场景所有游戏对象
        /// <summary>
        /// <para>获取当前激活场景里的所有游戏对象</para>
        /// <para>隐藏的根游戏对象也会包含在其中。DontDestroyOnLoad的游戏对象不包含在其中。</para>
        /// </summary>
        public GameObject[] GetActiveSceneRootGameObjects()
        {
            return SceneManager.GetActiveScene().GetRootGameObjects();
        }

        /// <summary>
        /// <para>获取所有加载的场景里的所有游戏对象</para>
        /// <para>隐藏的根游戏对象也会包含在其中。DontDestroyOnLoad的游戏对象不包含在其中。</para>
        /// </summary>
        public GameObject[] GetLoadedScenesRootGameObjects()
        {
            List<GameObject> list = new List<GameObject>();

            //把当前所有加载的场景的根游戏对象都逐一存储到列表中。
            for (int i = 0; i < SceneManager.sceneCount; i++)
                list.AddRange(SceneManager.GetSceneAt(i).GetRootGameObjects());

            return list.ToArray();
        }
        #endregion

        /// <summary>
        /// 判断指定的场景索引是否合法。
        /// </summary>
        /// <param name="buildIndex">指定的场景索引</param>
        bool IsSceneBuildIndexValid(int buildIndex)
        {
            if (buildIndex < 0)
            {
                Debug.LogWarning($"要加载的场景的索引不合法!该索引是{buildIndex},不能为负数。");
                return false;
            }

            if (buildIndex > SceneManager.sceneCountInBuildSettings - 1)
            {
                Debug.LogWarning($"要加载的场景的索引不合法!该索引越界,越界的索引是{buildIndex},但是场景最大的索引是{SceneManager.sceneCountInBuildSettings - 1}。");
                return false;
            }

            return true;
        }
    }
}

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述