21.SceneManager场景异步加载

21.场景异步加载


21.1 知识点

回顾场景同步切换

SceneManager.LoadScene 同步加载场景

  • 引用命名空间 UnityEngine.SceneManagement;
  • 场景同步切换
    SceneManager.LoadScene("Lesson21_场景异步加载Test");
    

场景同步切换的缺点

  • 在切换场景时,Unity会删除当前场景上所有对象,并且去加载下一个场景的相关信息。
  • 如果当前场景对象过多或者下一个场景对象过多,这个过程会非常耗时,会让玩家感受到卡顿。
  • 所以异步切换就是来解决该问题的。

场景异步切换

SceneManager.LoadSceneAsync 事件回调异步加载场景

void Start()
{
    // SceneManager中的LoadSceneAsync方法通过事件回调函数异步加载
    // 在后台异步加载场景。
    // 这个方法有一个异步加载的协程返回值
    // AsyncOperation类 异步操作协同程序。里面有isDone、progress、completed等成员
    AsyncOperation asyncOperation = SceneManager.LoadSceneAsync("Lesson21_场景异步加载Test");
    // 当场景异步加载结束后就会自动调用该事件函数,我们如果希望在加载结束后做一些事情,那么就可以在该函数中写处理逻辑
    asyncOperation.completed += (aO) =>
    {
        print("加载结束");
    };
    asyncOperation.completed += AsyncLoadOver;
    // 注意:
    // 直接使用这种方法异步加载场景的话,就算过场景脚本或脚本依附的对象被销毁,异步加载的回调也能执行
    // 因为异步加载的回调被存到事件中了
}


// 异步加载场景回调
private void AsyncLoadOver(AsyncOperation ao)
{
    print("AsyncLoadOver");
}

协程异步加载

void Start()
{
    // 通过协程异步加载
    // 需要注意的是,加载场景会把当前场景上没有特别处理的对象都删除了
    // 所以,协程中的部分逻辑可能是执行不了的
    // 因为过场景会把脚本或者脚本依附的对象都销毁了
    // 解决思路:让处理场景加载的脚本依附的对象过场景时不被移除

    // 让该脚本依附的对象过场景时不会被移除,这样脚本就不被移除,否则协程后面的逻辑会失效
    DontDestroyOnLoad(this.gameObject);
    // 协程后面的逻辑会失效是因为Unity的协程管理器的机制,只要依附的对象被删除或失活或者脚本被删除,协程就会失效,我们改不了这个机制

    StartCoroutine(CoroutineAsyncLoadScene("Lesson21_场景异步加载Test"));
}


// 异步加载场景协程函数
IEnumerator CoroutineAsyncLoadScene(string name)
{
    // 异步加载场景
    AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(name);

    // 协程的好处是,在异步加载场景时,我可以在加载的同时做一些别的逻辑
    print("异步加载过程中打印的信息");

    // 比如,我们可以在异步加载过程中去更新进度条,一般有两种做法
    // 更新进度条第一种做法就是利用场景异步加载的进度去更新,但是不是特别准确,一般也不会直接用
    while (!asyncOperation.isDone)
    {
        print(asyncOperation.progress);
        yield return null;
    }

    // 离开循环后就会认为场景加载结束
    // 可以把进度条顶满,然后隐藏进度条

    // Unity内部的协程协调器发现是异步加载类型的返回对象,那么就会等待
    // 等待异步加载结束后才会继续执行迭代器函数中后面的步骤
    yield return asyncOperation;
    print("异步加载结束后打印的信息");

    // 更新进度条第二种做法就是根据你游戏的规则自己定义进度条变化的条件
    // 比如:当场景加载结束更新20%进度
    // 接着去加载场景中的其他信息,比如动态加载怪物
    // 这时进度条再更新20%
    // 动态加载场景模型,这时就认为加载结束了,进度条顶满,隐藏进度条
}

总结

  • 场景异步加载资源异步加载 一样,有两种方式
    • 通过事件回调函数
    • 协程异步加载
  • 他们的优缺点表现和资源异步加载也是一样的
    • 事件回调函数
      • 优点:写法简单,逻辑清晰
      • 缺点:只能加载完场景做一些事情,不能再加载过程中处理逻辑
    • 协程异步加载
      • 优点:可以在加载过程中处理逻辑,比如进度条更新等
      • 缺点:写法较为麻烦,要通过协程

21.2 知识点代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Lesson21_场景异步加载 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 回顾场景同步切换

        //引用命名空间 UnityEngine.SceneManagement;
        //场景同步切换
        SceneManager.LoadScene("Lesson21_场景异步加载Test");

        //场景同步切换的缺点
        //在切换场景时
        //Unity会删除当前场景上所有对象
        //并且去加载下一个场景的相关信息
        //如果当前场景 对象过多或者下一个场景对象过多
        //这个过程会非常的耗时 会让玩家感受到卡顿
        //所以异步切换就是来解决该问题的

        #endregion

        #region 知识点二 场景异步切换
        //场景异步加载和资源异步加载 几乎一致 有两种方式

        //SceneManager中的LoadSceneAsync方法 通过事件回调函数 异步加载
        //在后台异步加载场景。
        //这个方法有一个异步加载的协程返回值
        //AsyncOperation类 异步操作协同程序。里面有isDone progress completed等成员
        AsyncOperation asyncOperation = SceneManager.LoadSceneAsync("Lesson21_场景异步加载Test");
        //当场景异步加载结束后 就会自动调用该事件函数 我们如果希望在加载结束后 做一些事情 那么就可以在该函数中
        //写处理逻辑
        asyncOperation.completed += (aO) =>
        {
            print("加载结束");
        };
        asyncOperation.completed += AsyncLoadOver;
        //注意:
        //直接使用这种方法异步加载场景的话,就算过场景脚本或脚本依附的对象被销毁,异步加载的回调也能执行
        //因为异步加载的回调被存到事件中了

        //通过协程异步加载
        //需要注意的是 加载场景会把当前场景上 没有特别处理的对象 都删除了
        //所以 协程中的部分逻辑 可能是执行不了的 
        //因为过场景会把脚本或者脚本依附的对象都销毁了
        //解决思路:让处理场景加载的脚本依附的对象 过场景时 不被移除

        //让该脚本依附的对象 过场景时 不会被 移除 这样脚本就不被移除 不然协程后面的逻辑会失效
        DontDestroyOnLoad(this.gameObject);
        //协程后面的逻辑会失效是因为Unity的协程管理器的机制,只要依附的对象被删除或失活或者脚本被删除协程就会失效,我们改不了这个机制

        StartCoroutine(CoroutineAsyncLoadScene("Lesson21_场景异步加载Test"));

        #endregion

        #region 总结
        //场景异步加载 和 资源异步加载 一样
        //有两种方式
        //1.通过事件回调函数
        //2.协程异步加载

        //他们的优缺点表现和资源异步加载 也是一样的
        //1.事件回调函数
        //优点:写法简单,逻辑清晰
        //缺点:只能加载完场景做一些事情 不能再加载过程中处理逻辑
        //2.协程异步加载
        //优点:可以在加载过程中处理逻辑,比如进度条更新等
        //缺点:写法较为麻烦,要通过协程

        #endregion
    }

    //异步加载场景回调
    private void AsyncLoadOver(AsyncOperation ao)
    {
        print("AsyncLoadOver");
    }

    //异步加载场景协程函数
    IEnumerator CoroutineAsyncLoadScene(string name)
    {
        //异步加载场景
        AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(name);

        //协程的好处 是异步加载场景时 我可以在加载的同时 做一些别的逻辑
        print("异步加载过程中 打印的信息");

        //比如 我们可以在异步加载过程中 去更新进度条 一般有两种做法
        //更新进度条第一种做法 就是利用 场景异步加载 的进度 去更新 但是 不是特别准确 一般也不会直接用
        while (!asyncOperation.isDone)
        {
            print(asyncOperation.progress);
            yield return null;
        }

        //离开循环后 就会认为场景加载结束
        //可以把进度条顶满 然后 隐藏进度条

        //Unity内部的 协程协调器 发现是异步加载类型的返回对象 那么就会等待
        //等待异步加载结束后 才会继续执行 迭代器函数中后面的步骤
        yield return asyncOperation;
        print("异步加载结束后 打印的信息");

        //更新进度条第二种做法 就是根据你游戏的规则 自己定义 进度条变化的条件
        //比如:当场景加载结束 更新20%进度
        //接着去加载场景中 的其它信息
        //比如
        //动态加载怪物
        //这时 进度条 再更新20%
        //动态加载 场景模型
        //这时 就认为 加载结束了 进度条顶满 
        //隐藏进度条
    }
}

