U3D客户端框架(资源管理篇)之资源加载管理器

该文章描述了一个资源加载管理器模块的设计,主要负责AssetBundle和Asset资源的加载、缓存以及卸载。模块通过UML静态图展示结构,并提供了C#代码实现,包括资源信息的初始化、资源包和资源的加载方法。此外,还包括了资源的释放功能和更新管理。
摘要由CSDN通过智能技术生成

一、资源加载管理器模块设计

模块设计

资源加载管理器模块的主要职责就是资源加载的管理,从物理结构上对该模块进行了拆分成了一个单独的文件,资源加载管理器是加载器中偏底层的一个部分;资源加载管理器负责AssetBundle的加载、Asset资源的加载、资源加载完成后缓存进对预制池、卸载功能;

UML静态图

 

二、代码实现

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouYou;
using UnityEngine;

namespace Myh
{
    //资源加载管理
    public class ResourceLoaderManager : ManagerBase, IDisposable
    {
        //资源加载管理器
        //资源分类:{资源名:资源实体}
        private Dictionary<AssetCategory, Dictionary<string, AssetEntity>> m_dicAssetInfo;

        //资源包加载器链表
        private LinkedList<AssetBundleLoaderRoutine> m_ListAssetBundleLoader;

        //资源加载链表
        private LinkedList<AssetLoaderRoutine> m_ListAssetLoader;

        //加载中的资源
        private Dictionary<string, LinkedList<Action<UnityEngine.Object>>> m_dicLoadingAsset = new Dictionary<string, LinkedList<Action<UnityEngine.Object>>>();
        
        //初始化资源信息完毕
        private BaseAction m_InitAssetInfoComplete;

        private Dictionary<string, LinkedList<Action<AssetBundle>>> m_dicLoadingAssetBundle = new Dictionary<string, LinkedList<Action<AssetBundle>>>();

        public ResourceLoaderManager()
        {
            m_dicAssetInfo = new Dictionary<AssetCategory, Dictionary<string, AssetEntity>>();
            m_ListAssetBundleLoader = new LinkedList<AssetBundleLoaderRoutine>();
            m_ListAssetLoader = new LinkedList<AssetLoaderRoutine>();
        }

        //初始化
        public override void Init()
        {
            //确保游戏刚开始运行的时候,分类字典已经初始化好了
            IEnumerator iter = Enum.GetValues(typeof(AssetCategory)).GetEnumerator();
            while (iter.MoveNext())
            {
                AssetCategory assetCategory = (AssetCategory)iter.Current;
                m_dicAssetInfo[assetCategory] = new Dictionary<string, AssetEntity>();
            }
        }

        #region 初始化资源信息
        //初始化资源信息(这个才是真正的初始化函数)
        private void InitAssetInfo(byte[] buffer)
        {
            buffer = ZlibHelper.DeCompressBytes(buffer);

            MMO_MemoryStream ms = new MMO_MemoryStream(buffer);
            int len = ms.ReadInt();
            int depLen = 0;

            for (int i = 0; i < len; ++i)
            {
                AssetEntity entity = new AssetEntity();
                entity.Category = (AssetCategory)ms.ReadByte();
                entity.AssetFullName = ms.ReadUTF8String();
                entity.AssetBundleName = ms.ReadUTF8String();
                depLen = ms.ReadInt();

                //有依赖的资源包
                if (depLen > 0)
                {
                    entity.ListDependsAsset = new List<AssetDependsEntity>();

                    //对依赖的资源包进行循环
                    for (int j = 0; j < depLen; ++j)
                    {
                        AssetDependsEntity assetDependsEntity = new AssetDependsEntity();
                        assetDependsEntity.Category = (AssetCategory)ms.ReadByte();
                        assetDependsEntity.AssetFullName = ms.ReadUTF8String();
                        entity.ListDependsAsset.Add(assetDependsEntity);
                    }
                }
                m_dicAssetInfo[entity.Category][entity.AssetFullName] = entity;
            }

            m_InitAssetInfoComplete?.Invoke();
        }

        //从CDN加载资源信息
        private void OnLoadAssetInfoFromCDN(HttpCallBackArgs args)
        {
            if (!args.HasError)
            {
                InitAssetInfo(args.Data);
            }
            else
            {
                GameEntry.Log(LogCategory.Resource, args.Value);
            }
        }

        #region 初始化资源信息

        //初始化资源信息
        public void InitAssetInfo(BaseAction initAssetInfoComplete)
        {
            m_InitAssetInfoComplete = initAssetInfoComplete;

            //资源信息?
            byte[] buffer = GameEntry.Resource.ResManager.LocalAssetsManager.GetFileBuffer(ConstDefine.AssetInfoName);

            if (buffer == null)
            {
                //如果只读区没有,从CDN读
                string url = string.Format("{0}{1}", GameEntry.Data.SysDataManager.CurrChannelConfig.RealSourceUrl, ConstDefine.AssetInfoName);
                GameEntry.Http.SendData(url, OnLoadAssetInfoFromCDN, isGetData: true);
            }
            else
            {
                InitAssetInfo(buffer);
            }
        }

        //根据资源分类和资源路径 获取资源信息
        public AssetEntity GetAssetEntity(AssetCategory assetCategory, string assetFullName)
        {
            Dictionary<string, AssetEntity> dicCategory = null;
            if (m_dicAssetInfo.TryGetValue(assetCategory, out dicCategory))
            {
                AssetEntity entity = null;
                if (dicCategory.TryGetValue(assetFullName, out entity))
                {
                    return entity;
                }
            }
            return null;
        }
        #endregion

        #endregion

        #region 加载资源包 LoadAssetBundle
        //加载中的资源包
        //{资源包名:加载资源包完成的回调list}

        /// <summary>
        /// 加载资源包
        /// </summary>
        /// <param name="assetBundlePath">ab包路径</param>
        /// <param name="onUpdate">加载中回调</param>
        /// <param name="onComplete">加载完成回调</param>
        public void LoadAssetBundle(string assetBundlePath, Action<float> onUpdate = null, Action<AssetBundle> onComplete = null)
        {
            //1.判断资源包是否存在于AssetBundlePool中
            ResourceEntity resourceEntity = GameEntry.Pool.AssetBundlePool.Spawn(assetBundlePath);

            //资源在资源包池中 存在
            if (resourceEntity != null)
            {
                AssetBundle assetBundle = resourceEntity.Target as AssetBundle;
                onComplete?.Invoke(assetBundle);
                return;
            }

            //资源在资源包池中 不存在
            LinkedList<Action<AssetBundle>> lst = null;
            if (m_dicLoadingAssetBundle.TryGetValue(assetBundlePath, out lst))
            {
                //如果存在加载中的assetbundle,把加载完成的回调函数 加入这个资源加载的链表,然后返回
                lst.AddLast(onComplete);
                return;
            }
            else
            {
                lst = GameEntry.Pool.DequeueClassObject<LinkedList<Action<AssetBundle>>>();
                lst.AddLast(onComplete);
                m_dicLoadingAssetBundle[assetBundlePath] = lst;
            }

            //资源包加载routine
            AssetBundleLoaderRoutine routine = GameEntry.Pool.DequeueClassObject<AssetBundleLoaderRoutine>();
            //这句话可以不加,Dequeue的时候如果没有一定会new一个
            if (null == routine)
            {
                routine = new AssetBundleLoaderRoutine();
            }
            
            //加入链表还是循环加载
            m_ListAssetBundleLoader.AddLast(routine);

            //开始加载assetBundle
            routine.LoadAssetBundle(assetBundlePath);

            //注册更新回调
            routine.OnAssetBundleCreateUpdate = (float progress) =>
             {
                 onUpdate?.Invoke(progress);
             };

            routine.OnLoadAssetBundleComplete = (AssetBundle assetBundle) =>
              {
                  //把资源包注册到资源池
                  resourceEntity = GameEntry.Pool.DequeueClassObject<ResourceEntity>();
                  resourceEntity.ResourceName = assetBundlePath;
                  resourceEntity.IsAssetBundle = true;
                  resourceEntity.Target = assetBundle;
                  GameEntry.Pool.AssetBundlePool.Register(resourceEntity);

                  //加载完成后,执行所有回调函数
                  for (LinkedListNode<Action<AssetBundle>> iter = lst.First; iter != null; iter = iter.Next)
                  {
                      iter.Value?.Invoke(assetBundle);
                  }

                  //一定要清空
                  lst.Clear();

                  GameEntry.Pool.EnqueueClassObject(lst);

                  //下载完成后,从下载中字段中清除这个字典
                  m_dicLoadingAssetBundle.Remove(assetBundlePath);

                  //结束循环 routine回池
                  m_ListAssetBundleLoader.Remove(routine);
                  GameEntry.Pool.EnqueueClassObject(routine);
              };
        }

        #endregion

        #region 从资源包中加载资源 LoadAsset
        /// <param name="assetName">资源名</param>
        /// <param name="assetBundle">assetBundle包</param>
        /// <param name="onUpdate">加载中回调</param>
        /// <param name="onComplete">加载完成回调</param>
        public void LoadAsset(string assetName, AssetBundle assetBundle, Action<float> onUpdate = null, Action<UnityEngine.Object> onComplete = null)
        {
            LinkedList<Action<UnityEngine.Object>> lst = null;

            //这个资源在加载中
            if (m_dicLoadingAsset.TryGetValue(assetName, out lst))
            {
                //把加载完成回调函数(委托)加入到对应的链表中,然后直接返回
                lst.AddLast(onComplete);
                return;
            }
            else
            {
                //如果不在加载中
                //从类对象池取一个对象,把链表保存到字典中
                lst = GameEntry.Pool.DequeueClassObject<LinkedList<Action<UnityEngine.Object>>>();
                lst.AddLast(onComplete);
                m_dicLoadingAsset[assetName] = lst;
            }

            AssetLoaderRoutine routine = GameEntry.Pool.DequeueClassObject<AssetLoaderRoutine>();
            if (routine == null)
                routine = new AssetLoaderRoutine();

            m_ListAssetLoader.AddLast(routine);

            //开始加载资源
            routine.LoadAsset(assetName, assetBundle);

            //下载中更新回调
            routine.OnAssetUpdate = (float progress) =>
            {
                onUpdate?.Invoke(progress);
            };

            //下载完成回调
            routine.OnLoadAssetComplete = (UnityEngine.Object obj) =>
              {
                  for (LinkedListNode<Action<UnityEngine.Object>> iter = lst.First; iter != null; iter = iter.Next)
                  {
                      iter.Value?.Invoke(obj);
                  }

                  //一定要清空
                  lst.Clear();

                  //链表回池
                  GameEntry.Pool.EnqueueClassObject(lst);

                  //资源对应的链表从字典删除
                  m_dicLoadingAsset.Remove(assetName);

                  //结束循环
                  m_ListAssetLoader.Remove(routine);

                  //资源加载器回池
                  GameEntry.Pool.EnqueueClassObject(routine);
              };

        }
        #endregion


        /// <summary>
        /// 加载主资源
        /// </summary>
        /// <param name="assetCategory">资源分类</param>
        /// <param name="assetFullName">资源完整名称</param>
        /// <param name="onComplete">加载完成回调</param>
        public void LoadMainAsset(AssetCategory assetCategory, string assetFullName, BaseAction<ResourceEntity> onComplete = null)
        {
            MainAssetLoaderRoutine routine = GameEntry.Pool.DequeueClassObject<MainAssetLoaderRoutine>();

            //加载
            routine.LoadAsset(assetCategory, assetFullName,(ResourceEntity resEntity)=> 
            {
                onComplete?.Invoke(resEntity);
            });
        }

        //释放资源
        public void UnLoadGameObject(GameObject go)
        {
            GameEntry.Pool.ReleaseInstanceResource(go.GetInstanceID());
        }

        // 更新所有 AssetBundleLoaderRoutine
        // 更新所有 AssetLoaderRoutine
        public void OnUpdate()
        {
            for (LinkedListNode<AssetBundleLoaderRoutine> iter = m_ListAssetBundleLoader.First; iter!=null; iter= iter.Next)
            {
                iter.Value.OnUpdate();
            }

            for (LinkedListNode<AssetLoaderRoutine> iter = m_ListAssetLoader.First; iter != null; iter = iter.Next)
            {
                iter.Value.OnUpdate();
            }
        }

        public void Dispose()
        {
            m_dicAssetInfo.Clear();
            m_ListAssetBundleLoader.Clear();
            m_ListAssetLoader.Clear();
        }


    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值