AssetBundle基础

AB包概念

什么是AB包:类似于压缩包,除了C#代码其他u3d常见的基本上都能打包进去
相比于resource.load的好处resource都是一大块的没法修改,AB包则可以任意方式压缩且放在其他位置
作用:实现资源/代码的热更,通过资源对比实现更新
并且减少初始包体的大小,可以后续在下载AB包

AB包生成

可以基于u3d编辑器开发实现自定义打包工具,官方也提供了一套工作流程,下载AssetBundleBrowser,可以把资源关联到包体
压缩方式:1.LZMA压缩的最小,解压的慢且需要全部解压 2.LZ4 用一个资源解压一部分,并且压缩率没有那么高,解压的快
压缩后的主包存储关键依赖信息,manifest存储配置
https://github.com/Unity-Technologies/AssetBundles-Browser

AB包读取

unity特殊文件夹:streamingasset,压缩时候把AB包拷贝到streamingasset,可以跟着项目一起打包出去,同时有api可以直接读streamingasset的ab包

Unity的API:

同步加载

AssetBundle.LoadFromFile(path)//加载ab包
ab.LoadAsset<T>(name)//加载ab包中对应的物体
ab.LoadAsset("Cube",typeof(T)) as T//不用泛型的方式,兼容Lua
Instaniate

异步加载

IEnumerator LoadABRes(abName,resName)
{
	var abcr = AssetBundle.LoadFromFileAsync(path);
	yield return abcr;
	var abq = ab.LoadAssetAsync("Cube",typeof(T)) ;
	yield return abq;
	abq.asset as T
}

U3D单线程是如何实现协程的

Unity 协程的原理,主要是利用了 C# 的迭代器(iterator)特性。迭代器是一种可以返回一个序列中的元素的函数,它使用 yield return 语句来返回每个元素,并且保留函数的状态。例如:

// 一个简单的迭代器函数
IEnumerator CountToTen {
for (int i = 1; i <= 10; i++) {
Console.WriteLine(i); // 输出 1 到 10
yield return null; // 暂停一帧
}
}

当我们调用一个迭代器函数时,它会返回一个 IEnumerator 接口的对象,这个对象有两个属性:Current 和 MoveNext。Current 表示当前返回的元素,MoveNext 表示是否还有下一个元素。每次调用 MoveNext 时,迭代器函数会从上次暂停的地方继续执行,直到遇到下一个 yield return 语句或者函数结束。

Unity 的协程就是基于这个机制实现的。当我们使用 StartCoroutine 方法开启一个协程时,Unity 会将这个协程加入到一个列表中,并且在每一帧的 Update 和 LateUpdate 之间检查这个列表中的所有协程,调用它们的 MoveNext 方法,并根据 yield return 的值判断是否要暂停或者继续这个协程。

AB包依赖关系

如果包里有资产依赖其他包,那么需要在加载包之前加载依赖包
用法是利用主包获取依赖信息
先把主包Load进来,再把主包的配置文件LoadAsset取出来
可以用主包的GetAllDependencies(包名),取到目标包所有的依赖包名,再去找所有的依赖包
缺点是我们知道的是包和包之间的关系,而不是资产和包之间的关系,所以要做好资源管理

AB包资源加载管理器

BV1LD4y1m7kF

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

/// <summary>
/// 知识点 1.单例模式 2.委托 3.协程 4.字典
/// </summary>
public class ABManager : SingletnoAutoMono<ABManager>
{
    //AB包管理器 目的是 让外部更方便的进行资源加载
    //主包
    private AssetBundle mainAB = null;

    //依赖包获取用的配置文件
    private AssetBundleManifest mainfest = null;

    //AB包不能重复加载 重读加载会报错
    //用字典来存储 加载过的AB包

    private Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle>();

    /// <summary>
    /// 存放AB包路径 方便修改
    /// </summary>
    private string PathUrl
    {
        get
        {
            return Application.streamingAssetsPath + "/";
        }
    }

    private string MainABName
    {
        get
        {
#if UNITY_IOS
            return "IOS"
#elif UNITY_ANDROID
            return "Android"
#else
            return "PC";
#endif
        }
    }/// <summary>

     /// 加载AB包 </summary> <param name="abname"></param>
    private void LoadAB(string abName)
    { //加载AB包
        if (mainAB == null)
        {
            mainAB = AssetBundle.LoadFromFile(PathUrl + MainABName);
            mainfest = mainAB.LoadAsset<AssetBundleManifest>(nameof(AssetBundleManifest));
        }
        //获取包的依赖信息
        AssetBundle ab = null;
        string[] strs = mainfest.GetAllDependencies(abName);
        for (int i = 0; i < strs.Length; i++)
        {
            //判断包是否加载过
            if (abDic.ContainsKey(strs[i]))
            {
                ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
                abDic.Add(strs[i], ab);
            }
        }
        //加载资源包
        //如果没有加载过 加载
        if (abDic.ContainsKey(abName))
        {
            ab = AssetBundle.LoadFromFile(PathUrl + abName);
            abDic.Add(abName, ab);
        }
    }

