原文地址:http://gad.qq.com/article/detail/7180936
作者:Loki+X
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 | 音頻剪輯 | 音頻:.aif .wav .mp3 .ogg音軌:.xm .mod .it .s3m |
| Cubemap Texture | 立方體貼圖紋理 | |
| Flare | 耀斑 | |
| Font | 字體 | .ttf |
| Material | 材質(zhì) | |
| Meshes | 網(wǎng)格 | .FBX .dae .3DS .dxf .obj |
| Movie Texture | 電影貼圖 | .mov .mpg .mpeg .mp4 .avi .asf (導(dǎo)入需要QuickTime) |
| Procedural Material Assets | 程序材質(zhì)資源 | |
| Render Texture | 渲染紋理 | |
| Text Asset | 文本資源 | .txt .html .htm .xml .bytes |
| Texture 2D | 二維紋理 | PSD TIFF JPG TGA PNG GIF BMP IFF PICT |
除此之外,想使用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è)字符串:程序庫名稱,類名稱,命名空間。
構(gòu)建工程時(shí),Unity會(huì)收集Assets文件夾中獨(dú)立的腳本文件并編譯他們,組成一個(gè)Mono程序庫。Unity會(huì)將Assets目錄中的語言分開編譯,Assets/Plugins目錄中的腳本同理。Plugin子目錄之外的C#腳本會(huì)放在Assembly-CSharp.dll中。而Plugin及其子目錄中的腳本則放置在Assembly-CSharp-firstpass.all中。
這些程序庫會(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"http://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)配置不同而不同??梢杂脕肀4鏀?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 | IOS | Android | |
|---|---|---|---|---|
| Standalone | √ | √ | √ | √ |
| WebPlayer | √ | √ | ||
| IOS | √ | |||
| 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卸載后才能生效。
資源卸載總覽

當(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ù)。