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;
}
}