熱更新基礎--AssetBundle學習筆記

一.簡介異步

  AssetBundle簡稱AB包,特定平臺的資產壓縮包(包括模型、貼圖、預設體、音效、材質球等資產)。工具

    做用:Resources下的資源只讀且打包後不可修改,而AB包存儲位置自定,後期能夠動態更新;AB包壓縮後節省空間;能夠進行資源熱更新和腳本熱更新。ui

二.官方打包工具AssetBundleBrowserspa

  1.下載安裝工具code

打開Package Manager窗口orm

搜索找到打包工具,右下角install安裝,這裏已經安裝。blog

安裝成功後能夠在Windows中看到AssetBundle Browser。遊戲

Project窗口中Packages文件夾下也會出現打包工具文件夾。ci

打包工具窗口。資源

  2.使用打包工具打AssetBundle包

點擊Project窗口中的資源(能夠同時選擇多個資源),在Inspector窗口的最下方會出現AssetBundle打包選項,在輸入下拉框中填寫或選擇當前選中的一個或多個資源要打包成的AB包文件的名稱和後綴。

再次打開打包工具後再Configure選項中出現了資源。

在Build選項中便可進行打包相關設置(平臺、路徑、是否清空文件夾、是否打包時同步複製文件到StreamingAssets文件夾下、壓縮方式等),壓縮方式有LZMA(壓縮率高,可是若是取用包中單個文件時會將整個包解壓縮,資源使用時效率低)、LZ4(壓縮率不如LZMA,可是若是是多個資源在同一個包中又取用單個資源時不須要解壓縮整個包,資源使用時效率高)、不壓縮3種,推薦LZ4。

打包後包文件存儲在工程中AssetBundles文件夾下。

  3.AB包文件查看

和Unity項目中的其餘文件相同,打包出來的包文件也有響應的manifest清單文件與之對應,打開清單文件:

清單文件中包含包的一些信息,如version、CRC、Hash、Assets、Dependencies等。

三.AB包相關API

  1.加載AB包及使用包中文件的相關API

void Start()
    {
        //加載AB包,AssetBundle類中有多個不一樣的靜態方法用於加載AB包,這裏使用從文件加載(根據文件地址加載)
        //在AB包打包時勾選StreamingAssets同步,在Assets文件夾下會出現一個StreamingAssets文件夾,AB包會同步複製到文件夾中,這裏從這個文件夾下加載AB包
        AssetBundle bundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/model");

        //加載AB包中的資源
        //AssetBundle類中提供了多個不一樣的成員方法加載AB包中的資源,這裏使用泛型加載,也推薦使用泛型加載
        GameObject obj = bundle.LoadAsset<GameObject>("Sphere");

        //使用資源
        Instantiate(obj).transform.position = Vector3.zero;

        //由於AB包不能重複加載,因此加載完成後須要將其卸載
        //參數是一個bool值,表明卸載AB包時是否一同卸載經過AB包中加載的資源
        //如剛纔將資源實例化在場景中,若是這裏參數給true,卸載AB包後剛纔實例化出來的Sphere會丟失,若是參數給false,卸載AB包後剛纔實例化出來的Sphere不會受到影響

        //卸載當前AB包
        bundle.Unload(false);
        //卸載全部AB包
        AssetBundle.UnloadAllAssetBundles(false);
    }

  Unity提供了AssetBundle類,使用其中的AB包加載的靜態方法加載AB包,返回值也是AssetBundle類型,使用加載具體資源的成員方法加載包種具體的資源,推薦使用泛型方法,不然須要進行類型轉換。

  注意:AB包不能重複加載,會報錯,因此使用完成後記得卸載方便下次使用。

  2.AB包依賴關係

    在打包時,若是某個資源用到了一些依賴資源(Meterial、Sprite等),須要將這個資源依賴的資源一併打包起來,不然會出現資源丟失的問題。可是若是一個資源同時被多個資源使用,會出現重複打包的問題,因此能夠將這些依賴資源單獨打包,利用主包獲取依賴信息。主包名稱和文件夾名稱相同,如上面的PC包。

void Start()
    {
        //加載主包
        AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/PC");
        //加載主包中的固定文件
        AssetBundleManifest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        //從固定文件中獲得依賴信息
        string[] strs = abManifest.GetAllDependencies("model");
        //獲得依賴包名字
        for (int i = 0; i < strs.Length; i++)
        {
       //加載AB包,須要保存下來,能夠聲明一個字典將加載好的AB包保存下來 AssetBundle.LoadFromFile(Application.streamingAssetsPath
+ "/" + strs[i]); } abMain.Unload(false); }

  3.AB包管理類,提供加載AB包中資源的同步和異步方法,各有3種重載(根據包名和資源名加載、根據包名和資源名和資源類型加載、根據包名和資源名和泛型資源類型加載),還提供了卸載指定ab包和卸載全部包的方法

public class AssetBundleManager : MonoBehaviour
{
    //單例模塊
    private static AssetBundleManager instance;
    public static AssetBundleManager Instance
    {
        get
        {
            if (instance == null)
            {
                GameObject obj = new GameObject("AssetBundleManager");
                DontDestroyOnLoad(obj);
                instance = obj.AddComponent<AssetBundleManager>();
            }
            return instance;
        }
    }

    //存儲全部加載過的AB包的容器
    private Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle>();
    //主包,只會加載一次
    private AssetBundle mainAB = null;
    //獲取依賴包的配置文件
    private AssetBundleManifest manifest = null;

    //ab包存放的路徑,方便修改
    private string PathUrl
    {
        get
        {
            return Application.streamingAssetsPath + "/";
        }
    }
    //主包名,根據平臺不一樣而不一樣
    private string MainABName
    {
        get
        {
#if UNITY_IOS
            return "IOS";
#elif UNITY_ANDROID
            return "Android";
#else 
            return "PC";
#endif
        }
    }

    /// <summary>
    /// 加載資源,同步加載,3種方法重載
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    public Object LoadRes(string abName, string resName)
    {
        LoadAB(abName);

        Object obj = abDic[abName].LoadAsset(resName);
        //若是是GameObject,加載後基本都是建立遊戲物體,因此這裏判斷一下若是是GameObject,直接返回建立好的遊戲物體
        if (obj is GameObject)
            return Object.Instantiate(obj);
        else
            return obj;
    }
    public Object LoadRes(string abName, string resName, System.Type type)
    {
        LoadAB(abName);

        Object obj = abDic[abName].LoadAsset(resName, type);
        //若是是GameObject,加載後基本都是建立遊戲物體,因此這裏判斷一下若是是GameObject,直接返回建立好的遊戲物體
        if (obj is GameObject)
            return Object.Instantiate(obj);
        else
            return obj;
    }
    public T LoadRes<T>(string abName, string resName) where T : Object
    {
        LoadAB(abName);

        T t = abDic[abName].LoadAsset<T>(resName);
        //若是是GameObject,加載後基本都是建立遊戲物體,因此這裏判斷一下若是是GameObject,直接返回建立好的遊戲物體
        if (t is GameObject)
            return Object.Instantiate(t);
        else
            return t;
    }

    /// <summary>
    /// 加載資源,異步加載,3種方法重載
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public void LoadResAsync(string abName, string resName, System.Action<Object> callBack)
    {
        StartCoroutine(ReallyLoadResAsync(abName, resName, callBack));
    }
    private IEnumerator ReallyLoadResAsync(string abName, string resName, System.Action<Object> callBack)
    {
        yield return StartCoroutine(LoadABAsync(abName));

        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName);
        yield return abr;

        //若是是GameObject,加載後基本都是建立遊戲物體,因此這裏判斷一下若是是GameObject,直接建立好遊戲物體
        if (abr.asset is GameObject)
            callBack(Instantiate(abr.asset));
        else
            callBack(abr.asset);
    }
    public void LoadResAsync(string abName, string resName, System.Type type, System.Action<Object> callBack)
    {
        StartCoroutine(ReallyLoadResAsync(abName, resName, type, callBack));
    }
    private IEnumerator ReallyLoadResAsync(string abName, string resName, System.Type type, System.Action<Object> callBack)
    {
        yield return StartCoroutine(LoadABAsync(abName));

        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName, type);
        yield return abr;

        //若是是GameObject,加載後基本都是建立遊戲物體,因此這裏判斷一下若是是GameObject,直接建立好遊戲物體
        if (abr.asset is GameObject)
            callBack(Instantiate(abr.asset));
        else
            callBack(abr.asset);
    }
    public void LoadResAsync<T>(string abName, string resName, System.Action<T> callBack) where T : Object
    {
        StartCoroutine(ReallyLoadResAsync<T>(abName, resName, callBack));
    }
    private IEnumerator ReallyLoadResAsync<T>(string abName, string resName, System.Action<T> callBack) where T : Object
    {
        yield return StartCoroutine(LoadABAsync(abName));

        AssetBundleRequest abr = abDic[abName].LoadAssetAsync<T>(resName);
        yield return abr;

        //若是是GameObject,加載後基本都是建立遊戲物體,因此這裏判斷一下若是是GameObject,直接建立好遊戲物體
        if (abr.asset is GameObject)
            callBack(Instantiate(abr.asset) as T);
        else
            callBack(abr.asset as T);
    }

    /// <summary>
    /// 同步加載依賴包和資源包
    /// </summary>
    /// <param name="abName"></param>
    private void LoadAB(string abName)
    {
        //先加載依賴包,再加載AB包,最後加載文件
        if (mainAB == null)
        {
            mainAB = AssetBundle.LoadFromFile(PathUrl + MainABName);
            manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }

        string[] strs = manifest.GetAllDependencies(abName);
        for (int i = 0; i < strs.Length; i++)
        {
            if (!abDic.ContainsKey(strs[i]))
                abDic.Add(strs[i], AssetBundle.LoadFromFile(PathUrl + strs[i]));
        }

        //沒有包加載包,有包直接取出來使用
        if (!abDic.ContainsKey(abName))
            abDic.Add(abName, AssetBundle.LoadFromFile(PathUrl + abName));
    }
    /// <summary>
    /// 異步加載依賴包和資源包
    /// </summary>
    /// <param name="abName"></param>
    /// <returns></returns>
    private IEnumerator LoadABAsync(string abName)
    {
        //先加載依賴包,再加載AB包,最後加載文件
        if (mainAB == null)
        {
            AssetBundleCreateRequest createRequest = AssetBundle.LoadFromFileAsync(PathUrl + MainABName);
            yield return createRequest;
            mainAB = createRequest.assetBundle;

            AssetBundleRequest request = mainAB.LoadAssetAsync<AssetBundleManifest>("AssetBundleManifest");
            yield return request;
            manifest = request.asset as AssetBundleManifest;
        }

        string[] strs = manifest.GetAllDependencies(abName);
        for (int i = 0; i < strs.Length; i++)
        {
            if (!abDic.ContainsKey(strs[i]))
            {
                AssetBundleCreateRequest createRequest = AssetBundle.LoadFromFileAsync(PathUrl + strs[i]);
                yield return createRequest;
                abDic.Add(strs[i], createRequest.assetBundle);
            }
        }

        //沒有包加載包,有包直接取出來使用
        if (!abDic.ContainsKey(abName))
            abDic.Add(abName, AssetBundle.LoadFromFile(PathUrl + abName));
    }

    /// <summary>
    /// 卸載單個包
    /// </summary>
    /// <param name="abName"></param>
    public void UnLoad(string abName)
    {
        if (abDic.ContainsKey(abName))
        {
            abDic[abName].Unload(false);
            abDic.Remove(abName);
        }

    }

    /// <summary>
    /// 卸載全部包
    /// </summary>
    public void ClearAssetBundles()
    {
        AssetBundle.UnloadAllAssetBundles(false);
        abDic.Clear();
        mainAB = null;
        manifest = null;
    }
}
相關文章
相關標籤/搜索