Unity AssetBundle加载框架

基于Unity2017版本的AB技术
1.项目中成百上千的大量资源需要(批量)打包处理,不可能手工维护方式给每个资源添加ab"包名称"
2.Unity对包依赖关系不是很完善
如果要加载一个多重依赖项的AB包,需要手工写代码,把底层所有依赖包关系需要预先进行加载后才可以。
3.AB包商业应用 步骤有 AB包加载 AB包依赖关系(不遗漏、不重复) 资源提取释放。繁重海量 手动写工作效率低下
4.可能会反复加载同一AB包中资源 导致加载过慢

AB框架包括自动标记脚本、创建与销毁打包资源、单一AB包加载
与测试脚本、专门读取manifest维护AB包依赖关系,实现递归依赖加载机制的脚本


image.png

自动创建AB包分类依据 按照场景分类
命名规则 场景名称/功能文件夹

在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打包工具
 * 定义需要打包资源的文件夹根目录
 * 遍历每个"场景"文件夹
 * 遍历本场景目录下所有目录或者文件
 * 如果是目录,则继续递归访问,直到定位到文件
 * 找到文件,则使用AssetImporter类 标记包名、后缀名
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
namespace ABFrameWork
{

    public class AutoSetLables
    {
        /// <summary>
        /// 设置AB包名
        /// </summary>
        [MenuItem("ABTools/SetABLable")]
        public static void SetABLable()
        {
            string strNeedSetLableRoot = string.Empty;
            //根目录下所有场景目录
            DirectoryInfo[] dirSencesDIRArray;

            //清空无用AB标记
            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设置标记完成");
        }
        /// <summary>
        /// 递归判断是否为目录与文件,修改AB标记
        /// </summary>
        /// <param name="currentDIR">当前文件信息 (和目录信息可以相互转换)</param>
        /// <param name="scenesName"></param>
        static void JudgeDIRorFileByRecursive(FileSystemInfo fileSystemInfo, string scenesName)
        {
            
            if (!fileSystemInfo.Exists)
            {
                Debug.LogError("目录或文件名称" + fileSystemInfo + "不存在,请检查");
                return;
            }

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

                if (fileInfo != null)
                {
                    //修改AB标签
                    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);
           //给资源文件设置AB名称及后缀
           AssetImporter tmpImpObj = AssetImporter.GetAtPath(assetFilePath);
            tmpImpObj.assetBundleName = ABName;
            if (fileInfo.Extension==".unity")
            {
                //定义AB包扩展名
                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("\\", "/");
            //定位"场景名称"后面字符位置
            int tmpScenceNamePos = tmpUnityPath.IndexOf(scenesName)+scenesName.Length;
            //AB包中"类型名称"所在区域
            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加载单个资源
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打包工具
 * 定义需要打包资源的文件夹根目录
 * 遍历每个"场景"文件夹
 * 遍历本场景目录下所有目录或者文件
 * 如果是目录,则继续递归访问,直到定位到文件
 * 找到文件,则使用AssetImporter类 标记包名、后缀名
 *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))
            {
                //这里参数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>
        /// 设置AB包名
        /// </summary>
        [MenuItem("ABTools/SetABLable")]
        public static void SetABLable()
        {
            string strNeedSetLableRoot = string.Empty;
            //根目录下所有场景目录
            DirectoryInfo[] dirSencesDIRArray;

            //清空无用AB标记
            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设置标记完成");
        }
        /// <summary>
        /// 递归判断是否为目录与文件,修改AB标记
        /// </summary>
        /// <param name="currentDIR">当前文件信息 (和目录信息可以相互转换)</param>
        /// <param name="scenesName"></param>
        static void JudgeDIRorFileByRecursive(FileSystemInfo fileSystemInfo, string scenesName)
        {

            if (!fileSystemInfo.Exists)
            {
                Debug.LogError("目录或文件名称" + fileSystemInfo + "不存在,请检查");
                return;
            }

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

                if (fileInfo != null)
                {
                    //修改AB标签
                    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);
            //给资源文件设置AB名称及后缀
            AssetImporter tmpImpObj = AssetImporter.GetAtPath(assetFilePath);
            tmpImpObj.assetBundleName = ABName;
            if (fileInfo.Extension == ".unity")
            {
                //定义AB包扩展名
                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("\\", "/");
            //定位"场景名称"后面字符位置
            int tmpScenceNamePos = tmpUnityPath.IndexOf(scenesName) + scenesName.Length;
            //AB包中"类型名称"所在区域
            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标记路径
        /// </summary>
        /// <returns></returns>
        public static string GetABResourcesPath()
        {
            return Application.dataPath + "/" + AB_ResourcesPath;
        }

        /// <summary>
        /// 获取AB输出路径
        /// 1.平台(PC/移动)路径
        /// </summary>
        public static string GetABOutPath()
        {        
            return GetPlatfromPath()+"/"+ GetPlatfromName();
        }

        /// <summary>
        /// 获取平台路径
        /// </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>
        /// 获取平台名称
        /// </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资源
 * 加载具有"缓存功能"的资源 带选用参数
 * 卸载、释放AB资源
 * 查看当前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>
        /// 加载当前包中指定数据
        /// </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
        {
            //是否缓存集合已经存在
            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>
        /// 释放当前AB内存镜像资源
        /// </summary>
        public void Dispose()
        {
            currentAB.Unload(false);
        }
        /// <summary>
        /// 释放当前AB内存镜像资源,且释放内存资源
        /// </summary>
        public void DisposeAll()
        {
            currentAB.Unload(true);
        }

        /// <summary>
        /// 查询当前AB包包含的所有资源名称
        /// </summary>
        /// <returns></returns>
        public string[] RetriveAllAssetName()
        {
            return currentAB.GetAllAssetNames();
        }
    }
}

网上加载资源类

/**
 *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:   工具类
 * 本项目所有的常量
 * 所有的委托定义
 * 枚举定义
 * 常量定义
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

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

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

测试类

/**
 *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:   测试类
 *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

因为没有依赖资源 所以丢失了材质
所以要先加载材质贴图再加载预制体
需要有一个处理依赖关系的类
获取Manifest里面的依赖


image.png

维护包与包之间的关系

/**
 *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的依赖关系(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>
        /// 系统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系统类的依赖项
        /// </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关系类
 * 储存制定AB包的所有依赖关系包
 * 储存制定AB包所有的引用关系包
 *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>
        /// 增加依赖关系
        /// </summary>
        /// <param name="Name"></param>
        public void AddDependence(string abName)
        {
            if (!allDependenceAB.Contains(abName))
            {
                allDependenceAB.Add(abName);
            }
            
        }
        /// <summary>
        /// 移除依赖关系
        /// </summary>
        /// <param name="abName"></param>
        /// true 此AB包没有依赖项
        /// false 此AB包还有其他的依赖项
        /// <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>
        /// 增加引用关系
        /// </summary>
        /// <param name="Name"></param>
        public void AddReference(string abName)
        {
            if (!allReferenceAB.Contains(abName))
            {
                allReferenceAB.Add(abName);
            }

        }
        /// <summary>
        /// 移除引用关系
        /// </summary>
        /// <param name="abName"></param>
        /// true 此AB包没有依赖项
        /// false 此AB包还有其他的依赖项
        /// <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;
        }
    }
}

一个场景多个包之间的关系

/**
 *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:   针对一个场景中关于多个AB综合管理
 * 获取AB包之间的依赖关系和引用关系
 * 管理AB包之间的自动连接(递归加载机制)
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ABFrameWork
{
    public class MultABMgr
    {
        /// <summary>
        /// 引用类 "单个AB包加载实现"
        /// </summary>
        SingleAssetLoader currentSingleABLoader;
        /// <summary>
        /// Ab包缓存集合(防止重复加载)
        /// </summary>
        Dictionary<string, SingleAssetLoader> singleABLoaderCache = new Dictionary<string, SingleAssetLoader>();
        /// <summary>
        /// 当前场景(调用使用)
        /// </summary>
        string currentScenceName;

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

        /// <summary>
        /// AB包与对应依赖关系集合
        /// </summary>
        Dictionary<string, ABRelation> abRelation = new Dictionary<string, ABRelation>();
        /// <summary>
        /// 所有AB包加载完成回调
        /// </summary>
        DelLoadComplete LoadALLABPackageCompleteHandel;

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

            LoadALLABPackageCompleteHandel = loadAllABPackCompleteHandle;
        }

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

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

            foreach (var item in strDependeceArray)
            {
                //添加依赖项
                abRel.AddDependence(item);
                //加载引用项(递归调用)
                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包引用关系(被依赖)
                tmpABRelation.AddReference(refName);
            }
            else
            {
                tmpABRelation = new ABRelation(abName);
                tmpABRelation.AddReference(refName);
                abRelation.Add(abName, tmpABRelation);

                //开始加载依赖包(递归)
                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
            {
                //逐一释放所有加载过的AB包中资源
                foreach (var item in singleABLoaderCache.Values)
                {
                    item.DisposeAll();
                }
            }
            finally
            {
                singleABLoaderCache.Clear();
                singleABLoaderCache = null;

                //释放其他对象占用资源
                abRelation.Clear();
                abRelation = null;
                currentABName = null;
                currentScenceName = null;
                LoadALLABPackageCompleteHandel = null;

                //卸载没有用到的资源
                Resources.UnloadUnusedAssets();
                //强制垃圾收集
                System.GC.Collect();

            }

        }
    }
}
image.png

最后封装一层调用类

/**
 *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:   多包管理按场景加载
 * 读取清单文件 缓存脚本
 * 以场景为单位 管理整个项目中所有AB包
 *History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ABFrameWork
{

    public class ABMgr : SingletonM<ABMgr>
    {
        //场景集合
        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)
        {
            //参数检查
            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;
            //把当前场景加入到集合中
            if (!allScenes.ContainsKey(sencesName))
            {
                multABMgr = new MultABMgr(sencesName, abName, loadAllCompleteHandle);
                allScenes.Add(sencesName, multABMgr);
            }

            //调用下一层(多包管理员)
            multABMgr = allScenes[sencesName];
            if (multABMgr == null)
            {
                Debug.LogError(GetType() + "/LoadAB()/multABMgr==null,please check!");
            }
            //调用多包管理类的加载指定AB包
            yield return multABMgr.LoadAB(abName);
        }
        /// <summary>
        /// 加载AB包资源
        /// </summary>
        /// <param name="scenesName">场景名</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">场景名</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}");
            }
        }
    }

}

测试类 根据场景名 包名 预制体名字 加载
好像是有点麻烦

/**
 *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:   测试类
 *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

里面用到一个单例类之后放出源码
https://github.com/1004019267/ABFrameWorkWithSence

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336