为什么要使用AssetsBundle包
减少安装包的大小
默认情况下,unity编译打包是对项目下的Assets文件夹全部内容进行压缩打包
那么按照这个原理,你的Assets文件夹的大小将会影响到你最终打包出的安装包的大小,假如你现在正在制作一个游戏项目,最终打出来的安装包过大可能会对玩家下载造成一定影响,对一般玩家来讲,下载10M的游戏安装包远远比下载100M的游戏安装包要更容易接受,而AssetBundle可以将一部分资源打包到一个压缩包里面,游戏运行到需要的时候再进行下载这部分资源
游戏一共有100个关卡,如果将这些全部打包则打出的安装包大小为100M,如果现在用AssetBundle技术将后面90关的资源分离出去进行打包,那么你现在打包的游戏内容只有10个关卡的资源,安装包大小仅为10M,等玩家体验了前十关觉得还不错,当他想玩10关以后的游戏内容时,提示他需要下载资源包体验更多精彩内容,然后在unity中加载剩余的90关的资源包。这样做的话,你的游戏安装包仅仅10M大小,因为下载比较快,也比较省流量,可能会吸引更多的玩家下载体验你的游戏
可以用热更新修复游戏的Bug
经历千辛万苦,你的游戏终于上线了,突然收到玩家反馈,游戏第20关通关后无法进入到下一关,据查证,是原来打包时某个同事粗心大意把第21关的资源弄错了,导致加载21关时报错,,我们现在需要第一时间修复这个问题,如果是按照原来的打包方式,需要重新出一个100M的包,然后把重新出的包交给游戏平台审核,审核通过后,玩家需要卸载掉原来的游戏软件,重新下载你们新打出的安装包
如果按照另一种打包方式,我们在后面90关的AssetBundle包里修复这个问题,这时候通过服务器传输AssetBundle最新的包,会把有问题的资源进行修复,提示玩家bug已经修复,玩家进入游戏,发现可以把20关打通了,并且成功进入到下一关了,在这个游戏过程中,玩家不需要重新下载那100M的安装包,只需要打开游戏下载部分需要更新的资源,这部分资源一般会比较小,一般情况下可能是几十k的大小,就可以重新体验游戏了。这就是AssetBundle的第二个作用
AssetsBundle包的打包方式
设置资源标签
需要进行AssetsBundle打包的时候,需要先对要打包的资源的资源标签进行设置
调用Unity官方函数打出AssetsBundle包
对资源打包的设置完成后,通过在代码中调用Unity官方函数的方式,将所有设置了资源标签的资源打出AssetsBundle包
调用的函数为:
public static AssetBundleManifest BuildAssetBundles(string outputPath, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform)
这个函数的功能为将所有设置了资源标签的资源打出AssetsBundle包
函数一共有三个参数:
第一个参数为打包出来文件的路径
第二个参数为设置打包资源的压缩方式,是枚举变量,其中比较常用的三个为:
BuildAssetBundleOptions.UncompressedAssetBundle:不压缩,包大,加载快
BuildAssetBundleOptions.None:使用LZMA算法压缩,压缩的包更小,但是加载时间更长,需要解压全部。
BuildAssetBundleOptions.ChunkBasedCompression:使用LZ4压缩,压缩率没有LZMA高,但是我们可以加载指定资源而不用解压全部。
这几种压缩方式中,LZ4比较常用
第三个参数为设置打包资源的应用平台,一般都使用
BuildTarget.StandaloneWindows64 //PC端
BuildTarget.Android //安卓端
BuildTarget.iOS //苹果端
示例代码:
BuildPipeline.BuildAssetBundles(Application.dataPath + "/AssetBundle", BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
调用函数改变资源标签
改变资源标签的方式,除了在Unity编辑器中设置之外,还可以使用代码设置资源标签
string path = "Assets/MainPrefab.prefab";
AssetImporter importer = AssetImporter.GetAtPath(path);
importer.assetBundleName = "prefab"
importer.assetBundleVariant = "u3d";
这里面应用到的类是AssetImporter类,这个类型代表了AssetBundole资源。
获取AssetImporter类型变量的方法为AssetImporter.GetAtPath(string)方法
这个方法的参数是传入一个由Assets开头,直到文件的后缀名结束的路径,然后将这个文件AssetsBundle相关属性的AssetImporter类返回
然后通过AssetImporter类的assetBundleName改变文件的资源标签
然后通过AssetImporter类的assetBundleVariant改变文件的资源版本标签
加载AssetBundle包
AssetBundle.LoadFromFile 本地资源加载
通过加载本地文件的形式加载AssetBundle资源,场景适用的情况下效率最高
AssetBundle ab = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundle/1.1");
在方法中传入本地资源路径,最后要以AssetBundle包的文件名和后缀名为止,将路径下的AssetBundle包以AssetBundle类型的变量保存下来
AssetBundle.LoadFromFileAsync 本地资源加载(异步)
上面方法的异步版本,在加载过程中不会因为包体过大导致程序进入未响应,需要用到协程(不会协程用法的自己去网上学一下,或者翻我以前的博客)
IEnumerator LoadAssetBundle(Action<AssetBundle> action){AssetBundleCreateRequest creat = AssetBundle.LoadFromFileAsync(Application.dataPath + "/AssetBundle/1.1");yield return creat;AssetBundle ab = creat.assetBundle;action(ab);}
AssetBundle.LoadFromFileAsync方法参数与同步版本相同,不过返回值由AssetBundle类型变为了AssetBundleCreateRequest类型,这个类型的变量可以作为yield return等待方法的参数,然后通过assetBundle属性获取AssetBundle类型变量
AssetBundle.LoadFromMemory 从数据流加载
通过数据流读取AssetBundle资源,这个方法读取本地文件的效率不如LoadFromFile,所以不要用这个方法加载本地文件的AssetBundle,这个方法应该大部分应用于通过网络协议传输的数据流上
AssetBundle ab = AssetBundle.LoadFromMemory(File.ReadAllBytes(Application.dataPath + "/AssetBundle/1.1"));
方法的参数为byte数组,返回一个AssetBundle类型的变量
AssetBundle.LoadFromMemoryAsync 从数据流加载(异步)
上述方法的异步版本,通过数据流加载速度相对较慢,比较建议使用异步
IEnumerator LoadAssetBundle(Action<AssetBundle> action){AssetBundleCreateRequest creat = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(Application.dataPath + "/AssetBundle/1.1"));yield return creat;AssetBundle ab = creat.assetBundle;action(ab);}
与LoadFromFileAsync方法相同,返回一个AssetBundleCreateRequest类型的参数,这个类型的变量可以作为yield return等待方法的参数,然后通过assetBundle属性获取AssetBundle类型变量
UnityWebRequestAssetBundle.GetAssetBundle 从网络获取(异步)
从网址下载AssetBundle包并读取,因为从网络下载的过程都较长,所以这个方法只有异步版本
IEnumerator LoadAssetBundle(Action<AssetBundle> action){string url = "file://" + Application.dataPath + "/AssetBundle/1.1";UnityWebRequest unityAb = UnityWebRequestAssetBundle.GetAssetBundle(url);yield return unityAb.SendWebRequest();AssetBundle ab = DownloadHandlerAssetBundle.GetContent(unityAb);action(ab);}
方法需要传入一个网址作为参数,将这个参数中的内容下载下来,这里传入了一个本地路径,本地路径也可以作为网址传入,方法返回一个UnityWebRequest类型的变量,这个类型的变量中SendWebRequest方法可以返回一个能够在yield return中应用的值,使协程等待至文件下载完毕,然后通过DownloadHandlerAssetBundle.GetContent方法,传入UnityWebRequest类型的参数,就能够返回AssetBundle类型的变量
从AssetBundle包中获取资源
单纯只是读取到AssetBundle类型的变量是无法直接使用的,需要将AssetBundle类型转换为平时使用的资源类型,比如GameObject,Material。Texture类型
AssetBundle.LoadAsset<>() 从包中读取单个文件
AssetBundle ab = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundle/1.1");
GameObject a = ab.LoadAsset<GameObject>("Cube");
AssetBundle.LoadAsset<>()方法分别有一个泛型类型和一个参数,方法的泛型类型中传入需要从方法中获取的返回值类型,参数为需要获取的类型的名字,上面代码中表示返回AssetBundle包中名为Cube的预设物并保存
AssetBundle.LoadAssetAsync<>() 从包中读取单个文件(异步)
IEnumerator LoadGameObject(AssetBundle ab , Action<GameObject> action){AssetBundleRequest abr = ab.LoadAssetAsync<GameObject>("Cube");yield return abr;GameObject g = (GameObject)abr.asset;action(g);}
与AssetBundle.LoadAsset<>()方法的泛型和参数应用方式相同,但是返回值为AssetBundleRequest类型,这个类型可以直接传入yield return,并且可以通过asset属性获取保存的资源,不过返回的资源是Object类型,需要进行强制转换
AssetBundle.LoadAllAssets 从包中获取全部文件
AssetBundle ab = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundle/1.1");
UnityEngine.Object[] objs = ab.LoadAllAssets();
AssetBundle.LoadAllAssets 不需要任何参数,返回一个Objcet数组,这个数组包含AssetBundle包中包含的全部资源
AssetBundle.LoadAllAssetsAsync 从包中获取全部文件(异步)
IEnumerator LoadGameObject(AssetBundle ab , Action<UnityEngine.Object[]> action){AssetBundleRequest abr = ab.LoadAllAssetsAsync();yield return abr;UnityEngine.Object[] objs = ab.LoadAllAssets();action(objs);}
使用方式和LoadAssetAsync相同,只不过是改为了使用LoadAllAssets()方法返回Object数组
卸载AssetBundle资源包
AssetBundle.Unlock 卸载AssetBundle包
AssetBundle ab = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundle/1.1");ab.Unload(true);
调用AssetBundle类型变量的Unload方法,就能够从内存中卸载AssetBundle包的资源,其中参数的布尔值为是否释放场景中正在使用的AssetBundle包中的资源,一般默认都会传入true作为参数,卸载AssetBundle包时,场景中有涉及到包内资源的物体会报错,所以一般在切换场景或特定情况下使用