GameFramework资源加载Resource
介绍
这里只是加载热更新的资源,unity 新版本已经提供 Addressable 实现了该功能
所以这里是围绕 AssetBundle 实现了热更资源的添加,打包,下载,使用
跟通常的热更流程并无不同:
- 添加要热更的资源
- 打包 AssetBundle 上传
- 应用按平台下载 AssetBundle
- 加载 AssetBundle 并从中取得所需资源
资源概念
- Asset 1个资源,跟 Unity 的 Asset 对应
- Resource 可以理解为文件,对应 AssetBundle ,包含多个 Asset
- FileSystem 文件系统,可以把多个 AssetBundle 打包成1个文件
- ResourceGroup 资源组,把多个 Resource 分成1组,主要是为了热更新时能分组更新
资源加载流程
- 找到 Asset 所属 Resource
- 找到 Resource 所属 FileSystem,如果有的话
- 使用 LoadResourceAgentHelperBase 加载 FileSystem 并提取出 Resource
- 从 Resource 中加载 Asset
类图
使用
// 比如 DataProvider 中读取数据表部分
DataProvider.ReadData(string dataAssetName, int priority, object userData)
{
// 热更资源打包时就会记录下该资源所属AssetBundle,以及是否多个AssetBundle打包成一个文件等信息
// 所以这里可以用 HasAsset 获得信息,然后再加载
IResourceManager m_ResourceManager;
HasAssetResult result = m_ResourceManager.HasAsset(dataAssetName);
// 不同类型资源要用不同方法加载, 这个类型是你添加打包资源时设置的
// 带 FileSystem 和不带 FileSystem 的用同一个函数加载,因为配置信息中已经包含了这个信息,内部会自动处理
// Asset 就是正常的从 AssetBundle 中加载 Asset,会检测依赖关系并先加载依赖项
// Binary 就是常说的 raw 资源,比如 视频 音频 图片等,不会打包成 AssetBundle,1个resource只能包含1个asset
// 内部其实是用 asset 找到 resource ,然后加载 resource 对应的文件当成 asset 返回
switch (result)
{
case HasAssetResult.AssetOnDisk:
case HasAssetResult.AssetOnFileSystem:
m_ResourceManager.LoadAsset(dataAssetName, priority, m_LoadAssetCallbacks, userData);
break;
case HasAssetResult.BinaryOnDisk:
m_ResourceManager.LoadBinary(dataAssetName, m_LoadBinaryCallbacks, userData);
break;
case HasAssetResult.BinaryOnFileSystem:
int dataLength = m_ResourceManager.GetBinaryLength(dataAssetName);
m_ResourceManager.LoadBinaryFromFileSystem(dataAssetName, s_CachedBytes);
m_DataProviderHelper.ReadData(m_Owner, dataAssetName, s_CachedBytes, 0, dataLength, userData);
break;
}
}
资源加载代码
// 判断 Asset 所属 Resource ,以及依赖的 Resource,对标 Asset 所属 AssetBundle ,以及依赖的 AssetBundle
ResourceManager.LoadAsset(string assetName, LoadAssetCallbacks loadAssetCallbacks)
{
ResourceLoader.LoadAsset(string assetName, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData)
{
ResourceInfo resourceInfo = null;
string[] dependencyAssetNames = null;
// 获得 Asset 所属的 Resource 和 依赖的 AssetList
CheckAsset(assetName, out resourceInfo, out dependencyAssetNames);
// 建立加载任务
LoadAssetTask mainTask = LoadAssetTask.Create(assetName, assetType, priority, resourceInfo, dependencyAssetNames, loadAssetCallbacks, userData);
m_TaskPool.AddTask(mainTask)
{
TaskTool.Update()
{
TaskTool.ProcessWaitingTasks()
{
ITaskAgent<T> agent = m_FreeAgents.Pop();
StartTaskStatus status = agent.Start(task) // LoadResourceAgent.Start(LoadAssetTask task)
{
//DefaultLoadResourceAgentHelper
ResourceInfo resourceInfo = m_Task.ResourceInfo;
string fullPath = Utility.Path.GetRegularPath(Path.Combine(resourceInfo.StorageInReadOnly ? m_ReadOnlyPath : m_ReadWritePath, resourceInfo.UseFileSystem ? resourceInfo.FileSystemName : resourceInfo.ResourceName.FullName));
if (resourceInfo.LoadType == LoadType.LoadFromFile)
{
if (resourceInfo.UseFileSystem)
{
IFileSystem fileSystem = m_ResourceLoader.m_ResourceManager.GetFileSystem(resourceInfo.FileSystemName, resourceInfo.StorageInReadOnly);
m_Helper.ReadFile(fileSystem, resourceInfo.ResourceName.FullName) // DefaultLoadResourceAgentHelper.ReadFile(fullPath)
{
-------------------------------> 接下面
}
}
else
{
m_Helper.ReadFile(fullPath) // DefaultLoadResourceAgentHelper.ReadFile(fullPath)
{
-------------------------------> 接下面
}
}
}
}
}
}
}
}
}
// 加载 Resource ,对标 AssetBundle
DefaultLoadResourceAgentHelper.ReadFile(fullPath)
{
// 加载 AssetBundle
m_FileAssetBundleCreateRequest = AssetBundle.LoadFromFileAsync(fullPath)
{
DefaultLoadResourceAgentHelper.Update()
{
DefaultLoadResourceAgentHelper.UpdateFileAssetBundleCreateRequest()
{
if (m_FileAssetBundleCreateRequest.isDone)
{
// AssetBundle 加载完成,发出通知
m_LoadResourceAgentHelperReadFileCompleteEventHandler(this, loadResourceAgentHelperLoadCompleteEventArgs)
{
LoadResourceAgent.OnLoadResourceAgentHelperReadFileComplete()
{
// e.Resource 就是 AssetBundle
ResourceObject resourceObject = ResourceObject.Create(m_Task.ResourceInfo.ResourceName.Name, e.Resource, m_ResourceHelper, m_ResourceLoader);
m_ResourceLoader.m_ResourcePool.Register(resourceObject, true);
OnResourceObjectReady(resourceObject)
{
// 从 AssetBundle 中加载 Asset
m_Task.LoadMain(this, resourceObject) // LoadResourceTaskBase.LoadMain(LoadResourceAgent agent, ResourceObject resourceObject)
{
-------------------------------> 接下面
}
}
}
}
}
}
}
}
}
// 从 Resource 中加载 Asset ,对标从 AssetBundle 中加载 Asset
LoadResourceTaskBase.LoadMain(LoadResourceAgent agent, ResourceObject resourceObject)
{
m_ResourceObject = resourceObject;
// resourceObject.Target 就是 AssetBundle,这里用 AssetBundle 加载 Asset
agent.Helper.LoadAsset(resourceObject.Target, AssetName, AssetType, IsScene) // DefaultLoadResourceAgentHelper.LoadAsset(object resource, string assetName, Type assetType, bool isScene)
{
m_AssetBundleRequest = assetBundle.LoadAssetAsync(assetName)
{
DefaultLoadResourceAgentHelper.UpdateAssetBundleRequest()
{
if (m_AssetBundleRequest.isDone)
{
m_LoadResourceAgentHelperLoadCompleteEventHandler(this, loadResourceAgentHelperLoadCompleteEventArgs)
{
LoadResourceAgent.OnLoadResourceAgentHelperLoadComplete(object sender, LoadResourceAgentHelperLoadCompleteEventArgs e)
{
// 到这里 Asset 加载成功
// e.Asset 就是 Asset, AssetObject 代表 Asset
AssetObject assetObject = AssetObject.Create(m_Task.AssetName, e.Asset, dependencyAssets, m_Task.ResourceObject.Target, m_ResourceHelper, m_ResourceLoader);
m_ResourceLoader.m_AssetPool.Register(assetObject, true);
// 通知加载完成
OnAssetObjectReady(assetObject)
{
m_Task.OnLoadAssetSuccess(this, asset, (float)(DateTime.UtcNow - m_Task.StartTime).TotalSeconds) // LoadAssetTask.OnLoadAssetSuccess()
{
// 资源加载完成,发出完成通知
m_LoadAssetCallbacks.LoadAssetSuccessCallback(AssetName, asset, duration, UserData);
}
}
}
}
}
}
}
}
}