溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Unity的資源管理是怎樣的

發(fā)布時間:2021-10-21 15:05:02 來源:億速云 閱讀:156 作者:柒染 欄目:大數(shù)據

這篇文章將為大家詳細講解有關Unity的資源管理是怎樣的,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

在Unity最佳實踐明確指出, 要使用AssetBundle而不是Resources目錄來管理資源。

然而,事情并不像Unity官方描述的那么美好。因為使用AssetBundle我們甚至無法實現(xiàn)一個易用的,完備的資源管理方案。

據Unity官方說,一般有兩種方案。

方案一,如果你的游戲是關卡性質的,可以在一個關卡里加載所有AssetBundle,然后在進入下一關卡時,卸載本關卡中加載的所有AssetBundle. 但這種機制似乎只對憤怒的小鳥這種小游戲才適用吧:D。

方案二,如果你的游戲不是關卡類的,那么Unity推薦做一個資源對AssetBundle引用計數(shù)。

如果一個對象(Asset或其他AssetBundle)引用此AssetBundle則其引用計數(shù)加1. 如果此AssetBundle首次加載(即加載前引用計數(shù)為0), 還需要遞歸對其依賴引用計數(shù)加1。

如果一個AssetBundle的引用計數(shù)為0則釋放這個AssetBundle,同時還需要遞歸對其依賴引用計數(shù)減1.

除非,我們做像憤怒小鳥一樣的通關游戲,不然似乎只有方案二給我們用。而且方案二乍一看是完備的,因為這正是GC算法的一種實現(xiàn)。

但是如果稍微仔細思考一下就會發(fā)現(xiàn),這個方案只是AssetBundle的管理方案,是個半成品,要如何管理管理資源之間的依賴,Unity卻只字未掉,看起來是讓用戶自己想辦法,這似乎與其易學易用的宗旨不太相符。

下面來分析一下Unity中資源之間的關系。

在Unity中資源大約分為以下幾種:紋理(Texture)、網格(Mesh)、動畫片段(AnimationClip)、音頻片段(AudioClip)、材質(Material)、著色器(Shader)、字體資源(Font)以及文本資源(TextAsset)。

AssetBundle中還有一個極其特殊的存在,那就是Prefab, AssetBundle.LoadAsset時返回的是GameObject, 但是又必須經過Instantitate之后變成另外一個GameObject才能使用。此后所說的GameObject均是Instantitate之后的GameObject。

GameObject可以添加各種Component來引用上述除資源,還可以通過代碼動態(tài)增減某個GameObject上的Component或者修改Component對資源的引用。這種靈活性給資源管理帶來了巨大麻煩,而沒有這種靈活性,邏輯的實現(xiàn)就會更麻煩。


下面,舉例來說明一下,要正確管理GameObject和資源之間的引用關系有多么艱難。

Prefab P能過Instantitate生成A,B,C,D四個GameObject.

執(zhí)行如下代碼之后,A引用{P,T1}, B引用{P,T1}, C引用{P,T3}。并且T2應該被Unload。

1: A.GetComponent<SpriteRender>().sprite = (Sprite)T1; 
2: B.GetComponent<SpriteRender>().sprite = (Sprite)T1; 
3: C.GetComponent<SpriteRender>().sprite = (Sprite)T2; 
4: C.GetComponent<SpriteRender>().sprite = (Sprite)T3;

要想自動正確的管理GameObject和資源的引用關系,就必須要感知到對GameObject的賦值操作。

例如:所有的sprite賦值都必須使用類似SpriteAssign(SpriteRender sr, Sprite s)的接口。

SpriteAssign的執(zhí)行流程通常是這樣的。

  1. 檢查sprite的值是不是T1相同,如果是相同則不做處理

  2. 檢查sprite的值是不是從P中clone過來的,如果不是,將此sprite的引用計數(shù)減1

  3. 將T1的引用計數(shù)加1


如果P是一個樹狀態(tài)結構,即有P–(child)–>p1–(child)–>p2。

1: A.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T1; 
2: B.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T1; 
3: C.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T2; 
4: C.p1.p2.GetComponent<SpriteRender>().sprite = (Sprite)T3;

SpriteAssign接口中的步驟2就顯得格外復雜,它必須修正引用關系如下:A引用{P,T1}, B引用{P,T1}, C引用{P,T3}。

同時Destory操作也要被感知,如果Destory(A)則需要釋放A引用的資源,而如果Destory(A.p1.p2)則需要修正A對資源的引用情況。因為此時的引用關系是,A引用{P}。換句話說Destroy的開銷也會變大。

而賦值和Destory都算不上低頻操作,尤其是賦值操作。這樣的開銷已經足夠讓程序慢上好幾倍了。如果不能承受這些開銷,全自動化資源管理是不可能實現(xiàn)的。

我想這也是Unity不默認提供一套標準的全自動化資源管理方案的根本原因吧。


受方案一的啟發(fā),我覺得可以通過如下接口做一個半自動化的資源管理器。

void level.open();
void level.close();
void level.dispose();
void stack.push() {
   level l = new level()
   l.open()
   push l in to stack
}
void stack.pop() {
   pop l from stack
   l.dispose()
}

每一個level對象都會記錄在level.open()和level.close()之間所有加載過的資源,加過載多少次就記錄多少次,這些資源會在執(zhí)行l(wèi)evel.dispose()時如實的進行釋放。

其中stack在管理UI資源方面幾乎已經達到了全自動化,當你打開一個UI時調用stack.push,在退出此UI時調用stack.pop會自動釋放在此UI期間你所加載的全部資源。

而在其他不具有棧式加載資源特征的地方,level類也提供了一種方便的半自動化管理方案。

最重要的是,此種方案的開銷和復雜度,都要遠低于全自動化管理方案。

關于Unity的資源管理是怎樣的就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI