<>GF资源加载流程图
<>GF加载资源简介
ResourceManager持有某个功能例如ResourceLoader(资源加载),ResourceLoader持有TaskPool,TaskPool持有agent,agent持有helper,真正执行操作的方法是存在helper中。
<>ResourceManager:IResourceManager
缓存资源组和已经加载过的资源。可以获取资源的版本编号和相关的路径地址,还有一些版本更新和资源加载的函数等等。
<>如何从asset名索引到对应bundle名
下面信息从描述文件获得,把GameFrameworkVersion.dat 文件中的 bundle ,asset
,asset的依赖项的关系,从文件中int[]变为string[],保存起来
调用过程
GameFramework.Resource.ResourceManager.ResourceChecker.CheckResources
主要解决问题,只需要记住工程中asset从assets开始的全路径就可以加载资源,而不需要记忆resource名字
private Dictionary<string, AssetInfo> m_AssetInfos; //用于描述 Unity
中的一个具体资产,如一个预制体、一个材质、一张图片等。 private Dictionary<ResourceName, ResourceInfo>
m_ResourceInfos; //用于描述 Unity 中的一个 AssetBundle(一些 Asset 的集合),或者一个 Game
Framework 定义下的二进制文件(策划表) private readonly Dictionary<string, ResourceGroup>
m_ResourceGroups;
//资源组用于将资源分类,是资源的一种特性标签,一个资源可以归属于多个资源组。通过资源组可以游戏中构造出类似于“插件”的更新机制,如高清材质包、游戏语音包等。
<>ResourceLoader
<>Asset,Bundle被依赖引用次数
private readonly Dictionary<object, int> m_AssetDependencyCount;
//asset引用计数,key即资源对象,例如texture等类型资源 private readonly Dictionary<object, int>
m_ResourceDependencyCount; //resource引用计数,object即assetbundle
每次加载asset,对应所有依赖项asset+1,依赖的bundle根据自己内部的asset被依赖次数 +1
每次卸载asset,为0,说没被别人依赖,可以卸载,把对应依赖asset-1,依赖bundle-1
引用计数为0的Asset,即可被释放,Resources.UnloadAsset(object)
引用计数为0的AssetBundle,即可被释放,AssetBundle.Unload(true)
使用任务池机制m_TaskPool
LoadAsset
LoadBinary
LoadScene
每来个加载任务LoadResourceTaskBase,放入到m_TaskPool中,任务可以无限加,s_Serial会自增。但是代理,即执行任务的数量为有限个数
增加代理,代理总数为UnityGameFramework.Runtime.ResourceComponent.m_LoadResourceAgentHelperCount
控制
GameFramework.Resource.ResourceManager.ResourceLoader.AddLoadResourceAgentHelper
最终代理为LoadResourceAgent
private IObjectPool<AssetObject> m_AssetPool; //缓存的asset对象池 private IObjectPool
<ResourceObject> m_ResourcePool;//缓存的resource对象池
<>加载依赖项
GameFramework.Resource.ResourceManager.ResourceLoader.LoadAsset
foreach (string dependencyAssetName in dependencyAssetNames) { if (!
LoadDependencyAsset(dependencyAssetName, priority, mainTask, userData))
GameFramework.Resource.ResourceManager.ResourceLoader.LoadDependencyAsset
private bool LoadDependencyAsset(string assetName, int priority,
LoadResourceTaskBase mainTask, object userData) { ResourceInfo resourceInfo =
null; string[] dependencyAssetNames = null; if (!CheckAsset(assetName, out
resourceInfo, out dependencyAssetNames)) { return false; }
LoadDependencyAssetTask dependencyTask = LoadDependencyAssetTask.Create(
assetName, priority, resourceInfo, dependencyAssetNames, mainTask, userData);
foreach (string dependencyAssetName in dependencyAssetNames) { if (!
LoadDependencyAsset(dependencyAssetName, priority, dependencyTask, userData))
//递归调用,加载依赖项,如果有循环依赖如何避免,a引用b,b引用a { return false; } } m_TaskPool.AddTask(
dependencyTask); if (!resourceInfo.Ready) { m_ResourceManager.UpdateResource(
resourceInfo.ResourceName); } return true; }
<>LoadResourceTaskBase:加载资源任务基类
创建任务并不是会立即执行任务
每增加任务,s_Serial会自增
<>LoadAssetTask
加载目标asset任务
<>LoadDependencyAssetTask
加载依赖任务
<>TaskPool
Update中驱动ProcessWaitingTasks
LoadResourceAgent代理Start
//等待处理的任务,把所有任务,从free中分配working 代理 private void ProcessWaitingTasks(float
elapseSeconds, float realElapseSeconds) { LinkedListNode<T> current =
m_WaitingTasks.First; //等待任务第一个 //如果当前有空闲代理 while (current != null &&
FreeAgentCount> 0) //当前还有free代理,且会一直循环到wait末尾 { ITaskAgent<T> agent =
m_FreeAgents.Pop(); LinkedListNode<ITaskAgent<T>> agentNode = m_WorkingAgents.
AddLast(agent);//加入到工作代理 T task = current.Value; LinkedListNode<T> next =
current.Next; StartTaskStatus status = agent.Start(task);//通过代理控制当前任务执行
//以下都对出错才处理 if (status == StartTaskStatus.Done || status == StartTaskStatus.
HasToWait|| status == StartTaskStatus.UnknownError) { //当前任务必须等待,归还到空闲代理,工作代理中移除
agent.Reset(); m_FreeAgents.Push(agent); m_WorkingAgents.Remove(agentNode); }
if (status == StartTaskStatus.Done || status == StartTaskStatus.CanResume ||
status== StartTaskStatus.UnknownError) { //可以继续处理 m_WaitingTasks.Remove(current)
; } if (status == StartTaskStatus.Done || status == StartTaskStatus.UnknownError
) { ReferencePool.Release(task); } current = next; } }
<>LoadResourceAgent
GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.Start
Start执行,任务状态
* 可以从m_AssetPool中实例出asset,任务做完了Done
* m_ResourcePool中可以实例出resource,任务可以接着做CanResume
* 调用辅助器执行加载
在taskPool.update中把空闲代理分配给到工作代理中,Start执行
//任务不是场景,说明要实例化 if (!m_Task.IsScene) { //从对象池里拿一个,已经可以从ab里实例出来asset,任务做完了
AssetObject assetObject = m_ResourceLoader.m_AssetPool.Spawn(m_Task.AssetName);
if (assetObject != null) { OnAssetObjectReady(assetObject); return
StartTaskStatus.Done; } } //遍历依赖 foreach (string dependencyAssetName in m_Task.
GetDependencyAssetNames()) { //如果依赖项目不能实例一个,接着等待 if (!m_ResourceLoader.
m_AssetPool.CanSpawn(dependencyAssetName)) { m_Task.StartTime = default(DateTime
); return StartTaskStatus.HasToWait; } } //从resource对象池中取出,说明任务可以接着执行
ResourceObject resourceObject = m_ResourceLoader.m_ResourcePool.Spawn(
resourceName); if (resourceObject != null) { OnResourceObjectReady(
resourceObject); return StartTaskStatus.CanResume; } //以上都不满足 m_Helper.ReadFile(
fullPath);
m_Helper.ReadFile(fullPath);这里最终调用DefaultLoadResourceAgentHelper.ReadFile
m_FileAssetBundleCreateRequest = AssetBundle.LoadFromFileAsync(fullPath);
<>DefaultLoadResourceAgentHelper
会创建GameObject在场景中
加载代理辅助器,会在场景中创建
ILoadResourceAgentHelper是加载资源代理辅助器接口,加载资源会伴随六大事件(异步加载资源更新事件,异步读取资源文件完成事件,异步读取资源二进制流完成事件,异步将资源二进制流转换为加载对象完成事件,异步加载资源完成事件,错误事件)。资源的文件和二进制异步加载还有重置辅助器的函数。是实际加载资源的接口,加载完成以后会缓存到ResourceManager下ResourceLoader的IObjectPool。
<>加载bundle
* Update中轮询UpdateFileAssetBundleCreateRequest()里AssetBundleCreateRequest
请求完成后,发送通知,
*
最终调用到GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.OnLoadResourceAgentHelperReadFileComplete
*
创建出ResourceObject,注册到ResourceLoader.m_ResourcePool。ResourceObject主要是增加上次使用时间戳,和一些信息Resource信息
* 并且从正在加载Resource表中清除,s_LoadingResourceNames
* Resource准备好了,加载 m_Task.LoadMain(this, resourceObject);,即加载目标asset
<>加载asset
UnityGameFramework.Runtime.DefaultLoadResourceAgentHelper.LoadAsset
* 如果是场景,开启异步加载:m_AsyncOperation = SceneManager.LoadSceneAsync(sceneName,
LoadSceneMode.Additive);
* 如果是资源,m_AssetBundleRequest = assetBundle.LoadAssetAsync(assetName);
在Update轮询
m_AssetBundleRequest.isDone,抛出事件LoadResourceAgentHelperLoadComplete
加载完成结果
GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.OnLoadResourceAgentHelperLoadComplete
* 创建AssetObject,自己+1,依赖的所有asset+1
* 加入到m_ResourceLoader.m_AssetPool.Register(assetObject, true);
* s_LoadingAssetNames.Remove(m_Task.AssetName);中移除正在加载资源
* 所有被依赖的Resource引用+1
引用计数
每次加载asset,对应所有依赖项asset+1,依赖的bundle根据自己内部的asset被依赖 +1
每次卸载asset,为0,说没被别人依赖,可以卸载,把对应依赖asset-1,依赖bundle-1
引用计数为0的Asset,即可被释放,Resources.UnloadAsset(object)
引用计数为0的AssetBundle,即可被释放,AssetBundle.Unload(true)
<>加载Log流程
* 创建加载asset主任务a:Assets/GameMain/UI/UIForms/DialogForm.prefab
GameFramework.Resource.ResourceManager/ResourceLoader/LoadAssetTask:Create
初始化加载任务Assets/GameMain/UI/UIForms/DialogForm.prefab–>[“Assets/GameMain/UI/UISprites/Common/dialog-title-background.png”,“Assets/GameMain/UI/UISprites/Common/button-outline.png”,“Assets/GameMain/UI/UISprites/Common/dialog-background.png”,“Assets/GameMain/UI/UISprites/Common/background.png”]
这里依赖4张PNG
这里要加载个perfab,对应依赖的资源也找出来
函数调用
GameFramework.Resource.ResourceManager.ResourceLoader.LoadAsset
* 创建依赖资源任务1:Assets/GameMain/UI/UISprites/Common/dialog-title-background.png
*
增加依赖资源任务1到等待列表中GameFramework.Resource.ResourceManager+ResourceLoader+LoadDependencyAssetTask到m_WaitingTasks.count
= 1
* 创建依赖资源任务2:Assets/GameMain/UI/UISprites/Common/button-outline.png
*
增加依赖资源任务2到等待列表中GameFramework.Resource.ResourceManager+ResourceLoader+LoadDependencyAssetTask到m_WaitingTasks.count
= 2
* 依次增加总共4个依赖任务 GameFramework.Resource.ResourceManager.ResourceLoader.
LoadDependencyAssetprivate bool LoadDependencyAsset(string assetName, int
priority, LoadResourceTaskBase mainTask, object userData) { ResourceInfo
resourceInfo= null; string[] dependencyAssetNames = null; if (!CheckAsset(
assetName, out resourceInfo, out dependencyAssetNames)) { return false; } if (
resourceInfo.IsLoadFromBinary) { return false; } LoadDependencyAssetTask
dependencyTask= LoadDependencyAssetTask.Create(assetName, priority, resourceInfo
, dependencyAssetNames, mainTask, userData); foreach (string dependencyAssetName
in dependencyAssetNames) { if (!LoadDependencyAsset(dependencyAssetName,
priority, dependencyTask, userData))
//递归调用,加载依赖项,如果有循环依赖如何避免,a引用b,b引用a,因为加载中,asset,resource都有缓存,不会重复创建任务 { return
false; } } m_TaskPool.AddTask(dependencyTask);
* 增加主任务到等待中,这样能确保是前面的依赖任务执行完毕,再会执行主任务a
增加任务:GameFramework.Resource.ResourceManager+ResourceLoader+LoadAssetTask到m_WaitingTasks.count
= 5
*
从等待任务池里取一个开始,代理执行GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.Start
虽然任务是读取asset,实际上还是先读ab
异步读取资源文件C:/Users/luoyikun_l/AppData/LocalLow/Game Framework/Star
Force/UI/UISprites/Common.dat
*
UnityGameFramework.Runtime.DefaultLoadResourceAgentHelper.UpdateFileAssetBundleCreateRequest
代理中轮询出来,ab加载完毕
* resource加载完成UI/UISprites/Common
* AB加载资源代理辅助器ui/uisprites/common
(UnityEngine.AssetBundle)里面Assets/GameMain/UI/UISprites/Common/dialog-title-background.png
task并未释放,还是在加载asset
* 其他task 在 TaskPool.update 中轮询时,知道上一个任务共同加载的bundle已加载好
//从resource对象池中取出,说明任务可以接着执行 ResourceObject resourceObject = m_ResourceLoader.
m_ResourcePool.Spawn(resourceName); if (resourceObject != null) {
OnResourceObjectReady(resourceObject); return StartTaskStatus.CanResume; }
task直接进入 加载asset阶段
//从resource里加载目标asset public void LoadMain(LoadResourceAgent agent,
ResourceObject resourceObject) { m_ResourceObject = resourceObject; agent.Helper
.LoadAsset(resourceObject.Target, AssetName, AssetType, IsScene); }
*
UnityGameFramework.Runtime.DefaultLoadResourceAgentHelper.UpdateAssetBundleRequest
轮询中asset加载完毕
asset加载完成Assets/GameMain/UI/UISprites/Common/dialog-background.png
加载完毕的asset都会放入缓冲池中
asset–>Assets/GameMain/UI/UISprites/Common/button-outline.png加载完成,并且创建assetObject到m_AssetPool缓冲池中
GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.
OnLoadResourceAgentHelperLoadComplete m_ResourceLoader.m_AssetPool.Register(
assetObject, true);
* 因为目标asset会一直等待所有依赖的asset加载完毕,才开始自身bundle加载,然后asset加载 GameFramework.Resource.
ResourceManager.ResourceLoader.LoadResourceAgent.Start //遍历依赖 foreach (string
dependencyAssetNamein m_Task.GetDependencyAssetNames()) {
//如果依赖asset不能spawn,接着等待 if (!m_ResourceLoader.m_AssetPool.CanSpawn(
dependencyAssetName)) { GameFrameworkLog.Info("{0}依赖项{1}未加载完成", m_Task.AssetName
,dependencyAssetName); m_Task.StartTime = default(DateTime); return
StartTaskStatus.HasToWait; } }
现在所有依赖asset全部加载完毕,加载目标asset。可能存在依赖项目的asset还会再次依赖别的asset,会导致依赖项的asset再次进入等待状态
<>资源卸载时
GameFramework.Resource.ResourceManager.ResourceLoader.AssetObject.OnUnspawn
把所有依赖asset都执行遍
protected internal override void OnUnspawn() { base.OnUnspawn(); //卸载时把相应的资源也处理
foreach (object dependencyAsset in m_DependencyAssets) { m_ResourceLoader.
m_AssetPool.Unspawn(dependencyAsset); } }
<>AssetObject : ObjectBase
protected internal override void Release(bool isShutdown) { if (!isShutdown) {
int targetReferenceCount = 0; if (m_ResourceLoader.m_ResourceDependencyCount.
TryGetValue(Target, out targetReferenceCount) && targetReferenceCount > 0) {
throw new GameFrameworkException(Utility.Text.Format("Resource target '{0}'
reference count is '{1}' larger than 0.", Name, targetReferenceCount)); }
foreach (object dependencyResource in m_DependencyResources) { int
referenceCount= 0; if (m_ResourceLoader.m_ResourceDependencyCount.TryGetValue(
dependencyResource, out referenceCount)) { m_ResourceLoader.
m_ResourceDependencyCount[dependencyResource] = referenceCount - 1; //只会-1,不会对为
0 的Assetbundle进行卸载 } else { throw new GameFrameworkException(Utility.Text.Format
("Resource target '{0}' dependency asset reference count is invalid.", Name)); }
} } m_ResourceLoader.m_ResourceDependencyCount.Remove(Target); m_ResourceHelper.
Release(Target); //AssetBundle.Unload(true) ,也可能是asset 执行释放,也可能是scene执行释放 }