Unity AssetBundle加載框架

基于Unity2017版本的AB技術(shù)
1.項(xiàng)目中成百上千的大量資源需要(批量)打包處理,不可能手工維護(hù)方式給每個(gè)資源添加ab"包名稱"
2.Unity對(duì)包依賴關(guān)系不是很完善
如果要加載一個(gè)多重依賴項(xiàng)的AB包,需要手工寫(xiě)代碼,把底層所有依賴包關(guān)系需要預(yù)先進(jìn)行加載后才可以。
3.AB包商業(yè)應(yīng)用 步驟有 AB包加載 AB包依賴關(guān)系(不遺漏、不重復(fù)) 資源提取釋放。繁重海量 手動(dòng)寫(xiě)工作效率低下
4.可能會(huì)反復(fù)加載同一AB包中資源 導(dǎo)致加載過(guò)慢

AB框架包括自動(dòng)標(biāo)記腳本、創(chuàng)建與銷毀打包資源、單一AB包加載
與測(cè)試腳本、專門(mén)讀取manifest維護(hù)AB包依賴關(guān)系,實(shí)現(xiàn)遞歸依賴加載機(jī)制的腳本


image.png

自動(dòng)創(chuàng)建AB包分類依據(jù) 按照?qǐng)鼍胺诸?br> 命名規(guī)則 場(chǎng)景名稱/功能文件夾

在Editor類添加自動(dòng)添加標(biāo)記類

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     AutoSetLables.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-13
 *Description:   AB打包工具
 * 定義需要打包資源的文件夾根目錄
 * 遍歷每個(gè)"場(chǎng)景"文件夾
 * 遍歷本場(chǎng)景目錄下所有目錄或者文件
 * 如果是目錄,則繼續(xù)遞歸訪問(wèn),直到定位到文件
 * 找到文件,則使用AssetImporter類 標(biāo)記包名、后綴名
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
namespace ABFrameWork
{

    public class AutoSetLables
    {
        /// <summary>
        /// 設(shè)置AB包名
        /// </summary>
        [MenuItem("ABTools/SetABLable")]
        public static void SetABLable()
        {
            string strNeedSetLableRoot = string.Empty;
            //根目錄下所有場(chǎng)景目錄
            DirectoryInfo[] dirSencesDIRArray;

            //清空無(wú)用AB標(biāo)記
            AssetDatabase.RemoveUnusedAssetBundleNames();

            strNeedSetLableRoot = PathTools.GetABResourcesPath();
            //獲取路徑下所有文件夾
            DirectoryInfo directoryInfo = new DirectoryInfo(strNeedSetLableRoot);
            dirSencesDIRArray = directoryInfo.GetDirectories();

            foreach (var item in dirSencesDIRArray)
            {
                string tmpSencesDIR = strNeedSetLableRoot + "/" + item.Name;
                int tmpIndex = tmpSencesDIR.LastIndexOf("/");
                string tmpScenesName = tmpSencesDIR.Substring(tmpIndex + 1);

                JudgeDIRorFileByRecursive(item, tmpScenesName);
            }
            AssetDatabase.Refresh();
            Debug.Log("AB設(shè)置標(biāo)記完成");
        }
        /// <summary>
        /// 遞歸判斷是否為目錄與文件,修改AB標(biāo)記
        /// </summary>
        /// <param name="currentDIR">當(dāng)前文件信息 (和目錄信息可以相互轉(zhuǎn)換)</param>
        /// <param name="scenesName"></param>
        static void JudgeDIRorFileByRecursive(FileSystemInfo fileSystemInfo, string scenesName)
        {
            
            if (!fileSystemInfo.Exists)
            {
                Debug.LogError("目錄或文件名稱" + fileSystemInfo + "不存在,請(qǐng)檢查");
                return;
            }

            DirectoryInfo directoryInfo = fileSystemInfo as DirectoryInfo;
            FileSystemInfo[] fileSystemInfos = directoryInfo.GetFileSystemInfos();
            foreach (var item in fileSystemInfos)
            {
                FileInfo fileInfo = item as FileInfo;

                if (fileInfo != null)
                {
                    //修改AB標(biāo)簽
                    SetFileABLable(fileInfo, scenesName);
                }
                else
                {
                    JudgeDIRorFileByRecursive(item, scenesName);
                }
            }
        }

        static void SetFileABLable(FileInfo fileInfo, string scenesName)
        {
            //AB包名
            string ABName = string.Empty;
            string assetFilePath = string.Empty;

            //檢查后綴
            if (fileInfo.Extension==".meta")
                return;

            ABName = GetABName(fileInfo, scenesName);

            //截取到Asset之后的目錄
            int tmpIndex = fileInfo.FullName.IndexOf("Assets");
            assetFilePath = fileInfo.FullName.Substring(tmpIndex);
           //給資源文件設(shè)置AB名稱及后綴
           AssetImporter tmpImpObj = AssetImporter.GetAtPath(assetFilePath);
            tmpImpObj.assetBundleName = ABName;
            if (fileInfo.Extension==".unity")
            {
                //定義AB包擴(kuò)展名
                tmpImpObj.assetBundleVariant = "u3d";
            }
            else
            {
                tmpImpObj.assetBundleVariant = "ab";
            }
        }

        /// <summary>
        /// 返回合法AB包名稱
        /// </summary>
        /// <param name="fileInfo"></param>
        /// <param name="scenesName"></param>
        /// <returns></returns>
        static string GetABName(FileInfo fileInfo,string scenesName)
        {
            string ABName = string.Empty;

            //Win路徑
            string tmpWinPath = fileInfo.FullName;
            //Unity路徑
            string tmpUnityPath = tmpWinPath.Replace("\\", "/");
            //定位"場(chǎng)景名稱"后面字符位置
            int tmpScenceNamePos = tmpUnityPath.IndexOf(scenesName)+scenesName.Length;
            //AB包中"類型名稱"所在區(qū)域
            string strABFileNameArea=tmpUnityPath.Substring(tmpScenceNamePos+1);

            if (strABFileNameArea.Contains("/"))
            {
                string[] tempStrArray = strABFileNameArea.Split('/');
                ABName = scenesName + "/" +tempStrArray[0];
            }
            else
            {
                //sences特殊名字
                ABName = scenesName + "/" + scenesName;
            }
            return ABName;
        }
    }
}

把路徑定義為常量類

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     PathTools.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-13
 *Description:   路徑工具類
 * 包含路徑常量
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace ABFrameWork
{
    public class PathTools 
    {

        public const string AB_ResourcesPath = "AB_Res";

        public static string GetABResourcesPath()
        {
            return Application.dataPath + "/" + AB_ResourcesPath;
        }
    }
}

然后添加本地加載和WebReq加載單個(gè)資源
Editor腳本

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     AutoSetLables.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-13
 *Description:   AB打包工具
 * 定義需要打包資源的文件夾根目錄
 * 遍歷每個(gè)"場(chǎng)景"文件夾
 * 遍歷本場(chǎng)景目錄下所有目錄或者文件
 * 如果是目錄,則繼續(xù)遞歸訪問(wèn),直到定位到文件
 * 找到文件,則使用AssetImporter類 標(biāo)記包名、后綴名
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
namespace ABFrameWork
{

    public class DeleteAB
    {
        [MenuItem("ABTools/DeleteAllAB")]
        public static void DeleteAllAB()
        {
            string strABNeedPathDir = string.Empty;

            strABNeedPathDir = PathTools.GetABOutPath();
            if (!string.IsNullOrEmpty(strABNeedPathDir))
            {
                //這里參數(shù)true表示可以刪除非空目錄
                Directory.Delete(strABNeedPathDir,true);

                File.Delete(strABNeedPathDir+".meta");
                AssetDatabase.Refresh();
            }
        }
    }


    public class BuildAB
    {
        [MenuItem("ABTools/BuildAllAB")]
        public static void BuildAllAB()
        {
            string strABOutPathDir = string.Empty;

            strABOutPathDir = PathTools.GetABOutPath();
            if (!Directory.Exists(strABOutPathDir))
            {
                Directory.CreateDirectory(strABOutPathDir);
            }

            BuildPipeline.BuildAssetBundles(strABOutPathDir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
            AssetDatabase.Refresh();
        }
    }



    public class AutoSetLables
    {
        /// <summary>
        /// 設(shè)置AB包名
        /// </summary>
        [MenuItem("ABTools/SetABLable")]
        public static void SetABLable()
        {
            string strNeedSetLableRoot = string.Empty;
            //根目錄下所有場(chǎng)景目錄
            DirectoryInfo[] dirSencesDIRArray;

            //清空無(wú)用AB標(biāo)記
            AssetDatabase.RemoveUnusedAssetBundleNames();

            strNeedSetLableRoot = PathTools.GetABResourcesPath();
            //獲取路徑下所有文件夾
            DirectoryInfo directoryInfo = new DirectoryInfo(strNeedSetLableRoot);
            dirSencesDIRArray = directoryInfo.GetDirectories();

            foreach (var item in dirSencesDIRArray)
            {
                string tmpSencesDIR = strNeedSetLableRoot + "/" + item.Name;
                int tmpIndex = tmpSencesDIR.LastIndexOf("/");
                string tmpScenesName = tmpSencesDIR.Substring(tmpIndex + 1);

                JudgeDIRorFileByRecursive(item, tmpScenesName);
            }
            AssetDatabase.Refresh();
            Debug.Log("AB設(shè)置標(biāo)記完成");
        }
        /// <summary>
        /// 遞歸判斷是否為目錄與文件,修改AB標(biāo)記
        /// </summary>
        /// <param name="currentDIR">當(dāng)前文件信息 (和目錄信息可以相互轉(zhuǎn)換)</param>
        /// <param name="scenesName"></param>
        static void JudgeDIRorFileByRecursive(FileSystemInfo fileSystemInfo, string scenesName)
        {

            if (!fileSystemInfo.Exists)
            {
                Debug.LogError("目錄或文件名稱" + fileSystemInfo + "不存在,請(qǐng)檢查");
                return;
            }

            DirectoryInfo directoryInfo = fileSystemInfo as DirectoryInfo;
            FileSystemInfo[] fileSystemInfos = directoryInfo.GetFileSystemInfos();
            foreach (var item in fileSystemInfos)
            {
                FileInfo fileInfo = item as FileInfo;

                if (fileInfo != null)
                {
                    //修改AB標(biāo)簽
                    SetFileABLable(fileInfo, scenesName);
                }
                else
                {
                    JudgeDIRorFileByRecursive(item, scenesName);
                }
            }
        }

        static void SetFileABLable(FileInfo fileInfo, string scenesName)
        {
            //AB包名
            string ABName = string.Empty;
            string assetFilePath = string.Empty;

            //檢查后綴
            if (fileInfo.Extension == ".meta")
                return;

            ABName = GetABName(fileInfo, scenesName);

            //截取到Asset之后的目錄
            int tmpIndex = fileInfo.FullName.IndexOf("Assets");
            assetFilePath = fileInfo.FullName.Substring(tmpIndex);
            //給資源文件設(shè)置AB名稱及后綴
            AssetImporter tmpImpObj = AssetImporter.GetAtPath(assetFilePath);
            tmpImpObj.assetBundleName = ABName;
            if (fileInfo.Extension == ".unity")
            {
                //定義AB包擴(kuò)展名
                tmpImpObj.assetBundleVariant = "u3d";
            }
            else
            {
                tmpImpObj.assetBundleVariant = "ab";
            }
        }

        /// <summary>
        /// 返回合法AB包名稱
        /// </summary>
        /// <param name="fileInfo"></param>
        /// <param name="scenesName"></param>
        /// <returns></returns>
        static string GetABName(FileInfo fileInfo, string scenesName)
        {
            string ABName = string.Empty;

            //Win路徑
            string tmpWinPath = fileInfo.FullName;
            //Unity路徑
            string tmpUnityPath = tmpWinPath.Replace("\\", "/");
            //定位"場(chǎng)景名稱"后面字符位置
            int tmpScenceNamePos = tmpUnityPath.IndexOf(scenesName) + scenesName.Length;
            //AB包中"類型名稱"所在區(qū)域
            string strABFileNameArea = tmpUnityPath.Substring(tmpScenceNamePos + 1);

            if (strABFileNameArea.Contains("/"))
            {
                string[] tempStrArray = strABFileNameArea.Split('/');
                ABName = scenesName + "/" + tempStrArray[0];
            }
            else
            {
                //sences特殊名字
                ABName = scenesName + "/" + scenesName;
            }
            return ABName;
        }
    }
}

工具路徑類

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     PathTools.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-13
 *Description:   路徑工具類
 * 包含路徑常量
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace ABFrameWork
{
    public class PathTools 
    {

        public const string AB_ResourcesPath = "AB_Res";

        /// <summary>
        /// 獲取AB標(biāo)記路徑
        /// </summary>
        /// <returns></returns>
        public static string GetABResourcesPath()
        {
            return Application.dataPath + "/" + AB_ResourcesPath;
        }

        /// <summary>
        /// 獲取AB輸出路徑
        /// 1.平臺(tái)(PC/移動(dòng))路徑
        /// </summary>
        public static string GetABOutPath()
        {        
            return GetPlatfromPath()+"/"+ GetPlatfromName();
        }

        /// <summary>
        /// 獲取平臺(tái)路徑
        /// </summary>
        /// <returns></returns>
        public static string GetPlatfromPath()
        {
            string strReturnPlatformPath = string.Empty;
            switch (Application.platform)
            {
                case RuntimePlatform.WindowsPlayer:
                case RuntimePlatform.WindowsEditor:
                    strReturnPlatformPath = Application.streamingAssetsPath;
                    break;
                case RuntimePlatform.IPhonePlayer:
                case RuntimePlatform.Android:
                    strReturnPlatformPath = Application.persistentDataPath;
                    break;
            }
            return strReturnPlatformPath;
        }

        /// <summary>
        /// 獲取平臺(tái)名稱
        /// </summary>
        /// <returns></returns>
        public static string GetPlatfromName()
        {
            string strReturnPlatformName = string.Empty;
            switch (Application.platform)
            {
                case RuntimePlatform.WindowsPlayer:
                case RuntimePlatform.WindowsEditor:
                    strReturnPlatformName = "Windows";
                    break;
                case RuntimePlatform.IPhonePlayer:
                    strReturnPlatformName = "IPhone";
                    break;
                case RuntimePlatform.Android:
                    strReturnPlatformName = "Android";
                    break;
            }
            return strReturnPlatformName;
        }
        public static string GetWWWPath()
        {
            string strWWWPath = string.Empty;
            switch (Application.platform)
            {
                case RuntimePlatform.WindowsPlayer:
                case RuntimePlatform.WindowsEditor:
                    strWWWPath = "file://"+GetABOutPath();
                    break;
                case RuntimePlatform.IPhonePlayer:
                    break;
                case RuntimePlatform.Android:
                    strWWWPath = "jar:file://" + GetABOutPath();
                    break;
            }
            return strWWWPath;
        }
    }
   
}

加載資源類

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     AssetLoader.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-15
 *Description:   
 * 管理與加載指定AB資源
 * 加載具有"緩存功能"的資源 帶選用參數(shù)
 * 卸載、釋放AB資源
 * 查看當(dāng)前AB的資源
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ABFrameWork
{
    public class AssetLoader : System.IDisposable
    {
        AssetBundle currentAB;
        /// <summary>
        /// 緩存容器
        /// </summary>
        Hashtable ht=new Hashtable();

        public AssetLoader(AssetBundle abObj)
        {
            if (abObj != null)
            {
                currentAB = abObj;
            }
            else
            {
                Debug.LogError($"{GetType()}/AssetLoader/abObj= null,please check!");
            }

        }

        /// <summary>
        /// 加載當(dāng)前包中指定數(shù)據(jù)
        /// </summary>
        /// <param name="assetName"></param>
        /// <param name="isCache"></param>
        /// <returns></returns>
        public Object LoadAsset(string assetName, bool isCache = false)
        {
            return LoadRes<Object>(assetName, isCache);
        }

        T LoadRes<T>(string assetName, bool isCache) where T : Object
        {
            //是否緩存集合已經(jīng)存在
            if (ht.Contains(assetName))
            {
                return ht[assetName] as T;
            }

            //正式加載
            T tmpRes = currentAB.LoadAsset<T>(assetName);
            if (tmpRes != null && isCache)
            {
                ht.Add(assetName, tmpRes);
            }
            else if (tmpRes == null)
            {
                Debug.LogError($"{GetType()}/LoadRes<T>/tmpRes == null,please check!");
            }
            return tmpRes;
        }

        /// <summary>
        /// 卸載指定的資源
        /// </summary>
        public bool UnLoadAsset(Object asset)
        {
            if (asset != null)
            {
                Resources.UnloadAsset(asset);
                return true;
            }
            Debug.LogError($"{GetType()}/UnLoadAsset/asset == null,please check!");
            return false;
        }


        /// <summary>
        /// 釋放當(dāng)前AB內(nèi)存鏡像資源
        /// </summary>
        public void Dispose()
        {
            currentAB.Unload(false);
        }
        /// <summary>
        /// 釋放當(dāng)前AB內(nèi)存鏡像資源,且釋放內(nèi)存資源
        /// </summary>
        public void DisposeAll()
        {
            currentAB.Unload(true);
        }

        /// <summary>
        /// 查詢當(dāng)前AB包包含的所有資源名稱
        /// </summary>
        /// <returns></returns>
        public string[] RetriveAllAssetName()
        {
            return currentAB.GetAllAssetNames();
        }
    }
}

網(wǎng)上加載資源類

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     SingleAssetLoader.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-15
 *Description:   
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

namespace ABFrameWork
{
    public class SingleAssetLoader : System.IDisposable
    {
        /// <summary>
        /// 引用類資源加載
        /// </summary>
        AssetLoader assetLoader;

        DelLoadComplete loadCompleteHandle;

        string ABName;

        string ABDownLoadPath;

        public SingleAssetLoader(string abName, DelLoadComplete loadComplete)
        {
            ABName = abName;
            loadCompleteHandle = loadComplete;
            ABDownLoadPath = PathTools.GetWWWPath() + "/" + abName;
        }

        public IEnumerator LoadAB()
        {
            using (UnityWebRequest req = UnityWebRequestAssetBundle.GetAssetBundle(ABDownLoadPath))
            {              
                yield return req.SendWebRequest();

                if (req.downloadProgress >= 1)
                {

                    AssetBundle ab = DownloadHandlerAssetBundle.GetContent(req);
                    if (ab != null)
                    {
                        assetLoader = new AssetLoader(ab);
                        loadCompleteHandle?.Invoke(ABName);
                    }
                    else
                    {
                        Debug.LogError($"{GetType() }/ LoadAB() loadError ,please check! the{ABDownLoadPath} should be null");
                    }
                }
            }
        }

        /// <summary>
        /// 加載AB包指定資源
        /// </summary>
        /// <param name="assetName"></param>
        /// <param name="isCache"></param>
        /// <returns></returns>
        public Object LoadAsset(string assetName, bool isCache)
        {
            if (assetLoader != null)
            {
                return assetLoader.LoadAsset(assetName, isCache);
            }
            Debug.LogError($"{GetType() }/LoadAsset() assetLoader==null,please check!");
            return null;
        }

        /// <summary>
        /// 卸載AB包指定資源
        /// </summary>
        /// <param name="asset"></param>
        public void UnLoadAsset(Object asset)
        {
            if (assetLoader != null)
            {
                assetLoader.UnLoadAsset(asset);
            }
            else
            {
                Debug.LogError($"{GetType() }/UnLoadAsset(Object asset) assetLoader==null,please check!");
            }
        }

        /// <summary>
        /// 釋放資源
        /// </summary>
        public void Dispose()
        {

            if (assetLoader != null)
            {
                assetLoader.Dispose();
            }
            else
            {
                Debug.LogError($"{GetType() }/Dispose() assetLoader==null,please check!");
            }
        }


        public void DisposeAll()
        {
            if (assetLoader != null)
            {
                assetLoader.DisposeAll();
            }
            else
            {
                Debug.LogError($"{GetType() }/DisposeAll() assetLoader==null,please check!");
            }
        }

        /// <summary>
        /// 查詢AB包中所有資源
        /// </summary>
        /// <returns></returns>
        public string[] RetrivalAllAssetName()
        {
            if (assetLoader != null)
            {
                return assetLoader.RetriveAllAssetName();
            }
            Debug.LogError($"{GetType() }/etrivalAllAssetName() assetLoader==null,please check!");
            return null;
        }
    }

}

常量類

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     ABDefine.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-15
 *Description:   工具類
 * 本項(xiàng)目所有的常量
 * 所有的委托定義
 * 枚舉定義
 * 常量定義
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace ABFrameWork
{
    public delegate void DelLoadComplete(string abName);

    /// <summary>
    /// 框架常量
    /// </summary>
    public class ABDefine
    {
      
    }
}

測(cè)試類

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     TestAB.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-15
 *Description:   測(cè)試類
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ABFrameWork;
public class TestAB : MonoBehaviour
{
    string abName = "sences_1/prefabs.ab";
    string assetName = "Cube.prefab";
    SingleAssetLoader loader;
    // Start is called before the first frame update
    void Start()
    {
        loader = new SingleAssetLoader(abName, (abName) =>
        {
            var a = loader.LoadAsset(assetName, false);
            Instantiate(a);
        });
        StartCoroutine(loader.LoadAB());
    }

    // Update is called once per frame
    void Update()
    {

    }
}

image.png

因?yàn)闆](méi)有依賴資源 所以丟失了材質(zhì)
所以要先加載材質(zhì)貼圖再加載預(yù)制體
需要有一個(gè)處理依賴關(guān)系的類
獲取Manifest里面的依賴


image.png

維護(hù)包與包之間的關(guān)系

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     ABManifestLoader.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-16
 *Description:   讀取AB的依賴關(guān)系(Windows.mainfest)
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

namespace ABFrameWork
{
    public class ABManifestLoader : Singleton<ABManifestLoader>,System.IDisposable
    {
       
        /// <summary>
        /// 系統(tǒng)ab清單文件
        /// </summary>
        AssetBundleManifest manifest;
        /// <summary>
        /// ab清單文件路徑
        /// </summary>
        string strManifestPath;
        /// <summary>
        /// 讀取AB清單文件的AB
        /// </summary>
        AssetBundle ABReadManifest;

        public bool isLoadFinish { get; private set; } = false;

        public ABManifestLoader()
        {
            strManifestPath = PathTools.GetWWWPath() + "/" + PathTools.GetPlatfromName();
        }

        /// <summary>
        /// 加載Mainfest清單文件
        /// </summary>
        /// <returns></returns>
        public IEnumerator LoadMainfestFile()
        {
            using (UnityWebRequest req = UnityWebRequestAssetBundle.GetAssetBundle(strManifestPath))
            {
                yield return req.SendWebRequest();

                if (req.downloadProgress >= 1)
                {
                    AssetBundle ab = DownloadHandlerAssetBundle.GetContent(req);
                    if (ab != null)
                    {
                        ABReadManifest = ab;
                        manifest = ab.LoadAsset(ABDefine.ASSETBUNLDE_MANIFEST) as AssetBundleManifest;
                        isLoadFinish = true;
                    }
                    else
                    {
                        Debug.LogError($"{GetType()}/LoadMainfestFile() downLoad Error, please check{strManifestPath}");
                    }
                }
            }
        }

        /// <summary>
        /// 獲取ABManifest
        /// </summary>
        /// <returns></returns>
        public AssetBundleManifest GetABManifest()
        {
            if (isLoadFinish)
            {
                if (manifest != null)
                {
                    return manifest;
                }
                else
                {
                    Debug.LogError($"{GetType()}/GetABManifest() manifest==null, please check");
                }
            }
            else
            {
                Debug.LogError($"{GetType()}/GetABManifest() isLoadFinish==false, please check");
            }
            return null;
        }

        /// <summary>
        /// 獲取ABManifest系統(tǒng)類的依賴項(xiàng)
        /// </summary>
        /// <param name="abName"></param>
        /// <returns></returns>
        public string[] RetrivalDependce(string abName)
        {
            if (!string.IsNullOrEmpty(abName))
            {
                return manifest?.GetAllDependencies(abName);
            }
            return null;
        }

        /// <summary>
        /// 釋放本類資源
        /// </summary>
        public void Dispose()
        {
            ABReadManifest?.Unload(true);
        }
    }
}
/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     ABRelation.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-16
 *Description:  AB關(guān)系類
 * 儲(chǔ)存制定AB包的所有依賴關(guān)系包
 * 儲(chǔ)存制定AB包所有的引用關(guān)系包
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ABFrameWork
{
    public class ABRelation
    {
        string ABName;
        List<string> allDependenceAB=new List<string>();
        List<string> allReferenceAB=new List<string>();

        public ABRelation(string ABName)
        {
            this.ABName = ABName;

        }
        /// <summary>
        /// 增加依賴關(guān)系
        /// </summary>
        /// <param name="Name"></param>
        public void AddDependence(string abName)
        {
            if (!allDependenceAB.Contains(abName))
            {
                allDependenceAB.Add(abName);
            }
            
        }
        /// <summary>
        /// 移除依賴關(guān)系
        /// </summary>
        /// <param name="abName"></param>
        /// true 此AB包沒(méi)有依賴項(xiàng)
        /// false 此AB包還有其他的依賴項(xiàng)
        /// <returns></returns>
        public bool RemoveDependence(string abName)
        {
            if (allDependenceAB.Contains(abName))
            {
                allDependenceAB.Remove(abName);
            }

            if (allDependenceAB.Count>0)
                return false;
            else
                return true;
        }

        public List<string>GetAllDependence()
        {
            return allDependenceAB;
        }


        /// <summary>
        /// 增加引用關(guān)系
        /// </summary>
        /// <param name="Name"></param>
        public void AddReference(string abName)
        {
            if (!allReferenceAB.Contains(abName))
            {
                allReferenceAB.Add(abName);
            }

        }
        /// <summary>
        /// 移除引用關(guān)系
        /// </summary>
        /// <param name="abName"></param>
        /// true 此AB包沒(méi)有依賴項(xiàng)
        /// false 此AB包還有其他的依賴項(xiàng)
        /// <returns></returns>
        public bool RemoveReference(string abName)
        {
            if (allReferenceAB.Contains(abName))
            {
                allReferenceAB.Remove(abName);
            }

            if (allReferenceAB.Count > 0)
                return false;
            else
                return true;
        }

        public List<string> GetAllReference()
        {
            return allReferenceAB;
        }
    }
}

一個(gè)場(chǎng)景多個(gè)包之間的關(guān)系

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     MultABMgr.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-16
 *Description:   針對(duì)一個(gè)場(chǎng)景中關(guān)于多個(gè)AB綜合管理
 * 獲取AB包之間的依賴關(guān)系和引用關(guān)系
 * 管理AB包之間的自動(dòng)連接(遞歸加載機(jī)制)
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ABFrameWork
{
    public class MultABMgr
    {
        /// <summary>
        /// 引用類 "單個(gè)AB包加載實(shí)現(xiàn)"
        /// </summary>
        SingleAssetLoader currentSingleABLoader;
        /// <summary>
        /// Ab包緩存集合(防止重復(fù)加載)
        /// </summary>
        Dictionary<string, SingleAssetLoader> singleABLoaderCache = new Dictionary<string, SingleAssetLoader>();
        /// <summary>
        /// 當(dāng)前場(chǎng)景(調(diào)用使用)
        /// </summary>
        string currentScenceName;

        /// <summary>
        /// Ab包名
        /// </summary>
        string currentABName;

        /// <summary>
        /// AB包與對(duì)應(yīng)依賴關(guān)系集合
        /// </summary>
        Dictionary<string, ABRelation> abRelation = new Dictionary<string, ABRelation>();
        /// <summary>
        /// 所有AB包加載完成回調(diào)
        /// </summary>
        DelLoadComplete LoadALLABPackageCompleteHandel;

        public MultABMgr(string senceName, string abName, DelLoadComplete loadAllABPackCompleteHandle)
        {
            currentScenceName = senceName;
            currentABName = abName;

            LoadALLABPackageCompleteHandel = loadAllABPackCompleteHandle;
        }

        /// <summary>
        /// 完成指定AB包調(diào)用
        /// </summary>
        /// <param name="name"></param>
        void CompleteLoadAB(string abName)
        {
            if (abName.Equals(currentABName))
            {
                LoadALLABPackageCompleteHandel?.Invoke(abName);
            }
        }

        public IEnumerator LoadAB(string abName)
        {
            ABRelation abRel;
            //AB包關(guān)系建立
            if (!abRelation.ContainsKey(abName))
            {
                abRel = new ABRelation(abName);
                abRelation.Add(abName, abRel);
            }
            abRel = abRelation[abName];
            //得到指定AB包所有依賴關(guān)系(查詢Manifest清單文件)
            string[] strDependeceArray = ABManifestLoader.Instance.RetrivalDependce(abName);

            foreach (var item in strDependeceArray)
            {
                //添加依賴項(xiàng)
                abRel.AddDependence(item);
                //加載引用項(xiàng)(遞歸調(diào)用)
                yield return LoadReference(item, abName);
            }

            //加載AB包
            if (singleABLoaderCache.ContainsKey(abName))
            {
                yield return singleABLoaderCache[abName].LoadAB();
            }
            else
            {
                currentSingleABLoader = new SingleAssetLoader(abName, CompleteLoadAB);
                singleABLoaderCache.Add(abName,currentSingleABLoader);
                yield return currentSingleABLoader.LoadAB();
            }
            yield return null;
        }
        /// <summary>
        /// 加載引用AB包
        /// </summary>
        /// <param name="abName">ab包名稱</param>
        /// <param name="refName">被引用AB包名稱</param>
        /// <returns></returns>
        IEnumerator LoadReference(string abName, string refName)
        {
            ABRelation tmpABRelation;
            if (abRelation.ContainsKey(abName))
            {
                tmpABRelation = abRelation[abName];
                //添加AB包引用關(guān)系(被依賴)
                tmpABRelation.AddReference(refName);
            }
            else
            {
                tmpABRelation = new ABRelation(abName);
                tmpABRelation.AddReference(refName);
                abRelation.Add(abName, tmpABRelation);

                //開(kāi)始加載依賴包(遞歸)
                yield return LoadAB(abName);
            }

            yield return null;
        }

        /// <summary>
        /// 加載AB包中資源
        /// </summary>
        /// <param name="abName"></param>
        /// <param name="assetName"></param>
        /// <param name="isCache"></param>
        /// <returns></returns>
        public Object LoadAsset(string abName, string assetName, bool isCache)
        {
            foreach (var item in singleABLoaderCache.Keys)
            {
                if (abName == item)
                {
                    return singleABLoaderCache[item].LoadAsset(assetName, isCache);
                }
            }
            Debug.LogError(GetType() + $"/LoadAsset()/do not find AB please check! abName{abName} assetName{assetName}");
            return null;
        }

        public void DisposeAllAsset()
        {
            try
            {
                //逐一釋放所有加載過(guò)的AB包中資源
                foreach (var item in singleABLoaderCache.Values)
                {
                    item.DisposeAll();
                }
            }
            finally
            {
                singleABLoaderCache.Clear();
                singleABLoaderCache = null;

                //釋放其他對(duì)象占用資源
                abRelation.Clear();
                abRelation = null;
                currentABName = null;
                currentScenceName = null;
                LoadALLABPackageCompleteHandel = null;

                //卸載沒(méi)有用到的資源
                Resources.UnloadUnusedAssets();
                //強(qiáng)制垃圾收集
                System.GC.Collect();

            }

        }
    }
}
image.png

最后封裝一層調(diào)用類

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     ABMgr.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-19
 *Description:   多包管理按場(chǎng)景加載
 * 讀取清單文件 緩存腳本
 * 以場(chǎng)景為單位 管理整個(gè)項(xiàng)目中所有AB包
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ABFrameWork
{

    public class ABMgr : SingletonM<ABMgr>
    {
        //場(chǎng)景集合
        Dictionary<string, MultABMgr> allScenes = new Dictionary<string, MultABMgr>();
        //AB清單文件
        AssetBundleManifest manifest;

        protected override void Awake()
        {
            base.Awake();
            StartCoroutine(ABManifestLoader.Instance.LoadMainfestFile());
        }
        public IEnumerator LoadAB(string sencesName, string abName, DelLoadComplete loadAllCompleteHandle)
        {
            //參數(shù)檢查
            if (string.IsNullOrEmpty(sencesName) || string.IsNullOrEmpty(abName))
            {
                Debug.LogError(GetType() + "/LoadAB()/sencesName or abName==null,please check!");
            }
            //等待Manifest清單文件加載完成
            while (!ABManifestLoader.Instance.isLoadFinish)
            {
                yield return null;
            }
            manifest = ABManifestLoader.Instance.GetABManifest();
            if (manifest == null)
            {
                Debug.LogError(GetType() + "/LoadAB()/manifest==null,please check!");
                yield return null;
            }

            MultABMgr multABMgr;
            //把當(dāng)前場(chǎng)景加入到集合中
            if (!allScenes.ContainsKey(sencesName))
            {
                multABMgr = new MultABMgr(sencesName, abName, loadAllCompleteHandle);
                allScenes.Add(sencesName, multABMgr);
            }

            //調(diào)用下一層(多包管理員)
            multABMgr = allScenes[sencesName];
            if (multABMgr == null)
            {
                Debug.LogError(GetType() + "/LoadAB()/multABMgr==null,please check!");
            }
            //調(diào)用多包管理類的加載指定AB包
            yield return multABMgr.LoadAB(abName);
        }
        /// <summary>
        /// 加載AB包資源
        /// </summary>
        /// <param name="scenesName">場(chǎng)景名</param>
        /// <param name="abName">包名</param>
        /// <param name="assetName">資源名</param>
        /// <param name="isCache">是否緩存</param>
        /// <returns></returns>
        public Object LoadAsset(string scenesName, string abName, string assetName, bool isCache)
        {
            if (allScenes.ContainsKey(scenesName))
            {
                MultABMgr multABMgr = allScenes[scenesName];
                return multABMgr.LoadAsset(abName, assetName, isCache);
            }
            Debug.LogError(GetType() + $"/LoadAsset()do not find scenes,can't loadAsset, scenesName={scenesName}");
            return null;
        }

        /// <summary>
        /// 加載AB包資源
        /// </summary>
        /// <param name="scenesName">場(chǎng)景名</param>
        /// <param name="abName">包名</param>
        /// <param name="assetName">資源名</param>
        /// <param name="isCache">是否緩存</param>
        /// <returns></returns>
        public void DisposeAllAssets(string scenesName)
        {
            if (allScenes.ContainsKey(scenesName))
            {
                MultABMgr multABMgr = allScenes[scenesName];
                multABMgr.DisposeAllAsset();
            }
            else
            {
                Debug.LogError(GetType() + $"/DisposeAllAssets()do not find scenes,can't DisposeAllAssets, scenesName={scenesName}");
            }
        }
    }

}

測(cè)試類 根據(jù)場(chǎng)景名 包名 預(yù)制體名字 加載
好像是有點(diǎn)麻煩

/**
 *Copyright(C) 2019 by DefaultCompany
 *All rights reserved.
 *FileName:     TestAB.cs
 *Author:       why
 *Version:      1.0
 *UnityVersion:2018.3.9f1
 *Date:         2019-05-15
 *Description:   測(cè)試類
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ABFrameWork;
public class TestAB : MonoBehaviour
{
    //string abName = "sences_1/prefabs.ab";
    //string assetName = "Cube.prefab";
    //SingleAssetLoader loader;

    string scenesName = "scence_1";
    string abName = "sences_1/prefabs.ab";
    string assetName = "Cube.prefab";
    void Start()
    {
        //loader = new SingleAssetLoader(abName, (abName) =>
        //{
        //    var a = loader.LoadAsset(assetName, false);
        //    Instantiate(a);
        //});
        //StartCoroutine(loader.LoadAB());

        StartCoroutine(ABMgr.Instance.LoadAB(scenesName, abName, (abName) =>
        {
            Object tmpObj = ABMgr.Instance.LoadAsset(scenesName, abName, assetName, false);
            if (tmpObj != null)
            {
                Instantiate(tmpObj);
            }
        }));
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            ABMgr.Instance.DisposeAllAssets(scenesName);
        }
    }

}
image.png

釋放后


image.png

里面用到一個(gè)單例類之后放出源碼
https://github.com/1004019267/ABFrameWorkWithSence

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容