【Unity&AssetBundle】AssetBundle管理器

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;

///


/// AB包管理器
///

public class AssetBundle_Manager_ : SingletonAutoMono
{
    #region 变量
    private Dictionary abDic = new();//用于管理AB的容器
    private Dictionary objDic = new();//用于存储被加载资源的容器

    private AssetBundle abMain = null;//主包
    private AssetBundleManifest abMainManifest = null;//主包依赖

    private string abPath = Application.streamingAssetsPath;//AB包文件夹路径
    public string ABPath
    {
        set => abPath = value;
        get => abPath;
    }

    //主包名称,这里可以根据实际情况修改主包名称
    private string ABMainName
    {
        get 
        {
#if UNITY_IOS
    return "IOS";
#elif UNITY_ANDROID
    return "Android";
#else
    return "AB Package";
#endif
        }

    }
    #endregion

    #region 同步加载AB包
    ///


    /// 加载主包以及主包配置文件
    ///

    private bool LoadMainAB()
    {
        //加载主包
        if (abMain == null)
        {   
            //尝试加载AB包
            string path = Path.Combine(ABPath, ABMainName);
            abMain = AssetBundle.LoadFromFile(path);

            //如果加载失败 则输出错误信息
            if (abMain == null)
            {
                Debug.LogError($"主包加载失败!路径:{path}");
                return false;
            }
        }

        //加载主包的依赖配置文件
        if (abMainManifest == null)
        {
            //尝试加载主包的AssetBundleManifest 
            abMainManifest = abMain.LoadAsset("AssetBundleManifest");

            //如果加载失败 则输出错误信息
            if (abMainManifest == null)
            {
                Debug.LogError("主包的AssetBundleManifest加载失败!");
                return false;
            }
        }

        //如果主包和主包配置文件都加载成功 则返回true 
        return true;
    }

    ///


    /// 加载目标包
    ///

    /// 目标包名称
    private AssetBundle LoadTargetAB(string targetABName)
    {
        //如果主包和主包配置文件没有加载成功 则返回null
        if (!LoadMainAB())
        {
            Debug.LogError("主包加载失败,无法加载目标包");
            return null;
        }

        //获取所有依赖的名称
        string[] dependenciesStrs = abMainManifest.GetAllDependencies(targetABName);
        //加载所有依赖
        foreach (string dependencyStr in dependenciesStrs)
        {
            //如果依赖包没有加载过 则加载依赖包
            if (!abDic.ContainsKey(dependencyStr))
            {
                //尝试加载依赖包
                var dependencyAB = AssetBundle.LoadFromFile(Path.Combine(ABPath, dependencyStr));
                //如果加载失败 则输出错误信息
                if (dependencyAB == null)
                {
                    Debug.LogError($"依赖包加载失败!路径:{Path.Combine(ABPath, dependencyStr)}");
                    continue;
                }
                //将加载的依赖包存入字典
                abDic.TryAdd(dependencyStr, dependencyAB);
            }
        }

        //加载目标AB包
        //如果目标AB包没有加载过 则加载目标AB包
        if (!abDic.TryGetValue(targetABName, out var targetAB))
        {
            targetAB = AssetBundle.LoadFromFile(Path.Combine(ABPath, targetABName));

            //如果加载失败 则输出错误信息
            if (targetAB == null)
            {
                Debug.LogError($"目标AB包加载失败!路径:{Path.Combine(ABPath, targetABName)}");
                return null;
            }

            //将加载的目标AB包存入字典
            abDic.TryAdd(targetABName, targetAB);
        }

        //返回加载的目标AB包
        return targetAB;
    }

    ///


    /// 生成资源Key
    ///

    /// AB包
    /// 资源包
    /// 后缀
    /// 资源Key
    private string GenerateObjKey(string abName, string resName, string suffix = "")
    {
        return $"{abName}_{resName}_{suffix}_";
    }

    ///


    /// 加载资源的接口
    ///

    /// 目标资源Key
    /// 目标AB包
    /// 目标资源名称
    /// 回调函数
    /// 目标资源
    private UnityEngine.Object LoadResInternal(string objKey, string targetABName, string targetResName,Func callBack)
    {
        //如果资源容器中没有该资源
        if (!objDic.TryGetValue(objKey, out UnityEngine.Object obj) || obj == null)
        {
            //则加载目标AB包
            AssetBundle ab = LoadTargetAB(targetABName);
            //如果加载失败 则返回null
            if (ab == null)
            {
                Debug.LogError($"目标AB包加载失败:{targetABName}");
                return null;
            }
            //加载目标资源
            obj = callBack(ab);
            //如果加载失败 则输出错误信息
            if (obj == null)
            {
                Debug.LogError($"资源加载失败:AB包:{targetABName},资源名:{targetResName}");
                return null;
            }
            else
            {
                //将加载的资源存入字典
                objDic[objKey] = obj;
            }
        }

        return obj;
    }

    ///


    /// 同步加载资源(以名称的形式加载资源)
    ///

    /// 目标包
    /// 目标资源
    /// 资源
    public UnityEngine.Object LoadABRes(string targetABName, string targetResName)
    {
        // 资源容器中存储的资源名称 
        string objKey = GenerateObjKey(targetABName, targetResName, "Name");
        // 获取资源
        return LoadResInternal(objKey, targetABName, targetResName, (ab) => ab.LoadAsset(targetResName));
    }

    ///


    /// 同步加载资源(以Type的形式加载资源)
    ///

    /// 目标AB包名称
    /// 目标资源名称
    /// 目标资源类型
    /// 资源
    public UnityEngine.Object LoadABRes(string targetABName, string targetResName, System.Type type)
    {
        // 资源容器中存储的资源名称 
        string objKey = GenerateObjKey(targetABName, targetResName, "Type");
        // 获取资源
        return LoadResInternal(objKey, targetABName, targetResName, (ab) => ab.LoadAsset(targetResName, type));
    }

    ///


    /// 同步加载资源(以泛型加载资源)
    ///

    /// 目标资源的类型(泛型)
    /// 目标AB包名称
    /// 目标资源名称
    /// 资源
    public T LoadABRes(string targetABName, string targetResName) where T : UnityEngine.Object
    {
        // 资源容器中存储的资源名称 
        string objKey = GenerateObjKey(targetABName, targetResName, "Generics");
        // 获取资源
        return LoadResInternal(objKey, targetABName, targetResName, (ab) => ab.LoadAsset(targetResName)) as T;
    }
    #endregion

    #region 异步加载AB包

    ///


    /// 用于将带回调的协程包装成 Task
    ///

    private Task WrapCoroutine(Func, string, IEnumerator> coroutineStarter, string path = null)
    {
        var tcs = new TaskCompletionSource();
        StartCoroutine(coroutineStarter((result) => tcs.SetResult(result), path));
        return tcs.Task;
    }

    ///


    /// 加载主包以及主包配置文件
    ///

    private IEnumerator LoadMainABAsyncCor(UnityAction tcsCallBack = null, string path = null)
    {
        //加载主包
        if (abMain == null)
        {
            //尝试加载AB包
            AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(path);
            //等待AssetBundleCreateRequest完成
            yield return abcr;
            //如果加载失败 则输出错误信息
            if (abcr.assetBundle == null)
            {
                Debug.LogError($"AssetBundleCreateRequest加载失败!");
                yield break;
            }
            
            //获取加载的主包
            abMain = abcr.assetBundle;
        }

        //加载主包的依赖配置文件
        if (abMainManifest == null)
        {
            //尝试加载主包的AssetBundleManifest
            AssetBundleRequest abr = abMain.LoadAssetAsync("AssetBundleManifest");
            yield return abr;
            if (abr.asset == null)
            {
                Debug.LogError("AssetBundleRequest加载失败!"); 
                yield break;
            }

            abMainManifest = abr.asset as AssetBundleManifest;
        }

        //如果主包和主包配置文件都加载成功 则执行回调函数
        tcsCallBack?.Invoke(abMain);
    }

    ///


    /// 异步加载目标AB包的协程
    ///

    /// 回调函数
    /// 目标AB包
    ///
    private IEnumerator LoadFromFileAsyncCor(UnityAction callBack = null, string targetABName = null)
    {
        //加载目标AB包
        AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(targetABName);
        //等待AssetBundle加载完成
        yield return abcr;
        //如果加载失败 则输出错误信息
        if (abcr.assetBundle == null)
        {
            Debug.LogError($"目标AssetBundleCreateRequest加载失败!");
            yield break;
        }

        //获取加载的AssetBundle
        AssetBundle ab = abcr.assetBundle;

        //如果有回调函数 则执行回调
        callBack?.Invoke(ab);
    }

    ///


    /// 异步加载目标AB包
    ///

    /// 目标AB包名称
    private async Task LoadTargetABAsync(string targetABName)
    {
        await WrapCoroutine(LoadMainABAsyncCor, Path.Combine(ABPath, ABMainName));

        //获取所有依赖的名称
        string[] dependenciesStrs = abMainManifest.GetAllDependencies(targetABName);
        //加载所有依赖
        foreach (string dependencyStr in dependenciesStrs)
        {
            if (!abDic.ContainsKey(dependencyStr))
            {
                //尝试加载依赖包
                var denpendencyAB = await WrapCoroutine(LoadFromFileAsyncCor, Path.Combine(ABPath, dependencyStr));
                //如果加载失败 则输出错误信息
                if (denpendencyAB == null)
                {
                    Debug.LogError($"依赖包加载失败!路径:{Path.Combine(ABPath, dependencyStr)}");
                    continue;
                }
                //将加载的依赖包存入字典
                abDic.TryAdd(dependencyStr, denpendencyAB);
            }
        }
        //加载目标AB包
        if (!abDic.TryGetValue(targetABName, out AssetBundle ab) || ab == null)
        {   
            string path = Path.Combine(ABPath, targetABName);
            ab = await WrapCoroutine(LoadFromFileAsyncCor, path);
            //如果加载失败 则输出错误信息
            if (ab == null)
            {
                Debug.LogError($"目标AB包加载失败!路径:{path}");
                return null;
            }
            //将加载的目标AB包存入字典
            abDic.TryAdd(targetABName, ab);
        }

        //返回加载的目标AB包
        return ab;
    }

    ///


    /// 用于封装异步加载资源的通用方法
    ///

    /// 泛型
    /// 目标AB包名称
    /// 目标资源名称
    /// 资源存储的Key
    /// 回调函数
    /// 加载资源的委托
    /// 对应类型的资源
    private async Task AwaitAssetBundleRequest(string targetABName, string targetResName, string objKey, UnityAction callBack, Func loadRequestGetter) where T : UnityEngine.Object
    {
        //如果资源容器中已经有该资源 则执行回调并返回资源
        if (objDic.TryGetValue(objKey, out UnityEngine.Object obj))
        {
            callBack?.Invoke(obj);
            return obj as T;
        }

        //异步加载目标AB包
        AssetBundle ab = await LoadTargetABAsync(targetABName);
        //判断callBack2是否为null
        if (loadRequestGetter == null)
        {
            Debug.LogError("加载函数 callBack2 为空!");
            return null;
        }
        AssetBundleRequest abr = loadRequestGetter.Invoke(ab, targetResName);
        //等待AssetBundleRequest完成
        while (!abr.isDone)
        {           
            await Task.Yield();
        }

        //判断加载的资源是否为null
        if (abr.asset == null)
        {
            Debug.LogError("AssetBundleRequest加载失败!(资源为空)");
            return null;
        }

        obj = abr.asset;

        //如果有回调函数 则执行回调wo1
        callBack?.Invoke(obj);

        //将加载的资源存入字典
        objDic[objKey] = obj;

        return abr.asset as T;
    }

    ///


    /// 异步加载资源(以名称的形式加载资源)
    ///

    /// 目标AB包
    /// 目标资源
    /// 对应类型的资源
    public async Task LoadABResAsync(string targetABName, string targetResName, UnityAction callBack)
    {
        //生成资源Key
        string objKey = GenerateObjKey(targetABName, targetResName, "Name");

        UnityEngine.Object obj = await AwaitAssetBundleRequest(targetABName, targetResName, objKey, callBack, (ab, targetResName) => ab.LoadAssetAsync(targetResName));

        //返回资源
        return obj;
    }

    ///


    /// 异步加载资源(以Type的形式加载资源)
    ///

    /// 目标AB包
    /// 目标资源
    /// 目标资源类型
    /// 对应类型的资源
    public async Task LoadABResAsync(string targetABName, string targetResName, System.Type type, UnityAction callBack)
    {
        //生成资源Key
        string objKey = GenerateObjKey(targetABName, targetResName, "Type");

        UnityEngine.Object obj = await AwaitAssetBundleRequest(targetABName, targetResName, objKey, callBack, (ab, targetResName) => ab.LoadAssetAsync(targetResName, type));

        //返回资源
        return obj;
    }

    ///


    /// 异步加载资源(以泛型加载资源)
    ///

    /// 泛型
    /// 目标AB包
    /// 目标资源
    /// 目标类型
    /// 对应类型的资源
    public async Task LoadABResAsync(string targetABName, string targetResName, UnityAction callBack) where T : UnityEngine.Object
    {
        //生成资源Key
        string objKey = GenerateObjKey(targetABName, targetResName, "Generics");

        T obj = await AwaitAssetBundleRequest(targetABName, targetResName, objKey, callBack, (ab, targetResName) => ab.LoadAssetAsync(targetResName));

        //返回资源
        return obj;
    }
    #endregion

    #region 同步卸载AB包
    ///


    /// 单个包卸载
    ///

    /// 目标包名称
    public void UnLoadAB(string targetABName)
    {
        if(abDic.ContainsKey(targetABName))
        {
            abDic[targetABName].Unload(false);
            abDic.Remove(targetABName);
        }
    }

    ///


    /// 所有包卸载
    ///

    public void UnLoadAllAB()
    {
        //卸载所有AB包 
        foreach (var ab in abDic)
        {
            ab.Value.Unload(false);
        }
        abDic.Clear();

        // 卸载主包和主包配置文件
        if (abMain != null)
        {
            abMain.Unload(false);
            abMain = null;
        }
        if (abMainManifest != null)
        {
            abMainManifest = null;
        }
    }
    #endregion

    #region 异步卸载AB包

    ///


    /// 异步卸载AB包的协程
    ///

    /// 目标AB包名称
    /// 回调函数
    /// 协程
    private IEnumerator UnLoadABAsyncCor(string targetABName, UnityAction callBack)
    {
        //先在字典中查找目标AB包
        if (abDic.ContainsKey(targetABName))
        {
            //等待一帧以确保资源加载完成
            yield return null;
            //卸载目标AB包
            abDic[targetABName].Unload(false);
            //从字典中移除目标AB包
            bool isSuccess = abDic.Remove(targetABName);
            //执行回调函数
            callBack?.Invoke(isSuccess);
        }
    }

    ///


    /// 异步卸载AB包
    ///

    /// 目标包名称
    /// 回调函数
    /// 是否成功卸载AB包
    public Task UnLoadABAsync(string targetABName, UnityAction callBack = null)
    {
        //使用TaskCompletionSource来封装异步操作
        var tcs = new TaskCompletionSource();
        //开始协程卸载AB包
        StartCoroutine(UnLoadABAsyncCor(targetABName, (b) => { tcs.SetResult(b); }));
        //执行回调函数
        callBack?.Invoke();
        //返回一个Task对象来判断当前协程是否完成
        return tcs.Task;
    }

    ///


    /// 清空所有AB包的协程
    ///

    /// 回调函数
    /// 协程
    private IEnumerator UnLoadAllABAsyncCor(UnityAction callBack)
    {
        //卸载内存中所有的AB包
        AssetBundle.UnloadAllAssetBundles(false);
        //清空字典
        abDic.Clear();
        //执行回调函数
        callBack?.Invoke(true);
        //等待一帧以确保资源加载完成
        yield return null;
    }

    ///


    /// 删除所有AB包的异步方法
    ///

    /// 回调函数
    /// 是否成功卸载资源
    public Task UnLoadAllABAsync(UnityAction callBack = null)
    {
        //使用TaskCompletionSource来封装异步操作
        var tcs = new TaskCompletionSource();
        //开始协程卸载所有AB包
        StartCoroutine(UnLoadAllABAsyncCor( (b) => { tcs.SetResult(b); }));
        //执行回调函数
        callBack?.Invoke();
        return tcs.Task;
    }

    ///


    /// 清理objDic里的缓存资源
    ///

    public void ClearUnusedObjects()
    {
        List toRemove = new List();
        foreach (var kvp in objDic)
        {
            if (kvp.Value == null) toRemove.Add(kvp.Key);
        }

        foreach (var key in toRemove)
        {
            objDic.Remove(key);
        }
    }
    #endregion

    #region objDic相关操作
    ///


    /// 清空objDic里的缓存资源
    ///

    public void ClearObjDic()
    {
        if (objDic.Count > 0)
        {
            objDic.Clear();
        }
    }
    #endregion
}

你可能感兴趣的:(【Unity&AssetBundle】AssetBundle管理器)