    /// <summary>
    /// 同步加载,不指定类型
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public object LoadRes(string abName, string resName)
    {//加载AB包
        LoadAB(abName);
        //在加载资源时,判断资源是不是Gameobjtct
        //如果是 直接实例化 再返还给外部
        Object obj = abDic[abName].LoadAsset(resName);
        if (obj is GameObject)
        {
            return Instantiate(obj);
        }
        else
        {
            return obj;
        }

        //加载资源
        //return abDic[abName].LoadAsset(resName);
    }

    /// <summary>
    /// 同步加载,根据type 指定类型 lua不支持泛型,因此写重载方法
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    public object LoadRes(string abName, string resName, System.Type type)
    {
        LoadAB(abName);
        //在加载资源时,判断资源是不是Gameobjtct
        //如果是 直接实例化 再返还给外部
        Object obj = abDic[abName].LoadAsset(resName, type);
        if (obj is GameObject)
        {
            return Instantiate(obj);
        }
        else
        {
            return obj;
        }
    }

    /// <summary>
    /// 同步加载 根据泛型指定类型
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public T LoadRes<T>(string abName, string resName) where T : Object
    {
        LoadAB(abName);
        //在加载资源时,判断资源是不是Gameobjtct
        //如果是 直接实例化 再返还给外部
        T obj = abDic[abName].LoadAsset<T>(resName);
        if (obj is GameObject)
        {
            return Instantiate(obj);
        }
        else
        {
            return obj;
        }
    }

    //异步加载
    //AB包没有使用异步加载
    //只是从AB包加载资源时,使用异步
    /// <summary>
    /// 根据名字异步加载
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="callback">使用delegate或者lamada表达式直接对传出的参数object进行操作</param>
    public void LoadResAsync(string abName, string resName, UnityAction<object> callback)
    {
        StartCoroutine(ReallyLoadResAsync(abName, resName, callback));
    }

    private IEnumerator ReallyLoadResAsync(string abName, string resName, UnityAction<object> callback)
    {//加载AB包
        LoadAB(abName);
        //在加载资源时,判断资源是不是Gameobjtct
        //如果是 直接实例化 再返还给外部
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName);

        yield return abr;
        //异步加载结束后 通过委托,传递给外部,外部来使用。 
        if (abr.asset is GameObject)
        {
            callback(Instantiate(abr.asset));
        }
        else
        {
            callback(abr.asset);//将参数传出去
        }
    }

    /// <summary>
    /// 根据Type 异步加载资源
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="type"></param>
    /// <param name="callback"></param>
    public void LoadResAsync(string abName, string resName, System.Type type, UnityAction<object> callback)
    {
        StartCoroutine(ReallyLoadResAsync(abName, resName, type, callback));
    }

    private IEnumerator ReallyLoadResAsync(string abName, string resName, System.Type type, UnityAction<object> callback)
    {//加载AB包
        LoadAB(abName);
        //在加载资源时,判断资源是不是Gameobjtct
        //如果是 直接实例化 再返还给外部
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName, type);

        yield return abr;
        //异步加载结束后 通过委托,传递给外部,外部来使用。
        if (abr.asset is GameObject)
        {
            callback(Instantiate(abr.asset));
        }
        else
        {
            callback(abr.asset);//将参数传出去
        }
    }

    /// <summary>
    /// 根据泛型 异步加载资源
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="callback"></param>
    public void LoadResAsync<T>(string abName, string resName, UnityAction<T> callback) where T : Object
    {
        StartCoroutine(ReallyLoadResAsync<T>(abName, resName, callback));
    }

    private IEnumerator ReallyLoadResAsync<T>(string abName, string resName, UnityAction<T> callback)where T:Object
    {//加载AB包
        LoadAB(abName);
        //在加载资源时,判断资源是不是Gameobjtct
        //如果是 直接实例化 再返还给外部
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync<T>(resName);

        yield return abr;
        //异步加载结束后 通过委托,传递给外部,外部来使用。
        if (abr.asset is GameObject)
        {
            callback(Instantiate(abr.asset) as T);
        }
        else
        {
            callback(abr.asset as T);//将参数传出去
        }
    }

    //单个包卸载
    public void UnLoad(string abName)
    {
        if (abDic.ContainsKey(abName))
        {
            abDic[abName].Unload(false);
            abDic.Remove(abName);
        }
    }

    //所有包的卸载
    public void ClearAB()
    {
        AssetBundle.UnloadAllAssetBundles(false);
        abDic.Clear();
        mainAB = null;
        mainfest = null;
    }
}

猜你喜欢

转载自blog.csdn.net/TongOuO/article/details/132125186