AssetBundle
什么是AssetBundle?
AssetBundle是把一些資源文件或場景文件,以某種方式保存在一個文件中。一個AssetBundle可以包含模型、材質、圖片或場景等。但是AssetBundle不能包含腳本文件。(腳本打包時需要編譯,界面邏輯的熱更新依賴Lua)
AssetBundle主要用於做熱更新使用。
如何創建AB包

第一個參數:
----無
----新建
----刪除未使用的AssetBundle
第二個參數:
----后綴:做資源高清和標清的區分,不能做資源AB包的區分

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; public class CreateAssetBundle : MonoBehaviour { [MenuItem("AssetBundle/CreateAB")] static void CreateAB() { //第一個參數:AB包的輸出路徑 //第二個參數:打包的參數設置,我們設置的是強制性的重新打包 //第三個參數:AB包的適用平台,不同的平台使用的AB包是不一樣的 BuildPipeline.BuildAssetBundles( Application.streamingAssetsPath + "/AssetBundle/", BuildAssetBundleOptions.ForceRebuildAssetBundle, BuildTarget.StandaloneWindows64 ); } }
.meta文件:記錄該資源在Unity里的相關設置(配置信息表)
作用:SVN團隊合作時,同時更新資源和.meta文件

如何使用AB包里的文件
注意:避免在一個AB包里出現同名同類型文件,只會加載其中一個
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class LoadABTexture : MonoBehaviour { private Image image; private void Awake() { image = GetComponent<Image>(); } // Use this for initialization void Start () { //要是AB包中的資源文件 //第一種方式:先加載AB包 //AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/ui"); //第二種方式: WWW www = new WWW("file://" + Application.streamingAssetsPath + "/AssetBundle/ui"); AssetBundle ab = www.assetBundle; Sprite sp = ab.LoadAsset<Sprite>("beijing_02.jpg"); image.sprite = sp; image.SetNativeSize(); } }
打包加載Json.txt文件

AssetBundle jsonAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/json"); TextAsset json = jsonAB.LoadAsset<TextAsset>("Json"); Debug.Log(json);
打包加載預制體
AB包的依賴性
如果一個AB包a中的資源,使用了另一個AB包b中的資源,那么這個a就依賴於b。
當你使用AB包a中的資源的時候,需要先把依賴的AB包b先加載進來。
三層依賴關系(常用):預制體prefab->材質mat->圖片ui
四層依賴關系(難做)
注意:同一個AB包(在卸載之前)只能加載一次(不管是否在同一腳本中,只要是在一個項目中)
//從AB包中加載預制體,把預制體實例到界面 //加載AB包 AssetBundle uiAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/ui"); AssetBundle matAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/mat"); AssetBundle prefabAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/prefab"); //從AB包中加載預制體 GameObject prefab = prefabAB.LoadAsset<GameObject>("Cube"); //實例化預制體 Instantiate(prefab, transform);
每一個AB包都對應一個.manifest文件(資源清單),包含如下信息
ManifestFileVersion: 0//版本號 CRC: 1860656504//CRC循環冗余碼 Hashes://資源文件的哈希碼,用於檢查增量的構建AB包 AssetFileHash: serializedVersion: 2 Hash: 82edae0095f36a303261e62246d831ae TypeTreeHash: serializedVersion: 2 Hash: 81fd706e1561f1cfc1872b1168421ee0 HashAppended: 0 ClassTypes://該AB包中所有資源使用到的類類型,一般情況下對應的都是地址 - Class: 1 Script: {instanceID: 0} - Class: 114 Script: {fileID: 11500000, guid: d533bd1959b71b4459e871de8a9975af, type: 3} - Class: 115 Script: {instanceID: 0} Assets://對應該AB包中的所有資源 - Assets/Prefabs/Cube.prefab Dependencies://該AB包的直接依賴 - "D:/Unity3D\u6E38\u620F\u5F00\u53D1\u5DE5\u7A0B\u5E08\u73ED1803\u671F-\u706B\u661F\u65F6\u4EE3/Unity3D/Unity_Projects/m3w3d2_Lesson32/Assets/StreamingAssets/AssetBundle/ui"//路徑里的中文用16進制顯示
單一的AssetBundle.Manifest文件:
1、所有的AssetBundle包
2、所有的AB包的依賴(可以通過獲取AssetBundle.Manifest文件,來獲取AB包的依賴關系)
注意:AssetBundle.Manifest文件的名字,會根據父文件夾的名字改變
ManifestFileVersion: 0 CRC: 1678997246 AssetBundleManifest: AssetBundleInfos: Info_0: Name: ui Dependencies: {} Info_1: Name: mat Dependencies: {} Info_2: Name: prefab Dependencies: Dependency_0: ui
不知道資源的依賴關系,自動去獲取依賴項和加載依賴項
使用AB包的步驟
1、加載總的構建的AB包
2、從單一的AB包中去加載構建清單
3、從構建清單中獲取指定的AB包的所有的依賴項,並加載所有的依賴項
4、加載資源所在的AB包
5、從AB包中加載資源
//使用AB包的步驟 //1、加載總的構建的AB包 AssetBundle singleAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/AssetBundle"); //2、從單一的AB包中去加載構建清單 AssetBundleManifest singleManifest = singleAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); //3、從構建清單中獲取指定的AB包的所有的依賴項,並加載所有的依賴項 //singleManifest.GetAllDependencies("prefab");獲取所有的依賴,不管直接還是間接 //singleManifest.GetDirectDependencies("prefab");獲取所有的直接依賴 string[] deps = singleManifest.GetAllDependencies("prefab"); for (int i = 0; i < deps.Length; i++) { //Debug.Log(deps[i]); AssetBundle depAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/" + deps[i]); } //4、加載資源所在的AB包 AssetBundle prefabAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/prefab"); //5、從AB包中加載資源 GameObject prefab = prefabAB.LoadAsset<GameObject>("Cube"); Instantiate<GameObject>(prefab, transform);
public string[] GetAllDependencies(string assetBundleName);//獲取所有依賴(老版本沒有) public string[] GetDirectDependencies(string assetBundleName);//獲取直接依賴
AB包的卸載
Unload(bool)
當傳入true時,不光卸載內存中的AssetBundle對象,還卸載從這個AB包中加載到的資源。
當傳入false時,只卸載內存中的AB包,不卸載從AB中加載的資源。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class UnloadAB : MonoBehaviour { public AssetBundle uiAB; public AssetBundle matAB; public AssetBundle prefabAB; public GameObject prefab; // Use this for initialization void Start () { uiAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/ui"); matAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/mat"); prefabAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/prefab"); prefab = prefabAB.LoadAsset<GameObject>("Cube"); Instantiate<GameObject>(prefab, transform); } // Update is called once per frame void Update () { if (Input.GetKeyDown(KeyCode.Space))//按空格鍵卸載 { uiAB.Unload(false); matAB.Unload(false); prefabAB.Unload(false); } } }
字典:
簡介:
1、字典里的每一個元素都是一對鍵值對(由兩個元素組成:一個是鍵一個是值)
2、字典的鍵必須是唯一的,值不需要唯一
3、鍵和值都可以是任意的類型(比如:基本類型,自定義類型)
4、可以通過鍵去訪問值
字典的使用:
1、必須引用命名空間using System.Collections.Generic;
2、
using System.Collections; using System.Collections.Generic; using UnityEngine; public class LessonDictionary : MonoBehaviour { //動態數組的申請方式 List<string> list = new List<string>(); //定義一個鍵是string類型,值是string類型的字典 Dictionary<string, string> dic1 = new Dictionary<string, string>(); //定義一個鍵是string類型,值是GameObject類型的字典 Dictionary<string, GameObject> dic2 = new Dictionary<string, GameObject>(); // Use this for initialization void Start () { //對list進行添加元素 list.Add("nihao"); //字典的添加元素,添加的是鍵值對,第一個參數鍵,第二個參數值 dic1.Add("a", "A"); //這種方式也能添加元素 dic1["b"] = "C"; //list改變值的方式 list[0] = "1"; //字典改變這個鍵對應的值的方式 dic1["a"] = "B"; //list通過索引訪問元素 Debug.Log(list[0]); //字典通過鍵去訪問這個鍵對應的值 Debug.Log(dic1["a"]); Debug.Log(dic1["b"]); //list刪除元素 list.Remove("1"); //字典刪除元素,通過鍵去刪除這個鍵值對 dic1.Remove("a"); //list的遍歷 for (int i = 0; i < list.Count; i++) { Debug.Log(list[i]); } foreach (var item in list)//不能改值,且效率低,產生內存碎片 { Debug.Log(item); } //字典的遍歷 //遍歷字典的鍵 foreach (var item in dic1.Keys) { Debug.Log(item);//打印的鍵 Debug.Log(dic1[item]);//打印的值 } //遍歷字典的值 foreach (var item in dic1.Values) { Debug.Log(item);//打印字典的值 } //遍歷字典的鍵值對 foreach (KeyValuePair<string,string> item in dic1) { Debug.Log(item.Key);//打印字典的鍵 Debug.Log(item.Value);//打印字典的值 } //list里是否有“1”這個元素 if (list.Contains("1")) { } //判斷字典里是否有“a”這個鍵 if (dic1.ContainsKey("a")) { } string value = ""; //這個方法的返回值,這個字典里有沒有第一個參數的這個鍵 //如果有,那么把這個鍵所對應的值放在out參數的value里 if (dic1.TryGetValue("a", out value)) { } //刪除list的所有元素 list.Clear(); //刪除字典的所有元素 dic1.Clear(); } }
專門負責加載AB包的管理器
using System.Collections; using System.Collections.Generic; using UnityEngine; public class LoadManager { #region 單例 private static LoadManager instance; public static LoadManager Instance { get { if (instance == null) { instance = new LoadManager(); } return instance; } } private LoadManager() { abDic = new Dictionary<string, AssetBundle>(); abPath = Application.streamingAssetsPath + "/AssetBundle/"; singleABName = "AssetBundle"; } #endregion /// <summary> /// 用來存儲已經加載的AB包,鍵是AB包的名字,值就是AB包。 /// </summary> public Dictionary<string, AssetBundle> abDic; //用來存儲單一的ab包 public AssetBundle singleAB; //單一的構建清單,所有的ab包的依賴全部從這獲取 public AssetBundleManifest singleManifest; //存儲ab包的路徑 public string abPath; //單一的ab包的名字 public string singleABName; /// <summary> /// 加載單一的ab包,和單一的構建清單 /// </summary> private void LoadSingleAssetBundle() { //每次加載單一的ab包需要判斷是否加載果過。 //singleAB為null沒加載過,不為null就是加載過 if (singleAB == null) { singleAB = AssetBundle.LoadFromFile(abPath + singleABName); } //從單一的ab包中加載單一的構建清單 if (singleManifest == null) { singleManifest = singleAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); } } /// <summary> /// 加載指定ab包的所有的依賴項 /// </summary> /// <param name="abName"></param> private void LoadAllDependencies(string abName) { LoadSingleAssetBundle(); //首先先獲取指定的這個ab包的所有的依賴項 //從單一的構建清單中獲取 string[] deps = singleManifest.GetAllDependencies(abName); //遍歷去加載依賴項 for (int i = 0; i < deps.Length; i++) { //加載該依賴項前,先判斷之前加載沒加載過該依賴項 //就是判斷存儲ab包的字典里有沒有這個ab包 if (!abDic.ContainsKey(deps[i]) ) { //如果未加載過,需要加載 AssetBundle ab = AssetBundle.LoadFromFile(abPath + deps[i]); //ab包加載完之后,把加載來的ab包存儲在字典里 abDic[deps[i]] = ab; } } } /// <summary> /// 加載指定的ab包,並且返回該ab包 /// </summary> /// <param name="abName"></param> /// <returns></returns> public AssetBundle LoadAssetBundle(string abName) { LoadAllDependencies(abName); //加載指定的ab包 //加載前先判斷是否已經加載過,如果加載過,把加載過的ab包給你 //如果未加載過,就加載該ab包 //方法一: //if (abDic.ContainsKey(abName))//證明該ab包已經加載過 //{ // return abDic[abName]; //} //AssetBundle ab = ab = AssetBundle.LoadFromFile(abPath + abName); //abDic[abName] = ab; //return ab; //方法二:優化重構 AssetBundle ab = null; if (!abDic.TryGetValue(abName, out ab)) { //如果進入到這,證明該鍵沒有指定的值,那么證明該ab包未加載,需要加載 ab = AssetBundle.LoadFromFile(abPath + abName); //把加載進來的ab包添加到字典中 abDic[abName] = ab; } return ab; } /// <summary> /// 加載指定的ab包中的指定名字的指定類型的資源 /// </summary> /// <typeparam name="T">指定資源的類型</typeparam> /// <param name="abName">ab包的名字</param> /// <param name="assetName">資源的名字</param> /// <returns></returns> public T LoadAssetByAB<T>(string abName, string assetName) where T : Object { //先獲取指定的ab包 AssetBundle ab = LoadAssetBundle(abName); if (ab != null) { return ab.LoadAsset<T>(assetName); } else { Debug.LogError("指定的ab包的名字有誤!"); } return null; } /// <summary> /// 卸載指定的ab包 /// </summary> /// <param name="abName"></param> /// <param name="unloadAllloadedObjects"></param> public void UnloadAssetBundle(string abName, bool unloadAllloadedObjects) { //方法一: //if (abDic.ContainsKey(abName)) //{ // abDic[abName].Unload(unloadAllloadedObjects); // abDic.Remove(abName); //} //方法二:優化重構 //先判斷有沒有這個ab包 AssetBundle ab = null; if (abDic.TryGetValue(abName, out ab)) { //卸載ab包 ab.Unload(unloadAllloadedObjects); //從容器中刪除該ab包 abDic.Remove(abName); } } /// <summary> /// 卸載全部的ab包 /// </summary> /// <param name="unloadAllloadedObjects"></param> public void UnloadAllAssetBundle(bool unloadAllloadedObjects) { //遍歷每一個ab包,調用ab包的卸載的方法 //遍歷鍵,通過鍵去獲取值進行卸載 foreach (var item in abDic.Keys) { abDic[item].Unload(unloadAllloadedObjects); } //直接遍歷值去卸載 foreach (var item in abDic.Values) { item.Unload(unloadAllloadedObjects); } abDic.Clear(); } }
測試管理器
using System.Collections; using System.Collections.Generic; using UnityEngine; public class TestManager : MonoBehaviour { public UnityEngine.UI.Image image; // Use this for initialization void Start () { //先加載指定ab包,再加載包中的指定名字的指定類型的資源 //AssetBundle ab = LoadManager.Instance.LoadAssetBundle("prefab"); //GameObject prefab = ab.LoadAsset<GameObject>("Cube"); //直接加載指定的ab包中的指定名字的指定類型的資源 GameObject prefab = LoadManager.Instance.LoadAssetByAB<GameObject>("prefab", "Cube"); Instantiate(prefab, transform); //AssetBundle ab1 = LoadManager.Instance.LoadAssetBundle("prefab");//重復加載也不會報錯 //AssetBundle ui = LoadManager.Instance.LoadAssetBundle("ui"); //Sprite sp = ui.LoadAsset<Sprite>("beijing_02"); Sprite sp = LoadManager.Instance.LoadAssetByAB<Sprite>("ui", "beijing_02"); image.sprite = sp; } // Update is called once per frame void Update () { } }
熱更新的流程(判斷是否進行熱更新)
1、先把最新的資源打包成AB包,把最新的AB包上傳到服務器上,並且修改服務器端資源版本號。
2、客戶端一啟動游戲,首先判斷本地的版本號與服務器的版本號是否一致。
3、如果不一致,從服務器下載最新的資源AB包替換本地的AB包。
4、 解析最新的AB包從中加載想要的資源。