【Unity】AssetBundle随笔


  近期项目正好在处理AB包的问题,趁这个机会,自己重新学习并梳理一下AB包相关知识,供以后回顾。

1、什么是AssetBundle?

  AsetBundle是unity提供的一种资源打包方式,可以通过LZMA和LZ4压缩方式将模型、贴图、预设体、音频文件甚至整个场景压缩成压缩包(当然也可以不压缩,后面会提及)。至于它的后缀名是什么完全不用在意,可以自己定。AssetBundle(后面简称AB包)自身会保存依赖关系,如AB包A中的材质可以引用AB包B中的纹理。因为AB包通过LZMA或LZ4压缩算法来压缩的,所以可以减少包的大小,使其能更快的进行网络传输。而且如果我们把一些下载内容放在AB包里,那么就可以减少安装包的大小。

  AB包是Unity动态下载资源的通用解。

2、unity提供的几种主要的资源加载方式对比(AssetBundle的优点)

  (1)、Resources//内嵌资源,使用Resources.Load可以加载任意资源,但是不能动态修改,而且需要把所有资源全部打入安装包。

  (2)、StreamingAssets//随包资源,使用IO或者WWW.Load,WWW.Load可以加载任意资源,而IO仅限bytes和text,该文件夹下所有资源也全部打入安装包。

  (3)、WWW从网络下载并加载。

  (4)、WWW从网络加载AB包。

  其中(1)(2)并不具备热更新的效果且两个文件下所有资源都会被打入安装包,而(3)(4)都是从网络加载,他们之间的区别就是(3)不具备缓存功能,容易导致让用户反复浪费流量,不可取。而(4)本来就是unity给我们准备的关于热更新的解决方案,AB包给我们提供了一个版本号来做缓存比对,可以有效解决资源热更新。

3、打包

  网上关于如何打包介绍很多,这里不多做赘述,最核心的也就一个API:

BuildPipeline.BuildAssetBundles ("AssetBundles", BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);

  第一个参数是打包路径。

  第二个参数是build的选项,常用的有三种:(1)BuildAssetBundleOptions.None:默认使用LZMA压缩算法进行压缩,这个压缩算法压缩出来的包小,当然相应的解压的时间会更长。在我们使用之前要整体解压,一但被解压之后就会用LZ4算法重新压缩。(2)BuildAssetBundleOptions.ChunkBasedCompression:使用LZ4压缩算法进行压缩,压缩出来的包会比用LZMA压缩的要大一些,解压快,解压速度可以和不压缩相媲美,而且可以指定资源解压。(3)BuildAssetBundleOptions.UncompressedAssetBundle:不压缩,包最大,解压速度最快。

  第三个参数就是选择build出来的AB包所使用的的平台,根据所选平台的不同,AB包只能在该平台使用。因此IOS和Android需要分开打包。

  打包自然是根据AssetBundleName来分组的,而打包整个过程最让人头疼的也就莫过于该如何分组了,要是没分好很容易出现重复打包,而如何才能给用户最小的资源更新量是永远没有最优解的,下面列举一些常用打包分组的小策略:

  (1)按照资源类型分组,如:声音资源一起打包,材质资源一起打包,shader一起打包,prefab一起打包等。

  (2)按照逻辑实体分组,如:一个UI界面或者所有UI界面一起打包,一个角色或者所有角色一起打包,所有场景共享的东西一起打包等。

  (3)按照使用打包,也就是同时加载的资源放在一个包里,如:同一时间内用到的资源一起打包,同一场景一起打包,同一关卡一起打包。

  (4)把经常更新的和不经常更新的分开打包。

  打包过程中还有一个比较特殊的打包方式就是依赖打包。依赖打包简单地说就是把多个资源所依赖的同一资源单独打包,unity会自动判断依赖关系,但是并不会自动加载具有依赖关系的资源包,所以我们在使用资源之前应先加载依赖资源的包,避免出现材质丢失的情况。而资源之间的依赖关系则可以在我们打包后生成的manifest中查看。另外,我们在打包的时候不建议把AB包拆的太碎,你想如果加载一个模型需要加载十几次依赖资源,那么加载过程中就需要发送这么多的WWW或者http请求,还是有点危险的。关于依赖资源包的加载直接上代码:

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

        // 加载依赖包

        string[] strs = manifest.GetAllDependencies ("cube.unity3d");
        foreach (string name in strs) 
        {
            Debug.Log (name);
            AssetBundle.LoadFromFile ("Assetbundles/" + name);
        }

 

