【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