AB包加载Demo

简单的异步AB加载框架

简述打包流程

  1. 设置资源的BundleName以及BundleVariant
  2. 打包AssetBundle
  3. (将AB包上传到服务端,若放在本地则可跳过)
  4. 加载AssetBundle
  5. 实例化AssetBundle中的资源
  6. AssetBundle的卸载
    在这里插入图片描述
    着重讲解AB包的异步加载。

包与包的依赖关系:
在这里插入图片描述
如上图所示包和包的依赖关系可能是以上图树状结构,如果我们要使用A包里面的资源,但是A包里面的资源依赖C包资源,而C包资源又依赖D包资源,所以需要根据Manefist的依赖关系去整合信息,使用A包资源前要先加载C包和D包。
依赖关系处理:
在这里插入图片描述

如上图所示通过Manefist可以获取到包与包之间的依赖关系,将依赖关系抽象为一个类,根据我们加载的资源判断该包是否需要加载依赖包(如果依赖包已经存在就不再重复加载依赖包)。
资源加载处理:
根据依赖关系层层递进将最深层次的包进行加载,然后再确定最初的包加载好,根据引用计数的方式处理包的卸载加载。
异步加载关于回调:
这个Demo使用的是协程,所以使用的是回调,网上很多大佬的处理方式是去携程或者使用await或者async的方式。

代码解析:

/// <summary>
/// 将AB包的信息抽象到一个类里面
/// </summary>
private class AssetBundleObject
{
    public string HashName; //hash标识符
    public int RefCount; //被引用计数
    public List<AssetBundleLoadCallBack> CallFunList = new List<AssetBundleLoadCallBack>(); //回调函数表 加载好后可以调用该函数表
    public AssetBundleCreateRequest Request; //异步加载请求
    public AssetBundle AB; //加载到的ab
    public int DependLoadingCount; //正在加载的依赖数量   为0代表其依赖已经加载完毕就可以被使用了
    public List<AssetBundleObject> Depends = new List<AssetBundleObject>(); //直接的依赖项ABObj
}
/// <summary>
/// Load主Manifest  能够获得AB包所有的依赖包
/// </summary>
/// <param name="rPath">Manifest路径</param>
public void LoadManifest(string rPath)
{
    GlobalABdps.Clear();
    //Manifest也是一种AB资源
    AssetBundle rAB = AssetBundle.LoadFromFile(rPath);
    AssetBundleManifest rMainfest = rAB.LoadAsset("AssetBundleManifest") as AssetBundleManifest;
    //遍历Manifest中的ABs
    foreach (string rAssetName in rMainfest.GetAllAssetBundles()) //GetAllAssetBundles =>  Get all the AssetBundles in the manifest.
    {
        string rHashName = rAssetName.Replace(".ab", "");
        string[] dps = rMainfest.GetAllDependencies(rAssetName); //返回该AB的依赖AB
        for (int i = 0; i < dps.Length; i++)
        {
            dps[i] = dps[i].Replace(".ab", "");
        }
        GlobalABdps.Add(rHashName, dps);
    }
    rAB.Unload(true);
    rAB = null;
}
/// <summary>
/// 深度递归遍历依赖项 刷新自己被引用计数
/// </summary>
private void DFSDependRef(AssetBundleObject rABObj)
{
    rABObj.RefCount++;
    foreach (var rParentAB in rABObj.Depends)
    {
        DFSDependRef(rParentAB);
    }
}
/// <summary>
/// 深度递归遍历依赖项 刷新被计数 达到条件则卸载资源
/// </summary>
private void DFSDependRefReduce(AssetBundleObject rABObj)
{
    rABObj.RefCount--;
    foreach (var rParentAB in rABObj.Depends)
    {
        DFSDependRefReduce(rParentAB);
    }

    if (rABObj.RefCount == 0)
    {
        rABObj.AB.Unload(false);
        this.LoadedAssetBundle[rABObj.HashName] = null;
        this.LoadedAssetBundle.Remove(rABObj.HashName);
        rABObj = null;
    }
}
/// <summary>
/// 递归加载资源  会同时加载其依赖包
/// </summary>
/// <param name="rABName">AB包的名字</param>
/// <returns></returns>
private AssetBundleObject LoadAssetBundleAsync(string rABName, UnityAction rCallBack = null)
{
    if (!GlobalABdps.ContainsKey(rABName))
    {
        Debug.Log($"没有 {rABName} 包");
        return null;
    }
    AssetBundleObject rABObj = null;
    //如果存在
    if (LoadedAssetBundle.ContainsKey(rABName))
    {
        rABObj = LoadedAssetBundle[rABName];
        DFSDependRef(rABObj);
        return rABObj;
    }
    //创建并初始化新ABObj
    rABObj = new AssetBundleObject();
    rABObj.HashName = rABName;
    rABObj.RefCount = 1;
    //加载依赖
    var rDps = this.GlobalABdps[rABObj.HashName];
    rABObj.DependLoadingCount = rDps.Length;
    foreach (var rDpName in rDps)
    {
        var dpObj = LoadAssetBundleAsync(rDpName, delegate { rABObj.DependLoadingCount--; });
        rABObj.Depends.Add(dpObj);
    }
    //异步加载AB包
    LoadAssetAsyn(rABObj, rCallBack);
    return rABObj;
}
/// <summary>
/// 启用协程异步加载AB包
/// </summary>
/// <param name="rABObj">ABObj</param>
/// <param name="rCallBack">回调函数表</param>
private void LoadAssetAsyn(AssetBundleObject rABObj, UnityAction rCallBack) //List<UnityAction<object[]>> rCallBack)
{
    Game.Instance.StartCoroutine(CoroutineLoad(rABObj, rCallBack));//因为这个类没有继承Mono 所以将协程抛到其他Mono下执行
}

private IEnumerator CoroutineLoad(AssetBundleObject rABObj, UnityAction rCallBack)
{
    AssetBundleCreateRequest rABAns = AssetBundle.LoadFromFileAsync(this.GetABPath(rABObj.HashName));
    yield return rABAns;
    while (rABObj.DependLoadingCount != 0)//如果依赖项都没加载完就一直执行
    {
        yield return new WaitForEndOfFrame();
    }
    rABObj.AB = rABAns.assetBundle;
    this.LoadedAssetBundle.Add(rABObj.HashName, rABObj);
    rCallBack?.Invoke();//执行回调
}
/// <summary>
/// 卸载资源
/// </summary>
/// <param name="rABpath">AB包路径</param>
/// <param name="rCallback">回调</param>
public void UnLoadAssetAsync(string rABpath, UnityAction rCallback)
{
    if (this.LoadedAssetBundle.ContainsKey(rABpath))
    {
        DFSDependRefReduce(this.LoadedAssetBundle[rABpath]);
        rCallback?.Invoke();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值