Unity3D學習筆記(二十九):AssetBundle


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包從中加載想要的資源。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM