U2L10-2——Resources资源异步加载

本章代码关键字

1
2
3
4
5
6
7
8
9
Resources.LoadAsync<>()                //Resources资源异步加载的方法,返回一个ResourceRequest类型的变量作为资源请求对其进行监听,获取等操作
AsyncOperation //异步操作类,继承了协程中断指令类,因此协程调度器可以识别它,等到它对应的异步操作完成再执行继续协程
AsyncOperation.completed //异步操作完成后就会执行的事件,外部需要向该事件添加监听函数,才能异步完成后通过监听函数获取加载的资源
AsyncOperation.isDone //返回异步操作是否完成的属性
AsyncOperation.progress //返回异步操作的进度的属性
AsyncOperation.priority //返回异步操作的优先级的属性
AsyncOperation.allowSceneActivation //返回异步操作是否允许在当前场景激活的属性
ResourceRequest //资源请求类,继承了异步操作类,因此可以用异步操作的成员也能在协程里返回它,异步加载完成后要从该类获取资源
resourceRequest.asset //异步操作完成后,资源通过该属性去获取

何为Resources资源异步加载

上节课学习的同步加载中
如果加载过大的资源可能会造成程序卡顿
卡顿的原因就是 从硬盘上把数据读取到内存中 是需要进行计算的
越大的资源耗时越长,就会造成掉帧卡顿

Resources资源异步加载 就是内部新开一个线程进行资源加载 不会造成主线程卡顿

缺点是这样的加载不能使主线程立刻得到数据,需要等待异步加载完毕后才能从公共内存中读取数据

本章涉及协程的知识——>协同程序、协同程序原理

Resources资源异步加载方法

异步加载也有和同步加载一样的普通加载方法和泛型加载方法,区别类似于Resources资源同步加载一般建议直接使用泛型方法,下面也只介绍泛型方法

有两种方法:

  • 第一种方法:完成事件监听异步加载
    好处:写法简单
    坏处:只能在资源加载结束后继续处理
    “线性处理”
  • 第二种方法:协程异步加载
    好处:可以在协程中处理复杂逻辑,比如同时加载多个资源,比如进度条更新
    坏处:写法较麻烦
    “并行加载”

注意:
理解为什么异步加载不能马上加载结束,为什么至少要等一帧
理解协程异步加载的原理

注意!异步加载不能马上得到加载的资源 至少要等一帧

通过异步加载中的完成事件监听,使用加载的资源

大致步骤为:
执行Resource.LoadAsync<获取类型>("文件名")​并用ResourceRequest​(资源请求)类变量装载其返回值
ResourceRequest​类里有 当完成资源加载时就执行的事件,因此向事件添加参数为AsyncOperation​(异步操作)类型的监听函数
在监听函数内执行获取资源的操作,最后再使用资源

具体的代码运行如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private Texture tex;

//定义监听函数,当异步完成时就会调用该函数,通过该函数获取资源
private void LoadOver(AsyncOperation rq)
{
print("加载结束");
tex = (rq as ResourceRequest).asset as Texture; //通过这个属性获取资源
print(Time.frameCount); //输出异步加载完成后是第几帧,通过这个可以确认异步获取资源至少需要等一帧
}

//代码从这里开始
void Start()
{
//这句代码 可以理解为 Unity在内部会开一个线程去进行资源下载
ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/test");
//添加一个 资源下载结束的 一个事件函数监听
rq.completed += LoadOver;
print(Time.frameCount); //输出异步开始执行时是第几帧,通过这个可以确认异步获取资源至少需要等一帧
//如果这时进行rq.asset获取资源是不对的,因为此时资源是没有加载完毕的,使用异步加载一定是至少等到下一帧或者更久才有资源
//一定要等到加载结束后 才能获取资源
}

//使用获取的资源的函数
private void OnGUI()
{
if (tex != null) //一定要确认获取到资源了才开始渲染GUI
GUI.DrawTexture(new Rect(0, 0, 100, 100), tex);
}

代码里涉及到的关键字有:

如何监听异步加载完成

completed​是AsyncOperation​异步操作完成后就会执行的事件,外部需要向该事件添加监听函数,才能异步完成后通过监听函数获取加载的资源
ResourceRequest​继承了AsyncOperation​,因此可以使用它

1
2
3
4
5
6
7
8
9
10
ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/test");
rq.completed += LoadOver; //这是AsyncOperation的事件,ResourceRequest继承了它所以可以用,该事件在异步完成执行
//该事件只能添加参数为AsyncOperation类的函数

private void LoadOver(AsyncOperation rq) //当异步完成后被调用执行
{
print("加载结束");
tex = (rq as ResourceRequest).asset as Texture; //通过这个属性获取资源,因为这是派生类的方法所以需要转化
print(Time.frameCount); //输出异步加载完成后是第几帧,通过这个可以确认异步获取资源至少需要等一帧
}

异步加载后怎么获取资源

因为AsyncOperation​没有这个方法,而继承它的ResourceRequest​有这个方法,所以可能需要转换一次才能使用

1
tex = (rq as ResourceRequest).asset as Texture;    //通过这个属性获取资源

通过协程,使用加载的资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void Start()
{
//通过协程 使用加载的资源
StartCoroutine(Load());
}

IEnumerator Load()
{
ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/test");
print(Time.frameCount);
yield return rq; //向Unity协程调度器返回这种类型的变量,Unity就会识别出当前正在执行异步下载资源操作
//Unity会自行判断该资源是否加载完毕了 加载完毕后才会继续执行后面的代码
tex = rq.asset as Texture;
print("加载结束");
print(Time.frameCount);
}

向Unity协程调度器返回一个ResourceRequest​类(或者说AsyncOperation​类),
Unity协程调度器会识别到这是一个异步操作,这样就会中断协程,直到异步操作完成后再继续执行协程

ResourceRequest类(AsyncOperation类)的属性

在这里也可以使用ResourceRequest​类(或者说AsyncOperation​类)的属性,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Start()
{
//通过协程 使用加载的资源
StartCoroutine(Load2());
}

IEnumerator Load2()
{
ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/test");
//判断资源是否加载结束
while (!rq.isDone)
{
//打印当前的加载进度
//但是 这个进度 不会特别准确 过渡也不是特别明显
print(rq.progress);
yield return null;
}
tex = rq.asset as Texture;
}
异步操作是否完成
1
2
3
4
5
6
7
8
9
ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/test");
//判断资源是否加载结束
while (!rq.isDone)
{
//打印当前的加载进度
//但是 这个进度 不会特别准确 过渡也不是特别明显
print(rq.progress);
yield return null;
}
异步操作进度查询
1
print(rq.progress);