【Unity】AssetBundles浅析

字数 1556阅读 352

是什么

AssetBundle是包含模型、材质、纹理、场景等资源的文件。是把unity资源按照一定的规则打包,然后这些资源可以上传的服务器,在游戏中可以下载这些资源,再把解包成资源。


打包上传服务器

分为两步:1.打包AssetBundles。2.上传AssetBundles到服务起。


下载解包

分为两步:1.下载AssetBundles到应用中。2.加载需要的资源从AssetBundles

怎么用

步骤在上面的介绍都可以清晰的看出来了,我们这里分为5个简单的步骤。

简单的工作流:
●在编辑器中组织和设置AssetBundles。
●打包AssetBundles。
●上传AssetBundles到外部存储。
●在运行时下载AssetBundles。
●加载AssetBundles中的资源。

下面我们来详细的看看每一个步骤具体怎么做。

在编辑器中组织和设置AssetBundles:

这里只需要设置名字与类别。



先在AssetBundle设置资源的AssetBundle的名字。这里这里设置名字时,设置二级菜单使用/斜杠编写名字例如environment/desert.


Paste_Image.png

名字后面还可以设置一个类别,叫作AssetBundle Variants,跟shader里面设置不同设备渲染算法不同一个道理,这个设置名称来区别加载那里一资源。比如说在好一点设备上可以加载高模不卡顿,而差一点的设备中只能加载低模才不卡顿,我们就可以使用这个来区别。这个也可用来做分包,不同版本包,最好的例子就是做多语言包。

打包AssetBundles:
然后再导出AssetBundle使用下面的代码。

  [MenuItem("Assets/Build AssetBundles")]
  private static void BuildAllAssetBUndles()
  {
      BuildPipeline.BuildAssetBundles("Assets/AssetBundles", BuildAssetBundleOptions.None,
        BuildTarget.StandaloneOSXUniversal);
  }

值得注意的是这里导出的路径是必须存在的,也就是这里的第一个参数。

第二个参数是可以设置压缩方式的,这里看看压缩有那些。
Asset Bundle 压缩分为三种:
1.LZMA:默认的压缩方式,特点是可以达到很小的容量,但是这样就会导致解压时会花更多的时间。设置为BuildAssetBundleOptions.None,作为DLCs的资源用这种压缩。
2.LZ4:5.3版本之后才有,压缩出来还是比较大容量,但是在加载使用时,不需要去解压缩,只需要校对一下即可。设置为BuildAssetBundleOptions.ChunkBasedCompression,加密资源,配置表,资源流等这些使用这种压缩。
3.不压缩:原始大小,加载快。设置为BuildAssetBundleOptions.UncompressedAssetBundle
当然还可以在这里设置一个条件,比如说不包含属性信息打包等。

第三个参数也就是设置打包到那个目标机型上去。

这里会生成两类文件,一个是Manifest,有一点像配置表的意思,它存储了打包资源的依赖文件的信息。这个依赖跟我们在unity中导出package包是一样的,他会把这个资源用到的其他资源记录下来。

在运行时下载AssetBundles:

这里有3种方式下载AssetBundles,通过名字我们就可以知道他们的区别:
1.无缓存:使用的是WWW object,也就是说使用的new WWW对象。例子如下:

    using System;
    using UnityEngine;
    using System.Collections; class NonCachingLoadExample : MonoBehaviour {
    public string BundleURL;
    public string AssetName;
    IEnumerator Start() {
        // Download the file from the URL. It will not be saved in the Cache
        using (WWW www = new WWW(BundleURL)) {
            yield return www;
            if (www.error != null)
                throw new Exception("WWW download had an error:" + www.error);
            AssetBundle bundle = www.assetBundle;
            if (AssetName == "")
                Instantiate(bundle.mainAsset);
            else
                Instantiate(bundle.LoadAsset(AssetName));
                    // Unload the AssetBundles compressed contents to conserve memory
                    bundle.Unload(false);

        } // memory is freed from the web stream (www.Dispose() gets called implicitly)
    }
    }

2.有缓存:使用的是WWW.LoadFromCacheOrDownload。使用这个至少用4GB。

