首先,這裏說明一下,我這邊的GameObject有點籠統,就是表達的是遊戲中的具體實例。html
遊戲中具體的資源,像texture,mesh,material,shader,script等,實實在在的遊戲項目文件夾中所須要堆放的資源。好比,var obj = Resource.Load<GameObject>("Prefabs/testItem"),這個obj就是Asset。程序員
var gameItem= Instantiate(obj),這個gameItem就是能夠存在於遊戲的實際場景中(這個比較簡單,很少說了)。GameObject是遊戲中實際使用的對象(就是你會在屏幕中實際看到的),是由Asset實例化後的對象。本質上其實仍是Asset的衍變,是對部分Asset的引用和複製出來的新東西,其本質仍是Asset。 算法
由上述可知,咱們在遊戲中生成實際的物體,須要Asset。而Asset,好比一張圖片,也是一個Asset,實際大小1M,遊戲中這種圖片不少,那就輕輕鬆鬆幾百M,幾個G的Asset,都是頗有可能的。做爲程序員,對於這種「原汁原味」拿過來確定不行。好比咱們工做中把文件什麼的發給同事的時候都知道壓縮一下,能夠傳輸的過程當中小一些。固然了,咱們在遊戲開發中使用Asset,也是須要相似的。因而,就推出了AssetBundle這一律念。固然咱們推出AssetBundle,遠不止壓縮這一需求。可是你須要知道,主要是爲了更好的傳輸,還有好比減小資源大小,利於網絡那邊的傳輸,方便加載。網絡
簡而言之,AssetBundle就是爲了讓遊戲項目中大量Asset適應實際遊戲運行時而被壓縮後的一種二進制文件。異步
①複製+引用關係測試
Instaniate一個Prefab(Asset),是對Asset進行clone(複製)+引用結合的過程。GameObject,transform是clone的。其餘mesh/texture/material/shader等,這些都是純引用的。引用的Asset對象不會被複制,只是一個簡單的指針指向已經load的Asset對象。專門要提一下Script Asset,Unity裏每一個Script都是一個封閉的class定義,並無寫調用代碼。光class是不會工做的。其實Unity引擎就是那個調用代碼。clone一個script asset等於new一個class實例,實例纔會工做 ,把它掛到Unity主線程的調用鏈去,class實例裏的update和start纔會被執行。多個物體掛同一個腳本,其實就是多個物體掛了掛了那個腳本的類的實例。在new class過程當中,數據區是複製的,代碼區是共享的,算是一種複製+引用關係。引用關係的話,會有一對一,一對多,多對一的關係。以下圖:優化
②釋放ui
若是你Destroy(GameObject,這個是遊戲中具備實例),只是釋放了clone的asset,引用的asset並不會被幹掉。還有由於destroy並不知道有沒有被別的asset引用。可是若是你想把asset也釋放,有兩種方案。加密
I. Resources.UnloadUnusedAssets()spa
釋放當前全部的沒有被引用的(無用)asset,可是不能保證釋放掉當前的被引用的資源(由於可能還被其餘資源引用,就不會去釋放了)。缺點:異步,會卡。因爲Unity資源的相互引用關係比較複雜,想要明確判斷某一資源不存在引用關係是有必定難度的,而且,若是我 們想要釋放的資源存在隱形的引用關係,UnloadUnUsedAssets將會無視這個資源而無任何反饋。根據實戰來看,最佳使用的時機是在場景切換進入新的場景後,Unity場景關閉會有效的銷燬全部的對象和全部代碼的引用,即在新場景開頭最爲穩妥。必要時加上GC.Collect().
II.Resouce.UnLoad(obj)
釋放當前實例的全部被引用的asset(無論這個asset是否還有被引用,因此,風險很大,除非保證肯定被他引用的資源沒有再被其餘資源引用,通常用於單獨的一張紋理釋放)缺點:風險太大,容易被報:UnloadAsset may only be used on individual assets and can not be used on GameObject's / Components or AssetBundles,不能用做卸載GameObject,只能用於紋理釋放。否則會報這個錯,我也不知道爲啥,有知道的告我一下,額,還有就是這個方法不多用,應用的話,也只是對紋理釋放。
對於這種使用,對於資源大的且無引用的可使用,若是資源消耗不大,能夠等到場景切換,使用I中 Resources.unloadUnusedAssets方案。
III.GameObject.DestroyImmediate(asset,true)
代替上述方案,能夠針對卸載asset(好像還能夠直接使用GameObject.Destroy(asset) ,也能夠直接使用。(待驗證))
IV.AssetBundle.unload(true) !!!慎用
這個也能夠卸載asset,可是卸載的是這個assetBundle裏面的全部的asset。(後面詳說)
①包含,依賴關係
一個AssetBundle中能夠包含一個或多個Asset。一個Asset依賴於AssetBundle。
②釋放
AssetBundle的釋放只能經過如下兩種方式釋放。即便系統在加載新場景的時候全部的內存對象都會被自動銷燬,包括你用AssetBundle.load加載的對象和Instaniate克隆的,可是不包括AssetBundle文件自己的內存鏡像,那個必需要用Unload來釋放,用.Net術語說該資源是非託管的。
I.AssetBundle.Unload(false) 使用頻率較多
用AssetBundle.Load加載須要的asset以後應該當即使用unlaod(false),釋放assetbundle文件自己的內存鏡像,但不銷燬該assetBundle加載過的asset對象。(儘可能釋放一部份內存,大多數遊戲這麼作)
II.AssetBundle.Unload(true)
釋放該assetBundle文件鏡像並釋放該assetBundle全部loaded的asset內存對象。(風險很大,由於通常不太能肯定是否該loaded的asset是否還被其餘資源引用)。
AssetBundle和Asset 項目工程中大小分析
①首先,準備10個同樣大小的texture(爲了測試結果更加準備,每一個圖1.33M)
texture 的原大小,即Asset自己的大小:1.33M *10 = 13.3M
②LZ4和LZMA打包方式案例對比
I.BuildAssetBundleOptions.None 打包方式,該爲默認壓縮,即LZMA,在使用AssetBundle以前須要解壓縮。使用LAMA格式壓縮的AssetBundle的包體最小
(高壓縮比),可是會增長相應的解壓縮時間和內存。
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None,BuildTarget.StandaloneWindows64);
以上述方式打包完以後 每張 texture :613K *10 = 6M
執行下面代碼。(爲方便數據對比,是在找不到大的texture了)
使用AssetBundle.LoadFromFile 加載那10個AssetBundle以後的內存顯示
對比上2張圖的消耗,大概消耗了13.6M的Unity內存,0.5M其餘內存。由於是十張紋理,原紋理沒有打包以前的大小大概是1.33m,也就是說,大概技術釋放的是原Asset的大小。
再Load其中的Asset
由上圖可知,申請的是Unity內存爆增100M左右,Mono的內存也相應的增長了。
II.ChunkBasedCompression,即LZ4壓縮方式,壓縮比通常,壓縮後的包體較大,可是解壓速度快,消耗內存小。
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.ChunkBasedCompression,BuildTarget.StandaloneWindows64);
以上述方式打包完以後 每張texture :1110K*10=11100K,11M左右
再執行同上一致的Load腳本.
AssetBundle,Asset,GameObject 加載中的內存對比
同上述步驟一致,給出LoadAssetBundle,即AssetBundle.LoadFromFile後的內存大小
大概消耗2M內存。(上述LAMA方式打出的包是23.5M,相差10倍啊,果斷使用這種方式,咱們項目也是這種方式)
再給出LoadAsset以後的內存圖
LoadAsset以後,Unity內存,Mono內存都增長不少
III.總結對比,I加載的時候LoadAssetBundle消耗明顯大於II方式10倍(甚至不止,由於總量越大,差距就越大)。移動遊戲中的內存多麼珍貴你們懂得。使用ChunkBasedCompression,即LZ4壓縮,更爲划算。由於寧願犧牲一點包體大小,也要消耗內存小一些。(咱們遊戲項目是這個需求,具體仍是看項目吧)
固然還有其餘打包選項,各有利弊,具體看遊戲實際狀況需求。我這裏只是對比出了告訴你打包選項會決定你的打出的資源包體大小和遊戲中加載消耗內存。很重要。
具體打包選項BuildAssetBundleOptions參考:https://blog.csdn.net/AnYuanLzh/article/details/81485762
補充:由上,咱們知道打包時壓縮方式會致使打出的包體和加載時的消耗都不一樣。加載時候的加載方式咱們這邊使用AssetBundle.CreateFromFile直接加載AssetBundle,Unity其實還提供了WWW加載AssetBundle的方式。可是這裏不做詳述了(講不完...)。
由上可知,文章雖然說AB,Asset,GameObject三者聯繫,可是GameObject主要是由Asset實例化而來,GameObject是Asset的引用和複製的關係(主要引用),這個也能夠說是Asset的一種。問題也就能夠簡化爲AB和Asset之間的關係。
由上圖,再總結一下,打包方式不一樣,加載方式不一樣,形成的消耗不一樣。即,若是想優化遊戲中的資源,須要注意打包AB的方式,以及加載AB的方式。固然,還要注意AB的卸載,和Asset的卸載。
關於加載AssetBundle和加載Asset的區別,詳情就很少說了,見UWA:https://blog.uwa4d.com/archives/ABTheory.html
打包加密壓縮算法區別參考:https://www.cnblogs.com/murongxiaopifu/p/5629415.html#autoid-3-3-0
附:測試Demo源碼:連接:https://pan.baidu.com/s/1ZHPoQbuxgdUVh9PGbz6ArA 提取碼:7lkv