翻譯自官網(wǎng)文檔:https://unity3d.com/cn/learn/tutorials/topics/best-practices/assetbundle-usage-patterns?playlist=30089
管理加載的資產(chǎn)
在內(nèi)存敏感的環(huán)境中仔細(xì)控制加載的對(duì)象的大小和數(shù)量非常重要。從活動(dòng)場(chǎng)景中移除時(shí)曼追,Unity不會(huì)自動(dòng)卸載對(duì)象即舌。資產(chǎn)清理在特定時(shí)間觸發(fā)屈溉,也可以手動(dòng)觸發(fā)娜氏。
AssetBundles本身必須小心管理馋辈。由本地存儲(chǔ)上的文件(在Unity緩存中或通過AssetBundle.LoadFromFile加載的文件)支持的AssetBundle具有最小的內(nèi)存開銷,很少消耗超過幾十千字節(jié)率拒。但是崩泡,如果存在大量的AssetBundles,則此開銷仍可能會(huì)出現(xiàn)問題俏橘。
由于大多數(shù)項(xiàng)目允許用戶重新體驗(yàn)內(nèi)容(例如重放級(jí)別)允华,因此知道何時(shí)加載或卸載AssetBundle非常重要。如果AssetBundle卸載不當(dāng)寥掐,可能會(huì)導(dǎo)致內(nèi)存中的對(duì)象重復(fù)。在某些情況下不當(dāng)卸載AssetBundles也會(huì)導(dǎo)致不良行為磷蜀,例如導(dǎo)致紋理丟失召耘。要理解為什么會(huì)發(fā)生這種情況,請(qǐng)參閱資產(chǎn)褐隆,對(duì)象和序列化章節(jié)的“ 對(duì)象間參考”部分污它。
管理資產(chǎn)和AssetBundles時(shí)要了解的最重要的一點(diǎn)是,在為UnloadAllLoadedObjects參數(shù)調(diào)用AssetBundle.Unload時(shí),行為與true或false 一致衫贬。
該API將卸載正在調(diào)用的AssetBundle的頭信息德澈。所述unloadAllLoadedObjects參數(shù)確定是否還從卸載這個(gè)AssetBundle實(shí)例化的所有對(duì)象。如果設(shè)置為true固惯,則源自AssetBundle的所有對(duì)象也將立即被卸載 - 即使它們當(dāng)前正在活動(dòng)場(chǎng)景中使用梆造。
例如,假設(shè)材料M是從AssetBundle AB加載的葬毫,并且假設(shè)M當(dāng)前處于活動(dòng)場(chǎng)景中镇辉。
如果調(diào)用了AssetBundle.Unload(true),那么M將從場(chǎng)景中移除贴捡,銷毀并卸載忽肛。但是,如果調(diào)用AssetBundle.Unload(false)烂斋,則AB的標(biāo)題信息將被卸載屹逛,但M仍將保留在場(chǎng)景中并且仍然有效。調(diào)用AssetBundle.Unload(false)會(huì)中斷M和AB之間的鏈接汛骂。如果稍后再次加載AB煎源,則AB中包含的對(duì)象的新副本將被加載到內(nèi)存中。
如果稍后再次加載AB香缺,則將重新加載AssetBundle標(biāo)題信息的新副本手销。但是,M沒有從AB的這個(gè)新副本中加載图张。Unity不會(huì)在AB和M的新副本之間建立任何關(guān)聯(lián)锋拖。
如果AssetBundle.LoadAsset()被稱為重載中號(hào),統(tǒng)一將不能解釋的舊副本中號(hào)為數(shù)據(jù)的實(shí)例AB祸轮。因此兽埃,Unity將加載M的新副本,并且在場(chǎng)景中將有兩個(gè)相同的M副本适袜。
對(duì)于大多數(shù)項(xiàng)目來說柄错,這種行為是不可取的。大多數(shù)項(xiàng)目應(yīng)該使用AssetBundle.Unload(true)并采用一種方法來確保對(duì)象不重復(fù)苦酱。兩種常用方法是:
在應(yīng)用程序的整個(gè)生命周期(例如在級(jí)別之間或在加載屏幕期間)卸載臨時(shí)AssetBundles時(shí)具有明確定義的點(diǎn)售貌。這是更簡單和最常見的選擇。
僅當(dāng)各個(gè)對(duì)象的所有構(gòu)成對(duì)象都未使用時(shí)疫萤,才維護(hù)各個(gè)對(duì)象的引用計(jì)數(shù)并卸載AssetBundles颂跨。這允許應(yīng)用程序卸載并重新加載單個(gè)對(duì)象而不復(fù)制內(nèi)存。
如果應(yīng)用程序必須使用AssetBundle.Unload(false)扯饶,那么單個(gè)對(duì)象只能通過兩種方式卸載:
在場(chǎng)景和代碼中消除對(duì)不需要的對(duì)象的所有引用恒削。完成后池颈,調(diào)用Resources.UnloadUnusedAssets。
非加性地加載場(chǎng)景钓丰。這將銷毀當(dāng)前場(chǎng)景中的所有對(duì)象并自動(dòng)調(diào)用Resources.UnloadUnusedAssets躯砰。
如果一個(gè)項(xiàng)目有明確定義的點(diǎn),可以使用戶等待對(duì)象加載和卸載(例如在游戲模式或級(jí)別之間)携丁,則應(yīng)使用這些點(diǎn)來卸載盡可能多的對(duì)象并加載新的對(duì)象琢歇。
最簡單的方法是將項(xiàng)目的離散塊打包到場(chǎng)景中,然后將這些場(chǎng)景及其所有依賴項(xiàng)構(gòu)建到AssetBundles中则北。然后矿微,應(yīng)用程序可以進(jìn)入“加載”場(chǎng)景,完全卸載包含舊場(chǎng)景的AssetBundle尚揣,然后加載包含新場(chǎng)景的AssetBundle涌矢。
雖然這是最簡單的流程,但有些項(xiàng)目需要更復(fù)雜的AssetBundle管理快骗。由于每個(gè)項(xiàng)目都不同娜庇,因此沒有通用的AssetBundle設(shè)計(jì)模式。
在決定如何將對(duì)象分組為AssetBundles時(shí)方篮,通常最好先將對(duì)象綁定到AssetBundles中名秀,如果它們必須同時(shí)加載或更新。例如藕溅,考慮角色扮演游戲匕得。個(gè)別地圖和過場(chǎng)動(dòng)畫可按場(chǎng)景分組為AssetBundles,但在大多數(shù)場(chǎng)景中都需要一些對(duì)象巾表≈樱可以構(gòu)建AssetBundles來提供肖像,游戲內(nèi)用戶界面以及不同的角色模型和紋理集币。后面的對(duì)象和資產(chǎn)可以被分組到第二組資產(chǎn)包中考阱,這些資產(chǎn)包在啟動(dòng)時(shí)加載并在應(yīng)用的整個(gè)生命周期內(nèi)保持加載狀態(tài)。
如果Unity必須在AssetBundle卸載后從它的AssetBundle重新加載對(duì)象鞠苟,則可能會(huì)出現(xiàn)另一個(gè)問題乞榨。在這種情況下,重新加載將失敗当娱,對(duì)象將作為(缺失)對(duì)象出現(xiàn)在Unity編輯器的層次結(jié)構(gòu)中吃既。
這主要發(fā)生在Unity丟失并恢復(fù)對(duì)其圖形上下文的控制時(shí),例如趾访,當(dāng)移動(dòng)應(yīng)用程序被暫吞恚或用戶鎖定其PC時(shí)。在這種情況下扼鞋,Unity必須將紋理和著色器重新上傳到GPU申鱼。如果這些資產(chǎn)的源AssetBundle不可用,則應(yīng)用程序?qū)⒁匝蠹t色呈現(xiàn)場(chǎng)景中的對(duì)象云头。
4.2捐友。分配
將項(xiàng)目的AssetBundles分發(fā)給客戶端有兩種基本方式:與項(xiàng)目同時(shí)安裝或在安裝后下載。
是否在安裝之后或之后發(fā)布AssetBundles的決定是由項(xiàng)目運(yùn)行平臺(tái)的功能和限制決定的溃槐。移動(dòng)項(xiàng)目通常選擇安裝后下載匣砖,以減少初始安裝大小并保持在無線下載大小限制以下』璧危控制臺(tái)和PC項(xiàng)目通常會(huì)在AssetBundles初始安裝時(shí)運(yùn)送猴鲫。
正確的體系結(jié)構(gòu)允許在安裝后將新內(nèi)容或修訂的內(nèi)容修補(bǔ)到補(bǔ)丁中,而不管最初如何交付AssetBundles谣殊。有關(guān)這方面的更多信息拂共,請(qǐng)參閱Unity手冊(cè)的“ 使用AssetBundles進(jìn)行修補(bǔ)”部分。
4.2.1姻几。隨項(xiàng)目一起發(fā)貨
將AssetBundles與項(xiàng)目一起發(fā)送是最簡單的發(fā)布方式宜狐,因?yàn)樗恍枰~外的下載管理代碼。為什么一個(gè)項(xiàng)目可能會(huì)在安裝時(shí)包含AssetBundles有兩個(gè)主要原因:
減少項(xiàng)目構(gòu)建時(shí)間并允許更簡單的迭代開發(fā)蛇捌。如果這些AssetBundles不需要與應(yīng)用程序本身分開更新抚恒,那么AssetBundles可以通過將資產(chǎn)包存儲(chǔ)在流式資產(chǎn)中而包含在應(yīng)用程序中。請(qǐng)參閱下面的流媒體資源部分络拌。
發(fā)布可更新內(nèi)容的初始版本俭驮。通常這樣做是為了節(jié)省最終用戶在初次安裝后的時(shí)間,或者作為以后打補(bǔ)丁的基礎(chǔ)春贸。流式資產(chǎn)對(duì)于這種情況并不理想混萝。但是,如果編寫自定義下載和緩存系統(tǒng)不是一種選擇祥诽,則可以從Streaming Assets將可更新內(nèi)容的初始修訂加載到Unity緩存中譬圣。
4.2.1.1。流媒體資產(chǎn)
在安裝時(shí)在Unity應(yīng)用程序中包含任何類型的內(nèi)容(包括AssetBundles)的最簡單方法是在構(gòu)建項(xiàng)目之前將內(nèi)容構(gòu)建到/ Assets / StreamingAssets /文件夾中雄坪。構(gòu)建時(shí)包含在StreamingAssets文件夾中的任何內(nèi)容都將被復(fù)制到最終的應(yīng)用程序中厘熟。
本地存儲(chǔ)上StreamingAssets文件夾的完整路徑可在運(yùn)行時(shí)通過屬性Application.streamingAssetsPath訪問。然后可以在大多數(shù)平臺(tái)上通過AssetBundle.LoadFromFile加載AssetBundles维哈。
Android開發(fā)人員:在Android上绳姨,StreamingAssets文件夾中的資源存儲(chǔ)到APK中,并且可能需要更多時(shí)間才能加載(因?yàn)榇鎯?chǔ)在APK中的文件可能使用不同的存儲(chǔ)算法)阔挠。使用的算法可能會(huì)因Unity版本而異飘庄。您可以使用7-zip等存檔器打開APK以確定文件是否被壓縮。如果是這樣购撼,您可以期望AssetBundle.LoadFromFile()執(zhí)行得更慢跪削。在這種情況下谴仙,您可以使用 UnityWebRequest.GetAssetBundle檢索緩存版本作為解決方法。通過使用UnityWebRequest碾盐,AssetBundle將在第一次運(yùn)行期間解壓縮并緩存晃跺,從而使后續(xù)執(zhí)行速度更快。請(qǐng)注意毫玖,這將需要更多的存儲(chǔ)空間掀虎,因?yàn)锳ssetBundle將被復(fù)制到緩存中「斗悖或者烹玉,您可以導(dǎo)出您的Gradle項(xiàng)目,并在構(gòu)建時(shí)向您的AssetBundles添加擴(kuò)展阐滩。然后二打,您可以編輯build.gradle文件并將該擴(kuò)展名添加到noCompress部分。完成后叶眉,您應(yīng)該可以使用AssetBundle.LoadFromFile()而無需支付解壓縮性能成本址儒。
注意:流式資產(chǎn)在某些平臺(tái)上不是可寫位置。如果安裝后需要更新項(xiàng)目的AssetBundles衅疙,則可以使用WWW.LoadFromCacheOrDownload或編寫自定義下載程序莲趣。
4.2.2。下載后安裝
將AssetBundles交付給移動(dòng)設(shè)備的最佳方法是在應(yīng)用程序安裝后下載它們饱溢。這也允許在安裝后更新內(nèi)容而不強(qiáng)制用戶重新下載整個(gè)應(yīng)用程序喧伞。在許多平臺(tái)上,應(yīng)用程序二進(jìn)制文件必須經(jīng)過昂貴且冗長的重新認(rèn)證過程绩郎。因此潘鲫,開發(fā)一個(gè)良好的安裝后下載系統(tǒng)至關(guān)重要。
交付AssetBundles的最簡單方法是將它們放置在Web服務(wù)器上并通過UnityWebRequest交付肋杖。Unity會(huì)自動(dòng)將下載的AssetBundles緩存在本地存儲(chǔ)上溉仑。如果下載的AssetBundle是LZMA壓縮的,則AssetBundle將以未壓縮或重新壓縮為LZ4(取決于Caching.compressionEnabled設(shè)置)存儲(chǔ)在緩存中状植,以便將來加載更快浊竟。如果下載的捆綁包壓縮了LZ4,則AssetBundle將被壓縮存儲(chǔ)津畸。如果緩存填滿振定,Unity將從緩存中刪除最近最少使用的AssetBundle。有關(guān)更多詳細(xì)信息肉拓,請(qǐng)參閱內(nèi)置緩存部分后频。
通常建議使用來啟動(dòng)UnityWebRequest如果可能,或WWW.LoadFromCacheOrDownload只有使用Unity 5.2或以上暖途。如果內(nèi)置API的內(nèi)存消耗卑惜,緩存行為或性能對(duì)于特定項(xiàng)目不可接受膏执,或者項(xiàng)目必須運(yùn)行特定于平臺(tái)的代碼以實(shí)現(xiàn)其要求,則只投資于定制下載系統(tǒng)残揉。
可能阻止使用UnityWebRequest或WWW.LoadFromCacheOrDownload的情況示例:
當(dāng)需要對(duì)AssetBundle緩存進(jìn)行細(xì)粒度控制時(shí)
當(dāng)項(xiàng)目需要實(shí)施自定義壓縮策略時(shí)
-
當(dāng)項(xiàng)目希望使用平臺(tái)特定的API來滿足某些要求時(shí)胧后,例如需要在非活動(dòng)狀態(tài)下傳輸數(shù)據(jù)芋浮。
- 示例:使用iOS的后臺(tái)任務(wù)API在后臺(tái)下載數(shù)據(jù)抱环。
如果AssetBundles必須在Unity沒有適當(dāng)?shù)腟SL支持的平臺(tái)(如PC)上通過SSL提供。
4.2.3纸巷。內(nèi)置緩存
Unity有一個(gè)內(nèi)置的AssetBundle緩存系統(tǒng)镇草,可用于緩存通過UnityWebRequest API 下載的AssetBundles,該API包含一個(gè)接受AssetBundle版本號(hào)作為參數(shù)的重載瘤旨。此編號(hào)不存儲(chǔ)在AssetBundle內(nèi)部梯啤,并且不由 AssetBundle系統(tǒng)生成。
緩存系統(tǒng)跟蹤傳遞給UnityWebRequest的最新版本號(hào)存哲。當(dāng)使用版本號(hào)調(diào)用此API時(shí)因宇,高速緩存系統(tǒng)通過比較版本號(hào)來檢查是否存在緩存的AssetBundle。如果這些數(shù)字匹配祟偷,系統(tǒng)將加載緩存的AssetBundle察滑。如果數(shù)字不匹配,或沒有緩存的AssetBundle修肠,Unity將下載一個(gè)新副本贺辰。這個(gè)新副本將與新版本號(hào)相關(guān)聯(lián)。
緩存系統(tǒng)中的AssetBundles僅由其文件名來標(biāo)識(shí)嵌施,而不是由其下載的完整URL標(biāo)識(shí)饲化。這意味著具有相同文件名的AssetBundle可以存儲(chǔ)在多個(gè)不同的位置,例如內(nèi)容傳送網(wǎng)絡(luò)吗伤。只要文件名稱相同吃靠,緩存系統(tǒng)就會(huì)將它們識(shí)別為相同的AssetBundle。
每個(gè)應(yīng)用程序都要決定將版本號(hào)分配給AssetBundles的適當(dāng)策略足淆,并將這些數(shù)字傳遞給UnityWebRequest巢块。這些數(shù)字可能來自各種唯一標(biāo)識(shí)符,例如CRC值缸浦。請(qǐng)注意夕冲,雖然AssetBundleManifest.GetAssetBundleHash()也可用于此目的,但我們不建議使用此功能進(jìn)行版本控制裂逐,因?yàn)樗鼉H提供估算值歹鱼,而不是真正的散列值計(jì)算)。
有關(guān)更多詳細(xì)信息卜高,請(qǐng)參閱Unity手冊(cè)的“ 使用AssetBundles進(jìn)行修補(bǔ)”部分弥姻。
在Unity 2017.1之后南片,緩存 API已經(jīng)被擴(kuò)展,以允許開發(fā)人員從多個(gè)緩存中選擇一個(gè)活動(dòng)緩存庭敦,以提供更精細(xì)的控制疼进。以前的Unity版本只能修改Caching.expirationDelay和Caching.maximumAvailableDiskSpace以刪除緩存的項(xiàng)目(這些屬性保留在Cache類中的Unity 2017.1中)。
expirationDelay是自動(dòng)刪除AssetBundle之前必須經(jīng)過的最小秒數(shù)秧廉。如果在此期間沒有訪問AssetBundle伞广,它將被自動(dòng)刪除。
maximumAvailableDiskSpace指定本地存儲(chǔ)空間量(以字節(jié)為單位)疼电,緩存在開始刪除最近使用過的最近比expirationDelay使用的AssetBundle之前可能會(huì)使用的空間量嚼锄。達(dá)到限制時(shí),Unity將刪除最近最少打開的緩存中的AssetBundle(或通過Caching.MarkAsUsed標(biāo)記為已使用)蔽豺。Unity會(huì)刪除緩存的AssetBundles区丑,直到有足夠的空間完成新的下載為止。
4.2.3.1修陡。緩存啟動(dòng)
由于AssetBundles是通過其文件名來標(biāo)識(shí)的沧侥,因此可以使用應(yīng)用程序隨附的AssetBundles來“啟動(dòng)”緩存。為此魄鸦,請(qǐng)將每個(gè)AssetBundle的初始版本或基本版本存儲(chǔ)在/ Assets / StreamingAssets /中宴杀。該過程與“項(xiàng)目發(fā)貨”部分中詳細(xì)介紹的過程相同。
第一次運(yùn)行應(yīng)用程序時(shí)号杏,可以通過從Application.streamingAssetsPath加載AssetBundles來填充緩存婴氮。從此,應(yīng)用程序可以正常調(diào)用UnityWebRequest(UnityWebRequest也可用于最初從StreamingAssets路徑加載AssetBundles)盾致。
4.2.3主经。自定義下載程序
編寫自定義下載程序可讓應(yīng)用程序完全控制AssetBundles的下載,解壓縮和存儲(chǔ)方式庭惜。由于所涉及的工程工作并不平凡罩驻,我們只為大型團(tuán)隊(duì)推薦這種方法。編寫自定義下載器時(shí)有四個(gè)主要考慮事項(xiàng):
下載機(jī)制
存儲(chǔ)位置
壓縮類型
修補(bǔ)
有關(guān)修補(bǔ)AssetBundles的信息护赊,請(qǐng)參閱使用AssetBundles修補(bǔ)部分惠遏。
4.2.3.1。下載
對(duì)于大多數(shù)應(yīng)用程序骏啰,HTTP是下載AssetBundles最簡單的方法节吮。但是,實(shí)現(xiàn)基于HTTP的下載程序不是最簡單的任務(wù)判耕。自定義下載程序必須避免過多的內(nèi)存分配透绩,過多的線程使用和過多的線程喚醒。Unity的WWW類不適合這里詳盡描述的原因。
在編寫自定義下載器時(shí)帚豪,有三個(gè)選項(xiàng):
C#的HttpWebRequest和WebClient類
自定義本機(jī)插件
資產(chǎn)商店包
4.2.3.1.1碳竟。C#類
如果應(yīng)用程序不需要HTTPS / SSL支持,則C#的WebClient類提供了下載AssetBundles最簡單的機(jī)制狸臣。它能夠?qū)⑷魏挝募苯赢惒较螺d到本地存儲(chǔ)莹桅,而無需過多管理內(nèi)存分配。
要使用WebClient下載AssetBundle烛亦,請(qǐng)分配該類的一個(gè)實(shí)例诈泼,并將其傳遞給AssetBundle的URL以下載和目標(biāo)路徑。如果需要對(duì)請(qǐng)求參數(shù)進(jìn)行更多控制此洲,可以使用C#的HttpWebRequest類編寫下載程序:
從HttpWebResponse.GetResponseStream獲取一個(gè)字節(jié)流厂汗。
在堆棧上分配一個(gè)固定大小的字節(jié)緩沖區(qū)。
從響應(yīng)流中讀入緩沖區(qū)呜师。
使用C#的File.IO API或任何其他流式IO系統(tǒng)將緩沖區(qū)寫入磁盤。
4.2.3.1.2贾节。資產(chǎn)商店包
多個(gè)資產(chǎn)商店軟件包提供本地代碼實(shí)現(xiàn)汁汗,以通過HTTP,HTTPS和其他協(xié)議下載文件栗涂。在為Unity編寫自定義本機(jī)代碼插件之前知牌,建議您先評(píng)估可用的Asset Store軟件包。
4.2.3.1.3斤程。自定義原生插件
編寫自定義本機(jī)插件是在Unity中下載數(shù)據(jù)最耗時(shí)角寸,但最靈活的方法。由于編程時(shí)間要求高且技術(shù)風(fēng)險(xiǎn)高忿墅,只有在沒有其他方法能夠滿足應(yīng)用程序要求的情況下才推薦此方法扁藕。例如,如果應(yīng)用程序必須在Unity中沒有C#SSL支持的平臺(tái)上使用SSL通信疚脐,則可能需要定制本機(jī)插件亿柑。
自定義本機(jī)插件通常會(huì)包裝目標(biāo)平臺(tái)的本機(jī)下載API。示例包括iOS 上的NSURLConnection和Android 上的java.net.HttpURLConnection棍弄。請(qǐng)參閱每個(gè)平臺(tái)的本機(jī)文檔以獲取有關(guān)使用這些API的更多詳細(xì)信息望薄。
4.2.3.2。存儲(chǔ)
在所有平臺(tái)上呼畸,Application.persistentDataPath指向一個(gè)可寫的位置痕支,應(yīng)該用于存儲(chǔ)應(yīng)該在應(yīng)用程序運(yùn)行之間保持的數(shù)據(jù)。在編寫自定義下載器時(shí)蛮原,強(qiáng)烈建議使用Application.persistentDataPath的子目錄來存儲(chǔ)下載的數(shù)據(jù)卧须。
Application.streamingAssetPath不可寫,對(duì)于AssetBundle緩存是一個(gè)糟糕的選擇。streamingAssetsPath的示例位置包括:
OSX:在.app包內(nèi); 不可寫故慈。
Windows:在安裝目錄中(例如Program Files); 通常不可寫
iOS:在.ipa包內(nèi); 不可寫
Android:在.apk文件中; 不可寫
4.3板熊。資產(chǎn)分配策略
決定如何將項(xiàng)目資產(chǎn)劃分為AssetBundles并不簡單。很容易采用簡單的策略察绷,比如將所有對(duì)象放置在自己的AssetBundle中或僅使用一個(gè)AssetBundle干签,但這些解決方案具有明顯的缺點(diǎn):
-
擁有太少的AssetBundles ...
增加運(yùn)行時(shí)內(nèi)存使用量
增加加載時(shí)間
需要更大的下載量
-
擁有太多的AssetBundles ...
增加構(gòu)建時(shí)間
可能會(huì)使開發(fā)復(fù)雜化
增加總下載時(shí)間
關(guān)鍵的決定是如何將對(duì)象分組為AssetBundles。主要戰(zhàn)略是:
邏輯實(shí)體
對(duì)象類型
并發(fā)內(nèi)容
有關(guān)這些分組策略的更多信息可以在手冊(cè)中找到拆撼。
4.4容劳。常見的陷阱
本節(jié)介紹使用AssetBundles項(xiàng)目中常見的幾個(gè)問題。
4.5.1闸度。資產(chǎn)重復(fù)
Unity 5的AssetBundle系統(tǒng)將在對(duì)象構(gòu)建到AssetBundle中時(shí)發(fā)現(xiàn)對(duì)象的所有依賴關(guān)系竭贩。此依賴關(guān)系信息用于確定將包含在AssetBundle中的一組對(duì)象。
明確分配給AssetBundle的對(duì)象只會(huì)構(gòu)建到該AssetBundle中莺禁。當(dāng)Object的AssetImporter的assetBundleName屬性設(shè)置為非空字符串時(shí)赖舟,對(duì)象將被“顯式分配” 财搁。這可以在Unity Editor中通過在對(duì)象的檢查器中選擇一個(gè)AssetBundle或從編輯器腳本中完成。
也可以將對(duì)象分配給AssetBundle,方法是將它們定義為AssetBundle構(gòu)建映射的一部分缕棵,該映射要與重載的BuildPipeline.BuildAssetBundles()函數(shù)一起使用蒸走,該函數(shù)接受一組AssetBundleBuild锥债。
在AssetBundle中未明確分配的任何對(duì)象都將包含在所有包含引用未標(biāo)記對(duì)象的1個(gè)或多個(gè)對(duì)象的AssetBundle中岸售。
例如,如果將兩個(gè)不同的對(duì)象分配給兩個(gè)不同的AssetBundles翰灾,但都具有對(duì)公共依賴項(xiàng)Object的引用缕粹,則該依賴Object將被復(fù)制到兩個(gè)AssetBundles中。重復(fù)的依賴關(guān)系也將被實(shí)例化纸淮,這意味著依賴關(guān)系對(duì)象的兩個(gè)副本將被視為具有不同標(biāo)識(shí)符的不同對(duì)象平斩。這將增加應(yīng)用程序AssetBundles的總大小。如果應(yīng)用程序加載它的父項(xiàng)萎馅,這也會(huì)導(dǎo)致Object的兩個(gè)不同副本被加載到內(nèi)存中双戳。
有幾種方法可以解決這個(gè)問題:
-
確保構(gòu)建到不同AssetBundles中的對(duì)象不共享依賴關(guān)系。任何共享依賴關(guān)系的對(duì)象都可以放置在同一個(gè)AssetBundle中糜芳,而不需要重復(fù)依賴關(guān)系飒货。
- 對(duì)于具有許多共享依賴項(xiàng)目的項(xiàng)目,此方法通常不可行峭竣。它生產(chǎn)的單片AssetBundles必須經(jīng)常重建和重新下載塘辅,以避免方便或高效。
-
分段AssetBundles皆撩,以便不會(huì)同時(shí)加載共享依賴關(guān)系的兩個(gè)AssetBundles扣墩。
- 此方法可能適用于某些類型的項(xiàng)目哲银,例如基于級(jí)別的游戲。但是呻惕,它仍然會(huì)不必要地增加項(xiàng)目的AssetBundles的大小荆责,并增加構(gòu)建時(shí)間和加載時(shí)間。
確保所有依賴項(xiàng)資產(chǎn)都內(nèi)置到自己的AssetBundles中亚脆。這完全消除了重復(fù)資產(chǎn)的風(fēng)險(xiǎn)做院,但也帶來了復(fù)雜性。應(yīng)用程序必須跟蹤AssetBundles之間的依賴關(guān)系濒持,并確保在調(diào)用任何AssetBundle.LoadAsset API 之前加載了正確的AssetBundles 键耕。
通過位于UnityEditor名稱空間中的AssetDatabase API 跟蹤對(duì)象依賴關(guān)系。正如命名空間所暗示的柑营,這個(gè)API僅在Unity編輯器中可用屈雄,而不是在運(yùn)行時(shí)。AssetDatabase.GetDependencies可用于查找特定對(duì)象或資產(chǎn)的所有直接依賴關(guān)系官套。請(qǐng)注意酒奶,這些依賴關(guān)系可能有其自己的依賴關(guān)系。此外虏杰,AssetImporter API可用于查詢分配有任何特定對(duì)象的AssetBundle讥蟆。
通過組合AssetDatabase和AssetImporter API,可以編寫一個(gè)編輯器腳本纺阔,以確保將所有AssetBundle的直接或間接依賴關(guān)系分配給AssetBundles,或者沒有兩個(gè)AssetBundle共享尚未分配給AssetBundle的依賴關(guān)系修然。由于復(fù)制資產(chǎn)的內(nèi)存成本笛钝,建議所有項(xiàng)目都有這樣的腳本。
4.5.2愕宋。雪碧地圖集重復(fù)
任何自動(dòng)生成的精靈圖集將被分配給包含從其生成精靈圖集的精靈對(duì)象的AssetBundle玻靡。如果精靈對(duì)象被分配給多個(gè)AssetBundles,那么精靈圖集將不會(huì)被分配給一個(gè)AssetBundle并且將被復(fù)制中贝。如果Sprite對(duì)象未分配給AssetBundle囤捻,則精靈地圖集也不會(huì)被分配給AssetBundle。
為了確保精靈圖集沒有重復(fù)邻寿,請(qǐng)檢查標(biāo)記在同一精靈圖集中的所有精靈是否被分配到同一個(gè)AssetBundle蝎土。
請(qǐng)注意,在Unity 5.2.2p3及更早版本中绣否,自動(dòng)生成的精靈地圖將永遠(yuǎn)不會(huì)分配給AssetBundle誊涯。因此,它們將被包含在包含其組成精靈的任何AssetBundles中蒜撮,以及任何引用其組成精靈的AssetBundles中暴构。由于這個(gè)問題,強(qiáng)烈建議所有使用Unity的sprite打包程序的Unity 5項(xiàng)目升級(jí)到Unity 5.2.2p4,5.3或任何更新版本的Unity。
4.5.3取逾。Android紋理
由于Android生態(tài)系統(tǒng)中的設(shè)備碎片較多耗绿,通常需要將紋理壓縮為多種不同的格式。雖然所有Android設(shè)備都支持ETC1砾隅,但ETC1不支持帶alpha通道的紋理误阻。如果應(yīng)用程序不需要OpenGL ES 2支持,則解決該問題的最簡單方法是使用所有Android OpenGL ES 3設(shè)備支持的ETC2琉用。
大多數(shù)應(yīng)用程序需要在ETC2支持不可用的舊設(shè)備上發(fā)貨堕绩。解決此問題的一種方法是使用Unity 5的AssetBundle變體(有關(guān)其他選項(xiàng)的詳細(xì)信息,請(qǐng)參閱Unity的Android優(yōu)化指南)邑时。
要使用AssetBundle變體奴紧,所有無法使用ETC1進(jìn)行干凈壓縮的紋理必須分離為僅紋理的AssetBundles。接下來晶丘,使用特定于供應(yīng)商的紋理壓縮格式(如DXT5黍氮,PVRTC和ATITC)創(chuàng)建足夠的這些AssetBundles變體以支持Android生態(tài)系統(tǒng)的非ETC2功能片。對(duì)于每個(gè)AssetBundle Variant浅浮,將包含的紋理的TextureImporter設(shè)置更改為適合Variant的壓縮格式沫浆。
在運(yùn)行時(shí),可以使用SystemInfo.SupportsTextureFormat API 檢測(cè)對(duì)不同紋理壓縮格式的支持滚秩。應(yīng)該使用此信息來選擇和加載包含以受支持格式壓縮的紋理的AssetBundle Variant专执。
有關(guān)Android紋理壓縮格式的更多信息可以在這里找到。
4.5.4郁油。iOS文件句柄過度使用
Unity的當(dāng)前版本不受此問題影響本股。
在Unity 5.3.2p2之前的版本中,Unity會(huì)在AssetBundle加載的整個(gè)過程中持有一個(gè)打開的文件句柄桐腌。這在大多數(shù)平臺(tái)上都不是問題拄显。但是,iOS將進(jìn)程可能同時(shí)打開的文件句柄數(shù)限制為255.如果加載AssetBundle會(huì)導(dǎo)致超出限制案站,則加載調(diào)用將失敗躬审,并顯示“Too Many Open File Handles”錯(cuò)誤。
對(duì)于嘗試將內(nèi)容分成數(shù)百或數(shù)千個(gè)AssetBundles的項(xiàng)目蟆盐,這是一個(gè)常見問題承边。
對(duì)于無法升級(jí)到補(bǔ)丁版本的Unity的項(xiàng)目,臨時(shí)解決方案是:
通過合并相關(guān)的AssetBundles來減少使用的AssetBundles的數(shù)量
使用AssetBundle.Unload(false)關(guān)閉AssetBundle的文件句柄舱禽,并手動(dòng)管理加載的對(duì)象的生命周期
4.5炒刁。AssetBundle變體
AssetBundle系統(tǒng)的一個(gè)關(guān)鍵特性是引入了AssetBundle變體。變體的目的是允許應(yīng)用程序調(diào)整其內(nèi)容以更好地適應(yīng)其運(yùn)行時(shí)環(huán)境誊稚。當(dāng)加載對(duì)象和解析實(shí)例ID引用時(shí)翔始,變體允許不同的AssetBundle文件中的不同UnityEngine.Objects顯示為“相同”對(duì)象罗心。從概念上講,它允許兩個(gè)UnityEngine.Objects顯示為共享相同的文件GUID和本地ID城瞎,并標(biāo)識(shí)實(shí)際的UnityEngine.Object以字符串變體ID加載渤闷。
這個(gè)系統(tǒng)有兩個(gè)主要用例:
-
變體簡化了適用于給定平臺(tái)的AssetBundles的加載。
- 示例:構(gòu)建系統(tǒng)可能會(huì)創(chuàng)建一個(gè)AssetBundle脖镀,其中包含適用于獨(dú)立DirectX11 Windows版本的高分辨率紋理和復(fù)雜著色器飒箭,以及適用于Android的具有較低保真度內(nèi)容的第二個(gè)AssetBundle。在運(yùn)行時(shí)蜒灰,項(xiàng)目的資源加載代碼可以為其平臺(tái)加載相應(yīng)的AssetBundle Variant弦蹂,并且傳遞到AssetBundle.Load API的對(duì)象名稱不需要更改。
-
變體允許應(yīng)用程序在同一平臺(tái)上加載不同的內(nèi)容强窖,但使用不同的硬件凸椿。
這是支持各種移動(dòng)設(shè)備的關(guān)鍵。在任何真實(shí)世界的應(yīng)用程序中翅溺,iPhone 4都不能像最新的iPhone一樣顯示相同的內(nèi)容保真度脑漫。
在Android上,AssetBundle Variants可用于解決設(shè)備間屏幕縱橫比和DPI之間巨大的分割問題咙崎。
4.5.1优幸。限制
AssetBundle Variant系統(tǒng)的一個(gè)關(guān)鍵限制是它需要使用不同的資產(chǎn)來構(gòu)建變體。即使這些資產(chǎn)之間的唯一差異是其導(dǎo)入設(shè)置褪猛,也適用此限制网杆。如果內(nèi)置到變體A和變體B中的紋理之間的唯一區(qū)別是在Unity紋理導(dǎo)入器中選擇的特定紋理壓縮算法,則變體A和變體B必須仍然是完全不同的資產(chǎn)伊滋。這意味著變體A和變體B必須是磁盤上的單獨(dú)文件跛璧。
這種限制使大型項(xiàng)目的管理復(fù)雜化,因?yàn)樘囟ㄙY產(chǎn)的多個(gè)副本必須保存在源代碼管理中新啼。當(dāng)開發(fā)人員希望更改資產(chǎn)的內(nèi)容時(shí),必須更新資產(chǎn)的所有副本刹碾。這個(gè)問題沒有內(nèi)置的解決方法燥撞。
大多數(shù)團(tuán)隊(duì)都實(shí)施他們自己的AssetBundle變體形式。這是通過建立AssetBundles來完成的迷帜,后者在文件名后面添加了明確的后綴物舒,以便識(shí)別給定AssetBundle所代表的特定變體。在構(gòu)建這些AssetBundles時(shí)戏锹,自定義代碼以編程方式更改包含的資產(chǎn)的導(dǎo)入器設(shè)置冠胯。一些開發(fā)者已經(jīng)擴(kuò)展了他們的定制系統(tǒng),以便能夠改變附屬于預(yù)制件的組件上的參數(shù)锦针。
4.6荠察。壓縮還是未壓縮置蜀?
是否壓縮AssetBundles需要一些重要的考慮因素,其中包括:
加載時(shí)間:從本地存儲(chǔ)或本地緩存加載時(shí)悉盆,未壓縮的AssetBundles比加載壓縮的AssetBundles要快得多盯荤。
構(gòu)建時(shí)間:在壓縮文件時(shí),LZMA和LZ4非常緩慢焕盟,Unity Editor按順序處理AssetBundles秋秤。具有大量AssetBundles的項(xiàng)目將花費(fèi)大量的時(shí)間壓縮它們。
應(yīng)用程序大小:如果AssetBundles在應(yīng)用程序中發(fā)貨脚翘,則壓縮它們將減少應(yīng)用程序的總大小灼卢。或者来农,可以在安裝后下載AssetBundles鞋真。
內(nèi)存使用情況:在Unity 5.3之前,所有Unity的解壓縮機(jī)制都要求在解壓縮之前將整個(gè)壓縮的AssetBundle加載到內(nèi)存中备图。如果內(nèi)存使用率很重要灿巧,請(qǐng)使用未壓縮或LZ4壓縮的AssetBundles。
下載時(shí)間:如果AssetBundles很大揽涮,或者用戶處于帶寬受限的環(huán)境中抠藕,例如在低速或計(jì)量連接上下載,則壓縮可能只需要蒋困。如果只有幾兆字節(jié)的數(shù)據(jù)通過高速連接傳送到PC盾似,則可能會(huì)忽略壓縮。
4.6.1雪标。緊縮壓縮
主要由使用Crunch壓縮算法的DXT壓縮紋理組成的束應(yīng)該被構(gòu)建為未壓縮的零院。
4.7。AssetBundles和WebGL
Unity強(qiáng)烈建議開發(fā)人員不要在WebGL項(xiàng)目上使用壓縮的AssetBundles村刨。
WebGL項(xiàng)目中的所有AssetBundle解壓縮和加載必須在主線程上進(jìn)行告抄。這是因?yàn)閁nity的WebGL導(dǎo)出選項(xiàng)當(dāng)前不支持工作線程。AssetBundles的下載使用XMLHttpRequest委托給瀏覽器嵌牺,XMLHttpRequest在Unity的主線程上執(zhí)行打洼。這意味著壓縮的AssetBundles在WebGL上加載非常昂貴。
如果您使用的是Unity 5.5或更高版本逆粹,請(qǐng)考慮避免使用LZMA作為您的AssetBundles募疮,并使用LZ4進(jìn)行壓縮,而不是按需解壓縮僻弹。如果您需要較小的壓縮大小阿浓,那么LZ4會(huì)提供,您可以配置您的Web服務(wù)器以在HTTP協(xié)議層面(在LZ4壓縮之上)對(duì)文件進(jìn)行g(shù)zip壓縮蹋绽。Unity 5.6將LZMA作為WebGL平臺(tái)的壓縮選項(xiàng)進(jìn)行刪除芭毙。