21.3 练习题

请写一个简单的场景管理器,提供统一的方法给外部用于场景异步切换,外部可以传入委托用于当异步切换结束时执行某些逻辑

创建场景管理器单例类,添加异步加载场景函数并实现:

// 定义一个场景管理类 
public class SceneMgr
{
    // 使用instance来实现单例模式,因为构造函数被声明为private,
    // 防止在类外部实例化本类的对象,保证了全局只有一个实例。
    private static SceneMgr instance = new SceneMgr();
        
    // 类似于getter方法,返回静态的SceneMgr实例
    public static SceneMgr Instance => instance;
    
    // 构造函数私有化,使得无法在类外部通过new直接创建该类的对象
    private SceneMgr() { }
    
    // 加载场景的方法,需要传入目标场景的名称和一个UnityAction类型的委托参数
    public void LoadScene(string name, UnityAction action)
    {
        //注意:资源异步加载的时候委托要有个泛型参数是因为要做的加载的是啥类型as一下
        
        // 异步加载目标场景,SceneManager.LoadSceneAsync返回一个AsyncOperation类型的值
        AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(name);
        
        // 在异步加载完成后执行的回调函数,将传入的委托类型的参数作为参数传入
        asyncOperation.completed += (aO) =>
        {
            //注意:资源异步加载的时候用到aO这个参数是因为要把aO处理下拿到asset
            //(asyncOperation as ResourceRequest).asset
            
            //通过拉姆达表达式 包裹一层
            //在内部 直接调用外部传入的委托即可
            action();  // 直接执行传入的委托
        };
    }
}

测试使用场景管理类异步加载:

SceneMgr.Instance.LoadScene("Lesson21_场景异步加载Test", () => {
    print("加载结束");
});
// 这样加载一次代码量会少很多
#endregion

21.4 练习题代码

SceneMgr

// 引入常用的Unity命名空间
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;

// 定义一个场景管理类 
public class SceneMgr
{
    // 使用instance来实现单例模式,因为构造函数被声明为private,
    // 防止在类外部实例化本类的对象,保证了全局只有一个实例。
    private static SceneMgr instance = new SceneMgr();
    
    // 类似于getter方法,返回静态的SceneMgr实例
    public static SceneMgr Instance => instance;

    // 构造函数私有化,使得无法在类外部通过new直接创建该类的对象
    private SceneMgr() { }

    // 加载场景的方法,需要传入目标场景的名称和一个UnityAction类型的委托参数
    public void LoadScene(string name, UnityAction action)
    {
        //注意:资源异步加载的时候委托要有个泛型参数是因为要做的加载的是啥类型as一下

        // 异步加载目标场景,SceneManager.LoadSceneAsync返回一个AsyncOperation类型的值
        AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(name);

        // 在异步加载完成后执行的回调函数,将传入的委托类型的参数作为参数传入
        asyncOperation.completed += (aO) =>
        {
            //注意:资源异步加载的时候用到aO这个参数是因为要把aO处理下拿到asset
            //(asyncOperation as ResourceRequest).asset

            //通过拉姆达表达式 包裹一层
            //在内部 直接调用外部传入的委托即可
            action();  // 直接执行传入的委托
        };
    }
}

Lesson21_练习题

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson21_练习题 : MonoBehaviour
{
    #region 练习题一
    //请写一个简单的场景管理器,提供统一的方法给外部用于场景异步切换,外部可以传入委托用于当异步切换结束时执行某些逻辑

    void Start()
    {
        SceneMgr.Instance.LoadScene("Lesson21_场景异步加载Test", () => {
            print("加载结束");
        });
        //这样加载一次代码量会少很多
    }

    #endregion
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