为什么要使用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 il2cpp打包dots unity打包设置_安装包

调用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包下载到本地,之后每次都从本地加载,那么从网络上下载的异步方法是

IEnumerator CopyAssetBundle(string url,string path)
    {
        UnityWebRequest unityWeb = UnityWebRequest.Get(url);
        yield return unityWeb.SendWebRequest();
        byte[] b = unityWeb.downloadHandler.data;
        FileInfo file = new FileInfo(path);
        FileStream stream = file.Create();
        stream.Write(b, 0, b.Length);
        stream.Flush();
        stream.Close();
    }

方法传入的第一个参数为下载AssetBundle包的网址,第二个参数为AssetBundle包下载到本地的位置,通过UnityWebRequest.Get方法将AssetBundle包以单纯数据流的方式下载下来,然后通过

unityWeb.downloadHandler.data属性获取数据流内容,再通过FileInfo的方式创建文件,再通过FileStream文件流的方式将数据流写入文件,完成下载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 = abr.allAssets;
        action(objs);
    }

使用方式和LoadAssetAsync相同,只不过是改为了使用abr.allAssets属性返回Object数组


卸载AssetBundle资源包

AssetBundle.Unlock 卸载AssetBundle包

AssetBundle ab = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundle/1.1");
        ab.Unload(true);

调用AssetBundle类型变量的Unload方法,就能够从内存中卸载AssetBundle包的资源,其中参数的布尔值为是否释放场景中正在使用的AssetBundle包中的资源,一般默认都会传入true作为参数,卸载AssetBundle包时,场景中有涉及到包内资源的物体会报错,所以一般在切换场景或特定情况下使用