Design and implementation of the main resource loader module of the U3D client framework (resource management)

1. Design and implementation of the main resource loader module

effect

The main resource loader is an upper application layer class designed for users. Users use the main resource loader as a file loader to load files.
The loader can load resources through Assetbundle or Http.


UML class diagram design

insert image description here

Front module

The main resource loader needs to refer to some basic modules and some basic loaders: writable area manager (LocalAssetsManager), resource loader (ResourceLoaderManager), task manager (TaskManager), resource pool (AssetPool), resource bundle pool ( AssetBundlePool), Resource Loader Manager (ResourceLoaderManager) If you have not implemented the above pre-modules, you can click the reference link below to implement the basic part first, and then the upper part, just like building a building. If there is no foundation, it is not It is possible to build a building directly from the second floor. ! ! There are still some missing, and I will make up later in the article! !

Pre-module
Task Manager (TaskManager): https://blog.csdn.net/qq_33531923/article/details/128545543


Load resource (Asset) design

The main resource loader can be used to load the Asset resources in the Assetbundle. When the main resource loads and loads the Asset, it will use the object pool pool technology to optimize the resources. The main loader classifies the resources to facilitate the management and classification of the resources.

1). It will first obtain whether the resource exists in the pool from the object pool. If it exists, it will consider that the resource exists, and it will directly call back the OnComplete callback to complete the resource loading;
2). When loading, it will record the AssetInfo.bytes file from the package Obtain the dependent assetbundle information of the file in the file, the dependent assetbundle/asset will be loaded first,
and then the main resource will be loaded last.
3). After the reference resource is loaded, the reference resource will be stored in the AssetBundlePool to close the current task executor;
4). After the main resource is loaded, the main resource will be saved to close the current task executor;
5). Then open the task executor, perform polling loading, and store all the resources to be loaded in a task group , to perform polling update processing;
6). When all dependent files and main files are loaded, the OnComplete function given by the user will be called to check again whether the same resource exists in the resource pool. It must be checked, otherwise it may cause The reference count will be wrong. If the same resource exists, it means that there are two places loading the same resource at the same time, and you can directly use the resource in the pool; if it does not exist, register the resource data to the resource pool for next time access;


2. Code Implementation of MainAssetLoaderRoutine

    //主资源加载器
    public class MainAssetLoaderRoutine
    {
    
    
        //当前的资源信息实体
        private AssetEntity m_CurrAssetEntity;

        //当前的资源实体
        private ResourceEntity m_CurrResourceEntity;

        //主资源包
        private AssetBundle m_MainAssetBundle;

        //依赖资源包名字哈希
        private HashSet<string> m_DependsAssetBundleNames = new HashSet<string>();

        //主资源加载完毕(资源加载完毕;完整资源加载完毕)
        private BaseAction<ResourceEntity> m_OnComplete;


        //真正的加载主资源
        public void LoadMainAsset()
        {
    
    
            //1.从分类资源池(AssetPool)中 查找某个资源
            m_CurrResourceEntity = GameEntry.Pool.AssetPool[m_CurrAssetEntity.Category].Spawn(m_CurrAssetEntity.AssetFullName);

            //如果要找的资源,在资源池中
            if (m_CurrResourceEntity != null)
            {
    
    
                m_OnComplete?.Invoke(m_CurrResourceEntity);
                return;
            }

            //2.加载这个资源所依赖的资源包
            List<AssetDependsEntity> dependsAssetList = m_CurrAssetEntity.ListDependsAsset;
            if (dependsAssetList != null)
            {
    
    
                foreach (AssetDependsEntity assetDependsEntity in dependsAssetList)
                {
    
    
                    //根绝资源分类和资源完整名字,去资源加载器里查找这个资源的信息
                    AssetEntity assetEntity = GameEntry.Resource.ResLoaderManager.GetAssetEntity(assetDependsEntity.Category, assetDependsEntity.AssetFullName);

                    //查询的到就把包名记录下来
                    if (assetEntity != null)
                    {
    
    
                        m_DependsAssetBundleNames.Add(assetEntity.AssetBundleName);
                    }
                }
            }

            //3.循环 依赖的资源哈希表,加入任务组
            TaskGroup taskGroup = GameEntry.Task.CreateTaskGroup();
            foreach (string assetBundleName in m_DependsAssetBundleNames)
            {
    
    
                TaskRoutine taskRoutine = GameEntry.Task.CreateTaskRoutine();
                taskRoutine.CurrTask = () =>
                {
    
    
                    //依赖资源,只是加载资源包
                    GameEntry.Resource.ResLoaderManager.LoadAssetBundle(assetBundleName, onComplete: (AssetBundle bundle) =>
                      {
    
    
                          taskRoutine.Leave();
                      });
                };

                taskGroup.AddTask(taskRoutine);
            }

            //4.加载主资源包
            TaskRoutine taskRoutineLoadMainAssetBundle = GameEntry.Task.CreateTaskRoutine();
            taskRoutineLoadMainAssetBundle.CurrTask = () =>
              {
    
    
                  GameEntry.Resource.ResLoaderManager.LoadAssetBundle(m_CurrAssetEntity.AssetBundleName, onComplete: (AssetBundle bundle) =>
                    {
    
    
                        m_MainAssetBundle = bundle;
                        taskRoutineLoadMainAssetBundle.Leave();
                    });
              };

            //把主资源任务 最后一个加载任务组里
            taskGroup.AddTask(taskRoutineLoadMainAssetBundle);

            //依赖的assetbundle和主资源包加载完成后的任务回调
            taskGroup.OnComplete = () =>
              {
    
    
                  if (m_MainAssetBundle == null)
                  {
    
    
                      GameEntry.LogError(" MainAssetBundle not exist:" + m_CurrAssetEntity.AssetFullName);
                  }

                  GameEntry.Resource.ResLoaderManager.LoadAsset(m_CurrAssetEntity.AssetFullName, m_MainAssetBundle, onComplete: (UnityEngine.Object obj) =>
                     {
    
    
                         //再次检查,  很重要,不检查引用计数会出错
                         //再获取一次,如果又又了,可能是其他地方已经加载完注册进去了
                         m_CurrResourceEntity = GameEntry.Pool.AssetPool[m_CurrAssetEntity.Category].Spawn(m_CurrAssetEntity.AssetFullName);
                         if (m_CurrResourceEntity != null)
                         {
    
    
                             m_OnComplete?.Invoke(m_CurrResourceEntity);
                             Reset();

                             //如果这个资源在这之前可能被加载了,被注册了,则不用再new一次了,否则之前的引用计数会丢失,导致引用计数出现问题
                             //返回
                             return;
                         }

                         m_CurrResourceEntity = GameEntry.Pool.DequeueClassObject<ResourceEntity>();
                         m_CurrResourceEntity.Category = m_CurrAssetEntity.Category;
                         m_CurrResourceEntity.IsAssetBundle = false;
                         m_CurrResourceEntity.ResourceName = m_CurrAssetEntity.AssetFullName;
                         m_CurrResourceEntity.Target = obj;

                         GameEntry.Pool.AssetPool[m_CurrAssetEntity.Category].Register(m_CurrResourceEntity);
                         m_OnComplete?.Invoke(m_CurrResourceEntity);
                         Reset();
                     });
              };

            taskGroup.Run(true);
        }

        //加载主资源
        public void LoadAsset(AssetCategory assetCategory, string assetFullName, BaseAction<ResourceEntity> onComplete = null)
        {
    
    
#if DISABLE_ASSETBUNDLE && UNITY_EDITOR
            m_CurrResourceEntity = GameEntry.Pool.DequeueClassObject<ResourceEntity>();
            m_CurrResourceEntity.Category = assetCategory;
            m_CurrResourceEntity.IsAssetBundle = false;
            m_CurrResourceEntity.ResourceName = assetFullName;
            m_CurrResourceEntity.Target = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetFullName);
            onComplete?.Invoke(m_CurrResourceEntity);
#else
            m_OnComplete = onComplete;
            m_CurrAssetEntity = GameEntry.Resource.ResLoaderManager.GetAssetEntity(assetCategory,assetFullName);
            if (m_CurrAssetEntity == null)
            {
    
    
                Debug.LogError("assetFullName no exists "+assetFullName);
                return;
            }
            LoadMainAsset();
#endif
        }

        //重置
        private void Reset()
        {
    
    
            m_OnComplete = null;
            m_CurrAssetEntity = null;
            m_CurrResourceEntity = null;
            m_MainAssetBundle = null;
            m_DependsAssetBundleNames.Clear();
            GameEntry.Pool.EnqueueClassObject(this);
        }
    }

i love to write code

Guess you like

Origin blog.csdn.net/qq_33531923/article/details/128545388