using System;
using UnityEngine;
using System.Collections;

public class CachingLoadExample : MonoBehaviour {
public string BundleURL;
public string AssetName;
public int version;

void Start() {
    StartCoroutine (DownloadAndCache());
}

IEnumerator DownloadAndCache (){
    // Wait for the Caching system to be ready
    while (!Caching.ready)
        yield return null;

    // Load the AssetBundle file from Cache if it exists with the same version or download and store it in the cache
    using(WWW www = WWW.LoadFromCacheOrDownload (BundleURL, version)){
        yield return www;
        if (www.error != null)
            throw new Exception("WWW download had an error:" + www.error);
        AssetBundle bundle = www.assetBundle;
        if (AssetName == "")
            Instantiate(bundle.mainAsset);
        else
            Instantiate(bundle.LoadAsset(AssetName));
                // Unload the AssetBundles compressed contents to conserve memory
                bundle.Unload(false);

    } // memory is freed from the web stream (www.Dispose() gets called implicitly)
}
}

3.使用UnityWebRequest,这个可以取代www来使用,具体写法:

      //string uri = @"file:///E:\Unity Project Workspace\AssetBundleProject\AssetBundles\cubewall.unity3d";
    string uri = @"http://localhost/AssetBundles/cubewall.unity3d";
    UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri);
    yield return request.Send();
    //这里可以使用两种方式来获得AssetBundle 
    //AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
    AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
    
    //使用里面的资源
    GameObject wallPrefab = ab.LoadAsset<GameObject>("CubeWall");
    Instantiate(wallPrefab);

加载AssetBundles中的资源:

资源下载来了,这里像是Resource.Load的用法。我们可以使用三个方式来加载资源:

●AssetBundle.LoadAsset:直接加载
●AssetBundle.LoadAssetAsync:异步加载
●AssetBundle.LoadAllAssets:加载所有

这里的异步加载需要在加载大量资源或者资源容量大的时候使用。

这里有一个AssetBundle.Unload方法,是用来释放内存的,这个方法需要传入一个bool值,如果传入的是FALSE,这个AssetBundle就会被消除,但是从这个AssetBundle中加载出来的资源不会被删除。传入TRUE的话加载出来的资源也会被消除。在后面也可以使用Resource.UnLoadUnuseAssetBundle来卸载。

这里如果是依赖打包的,需要先加载共享资源包,然后再加载使用这个资源的预制物体这些。 这个加载顺序无所谓,但是在使用预制物体时,资源是要先加载出来的。这里的加载就是实例化出一个assetbundle对象。

我们可以直接加载总的生成的AssetBundles文件,再通过这个文件获得AssetBundleManifest依赖文件,获得这个打包文件的所有文件名字,然后根据名字来加载这个包里的文件。

    var manifestAB = AssetBundle.LoadFromFile("AssetBundles/AssetBundles");
    var manifest = manifestAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

    foreach (var name in manifest.GetAllAssetBundles())
    {
        print(name);
    }
    string[] strs = manifest.GetAllDependencies("cubewall.unity3d");
    foreach (string name in strs)
    {
        print(name);
        AssetBundle.LoadFromFile("AssetBundles/" + name);
    }

相关小记

分包策略
这里就是设置AssetBundle Variants这个,一些小策略有这些:
1.根据文件类型来分包,比如:贴图、声音这样的分类。
2.根据场景来分包,每有一个场景都设置一个包单独来控制。
3.根据物体的整理来分包,比如一个UI界面的预制体,这个预制体上用到的图片、脚本、shader等都打包在一起。
4.经常更新的资源放在一个包,不经常更新的资源放在一个包。
5.把共享资源打包成单独的包,这个也就是以来打包。

储存小原理

存储文件系统的结构

压缩区域指的是了chunk-base压缩(LZ4)或者stream-base压缩(LZMA)。
LZ4就是把资源平均分成很多个数据块,然后再把每个数据块单独压缩。使用这种压缩的话就是可以在运行时读取随机的数据消耗会很小,个人理解这里用这种压缩方式的一般是数据表之类的。
LZMA就是尽可能的压缩资源,但是读取的话只能序列读取。

推荐阅读更多精彩内容