一、主资源加载器模块设计实现
作用
主资源加载器是为面向用户而设计的上层应用层类,用户使用主资源加载器作为文件加载器加载文件,
加载器可以通过Assetbundle方式加载,Http方式加载资源。
UML类图设计
前置模块
主资源加载器需要引用到一些基础模块和一些基础的加载器:可写区域管理器(LocalAssetsManager),资源加载器(ResourceLoaderManager),任务管理器(TaskManager),资源池(AssetPool),资源包池(AssetBundlePool),可寻址管理器(AddressableManager)如果还没有实现以上的前置模块,可以点击下方的引用连接,先实现基础部分,再来实现上层部分,就像是造楼房一样,如果没有地基,是不可能直接从2楼开始造楼房的。!!还漏了一些,文章后面就补上!!
前置模块
任务管理器(TaskManager) :https://blog.csdn.net/qq_33531923/article/details/128545543
加载资源(Asset)设计
主资源加载器可以用来加载Assetbundle中的Asset资源, 主资源加载加载Asset时候会使用对象池池技术优化资源,主加载器对资源进行了分类,以便于对资源的管理和分类.
1).会先从对象池中获取该资源是否存在池中,如果存在,认为资源存在,则会直接回调OnComplete回调,完成资源加载;
2).加载时会从打包时候记录的AssetInfo.bytes文件中获取该文件的依赖assetbundle信息,会优先加载依赖的assetbundle/asset,
然后最后加载主资源,
3).引用资源加载完成后,会将引用资源存到AssetBundlePool内,使关闭当前任务执行器关闭;
4).主资源加载完成后,会将主资源保存下来,使关闭当前任务执行器关闭;
5).然后开启任务执行器,行进轮询加载,把所有要加载的资源存放到一个任务组内,进行轮询更新处理;
6).当所有的依赖文件和主文件加载完毕后,会调用用户给定的OnComplete函数,会再次检查一次资源池中是否存在相同资源, 必须要检查,否则可能引起引用计数会出错,如果存在相同资源说明同时有两处在加载相同资源,直接使用池中资源即可;如果不存在,则注册资源数据到资源池中,以便下次取用;
二、主资源加载器(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);
}
}