实现一个简单的AB包资源管理器

前置知识

Unity AB包基础知识与操作-优快云博客

AB包资源管理器的实现

此AB包管理器(ABManager)用于管理Unity中AssetBundle(AB包)的加载、资源获取和卸载操作。采用单例模式确保全局唯一实例,根据不同平台确定主包名称,在初始化时加载主包及其manifest文件,之后可同步或异步加载AB包和资源,同时管理已加载的AB包避免重复加载,也提供卸载AB包的功能。

细节部分

1. 单例模式

通过静态属性Instance实现单例模式。若实例为空,则创建一个名为AssetBundleManager的游戏对象,并为其添加ABManager组件。

2. 主包加载
  • 主包名称确定:根据不同平台(Windows、MacOS、Android)通过预处理器指令返回不同的主包名称,若平台未知则返回"Unknown"
  • 主包及manifest文件加载:在Awake方法中,若主包未加载,则从默认路径加载主包及其manifest文件,并将主包添加到已加载AB包字典中。
 3.同步加载
  • AB包加载:LoadAB方法检查AB包是否已加载,若未加载则从默认路径同步加载,并递归加载其依赖的AB包。
  • 资源加载:LoadRes方法先调用LoadAB确保AB包已加载,然后从AB包中同步加载指定资源。
4. 异步加载
  • 采用协程实现异步加载,使用队列管理异步加载任务,避免多个异步加载任务同时执行造成混乱。
  • LoadResAsync方法将异步加载任务添加到队列或直接启动协程执行,loadABAsync方法异步加载AB包及其依赖的AB包,loadResAsync方法异步加载资源并在加载完成后调用回调函数。
5. 卸载操作
  • UnLoad方法卸载指定的AB包,可选择是否清除资源。
  • UnLoadALL方法卸载所有AB包,可选择是否清除所有资源,并清空已加载AB包字典。
源码:
#define WINDOWS
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class ABManager :MonoBehaviour
{
    private static ABManager instance;
    public static ABManager Instance
    {
        get
        {
            if(instance == null)
            {
                GameObject obj = new GameObject("AssetBundleManager");
                instance = obj.AddComponent<ABManager>();
            }
            return instance;
        }
    }
    #region 主包
    private string mainABName()
    {
#if WINDOWS
        return "PC";
#elif MACOS
        return "MAC";
#elif ANDROID
        return "Android";
#else
        return "Unknown";
#endif
    }//主包名字
    private string defaultPath = Application.streamingAssetsPath;//默认路径
    private AssetBundle mainAB = null;//主包
    private AssetBundleManifest mainABManifest = null;//主包的manifest文件
    void Awake()
    {
        //初始化即加载主包及其manifest文件
        if (mainAB == null)
        {
            mainAB = AssetBundle.LoadFromFile(defaultPath + "/" + mainABName());
            mainABManifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            abDic.Add(mainABName(), mainAB);
        }
    }
    #endregion

    #region 基础操作
    private Dictionary<string, AssetBundle> abDic=new Dictionary<string, AssetBundle>();//记录已经加载过的包,避免重复加载

    #region 同步加载
    public void LoadAB(string abName)
    {
        //如果已经加载过包,不再加载
        if (abDic.ContainsKey(abName)) return;
        try
        {
            AssetBundle ab = AssetBundle.LoadFromFile(defaultPath + "/" + abName);
            abDic.Add(abName, ab);
            //加载其所依赖的包
            string[] abDepencies = mainABManifest.GetAllDependencies(abName);
            foreach (string depencyAB in abDepencies)
            {
                if(!abDic.ContainsKey(depencyAB))
                {
                    AssetBundle depency =  AssetBundle.LoadFromFile(defaultPath + "/" + depencyAB);
                    abDic.Add(depencyAB, depency);
                }
            }
        }
        catch(UnityException e)
        {
            Debug.LogError("AB包加载出错");
        }

    }
    public T LoadRes<T>(string abName,string resName) where T : Object
    {
        LoadAB(abName);
        if (abDic.ContainsKey(abName))
        {
            T res = abDic[abName].LoadAsset<T>(resName);
            return res;
        }
        else
        {
            return null;
        }
    }
    public Object LoadRes(string abName,string resName,System.Type type)
    {
        LoadAB(abName);
        if (abDic.ContainsKey(abName))
        {
           Object res = abDic[abName].LoadAsset(resName,type);
           return res;
        }
        else
        {
            return null;
        }
    }
    #endregion

    #region 异步加载
    bool isRunning = false;
    private Queue<IEnumerator> coroutineQueue = new Queue<IEnumerator>();

    public void LoadResAsync<T>(string abName,string resName,UnityAction<T> callBack) where T: Object
    {
        if (!isRunning)
            StartCoroutine(loadResAsync<T>(abName, resName, callBack));
        else
            coroutineQueue.Enqueue(loadResAsync<T>(abName, resName, callBack));
    }
    public void LoadResAsync(string abName, string resName,System.Type type,UnityAction<Object> callBack)
    {
        if(!isRunning)
            StartCoroutine(loadResAsync(abName, resName,type,callBack));
        else
            coroutineQueue.Enqueue(loadResAsync(abName, resName, type, callBack));
    }
    IEnumerator loadABAsync(string abName)
    {
        if(abDic.ContainsKey(abName)) yield break;
        AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(defaultPath + "/" + abName);
        yield return abcr;
        abDic.Add(abName, abcr.assetBundle);
        //异步加载其所依赖的包
        string[] abDepencies = mainABManifest.GetAllDependencies(abName);
        foreach (string depencyAB in abDepencies)
        {
            if (!abDic.ContainsKey(depencyAB))
            {
                AssetBundleCreateRequest abcrDepency = AssetBundle.LoadFromFileAsync(defaultPath + "/" + depencyAB);
                yield return abcrDepency;
                abDic.Add(depencyAB, abcrDepency.assetBundle);
            }
        }
    }
    IEnumerator loadResAsync<T>(string abName,string resName,UnityAction<T> callBack) where T: Object
    {
        isRunning=true;
        yield return loadABAsync(abName);
        if (abDic.ContainsKey(abName))
        {
            AssetBundleRequest abr = abDic[abName].LoadAssetAsync<T>(resName);
            yield return abr;
            callBack(abr.asset as T);
        }
        else
        {
            Debug.LogError("AB包加载出错");
        }
        if (coroutineQueue.Count > 0)
        {
            yield return StartCoroutine(coroutineQueue.Dequeue());
        }
        isRunning = false;
    }
    IEnumerator loadResAsync(string abName,string resName,System.Type type,UnityAction<Object> callBack)
    {
        isRunning = true;
        yield return loadABAsync(abName);
        if (abDic.ContainsKey(abName))
        {
            AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName,type);
            yield return abr;
            callBack(abr.asset);
        }
        else
        {
            Debug.LogError("AB包加载出错");
        }
        if (coroutineQueue.Count > 0)
        {
            yield return StartCoroutine(coroutineQueue.Dequeue());
        }
        isRunning = false;

    }
    #endregion

    #region 卸载
    public void UnLoad(string abName,bool clearResource=false)
    {
        if (abDic.ContainsKey(abName))
        {
            abDic[abName].Unload(clearResource);
            abDic.Remove(abName);
        }
    }
    public void UnLoadALL(bool clearResources=false)
    {
        AssetBundle.UnloadAllAssetBundles(clearResources);
        abDic.Clear();
    }
    #endregion

    #endregion
}

(简单测试了一下没什么问题。因为我也是菜鸡可能有考虑不到的地方,如果大家测试出了问题,可以跟我说哦。)

参考教材:AB包管理器_ab包加载管理器-优快云博客

资源管理在游戏开发中是提升性能与用户体验的重要环节。Unity中通过ABAssetBundle)打与内存资源管理可以实现高效的资源管理机制。 ### AB在资源管理中的应用 ABUnity提供的一种资源打机制,允许将资源文件(如模型、纹理、音频等)打成独立的二进制文件,便于资源的模块化管理与动态加载。通过AB,开发者可以实现以下功能: - **动态加载资源**:AB支持在运行时从本地或服务器加载资源,避免一次性加载所有资源导致内存占用过高。这尤其适用于大型游戏或需要按需加载的场景[^3]。 - **资源分离与模块化**:AB可以将资源按功能或场景进行划分,实现资源的模块化管理,便于维护和更新。例如,不同关卡的资源可以分别打,按需加载和卸载[^1]。 - **资源压缩与加密**:AB支持资源压缩以减少存储空间占用,同时可以通过加密手段保护资源安全,防止未经授权的访问[^3]。 - **资源管理与更新**:AB可以通过服务器分发资源,实现热更新功能,无需重新发布整个游戏即可更新资源内容[^4]。 ### 内存资源管理在资源管理中的应用 内存资源管理主要涉及资源的加载、使用和卸载,确保游戏在运行过程中内存占用保持在一个合理的范围内。AB与内存资源管理结合,可以实现更高效的资源管理: - **资源加载与卸载**:AB允许在需要时加载资源,并在不再使用时卸载资源,从而释放内存空间。例如,在加载一个AB后,可以通过引用计数机制跟踪资源的使用情况,并在资源不再需要时及时卸载[^1]。 - **资源缓存机制**:为了提高加载效率,AB可以配合资源缓存机制,将已加载的资源缓存起来,避免重复加载。同时,缓存机制可以结合LRU(Least Recently Used)算法,自动清理长时间未使用的资源,释放内存[^2]。 - **资源依赖管理**:AB支持资源依赖关系的管理,确保在加载一个资源时,其依赖的资源也能够正确加载。例如,一个AB可能依赖于其他AB中的资源,通过依赖管理可以避免资源加载失败的问题[^1]。 ### AB与内存资源管理的结合 AB与内存资源管理的结合,能够有效解决资源加载与内存占用的问题。通过AB,可以将资源按需加载到内存中,并在使用完毕后及时卸载,避免内存泄漏。同时,AB的依赖管理机制可以确保资源加载的完整性,而资源缓存机制则可以减少重复加载带来的性能损耗。 例如,在Unity中可以通过以下代码实现AB的加载与卸载: ```csharp // 加载AB AssetBundle mainBundle = AssetBundle.LoadFromFile("path/to/abfile"); // 加载AB中的资源 GameObject asset = mainBundle.LoadAsset<GameObject>("assetName"); // 使用资源 Instantiate(asset); // 卸载AB及其资源 mainBundle.Unload(true); ``` 通过上述代码,可以实现AB的加载、资源使用以及卸载,确保内存资源的合理管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值