Unity资源处理机制(Assets/WWW/AssetBundle/...)读取和加载资源方式详解
Unity資源機(jī)制
1、概述
? ? ? 本文意在闡述Unity資源機(jī)制相關(guān)的信息,以及一些關(guān)于個(gè)人的理解與試驗(yàn)結(jié)果。另外還會(huì)提及一些因機(jī)制問題可能會(huì)出現(xiàn)的異常以及處理建議。大部分機(jī)制信息來源于官方文檔,另外為自我驗(yàn)證后的結(jié)果。
?
2、資源
概述
? ? ? Unity必須通過導(dǎo)入將所支持的資源序列化,生成AssetComponents后,才能被Unity使用。以下是Unity對(duì)Assets的描述:
? ? ? Assets are the models,textures,sounds and all other “content”files from which you make your game。
? ? ? 資源(Asset)是硬盤中的文件,存儲(chǔ)在Unity工程的Assets文件夾內(nèi)。有些資源的數(shù)據(jù)格式是Unity原聲支持的,有些資源則需要轉(zhuǎn)換為源生的數(shù)據(jù)格式后才能被使用。
? ? ? ? 對(duì)象(UnityEngine.Object),代表序列化數(shù)據(jù)的集合,表示某個(gè)資源的具體實(shí)例。它可以是Unity使用的任何類型的資源,所有對(duì)象都是UnityEngine.Object基類的子類
? ? ? ??資源與對(duì)象時(shí)一對(duì)多的關(guān)系。
? | ?
? | ?
? | |||
| Audio Clip | ?
? | ?
? | |||
| Cubemap Texture |
? | ? | |||
| Flare | ?
? | ? | |||
| Font | ?
? | ?
? | |||
| Material | ?
? | ? | |||
| Meshes | 網(wǎng)格 | .FBX .dae .3DS .dxf .obj | |||
| Movie Texture | ?
? | ?
? | |||
| Procedural Material Assets | 程序材質(zhì)資源 | ? | |||
| Render Texture | ?
? | ? | |||
| Text Asset | ?
? | .txt .html .htm .xml .bytes | |||
| Texture 2D | ?
? | ?
? |
? ? ? ??除此之外,想使用Unity不支持導(dǎo)入,或者未經(jīng)導(dǎo)入的資源,只能使用IO Stream或者WWW 方法,這些將在下文對(duì)應(yīng)欄目中說明。?
? ? ? ??注意:AssetBundle不是資源組件,故無法用資源組件的方式載入,只能使用WWW或者AssetBundle相關(guān)接口載入與讀取
?
GUID與fileID(本地ID)
? ? ? ??Unity會(huì)為每個(gè)導(dǎo)入到Assets目錄中的資源創(chuàng)建一個(gè)meta文件,文件中記錄了GUID,GUID用來記錄資源之間的引用關(guān)系。還有fileID(本地ID),用于標(biāo)識(shí)資源內(nèi)部的資源。資源間的依賴關(guān)系通過GUID來確定;資源內(nèi)部的依賴關(guān)系使用fileID來確定。
?
InstanceID(實(shí)例ID)
? ? ? ??Unity為了在運(yùn)行時(shí),提升資源管理的效率,會(huì)在內(nèi)部維護(hù)一個(gè)緩存表,負(fù)責(zé)將文件的GUID與fileID轉(zhuǎn)換成為整數(shù)數(shù)值,這個(gè)數(shù)值在本次會(huì)話中是唯一的,稱作實(shí)例ID(InstanceID)。
? ? ? ??程序啟動(dòng)時(shí),實(shí)例ID緩存與所有工程內(nèi)建的對(duì)象(例如在場(chǎng)景中被引用),以及Resource文件夾下的所有對(duì)象,都會(huì)被一起初始化。如果在運(yùn)行時(shí)導(dǎo)入了新的資源,或從AssetBundle中載入了新的對(duì)象,緩存會(huì)被更新,并為這些對(duì)象添加相應(yīng)條目。實(shí)例ID僅在失效時(shí)才會(huì)被從緩存中移除,當(dāng)提供了指定文件GUID和fileID的AssetBundle被卸載時(shí)會(huì)產(chǎn)生移除操作。
? ? ? ??卸載AssetBundle會(huì)使實(shí)例ID失效,實(shí)例ID與其文件GUID和fileID之間的映射會(huì)被刪除以便節(jié)省內(nèi)存。重新載入AssetBundle后,載入的每個(gè)對(duì)象都會(huì)獲得新的實(shí)例ID。
?
?資源的生命周期
? ? ? ??Object從內(nèi)存中加載或卸載的時(shí)間點(diǎn)是定義好的。Object有兩種加載方式:自動(dòng)加載與外部加載。當(dāng)對(duì)象的實(shí)例ID與對(duì)象本身解引用,對(duì)象當(dāng)前未被加載到內(nèi)存中,而且可以定位到對(duì)象的源數(shù)據(jù),此時(shí)對(duì)象會(huì)被自動(dòng)加載。對(duì)象也可以外部加載,通過在腳本中創(chuàng)建對(duì)象或者調(diào)用資源加載API來載入對(duì)象(例如:AssetBundle.LoadAsset)?
對(duì)象加載后,Unity會(huì)嘗試修復(fù)任何可能存在的引用關(guān)系,通過將每個(gè)引用文件的GUID與FileID轉(zhuǎn)化成實(shí)例ID的方式。一旦對(duì)象的實(shí)例ID被解引用且滿足以下兩個(gè)標(biāo)準(zhǔn)時(shí),對(duì)象會(huì)被強(qiáng)制加載:
? ? ? ??實(shí)例ID引用了一個(gè)沒有被加載的對(duì)象。
? ? ? ??實(shí)例ID在緩存中存在對(duì)應(yīng)的有效GUID和本地ID。
? ? ? ??如果文件GUID和本地ID沒有實(shí)例ID,或一個(gè)已卸載對(duì)象的實(shí)例ID引用了非法的文件GUID和本地ID,則引用本身會(huì)被保留,但實(shí)例對(duì)象不會(huì)被加載。在Unity編輯器中表現(xiàn)為空引用,在運(yùn)行的應(yīng)用中,或場(chǎng)景視圖里,空對(duì)象會(huì)以多種方式表示,取決于丟失對(duì)象的類型:網(wǎng)格會(huì)變得不可見,紋理呈現(xiàn)為紫紅色等等。
?
?MonoScripts
? ? ? ??一個(gè)MonoScripts含有三個(gè)字符串:程序庫(kù)名稱,類名稱,命名空間。?
構(gòu)建工程時(shí),Unity會(huì)收集Assets文件夾中獨(dú)立的腳本文件并編譯他們,組成一個(gè)Mono程序庫(kù)。Unity會(huì)將Assets目錄中的語(yǔ)言分開編譯,Assets/Plugins目錄中的腳本同理。Plugin子目錄之外的C#腳本會(huì)放在Assembly-CSharp.dll中。而Plugin及其子目錄中的腳本則放置在Assembly-CSharp-firstpass.all中。?
這些程序庫(kù)會(huì)被MonoScripts所引用,并在程序第一次啟動(dòng)時(shí)被加載。
?
3、資源文件夾
?Assets
? ? ? ??為Unity編輯器下的資源文件夾,Unity項(xiàng)目編輯時(shí)的所有資源都將置入此文件夾內(nèi)。在編輯器下,可以使用以下方法獲得資源對(duì)象:
? ? ? ??AssetDatabase.LoadAssetAtPath("Assets/x.txt");?
? ? ? ??注意:此方法只能在編輯器下使用,當(dāng)項(xiàng)目打包后,在游戲內(nèi)無法運(yùn)作。參數(shù)為包含Assets內(nèi)的文件全路徑,并且需要文件后綴。?
? ? ? ??Assets下的資源除特殊文件夾內(nèi),或者在會(huì)打入包內(nèi)的場(chǎng)景中引用的資源,其余資源不會(huì)被打入包中。
?
Resources
資源載入
? ? ? ??Assets下的特殊文件夾,此文件夾內(nèi)的資源將會(huì)在項(xiàng)目打包時(shí),全部打入包內(nèi),并能通過以下方法獲得對(duì)象:
? ? ? ??Resources.Load("fileName");?
? ? ? ??Resources.Load("fileName");?
? ? ? ??注意:函數(shù)內(nèi)的參數(shù)為相對(duì)于Resource目錄下的文件路徑與名稱,不包含后綴。Assets目錄下可以擁有任意路徑及數(shù)量的Resources文件夾,在運(yùn)行時(shí),Resources下的文件路徑將被合并。
? ? ? ??例:Assets/Resources/test.txt與 Assets/TestFloder/Resources/test.png在使用Resource.Load("test")載入時(shí),將被視為同一資源,只會(huì)返回第一個(gè)符合名稱的對(duì)象。如果使用Resource.Load(“test”)將返回text.txt;
? ? ? ??如果在Resources下有相同路徑及名稱的資源,使用以上方法只能獲得第一個(gè)符合查找條件的對(duì)象,使用以下方法能或得到所有符合條件的對(duì)象:
Object[] assets = Resources.LoadAll("fileName");?
TextAsset[] assets = Resources.LoadAll("fileName");?
?
?相關(guān)機(jī)制
? ? ? ??在工程進(jìn)行打包后,Resource文件夾中的資源將進(jìn)行加密與壓縮,打包后的程序內(nèi)將不存在Resource文件夾,故無法通過路徑訪問以及更新資源。
? ? ? ??依本文2.3章節(jié)所述,在程序啟動(dòng)時(shí)會(huì)為Resource下的所有對(duì)象進(jìn)行初始化,構(gòu)建實(shí)例ID。隨著Resource內(nèi)資源的數(shù)量增加,此過程耗時(shí)的增加是非線性的。故會(huì)出現(xiàn)程序啟動(dòng)時(shí)間過長(zhǎng)的問題,請(qǐng)密切留意Resource內(nèi)的資源數(shù)量。
?
卸載資源
? ? ? ??所有實(shí)例化后的GameObject 可以通過Destroy函數(shù)銷毀。請(qǐng)留意Object與GameObject之間的區(qū)別與聯(lián)系
Object可以通過Resources中的相關(guān)Api進(jìn)行卸載
Resources.UnloadAsset(Object);//卸載對(duì)應(yīng)Object?
Resources.UnloadUnusedAssets();//卸載所有沒有被引用以及實(shí)例化的Object?
? ? ? ??注意以下情況:
Object obj = Resources.Load("MyPrefab");?
GameObject instance = Instantiate(obj) as GameObjct;?
......?
Destroy(instance);?
Resources.UnloadUnusedAssets();?
? ? ? ??此時(shí)UnloadUnusedAssets將不會(huì)生效,因?yàn)閛bj依然引用了MyPrefab,需要將obj = null,才可生效。
?
StreamingAssets
概述
? ? ? ??StreamingAssets文件夾為流媒體文件夾,此文件夾內(nèi)的資源將不會(huì)經(jīng)過壓縮與加密,原封不動(dòng)的打包進(jìn)游戲包內(nèi)。在游戲安裝時(shí),StreamAssets文件件內(nèi)的資源將根據(jù)平臺(tái),移動(dòng)到對(duì)應(yīng)的文件夾內(nèi)。StreamingAssets文件夾在Android與IOS平臺(tái)上為只讀文件夾.?
? ? ? ??你可以使用以下函數(shù)獲得不同平臺(tái)下的StreamingAssets文件夾路徑:
Application.streamingAssetsPath?
? ? ? ??請(qǐng)參考以下各平臺(tái)下StreamingAssets文件夾的等價(jià)路徑,Application.dataPath為程序安裝路徑。Android平臺(tái)下的路徑比較特殊,請(qǐng)留意此路徑的前綴,在一些資源讀取的方法中是不必要的(AssetBundle.LoadFromFile,下詳)
Application.dataPath+"/StreamingAssets"//Windows OR MacOS?
Application.dataPath+"/Raw" //IOS?
"jar:file://"+Application.dataPath+"!/assets/" //Android?
?
文件讀取
? ? ? ??StreamingAssets文件夾下的文件在游戲中只能通過IO Stream或者WWW的方式讀取(AssetBundle除外)
IO Stream方式
using(FileStream stream = ?
File.Open(Application.streamingAssetsPath+"fileName",?
FileMode.Open))?
{?
//處理方法?
}?
WWW方式(注意協(xié)議與不同平臺(tái)下路徑的區(qū)別)
using(WWW www = new WWW(?
Application.streamingAssetsPath+"fileName"))?
{?
yield return www;?
www.text;?
www.texture;?
}?
? ? ? ??AssetBundle特有的同步讀取方式(注意安卓平臺(tái)下的路徑區(qū)別)
string assetbundlePath =?
#if UNITY_ANDROID?
Application.dataPath+"!/assets";?
#else?
Application.streamingAssetsPath;?
#endif ?
AssetBundle.LoadFromFile(assetbundlePath+"/name.unity3d");?
?
?PersistentDataPath
Application.persistentDataPath?
? ? ? ??Unity指定的一個(gè)可讀寫的外部文件夾,該路徑因平臺(tái)及系統(tǒng)配置不同而不同。可以用來保存數(shù)據(jù)及文件。該目錄下的資源不會(huì)在打包時(shí)被打入包中,也不會(huì)自動(dòng)被Unity導(dǎo)入及轉(zhuǎn)換。該文件夾只能通過IO Stream以及WWW的方式進(jìn)行資源加載。
?
4、WWW載入資源
?概述
? ? ? ??WWW是一個(gè)Unity封裝的網(wǎng)絡(luò)下載模塊,支持Http以及file兩種URL協(xié)議,并會(huì)嘗試將資源轉(zhuǎn)換成Unity能使用的AssetsComponents(如果資源是Unity不支持的格式,則只能取出byte[])。具體對(duì)應(yīng)的格式參考第一章表格。WWW加載是異步方法。
byte[] bytes = WWW.bytes;?
string text = WWW.text;?
Texture2D texture = WWW.texture;?
MovieTexture movie = WWW.movie;?
AssetBundle assetbundle = WWW.assetBundle;?
AudioClip audioClip = WWW.audioClip;?
?
相關(guān)機(jī)制
?new WWW
? ? ? ??每次new WWW時(shí),Unity都會(huì)啟用一個(gè)線程去進(jìn)行下載。通過此方式讀取或者下載資源,會(huì)在內(nèi)存中生成WebStream,WebStream為下載文件轉(zhuǎn)換后的內(nèi)容,占用內(nèi)存較大。使用WWW.Dispose將終止仍在加載過程中的進(jìn)程,并釋放掉內(nèi)存中的WebStream。?
? ? ? ??如果WWW不及時(shí)釋放,將占用大量的內(nèi)存,推薦搭配using方式使用,以下兩種方式等價(jià)。
WWW www = new WWW(Application.streamingAssetsPath+"fileName");?
try?
{?
yield return www;?
www.text;?
www.texture;?
}?
finally?
{?
www.Dispose();?
}?
using(WWW www = new WWW(?
Application.streamingAssetsPath+"fileName"))?
{?
yield return www;?
www.text;?
www.texture;?
}?
? ? ? ??如果載入的為Assetbundle且進(jìn)行過壓縮,則還會(huì)在內(nèi)存中占用一份AssetBundle解壓用的緩沖區(qū)Deompresion Buffer,AssetBundle壓縮格式的不同會(huì)影響此區(qū)域的大小。
?
WWW.LoadFromCacheOrDownload
int version = 1;?
WWW.LoadFromCacheOrDownload(PathURL+"/fileName",version);?
? ? ? ??使用此方式加載,將先從硬盤上的存儲(chǔ)區(qū)域查找是否有對(duì)應(yīng)的資源,再驗(yàn)證本地Version與傳入值之間的關(guān)系,如果傳入的Version>本地,則從傳入的URL地址下載資源,并緩存到硬盤,替換掉現(xiàn)有資源,如果傳入Version<=本地,則直接從本地讀取資源;如果本地沒有存儲(chǔ)資源,則下載資源。此方法的存儲(chǔ)路徑無法設(shè)定以及訪問。使用此方法載入資源,不會(huì)在內(nèi)存中生成 WebStream(其實(shí)已經(jīng)將WebStream保存在本地),如果硬盤空間不夠進(jìn)行存儲(chǔ),將自動(dòng)使用new WWW方法加載,并在內(nèi)存中生成WebStream。在本地存儲(chǔ)中,使用fileName作為標(biāo)識(shí)符,所以更換URL地址而不更改文件名,將不會(huì)造成緩存資源的變更。?
保存的路徑無法更改,也沒有接口去獲取此路徑
?
5、 AssetBundle
概述
? ? ? ??AssetBundles let you stream additional assets via the WWW class and instantiate them at runtime. AssetBundles are created via BuildPipeline.BuildAssetBundle.
? ? ? ??AssetBundle是Unity支持的一種文件儲(chǔ)存格式,也是Unity官方推薦的資源存儲(chǔ)與更新方式,它可以對(duì)資源(Asset)進(jìn)行壓縮,分組打包,動(dòng)態(tài)加載,以及實(shí)現(xiàn)熱更新,但是AssetBundle無法對(duì)Unity腳本進(jìn)行熱更新,因?yàn)槠湫枰诖虬鼤r(shí)進(jìn)行編譯。
?
Assetbundle打包
平臺(tái)兼容性
? ? ? ??AssetBundle適用于多種平臺(tái),但不同平臺(tái)所使用的AssetBundle并不相同,在創(chuàng)建AssetBundle時(shí)需要通過參數(shù)來指定目標(biāo)平臺(tái),其關(guān)系如下表
| ? | Standalone | WebPlayer |
? | Android | |
| Standalone | √ | √ | √ | √ | |
| WebPlayer | √ | √ | ? | ? | |
? | ? | ? | √ | ? | |
| Android | ? | ? | ? | √ |
?
創(chuàng)建API
public enum BuildAssetBundleOptions?
{?
None = 0,?
//Build assetBundle without any special option.?
UncompressedAssetBundle = 1,?
//Don't compress the data when creating the asset bundle.?
CollectDependencies = 2,?
//Includes all dependencies.?
CompleteAssets = 4,?
//Forces inclusion of the entire asset.?
DisableWriteTypeTree = 8,?
//Do not include type information within the AssetBundle.?
DeterministicAssetBundle = 16,?
//Builds an asset bundle using a hash for the id ?
ForceRebuildAssetBundle = 32,?
//Force rebuild the assetBundles.?
IgnoreTypeTreeChanges = 64,?
//Ignore the type tree changes when doing the incremental build check.?
AppendHashToAssetBundleName = 128,?
//Append the hash to the assetBundle name.?
ChunkBasedCompression = 256?
//Use chunk-based LZ4 compression when creating the AssetBundle.?
}?
AssetBundleManifest manifest = ?
BuildPipeline.BuildAssetBundles("OutputPath",BuildAssetBundleOptions,tragetPlatform);?
? ? ? ?在Unity的5.3版本中,簡(jiǎn)化了AssetBundle的打包方式,只留下了一個(gè)api與寥寥幾個(gè)設(shè)置參數(shù),而之前最讓人頭痛的資源依賴管理,也被默認(rèn)進(jìn)行處理。 而在每個(gè)Asset文件的Inspector面板上都會(huì)多出一個(gè)Asset Labels的設(shè)定欄:
?
?
? ? ? AssetBundle name:需要將此資源打包的AssetBundle名稱
? ? ? AssetBundle Variant:需要將此資源打包的AssetBundle的變體名
?
Variant
? ? ? ? ?Variant是5.3以后新添加的一個(gè)概念,這個(gè)值其實(shí)是一個(gè)尾綴,將添加在對(duì)應(yīng)AssetBundle的名稱之后,如:ddzgame.hd,hd就是Variant(從此以后AssetBundle的尾綴已經(jīng)跟其文件類型本身沒有任何聯(lián)系)。
?
自動(dòng)打包腳本
? ? ? ? ?從以上可知,如果需要一個(gè)一個(gè)的對(duì)資源設(shè)置AssetBundle Name與Variant實(shí)在太過繁瑣與麻煩,也可能出現(xiàn)紕漏,好在可以通過腳本去批量設(shè)置這兩個(gè)參數(shù):
AssetImporter assetImporter = AssetImporter.GetAtPath("path");
assetImporter.assetBundleName = "Assetbundle Name";
assetImporter.assetBundleVariant = "Assetbundle Variant";
? ? 其中path是資源在Assets目錄下的路徑。
?
Scene打包
? ? ? ? ?Scene打包跟資源打包無異,唯一需要注意的是:Scene只能與Scene打入同一個(gè)AssetBundle內(nèi),而無法與其他資源打入同一個(gè)AssetBundle。
? ? ? ? ?PS:AssetBundle內(nèi)的Scene需要在AssetBundle加載后,通過SceneManager來加載。
?
AssetBundle依賴
依賴機(jī)制
? ? ? ? ?假設(shè)有AssetBundleA與 AssetBundleB兩個(gè)AssetBundle,AssetBundle中的資源引用了AssetBundleB中的資源,則稱AssetBundleA依賴于AssetBundleB。具體實(shí)例請(qǐng)看下圖注意被依賴AssetBundle需要加載的時(shí)機(jī)
?
?
? ? ? ? ?注意其依賴的機(jī)制: AssetBundle中保存有其中所有資源的GUID,FileID等序列化信息,AssetBundle只會(huì)在內(nèi)存中尋找其依賴資源所在的AssetBundle,并自動(dòng)從中加載出所需資源。具體可參考本文2.3章節(jié)
?
?Manifest
??
?
? ? ? ? ?在前面有提到,在5.3中,Unity會(huì)自動(dòng)處理AssetBundle中資源的依賴關(guān)系。在默認(rèn)情況下,如果AssetBundle間有交叉的資源引用,不會(huì)再重復(fù)打包,在打包AssetBundle后,會(huì)發(fā)現(xiàn)其在輸出目錄多出了一個(gè)與目錄名稱相同的無后綴AssetBundle文件,其為自動(dòng)生成的AssetBundleManifest文件,其內(nèi)保存有此次生成的所有AssetBundle之間的依賴關(guān)系與清單。我們可以在載入這個(gè)AssetBundle后使用以下方法獲得此對(duì)象。
AssetBundle.LoadAsset("AssetBundleManifest");
? ? ? ? ? Manifest保存有重要的依賴信息,在載入AssetBundle時(shí),可以通過Manifest查詢其是否有依賴的AssetBundle,然后我們手動(dòng)對(duì)其進(jìn)行管理,避免依賴項(xiàng)丟失而出現(xiàn)bug
string[] fullnames = AssetBundle.GetDirectDependencies(fullname);
string[] fullnames = AssetBundle.GetAllDependencies(fullname);
? ? ? ? ?Direct方法會(huì)返回所有直接依賴的AssetBundle名稱數(shù)組,All方法會(huì)返回所有依賴的AssetBundle名稱數(shù)組,fullname包括名稱與Variant。推薦使用Direct方法做遞歸處理,避免重復(fù)載入。
?
AssetBundle加載
加載方式
? ? ? ? ?之前已經(jīng)提及,不再詳細(xì)說明,使用WWW 或者 AssetBundle相關(guān)API加載,其中AssetBundle的API只能進(jìn)行本地加載。
AssetBundle.LoadfromMemory(byte[] bytes)
? ? ? ? ?此API是一個(gè)例外,用來對(duì)加密的Assetbundle進(jìn)行讀取,可以結(jié)合WWW使用。
?
壓縮
? ? ? ? ?LZMA(Ziv-Markov chain algorithm)格式
? ? ? ? ?Unity打包成AssetBundle時(shí)的默認(rèn)格式,會(huì)將序列化數(shù)據(jù)壓縮成LZMA流,使用時(shí)需要整體解包。優(yōu)點(diǎn)是打包后體積小,缺點(diǎn)是解包時(shí)間長(zhǎng),且占用內(nèi)存。
?
LZ4格式
? ? ? ? ?5.3新版本添加的壓縮格式,壓縮率不及LZMA,但是不需要整體解壓。LZ4是基于chunk的算法,加載對(duì)象時(shí)只有響應(yīng)的chunk會(huì)被解壓。
? ? ? ? ?壓縮格式在打包時(shí)通過AssetBundleOption參數(shù)選擇。
?
內(nèi)存占用
?
?
? ? ? ?AssetBundle加載后會(huì)在內(nèi)存中生成AssetBundle的序列化架構(gòu)的占用,一般來說遠(yuǎn)遠(yuǎn)小于資源本身,除非包含復(fù)雜的序列化信息(復(fù)雜多層級(jí)關(guān)系或復(fù)雜靜態(tài)數(shù)據(jù)的prefab等)
?
AssetBundle卸載
卸載API
AssetBundle.Unload(bool unloadAllLoadedObjects);
? ? ? ?AssetBundle只有唯一的一個(gè)卸載函數(shù),傳入的參數(shù)用來選擇是否將已經(jīng)從此AssetBundle中加載的資源一起卸載。另外,已經(jīng)從AssetBundle中加載的資源可以通過Resources.UnloadAsset(Object)卸載。如果想通過Resources.UnloadUnusedAssets()卸載從AssetBundle加載的資源,一定要先將AssetBundle卸載后才能生效。
?
資源卸載總覽
?
內(nèi)存關(guān)系圖
?
? ? ? ?當(dāng)AssetBundle被卸載后,實(shí)例ID與其文件GUID和本地ID之間的映射會(huì)被刪除, 即其無法被其后加載的依賴于它的資源所查找及引用。詳情請(qǐng)參考本文2.3章節(jié)
?
?案例分析
? ? ?案例1 游戲切換到后臺(tái)一段時(shí)候切回,出現(xiàn)shader或者Texture丟失。
? ? ?在移動(dòng)平臺(tái),當(dāng)程序切到主界面或者在后臺(tái)長(zhǎng)時(shí)間運(yùn)行時(shí),GPU會(huì)自動(dòng)對(duì)后臺(tái)程序的資源進(jìn)行清理。如果shader或者Texture是從AssetBundle中加載出來,而此AssetBundle已經(jīng)被卸載的話,Unity無法在程序恢復(fù)時(shí)從內(nèi)存中加載這些資源,從而造成丟失。有人會(huì)問,這些資源不是已經(jīng)加載到內(nèi)存中了么?但是,他們?cè)诒患虞d到GPU之后會(huì)被從內(nèi)存中清除。因此要防止此狀況最穩(wěn)健的方法,就是在場(chǎng)景切換前,不要卸載掉其所屬的AssetBundle。
? ? ? ? 案例2 當(dāng)經(jīng)常使用AssetBundleB.Unload(false)卸載時(shí),有時(shí)會(huì)發(fā)現(xiàn)AssetBundle中的資源在內(nèi)存中有多份同時(shí)存在。
? ? ? ?問題的根源在于從AssetBundle中加載出來的資源,在該AssetBundle卸載之后與其的聯(lián)系就斷開了。
?
?
? ? ? ?例如:從AssetBundleA中加載出來一個(gè)Prefab p1,p1依賴資源tex1也會(huì)自動(dòng)加載到內(nèi)存中。然后用AssetBundle.Unload(false)卸載AssetBundleA,此時(shí)p1與AssetBundleA的聯(lián)系斷開。之后,從AssetBundleA中加載Prefab p2,p2也依賴資源tex1,那么在加載p2時(shí)tex1會(huì)再次被加載到內(nèi)存中,導(dǎo)致重復(fù)。
?
總結(jié)
以上是生活随笔為你收集整理的Unity资源处理机制(Assets/WWW/AssetBundle/...)读取和加载资源方式详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 急~为啥我指定的的maven依赖版本没有
- 下一篇: mac不用 linux知乎,用 Linu