前言.
【Unity游戏客户端框架搭建】四、资源管理
上一篇文章最后一点,我们讲到AB,结合这篇文章,【unity游戏开发】AB学习(一)—AB基础知识 我们已经能够打出AB,接着我们讲讲AB的加载和卸载的细节。
终于到了一个框架的开始阶段,前面都是准备阶段,只有开始加载东西,游戏的资源才能展现在我们面前。 那么资源加载模块必然相当重要,一个稳定的加载模块是游戏运行的根基。这篇文章主要LoadModule的封装。
零、框架
- 驱动层:update
- 加载不同对象: KEngine/ResourceModule
一、加载
前面我们有说到,资源有各种各样的格式,有音效、图片、字体、场景、ab等,所以我们分析了各个资源加载过程,抽象了一个基类:
Loader对象,在不同类型的资源加载中,不同的行为被划分成不同的Loader对象。来给资源加载代码赋予更好的维护性和可读性。
Loader对象
Init、 Load、Stop、Update、Reset
public enum LoaderType
{
STREAM, // 流(原则上可以是任何文件,包括远程服务器上的)
ASSET, // Asset目录下的资源
BUNDLE, // AssetBundle
BUNDLEASSET, // AssetBundle中的资源
SCENE, // 场景
}
public enum LoaderState
{
NONE, // 默认
LOADING, // 加载中
FINISHED, // 完成
}
public virtual void Init(string path, LoadedCallback loadedCallback,bool async = true)
抽象好Loader对象,在加载的时候,我们要分情况讨论,加载方式是同步还是异步。一般情况,加载本地的Asset,都直接用同步,对于一些场景资源比较大的,或者需要网络下载的,我们用异步加载。
// 加载队列
private List<Loader> m_loadingList = new List<Loader>(); // 加载中列表
private List<Loader> m_loaderQueue = new List<Loader>(); // 异步加载列表
我们维护2个列表去管理加载中和异步加载的对象。
- ab打完会生成一个manifest清单文件,我们的加载模块加载ab的时候,需要依赖到这个清单,所以在我们模块初始化的时候就去加载manifest,并且用局部变量m_manifest记录引用,把所有的ab名称用m_bundleNames存起来,在实际加载的时候,先判断这个ab是否存在在m_bundleNames中,不存在就是一个非法ab。
2. 然后加载ab之前,先把ab的依赖先加载进来,递归加载依赖;
AssetBundleManifest
public string[] GetDirectDependencies(string assetBundleName);
3. 加载完依赖开始真正加载该ab,引用计数+1
4. 接下来,我们要做的就是初始化Loader对象,创建对应的Loader,给Loader对象传需要加载的路径,调用Loader对象的Load方法,然后去实现不同对象的Load方法和Update方法。这边主要讨论bundle的加载,所以我们看一下BundleLoader的实现。
5. 异步加载或者加载还未准备好,则当做异步处理,加入到m_loaderQueue队列;同步加载,并且已经具备加载条件,则直接调用Loader的Load方法进行加载。加入到m_loadingList队列
6. 然后在Update去遍历异步加载列表m_loaderQueue,把满足开始加载条件的Loader从该列表移除,添加到加载中列表m_loadingList,调用Loader的Load方法;遍历加载中列表m_loadingList,逐个调用Loader的Update方法,去刷新每个加载的进度
接下来我们详细解读一下BundleLoader的Load方法,
// 如果是压缩模式,并且包在StreamingAsset目录下,才需要解压缩
安卓的话需要把ab文件拷贝到临时ab路径,拷贝之前,调用了sdk的获取硬盘容量,判断是否有足够空间
如果需要解压缩的话,调用C的lzma解压缩,这边特意自己编译了lzma的dll,讲解压缩放在c处理,
不然会解压缩在C#做的话,会撑大mono堆内存
然后如果是异步就调用m_abRequest = AssetBundle.LoadFromFileAsync(apkPath);
同步就调用ab = AssetBundle.LoadFromFile(apkPath);
二、卸载
卸载的时候:
- 引用计数-1
- 递归卸载依赖
- 卸载的时候要注意:
ab能否卸载,比如常驻资源就不卸载;非常驻资源,并且引用计数为0才能卸载
2. 如果资源正在异步加载中也不能卸载
3. 调用 AssetBundle.Unload(false),则AB的包头信息将被卸载,但M将保持在场景中,并且仍然是可用的。这个没有理解的,具体见Unity手游实战:从0开始SLG——资源管理系统-基础篇(四)AssetBundle 最佳实践
如果应用程序必须使用AssetBundle.Unload(False),那么只能通过两种方式卸载各个对象:
- 1、在场景和代码中消除对不需要的对象的所有引用。完成后,调用Resources.UnusedAsset。
- 2、非附加加载场景。这将销毁当前场景中的所有对象并调用Resources.UnusedAsset。
2020.12.11 XAsset 开箱试玩
使用教程:xasset入门指南 - TA养成记 、默默的奶爸:XASSET 4.0入门指南
自己实践的代码:Aver58/TeddyFrameWork
参考:
用面向对象思想,管住Unity调皮的AssetBundle
谈谈Unity资源管理 几个经典问题:资源引用计数、异步加载
推荐一个写超好的系列:
Unity手游实战:从0开始SLG——资源管理系统-基础篇(一)浅谈Asset(Unity资产映射)
Unity手游实战:从0开始SLG——资源管理系统-基础篇(二)Resources 目录的优点与痛点
Unity手游实战:从0开始SLG——资源管理系统-基础篇(三)AssetBundle原理
Unity手游实战:从0开始SLG——资源管理系统-基础篇(四)AssetBundle 最佳实践