4、manifest文件与版本号的简单介绍

  既然提到manifest文件,那这里就做一个简单介绍。manifest是unity打AB包时自动生成的一个清单,里面最重要的三个信息是CRC校验码、Assets、Dependencies。其中CRC校验码是用来校验文件是否完整,Assets记录的是资源路径,Dependencies指定的则是该资源所依赖的文件。

  而在做资源热更新的过程中版本号是必不可少的,资源版本号是自己设定的,可以自己写版本号的生成方法,也可以用MD5值作为版本号,我们需要根据版本号的对比来确定资源包是否需要更新。资源热更新可分为如下五个步骤:

  (1)通过请求服务器获取到服务器的MD5码配置文件;

  (2)获取本地的MD5码配置文件;

  (3)逐个对比每个文件的MD5码;

  (4)统计MD5码不一致的文件列表;

  (5)从服务器下载更新文件列表中的文件。

  那么问题来了,该如何生成相应的MD5码呢?生成步骤如下:

  (1)读取文件流;

  (2)读取文件流中的字节数据;

  (3)通过MD5接口生成MD5码;

  (4)将步骤3获得的Hash字节数组转换成字符串数组。

  关键代码如下:

public static string getFileHash(string filePath)  
{             
    try  
    {  
        FileStream fs = new FileStream(filePath, FileMode.Open);  
        int len = (int)fs.Length;  
        byte[] data = new byte[len];  
        fs.Read(data, 0, len);  
        fs.Close();  
        MD5 md5 = new MD5CryptoServiceProvider();  
        byte[] result = md5.ComputeHash(data);  
        string fileMD5 = "";  
        foreach (byte b in result)  
        {  
            fileMD5 += Convert.ToString(b, 16);  
        }  
        return fileMD5;     
    }  
    catch (FileNotFoundException e)  
    {  
        Console.WriteLine(e.Message);  
        return "";  
    }                                   
}  

  调用的时候通过填写制定文件的完整目录,即可获得对应文件的MD5码:

string md5 = getFileHash("E:\\MyPro\\cubetest.unity3d");  

  将所有文件的MD5码都写到一个文本.txt文件中,此即为热更新文件的配置文件,每次上传新版本到服务器时,将这份文件也存放到服务器中。

5、AB包的加载

  关于AB包的加载,unity官方提供了四个API,分别是:

  (1)AssetBundle.LoadFromMemoryAsync:从内存中异步加载

  (2)AssetBundle.LoadFromFile:本地加载

  (3)WWW.LoadFromCacheOrDownLoad:即可从远程服务器下载AB包,又可以从本地加载。从远程加载AB包后自动缓存

  (4)UnityWebRequest:5.3版本后新出的API,unity新增的一种网络请求方式,目的就是为了取代(3)中加载方式,因为WWW在IOS上加载资源偶尔会出现卡死现象。

  因为加载并没有什么特别需要注意的地方,而且网上资料很多,这里只贴第四种加载方式的代码:

  IEnumerator GetText() 
  {    
        UnityWebRequest request = UnityWebRequest.Get("http://example.com");    
        //     
        // UnityWebRequest request = new UnityWebRequest("http://example.com");    
        //     
        // request.method = UnityWebRequest.kHttpVerbGET;    
     
        //     
        yield return request.Send();    
     
        //     
        if (request.isError) 
        {    
            Debug.Log(request.error);    
        } 
        else 
        {    
            if (request.responseCode == 200) 
            {    
                //     
                string text = request.downloadHandler.text;    
     
                //     
                byte [] results = request.downloadHandler.data;    
            }    
        }    
    }                              

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM