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

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

模块设计

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

UML静态图

U3D客户端框架(资源管理篇)之资源加载管理器_第1张图片

 

二、代码实现

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> m_dicAssetInfo;

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

        //资源加载链表
        private LinkedList m_ListAssetLoader;

        //加载中的资源
        private Dictionary>> m_dicLoadingAsset = new Dictionary>>();
        
        //初始化资源信息完毕
        private BaseAction m_InitAssetInfoComplete;

        private Dictionary>> m_dicLoadingAssetBundle = new Dictionary>>();

        public ResourceLoaderManager()
        {
            m_dicAssetInfo = new Dictionary>();
            m_ListAssetBundleLoader = new LinkedList();
            m_ListAssetLoader = new LinkedList();
        }

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

        #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();

                    //对依赖的资源包进行循环
                    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 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}

        /// 
        /// 加载资源包
        /// 
        /// ab包路径
        /// 加载中回调
        /// 加载完成回调
        public void LoadAssetBundle(string assetBundlePath, Action onUpdate = null, Action 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> lst = null;
            if (m_dicLoadingAssetBundle.TryGetValue(assetBundlePath, out lst))
            {
                //如果存在加载中的assetbundle,把加载完成的回调函数 加入这个资源加载的链表,然后返回
                lst.AddLast(onComplete);
                return;
            }
            else
            {
                lst = GameEntry.Pool.DequeueClassObject>>();
                lst.AddLast(onComplete);
                m_dicLoadingAssetBundle[assetBundlePath] = lst;
            }

            //资源包加载routine
            AssetBundleLoaderRoutine routine = GameEntry.Pool.DequeueClassObject();
            //这句话可以不加,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.ResourceName = assetBundlePath;
                  resourceEntity.IsAssetBundle = true;
                  resourceEntity.Target = assetBundle;
                  GameEntry.Pool.AssetBundlePool.Register(resourceEntity);

                  //加载完成后,执行所有回调函数
                  for (LinkedListNode> 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
        /// 资源名
        /// assetBundle包
        /// 加载中回调
        /// 加载完成回调
        public void LoadAsset(string assetName, AssetBundle assetBundle, Action onUpdate = null, Action onComplete = null)
        {
            LinkedList> lst = null;

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

            AssetLoaderRoutine routine = GameEntry.Pool.DequeueClassObject();
            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> 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


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

            //加载
            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 iter = m_ListAssetBundleLoader.First; iter!=null; iter= iter.Next)
            {
                iter.Value.OnUpdate();
            }

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

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


    }
}

你可能感兴趣的:(unity,游戏引擎,3d,自动化)