前言.

【Unity游戏客户端框架搭建】四、资源管理
上一篇文章最后一点,我们讲到AB,结合这篇文章,【unity游戏开发】AB学习(一)—AB基础知识 我们已经能够打出AB,接着我们讲讲AB的加载和卸载的细节。

终于到了一个框架的开始阶段,前面都是准备阶段,只有开始加载东西,游戏的资源才能展现在我们面前。 那么资源加载模块必然相当重要,一个稳定的加载模块是游戏运行的根基。这篇文章主要LoadModule的封装。


零、框架

  1. 驱动层:update
  2. 加载不同对象: KEngine/ResourceModule

一、加载

前面我们有说到,资源有各种各样的格式,有音效、图片、字体、场景、ab等,所以我们分析了各个资源加载过程,抽象了一个基类:

Loader对象,在不同类型的资源加载中,不同的行为被划分成不同的Loader对象。来给资源加载代码赋予更好的维护性和可读性。

Loader对象

InitLoad、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)





unity资源管理系统_asset文件夹路径 unity


抽象好Loader对象,在加载的时候,我们要分情况讨论,加载方式是同步还是异步。一般情况,加载本地的Asset,都直接用同步,对于一些场景资源比较大的,或者需要网络下载的,我们用异步加载。


// 加载队列
    private List<Loader> m_loadingList = new List<Loader>();    // 加载中列表
    private List<Loader> m_loaderQueue = new List<Loader>();    // 异步加载列表


我们维护2个列表去管理加载中和异步加载的对象。

  1. ab打完会生成一个manifest清单文件,我们的加载模块加载ab的时候,需要依赖到这个清单,所以在我们模块初始化的时候就去加载manifest,并且用局部变量m_manifest记录引用,把所有的ab名称用m_bundleNames存起来,在实际加载的时候,先判断这个ab是否存在在m_bundleNames中,不存在就是一个非法ab。


unity资源管理系统_asset文件夹路径 unity_02


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. 引用计数-1
  2. 递归卸载依赖
  3. 卸载的时候要注意:

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 最佳实践