GF リソースの読み込みフローチャート
GF ローディング リソースの概要
ResourceManager は ResourceLoader (リソース読み込み) などの機能を保持し、ResourceLoader は TaskPool を保持し、TaskPool はエージェントを保持し、エージェントはヘルパーを保持し、実際に操作を実行するメソッドはヘルパーに格納されます。
ResourceManager:IResourceManager
リソース グループと読み込まれたリソースをキャッシュします。リソースのバージョン番号と関連するパス アドレス、およびバージョンの更新とリソースの読み込みなどの関数を取得できます。
アセット名から対応するバンドル名にインデックスを付ける方法
記述ファイルから以下の情報を取得し、GameFrameworkVersion.dat ファイル内のバンドル、アセット、アセットの依存関係を int[] から string[] に変更して保存し、プロセス
GameFramework.Resource.ResourceManager.ResourceChecker.CheckResourcesを呼び出します。
主に問題を解決するには、リソース名を覚える必要はなく、アセットからリソースをロードするプロジェクト内のアセットのフル パスを覚えるだけで済みます。
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;//资源组用于将资源分类,是资源的一种特性标签,一个资源可以归属于多个资源组。通过资源组可以游戏中构造出类似于“插件”的更新机制,如高清材质包、游戏语音包等。
リソースローダー
アセット、バンドルは参照の数に依存します
private readonly Dictionary<object, int> m_AssetDependencyCount; //asset引用计数,key即资源对象,例如texture等类型资源
private readonly Dictionary<object, int> m_ResourceDependencyCount; //resource引用计数,object即assetbundle
アセットがロードされるたびに、すべての依存関係アセット + 1 に対応し、依存バンドルは、それ自身の内部アセットが +1 に依存する回数に依存します。アセットがアンロードされるたびに、それは 0 になります。つまり
、他の人に依存されておらず、アンインストールできること、および対応する依存アセット-1、
参照カウントが 0 のバンドル-1 に依存するアセットを解放できること Resources.UnloadAsset(object) は、
参照付きのアセットを解放できます0 のカウント。 AssetBundle.Unload(true)
タスク プール メカニズム m_TaskPool
LoadAsset
LoadBinary
LoadSceneを使用して
、各ロード タスク LoadResourceTaskBase を m_TaskPool にロードすると、タスクは無限に追加でき、s_Serial は自動的に増加します。ただし、エージェントの数、つまり実行タスクの数には制限があります
エージェントを追加すると、エージェントの総数は UnityGameFramework.Runtime.ResourceComponent.m_LoadResourceAgentHelperCount Control
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
ターゲット アセットのロード タスク
LoadDependencyAssetTask
負荷依存性
タスクプール
Driver ProcessWaitingTasks LoadResourceAgent Agent Start in Update
//等待处理的任务,把所有任务,从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
実行開始、タスクステータス
- アセットは m_AssetPool からインスタンス化でき、タスクは完了です
- リソースは m_ResourcePool でインスタンス化でき、タスクの後に CanResume を実行できます
- ヘルパーを呼び出してロードを実行する
taskPool.update でアイドル状態のエージェントを作業中のエージェントに割り当て、実行を開始します。
//任务不是场景,说明要实例化
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);
ここで最後に DefaultLoadResourceAgentHelper.ReadFile を呼び出します
m_FileAssetBundleCreateRequest = AssetBundle.LoadFromFileAsync(fullPath);
DefaultLoadResourceAgentHelper
シーン内に作成される
プロキシ ヘルパーをロードするために、ゲーム オブジェクトがシーン内に作成されます。
ILoadResourceAgentHelper is the loading resource agent helper interface. リソースの読み込みには、6 つの主要なイベント (リソース更新イベントの非同期読み込み、リソース ファイル完了イベントの非同期読み取り、リソース バイナリ ストリーム完了イベントの非同期読み取り、およびリソース バイナリ ストリームの読み込みオブジェクトへの非同期変換) が伴います。イベント、非同期ロードされたリソースの完了イベント、エラー イベント)。ヘルパーをリセットするためのリソースと関数のファイルとバイナリの非同期読み込み。実際にリソースをロードするためのインターフェースで、ロード後はResourceManager配下のResourceLoaderのIObjectPoolにキャッシュされる。
バンドルをロード
- UpdateFileAssetBundleCreateRequest() の AssetBundleCreateRequest リクエストが Update でポーリングされた後、通知が送信されます。
- GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.OnLoadResourceAgentHelperReadFileComplete への最後の呼び出し
- ResourceObject を作成し、ResourceLoader.m_ResourcePool に登録します。ResourceObject は主に、最後に使用されたタイムスタンプを増やすためのものであり、一部の情報 リソース情報
- 読み込み中のリソース テーブル s_LoadingResourceNames から消去されます
- Resource is ready, load m_Task.LoadMain(this, resourceObject); つまり、ターゲット アセットをロードします
アセットを読み込む
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、依存するすべてのアセットに +1
- m_ResourceLoader.m_AssetPool.Register(assetObject, true); に追加します。
- s_LoadingAssetNames.Remove(m_Task.AssetName); ロード中のリソースを削除します
- すべての依存リソース参照 + 1
参照カウント
アセットがロードされるたびに、すべての依存アセット + 1 に対応します. 依存バンドルは、
アセットがアンロードされるたびに、それ自身の内部アセット + 1 に依存しています.これは 0 です。アンロードするには、asset-1 に依存し、
参照カウント 0 の bundle-1 に依存する Asset を解放し、
Resources.UnloadAsset(object) で参照カウント 0 の AssetBundle を解放できます。リリース可能、および AssetBundle.Unload(true)
ログの読み込み処理
- アセットのメイン タスクの作成とロード a: Assets/GameMain/UI/UIForms/DialogForm.prefab
GameFramework.Resource.ResourceManager/ResourceLoader/LoadAssetTask:Create
initialize loading task 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 to m_WaitingTasks.count = 1
- 依存リソースの作成タスク 2: Assets/GameMain/UI/UISprites/Common/button-outline.png
- 依存リソース タスク 2 を待機リストに追加する GameFramework.Resource.ResourceManager+ResourceLoader+LoadDependencyAssetTask to m_WaitingTasks.count = 2
- 合計 4 つの依存タスクを順番に追加する
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;
}
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);
- 前の依存タスクが確実に実行されるようにメイン タスクを Waiting に追加してから、メイン タスク a を実行
Add task: GameFramework.Resource.ResourceManager+ResourceLoader+LoadAssetTask to m_WaitingTasks.count = 5 - エージェントは待機中のタスク プールから開始して、GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.Start を実行します
。タスクはアセットを読み取ることですが、実際には
リソース ファイルを読み取るために非同期で ab を読み取ります C:/Users/luoyikun_l/AppData/LocalLow /ゲームフレームワーク/スターフォース/UI/UISprites/Common.dat - UnityGameFramework.Runtime.DefaultLoadResourceAgentHelper.UpdateFileAssetBundleCreateRequest がエージェントによってポーリングされ、ab が読み込まれます
- リソース読み込み完了 UI/UISprites/Common
- AB のリソース エージェント アシスタント ui/uisprites/common (UnityEngine.AssetBundle) の Assets/GameMain/UI/UISprites/Common/dialog-title-background.png タスクは解放されていませんが、アセットはまだ読み込まれています
。 - 他のタスクが TaskPool.update でポーリングしている場合、前のタスクによってロードされたバンドルがロードされたことを認識します。
//从resource对象池中取出,说明任务可以接着执行
ResourceObject resourceObject = m_ResourceLoader.m_ResourcePool.Spawn(resourceName);
if (resourceObject != null)
{
OnResourceObjectReady(resourceObject);
return StartTaskStatus.CanResume;
}
タスクはアセットのロード段階に直接入る
//从resource里加载目标asset
public void LoadMain(LoadResourceAgent agent, ResourceObject resourceObject)
{
m_ResourceObject = resourceObject;
agent.Helper.LoadAsset(resourceObject.Target, AssetName, AssetType, IsScene);
}
- UnityGameFramework.Runtime.DefaultLoadResourceAgentHelper.UpdateAssetBundleRequest ポーリング アセットの読み込みが完了しました
アセットの読み込みが完了しました Assets/GameMain/UI/UISprites/Common/dialog-background.png
読み込まれたアセットはバッファー プールに置かれます
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);
- ターゲット アセットは、すべての依存アセットが読み込まれるのを待ってから、独自のバンドルの読み込みを開始してから、アセットを読み込みます。
GameFramework.Resource.ResourceManager.ResourceLoader.LoadResourceAgent.Start
//遍历依赖
foreach (string dependencyAssetName in 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;
}
}
これで、すべての依存アセットが読み込まれ、ターゲット アセットが読み込まれます。プロジェクトに依存し、他のアセットに再び依存するアセットが存在する可能性があります。これにより、依存アセットが再び待機状態になります
リソースがアンロードされるとき
GameFramework.Resource.ResourceManager.ResourceLoader.AssetObject.OnUnspawn は
すべての依存アセットを実行します
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执行释放
}