AssetBundle_介紹 (基於unity 2017 版本 --- 如要知曉最新資料建議去看官方文檔)
(最近看到的一篇AB不錯的文章:https://blog.csdn.net/Mars___Z/article/details/90199004。簡明扼要)
一: AssetBundle的定義和作用
用處?
1,AssetBundle是一個壓縮包包含模型、貼圖、預制體、聲音、甚至是整個場景、可以在游戲運行時被加載。
2,AssetBundle自身保存着相互依賴的關系;---(AB包中后綴為manifest的文件夾中可被文本形式打開)
3,壓縮包可以使用LZMA和LZ4壓縮算法,減少包大小,更快的進行網絡傳輸;
4,把一些可以下載的內容放在AssetBundle里面,可以減少安裝包大小。---(便於游戲體驗和更便於實時加載問題)
二: 什么是AssetBundle
可以歸為兩點
1,他是一個存在於硬盤上的文件。可以稱之為壓縮包,這個壓縮包可以被認為是一個文件夾,里面包含了多個文件,
這些文件可以分為兩類:Serializad file(序列化文件或流文件)和Resource file (源文件)。
a) Serializad file 資源是被打碎放在一個對象中,最后統一寫進一個單獨的文件(只有一個 類似Instance);
b) Resource file 某些二進制文件資源(如:圖片 、聲音等)被單獨保存,方便快速的加載。
2,他是一個AssetBundle對象,我們可以通過代碼從一個特定的壓縮包加載出來的對象,這個對象包含了所有我們當初添加到這個文件壓縮包里面的內容,我們可以通過這個對象加載出來使用。
三: AssetBundle 概念圖
資源打包流程
資源使用流程
四 :AssetBundle 使用流程(簡稱AB)---“重要”
(基本設置)指定資源的AssetBundle屬性 (xxxa/xxx)這里xxxa會生成目錄,名字為xxx ;
如圖:
選擇一個需要打包的資源。
關於AB打包的面板和設置方法(圖片內有說明)
//-----------------------------------------------------------------------------------------------------
A_使用編譯器擴展---直接Build所有以及設置好要打包成AssetBundle的資料
當設置好后可以使用這個編譯器擴展進行一次性全部打包模式
using UnityEngine; using UnityEditor; using System.IO; public class CreateAssetBundles : MonoBehaviour { [MenuItem("AssetBundles/Build AssetBundles")] //特性 static void BuildAssetBundle() { string dir = "AssetBundles"; //相對路徑 if(!Directory.Exists(dir)) //判斷是否存在 { Directory.CreateDirectory(dir); } BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64); } }
//-----------------------------------------------------------------------------------------------------
B_AssetBundle包_結構。
當設置好面板屬性后,直接使用編譯器(AssetBundle/Build AssetBundle)按鍵進行生成文件。創建后的樣式
---本地項目文件夾下創建AB包文件夾
文件夾下的文件格式-----采取的是歸類方式 (即 scene/map 這種形式)
最后AssetBundle的文件格式 ,因后綴沒添加則只有文件名(注意manifest后綴的文件為管理文件,用於管理記錄,為打包后自動生成的)
//-----------------------------------------------------------------------------------------------------
C_加載AB包和包里面的資源。----(需要注意的是帶Async的API即是異步的方法不帶則是同步方式)
首先介紹一下主要加載AB包的四種方式(即四種類型的API) :
1, 從文件中加載 AssetBundle.LoadFromFile 。
1 using System.Collections; 2 using UnityEngine; 3 using UnityEngine.Networking; 4 5 public class LoadFromFileExample : MonoBehaviour 6 { 7 private IEnumerator Start() 8 { 9 //這里需要注意的是,如果你想加載的資源有依賴的資源 如貼圖等資源,則必須先加載依賴資源后加載你所想加載的資源。 10 //即A依賴B 則必須先加載B到內存中,然后再加載A 並實例化---這里注意依賴項一般可以不用實例化只需要在內存中即可。 11 AssetBundle abShare = AssetBundle.LoadFromFile("AssetBundles/materials/mapmaterials"); 12 13 AssetBundle ab = AssetBundle.LoadFromFile("AssetBundles/scene/map_001"); 14 15 16 17 18 //這里需要注意的是 單獨加載和加載所有壓縮包內的對象處理方式 19 20 //加載單獨的 21 GameObject panelPrefab = ab.LoadAsset<GameObject>("Plane"); //注意名字不能寫錯了 22 23 Instantiate(panelPrefab);//實例化資源 24 25 ////加載所有 26 //Object[] panelPrefab = ab.LoadAllAssets(); 27 28 //foreach (var obj in panelPrefab) 29 //{ 30 // Instantiate(obj);//實例化資源 31 //} 32 33 yield return null; 34 } 35 }
2、從內存中加載 AssetBundle.LoadFromMemoryAsync(注意是異步,去掉Asyns既是同步方法)
1 using System.Collections; 2 using UnityEngine; 3 using UnityEngine.Networking; 4 5 public class LoadFromFileExample : MonoBehaviour 6 { 7 private IEnumerator Start() 8 { 9 //這里需要注意的是,如果你想加載的資源有依賴的資源 如貼圖等資源,則必須先加載依賴資源后加載你所想加載的資源。 10 //即A依賴B 則必須先加載B到內存中,然后再加載A 並實例化---這里注意依賴項一般可以不用實例化只需要在內存中即可。 11 AssetBundle abShare = AssetBundle.LoadFromFile("AssetBundles/materials/mapmaterials"); 12 13 14 string path = "AssetBundles/scene/map_01"; 15 16 //LoadFromMemoryAsync 和 LoadFromMemory --內存讀取 17 AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path)); //異步的方式 18 yield return request; //異步的方式 必須等待完成后才繼續執行 19 AssetBundle ab = request.assetBundle; 20 21 //AssetBundle ab = AssetBundle.LoadFromMemory(File.ReadAllBytes(path));//同步的方式 22 23 //使用資源 24 GameObject gameObj = ab.LoadAsset<GameObject>("map_001"); 25 Instantiate(gameObj); 26 27 yield return null; 28 } 29 }
3、第三種加載AB包的方式WWW(萬維網) --WWW(萬維網)對象方法可以從本地讀取,也可以從服務器讀取。
---需要注意的是這個方法在2017以后基本被UnityWebRequest(這個方法代替的是WWW遠程讀取)代替。做了一定程度的封裝。
1 using System.Collections; 2 using UnityEngine; 3 using UnityEngine.Networking; 4 5 public class LoadFromFileExample : MonoBehaviour 6 { 7 private IEnumerator Start() 8 { 9 //這里需要注意的是,如果你想加載的資源有依賴的資源 如貼圖等資源,則必須先加載依賴資源后加載你所想加載的資源。 10 //即A依賴B 則必須先加載B到內存中,然后再加載A 並實例化---這里注意依賴項一般可以不用實例化只需要在內存中即可。 11 //AssetBundle abShare = AssetBundle.LoadFromFile("AssetBundles/materials/mapmaterials"); 12 13 //注意!!! 14 //www.LoadFromCacheOrDownload 這個方法是先下載存到cache的緩存空間內 然后在進行提取。 15 //如果第二次也進行下載一樣的 則會在這個緩存空間內提取之前已經下載的數據 16 //開始下載前必須先判斷是否准備好了,否則一直循環下去,等待准備好 17 while (!Caching.ready) //檢查cache 緩存是否准備好了 18 yield return null; 19 20 //完整的路徑---本地下載 21 //string allPath = @"E:/03_TestDemo/01_UnityDemo/ShowMap_ABPackageTest_001/AssetBundles/scene/map_01"; 22 //WWW www = WWW.LoadFromCacheOrDownload(allPath, 1); 23 24 //遠程服務器下載 25 WWW www = WWW.LoadFromCacheOrDownload(@"http://localhost/AssetBundles/scene/map_01", 1); //從遠程服務器上下載AB包 26 yield return www; 27 28 //因萬維網是非常復雜的,經常容易出現非常意外的錯誤,所以必須加入一個判斷。 29 if(!string.IsNullOrEmpty(www.error)) //當什么都沒有時 30 { 31 Debug.Log(www.error); 32 yield break; //跳出協程 33 } 34 35 AssetBundle ab = www.assetBundle; 36 37 //使用資源 38 GameObject gameObj = ab.LoadAsset<GameObject>("map_001"); 39 Instantiate(gameObj); 40 41 yield return null; //上述下載時沒有加載相應的貼圖材質。。。注意!!! 42 } 43 }
4、第四種Unity封裝的UnityWebRequest對象---(專用於替代WWW的遠程加載方式)
1 using System.Collections; 2 using UnityEngine; 3 using UnityEngine.Networking; 4 5 public class LoadFromFileExample : MonoBehaviour 6 { 7 private IEnumerator Start() 8 { 9 //這里需要注意的是,如果你想加載的資源有依賴的資源 如貼圖等資源,則必須先加載依賴資源后加載你所想加載的資源。 10 //即A依賴B 則必須先加載B到內存中,然后再加載A 並實例化---這里注意依賴項一般可以不用實例化只需要在內存中即可。 11 //AssetBundle abShare = AssetBundle.LoadFromFile("AssetBundles/materials/mapmaterials"); 12 13 //第四種遠程加載方式---(專門用於替換WWW的遠程加載方式) 14 //string uri = @"E:/03_TestDemo/01_UnityDemo/ShowMap_ABPackageTest_001/AssetBundles/scene/map_001"; 15 string uri = @"http://localhost/AssetBundles/scene/map_001"; 16 UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri); 17 yield return request.SendWebRequest(); //注意是異步方式所以必須等待他完成后再執行 18 19 //兩種獲取ab對象的方式 20 //AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request); //第一種方法 21 AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle; //第二種方法 22 23 //使用資源 24 GameObject gameObj = ab.LoadAsset<GameObject>("map_001"); 25 Instantiate(gameObj); 26 27 yield return null; //注意上述的遠程加載方式是沒有加載其依賴的資源 28 } 29 }
5_加載<AB包依賴項> ---(根據AB包文件夾下的 "AssetBundle文件以及AssetBundle.manifest文件")
1 using System.Collections; 2 using UnityEngine; 3 using UnityEngine.Networking; 4 5 public class Load : MonoBehaviour 6 { 7 //第一種是服務端加載AB資源包 和依賴包 8 private IEnumerator Start() 9 { 10 //從一個服務器下載一個AB包的管理文件AssetBundles 和 AssetBundles.manifest 11 string uri = @"http://localhost/AssetBundles/AssetBundles"; 12 UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri); 13 yield return request.SendWebRequest(); 14 15 //AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle; //第一種方法 16 AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);//第二種方法 17 AssetBundleManifest manifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); 18 19 string[] str = manifest.GetAllAssetBundles(); 20 21 foreach(string s in str ) 22 { 23 24 UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(@"http://localhost/AssetBundles/" + s); 25 yield return uwr.SendWebRequest(); 26 AssetBundle TmpAB = DownloadHandlerAssetBundle.GetContent(uwr);//第二種方法 27 Object[] obj = TmpAB.LoadAllAssets(); 28 29 foreach (Object o in obj) 30 { 31 if(o is GameObject) 32 { 33 Instantiate(o); 34 } 35 36 print(obj.Length); 37 } 38 39 } 40 41 42 43 yield return null; 44 } 45 46 47 48 //第二種本地加載AB資源包 和依賴包 49 #region LoadAB.manifest 50 //private void Start() 51 //{ 52 // //加載得到Manifest文件 53 // AssetBundle ab = AssetBundle.LoadFromFile("Assets /AssetBundle/AssetBundle"); 54 // AssetBundleManifest manifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); 55 56 // //從Manifest文件中得到所有的AB包的路徑(包括依賴項) 57 // string[] str = manifest.GetAllAssetBundles(); 58 // foreach (string s in str) 59 // { 60 // AssetBundle assetBundle = AssetBundle.LoadFromFile("Assets/AssetBundle/" + s); 61 // Object[] o = assetBundle.LoadAllAssets(); 62 // foreach (Object temp in o) 63 // { 64 // if (temp is GameObject) 65 // { 66 // Instantiate(temp); 67 // } 68 // } 69 // } 70 //} 71 #endregion 72 }
小結:上述方法直接在場景中見一個空物體掛載其腳本即可(注意前提是AB包必須已經按相應方式創建好了)。
//=============================================================================================================
五:AssetBundle 打包時的分組(僅供參考)
1、邏輯實體分組:
a、一個UI界面或者所有UI界面一個包(這個界面里面所有的貼圖和布局信息一個包);
b、一個角色或者所有角色一個包(這個角色里面的模型和動畫一個包);
c、所有場景所共有的部分一個包(包括貼圖和模型);
2、按照類型分組
所有的聲音資源打成一個包,所有shader打成一個包,所有模型打成一個包,所有材質打成一個包。
3、按使用分組
把在某一時間內使用的所有資源打成一個包,可以按照關卡分,一個關卡所需要的所有資源包,包括角色、
貼圖、聲音等打成一個包,也可以按照場景分,一個場景所需要的資源一個包。
小結:
1、把經常更新的資源放在一個單獨的包里、跟不經常更新的包分離。
2、把需要同時加載的資源放在一個包里面。
3、可以把其他包共享的資源放在一個單獨的包里面。(做依賴,這樣可以減少很多比較資源占用)
4、把一些需要同時加載的小資源打包成一個包。
5、如果對一個同一個資源有兩個版本,可以考慮通過后綴來區分(如:v1 v2 v3 )(如unityAB_V1 、unityAV_V2);
//=============================================================================================================
六 : 依賴打包簡介
小結:如圖所示,其主要的特點是把模型預制體所使用的材質貼圖進行整合,這樣可以避免占用過多的內存資源,
在加載時也可以避免包過大使用的時間太長,同時避免了資源的重復加載。在需要加載時也可以通過依賴
關系進行逆向加載。
//=============================================================================================================
七: Build AssetBundle方法參數詳解(BuildPipline.BuildAssetBundle)
1、Build的路徑(只要是在硬盤上都可以的)
2、BuildAssetBundleOptions(枚舉類型)
a)、BuildAssetBundleOptions.None:使用LZMA算法壓縮,壓縮的包更小,但加載時間更長,
使用之前需要整體解壓 。一旦被解壓,這個包會使用LZ4重新壓縮。使用資源的時候不需要整體解壓
。在下載的時候可以使用LZMA算法。一旦它被下載了之后,它會使用LZ4算法保存到本地上。
b)、BuildAssetBundleOption.UncompressedAssetBundle (不壓縮,包大,加載速度快)。
c)、BuildAssetBundleOption.ChunkBasedCompression:(使用LZ4算法,壓縮率沒有LZMA高,
但我們可以加載指定資源的不用解壓全部);
注意:使用LZ4算法,可以獲得可以跟不壓縮相媲美的加載速度,而且比不壓縮的文件要小。
3、BuildTarget:(選擇Build出來的AB包需要的平台)
圖解:
//=============================================================================================================
八:關於AssetBundle的卸載
首先為什么要把AB 包卸載了?其實也很簡單,內存永遠是有限的,當你轉換一個場景或者關卡時,之前不需要的AB包所占用的內存是需要把它
釋放掉的,這樣才能讓內存永遠保持着一個健康的容量。
卸載主要有兩個方面:
1、 減少內存使用,可以保證一個良好且健康的運行內存。
2、 有可能導致資源丟失問題。
所以一般什么時候卸載資源呢?
AssetBundle.Unload(true)卸載所有資源,即使有資源使用着(1,在關卡切換或場景切換時 2、資源沒被用的時候調用);
AssetBundle.Unload(false)卸載所有沒有被使用的資源。(這個適用時間比較多 ,可自行把控)。
個別資源怎么卸載,通過Resources.UnloadUnusedAssets();
當場景切換時,unity會自行執行(Resources.UnloadUnusedAssets()這個方法);
//=============================================================================================================
九:關於AssetBundle的文件效驗(每個AB包中有一個manifest文件中就有一個CRC)
CRC、DM5、SHA1
相同點:
CRC、DM5、SHA1都是通過對數據進行計算,來生成一個效驗值,該效驗值用來效驗數據的完整性。
不同點:
1、算法的不同。CRC采用多項式除法,MD5和SHA1使用的替換,輪轉等方法。
2、效驗值的長度不同。CRC效驗值的長度基本跟其多項式有關系,一般為16位或32位,MD5是16個字節(128位);SHA1是20個字節(160位);
3、效驗值的稱呼不同。CRC一般叫做CRC值;MD5和SHA1一般叫哈希值(Hash)或散列值。
4、安全性不同。這里的安全性是指效驗的能力,即數據的錯誤能通過效驗位檢測出來,CRC的安全性跟多項式有很大關系,相對於MD5和SHA1要弱很多;MD5的安全性很高,不過大概在04年的時候被山東大學的王小雲破解了;SHA1安全性最高。
(算法的過程一般是不可逆的,破解即是逆向根據效驗值推導出算法過程列表)
5、效率不同。CRC的計算效率很高;MD5和SHA1比較慢。
6、用途不同。CRC一般用作通信數據的效驗,MD5和SHA1用於安全(Security)領域。比如文件效驗、數字簽名等。