近期項目正好在處理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; } } }
