天天看點

Unity最佳實踐-AssetBundle使用模式Best Practices(5) - AssetBundle usage patterns

Best Practices(5) - AssetBundle usage patterns

  • 适用版本:2017.3
  • 原文位址:https://unity3d.com/cn/learn/tutorials/topics/best-practices/assetbundle-usage-patterns?playlist=30089

本系列的前一章介紹了AssetBundles的基礎知識,其中包括各種加載API的底層行為。 本章讨論在實踐中使用AssetBundles的各個方面的問題和可能的解決方案。

管理已加載的資産

在記憶體敏感的環境中仔細控制加載的對象的大小和數量非常重要。從目前激活的場景中移除時,Unity不會自動解除安裝對象。資産清理隻在特定時間觸發,當然也可以手動觸發。

AssetBundles本身必須小心管理。由本地存儲上的檔案(在Unity緩存中或通過AssetBundle.LoadFromFile加載的檔案)支援的AssetBundle具有最小的記憶體開銷(這裡應該是說,如果AB來自本地,加載AB隻會加載頭檔案,相較于從網絡上下載下傳的AB),很少超過幾萬位元組。但是,如果存在大量的AssetBundles,則此項開銷仍是一個大問題。

由于大多數項目允許使用者重新體驗内容(例如重新調整關卡),是以知道何時加載或解除安裝AssetBundle非常重要。如果AssetBundle解除安裝不當,可能會導緻記憶體中的對象重複。在某些情況下不當解除安裝AssetBundles也會導緻不良行為,例如導緻紋理丢失。要了解為什麼會發生這種情況,請參閱資産,對象和序列化章節的“對象間引用”部分。

管理資産和AssetBundles時要了解的最重要的一點是,在為AssetBundle.Unload的unloadAllLoadedObjects參數設定true或false時的行為差異。

該API将解除安裝正在調用的AssetBundle的頭檔案。 unloadAllLoadedObjects參數确定是否也解除安裝從此AssetBundle執行個體化的所有對象。如果設定為true,那麼從該AssetBundle執行個體化的所有對象也将立即被解除安裝 - 即使它們目前正在活動場景中使用。

例如,假設材質M是從AssetBundle AB加載的,并且假設M目前處于活動場景中。

Unity最佳實踐-AssetBundle使用模式Best Practices(5) - AssetBundle usage patterns

如果調用了AssetBundle.Unload(true),那麼M将從場景中移除,銷毀并解除安裝。 但是,如果調用AssetBundle.Unload(false),則AB的頭檔案将被解除安裝,但M仍将保留在場景中并且仍然有效。 調用AssetBundle.Unload(false)會中斷M和AB之間的連結。 如果稍後再次加載AB,則AB中包含的對象的新副本将被加載到記憶體中。

Unity最佳實踐-AssetBundle使用模式Best Practices(5) - AssetBundle usage patterns

如果稍後再次加載AB,則将重新加載AssetBundle頭檔案的新副本。 但是,M并未從AB的這個新副本中加載。 Unity沒有在AB和M的新副本之間建立任何關聯。

Unity最佳實踐-AssetBundle使用模式Best Practices(5) - AssetBundle usage patterns

如果調用AssetBundle.LoadAsset()來重新加載M,則Unity不會将M的舊副本解釋為AB中資料的一個執行個體。 是以,Unity将加載M的新副本,并且在場景中将有兩個相同的M副本。

Unity最佳實踐-AssetBundle使用模式Best Practices(5) - AssetBundle usage patterns

對于大多數項目來說,這種行為是不可取的。大多數項目應該使用AssetBundle.Unload(true)并采用一種方法來確定對象不重複。兩種常用方法是:

  • 在應用程式的整個生命周期中特定的時間點(例如在關卡之間或在轉場讀條期間)解除安裝臨時的AssetBundles。這是更簡單和最常見的選擇。
  • 為每個執行個體維護一個引用計數,這樣就可以僅在所有執行個體對象都不使用的時候,解除安裝AssetBundle。這允許應用程式解除安裝并重新加載單個對象而不重複消耗記憶體。

如果應用程式必須使用AssetBundle.Unload(false),那麼單個對象隻能通過兩種方式解除安裝:

  • 在場景和代碼中消除對不需要的對象的所有引用。然後,調用Resources.UnloadUnusedAssets。
  • 非疊加地加載場景。這将銷毀目前場景中的所有對象并自動調用Resources.UnloadUnusedAssets。

如果一個項目有明确定義的解除安裝時間點,可以使使用者等待對象加載和解除安裝(例如在遊戲轉場之間),那麼應使用這些點來解除安裝盡可能多的對象并加載新的對象。

最簡單的方法是将項目的離散塊打包到場景中,然後将這些場景及其所有依賴項建構到AssetBundles中。然後,應用程式可以進入“加載”場景,完全解除安裝包含舊場景的AssetBundle,然後加載包含新場景的AssetBundle。

雖然這是最簡單的流程,但有些項目需要更複雜的AssetBundle管理。由于每個項目都不同,是以沒有通用的AssetBundle設計模式。

在決定如何将對象分組為AssetBundles時,如果必須同時加載或更新對象,通常最好先将對象捆綁到AssetBundles中。例如,考慮角色扮演遊戲。個别地圖和過場動畫可按場景分組為AssetBundles,但在大多數場景中都需要一些對象。可以建構AssetBundles來提供肖像,遊戲中的使用者界面以及不同的角色模型和紋理。後面的對象和資産可以被分組到第二組資産包中,這些資産包在啟動時加載并在應用的整個生命周期内保持加載狀态。

如果Unity必須在AssetBundle解除安裝後從它的AssetBundle重新加載對象,則可能會出現另一個問題。在這種情況下,重新加載将失敗,對象将作為(缺失)對象出現在Unity編輯器的層次結構中。

這主要發生在Unity丢失并恢複對其圖形上下文的控制時,例如,當移動應用程式被暫停或使用者鎖定其PC時。在這種情況下,Unity必須将紋理和着色器重新上傳到GPU。如果這些資産的源AssetBundle不可用,則應用程式将以洋紅色呈現場景中的對象。

1.2 分發

将項目的AssetBundles分發給用戶端有兩種基本方式:與項目同時安裝或在安裝後下載下傳。

移動項目通常選擇後一種。 主機和PC項目通常選擇前一種。

正确的程式架構允許在安裝後将新内容或修補的内容作為更新檔更新,而不用關心初始的AssetBundles。 有關這方面的更多資訊,請參閱Unity手冊的“使用AssetBundles進行修補”部分。

1.2.1 跟随項目釋出

将AssetBundles與項目一起釋出是最簡單的釋出方式,因為它不需要額外的下載下傳管理代碼。 為什麼一個項目需要在安裝時包含AssetBundles有兩個主要原因:

  • 減少項目建構時間并允許更簡單的疊代開發。 如果這些AssetBundles不需要與應用程式本身分開更新,那麼AssetBundles可以通過将資産包存儲在StreamingAsset中進而包含在應用程式中。 請參閱下面的StreamingAsset部分。
  • 釋出可更新内容的初始版本。 通常這樣做是為了節省最終使用者在初次安裝後的時間,或者作為以後打更新檔的基礎。 SteamingAsset對于這種情況并不是最理想的方案。 但是,如果不願意編寫一個自定義下載下傳和緩存的系統,那麼可以使用這種方法,從StreamingAssets将可更新内容的初始修訂加載到Unity緩存中。

1.2.1.1 StreamingAsset

想在安裝時,讓Unity應用程式包含任何類型的内容(包括AssetBundles)的最簡單方法是在建構項目之前将内容建構到/Assets/StreamingAssets 檔案夾中。建構時包含在StreamingAssets檔案夾中的任何内容都将被複制到最終的應用程式中。

本地存儲的StreamingAssets檔案夾的完整路徑可在運作時通過屬性Application.streamingAssetsPath通路。然後可以在大多數平台上通過AssetBundle.LoadFromFile加載AssetBundles。

Android開發人員:在Android上,StreamingAssets檔案夾中的資源會存儲到APK中,并且可能需要更多時間才能加載(因為存儲在APK中的檔案可能使用不同的存儲算法)。使用的算法可能會因Unity版本而異。您可以使用7-zip等解壓工具打開APK以确定檔案是否被壓縮。如果被壓縮,AssetBundle.LoadFromFile()執行得更慢。在這種情況下,您可以使用UnityWebRequest.GetAssetBundle作為一個優化方法來檢索是否有已經緩存的版本。通過使用UnityWebRequest.AssetBundle将在第一次運作期間解壓縮并緩存,進而使後續執行速度更快。請注意,這将需要更多的存儲空間,因為AssetBundle将被複制到緩存中。或者,您可以導出您的Gradle項目,并在建構時向您的AssetBundles添加擴充。然後,您可以編輯build.gradle檔案并将該擴充名添加到noCompress部分。完成後,您應該可以使用AssetBundle.LoadFromFile()而無需支付解壓縮性能成本。

注意:StreamingAsset在某些平台上不是可寫檔案。如果安裝後需要更新項目的AssetBundles,則可以使用WWW.LoadFromCacheOrDownload或編寫自定義下載下傳程式。

1.2.2 下載下傳後安裝

将AssetBundles部署到移動裝置的最佳方法是在安裝應用程式後下載下傳它們。這也允許在安裝後更新内容而不強制使用者重新下載下傳整個應用程式。在許多平台上,應用程式二進制檔案必須經過昂貴且冗長的重新認證過程。是以,開發一個良好的安裝後下載下傳系統至關重要。

傳遞AssetBundles的最簡單方法是将它們放置在Web伺服器上并通過UnityWebRequest部署。 Unity會自動将下載下傳的AssetBundles緩存在本地存儲上。如果下載下傳的AssetBundle是LZMA壓縮的,則AssetBundle将以未壓縮或重新壓縮為LZ4(取決于Caching.compressionEnabled設定)的形式存儲在緩存中,以便将來加載更快。如果下載下傳的捆綁包壓縮了LZ4,則AssetBundle将被壓縮存儲。如果緩存填滿,Unity将從緩存中删除最近最少使用的AssetBundle。有關更多詳細資訊,請參閱下面的内置緩存部分。

通常建議盡可能使用UnityWebRequest,或者僅在使用Unity 5.2或更早版本時使用WWW.LoadFromCacheOrDownload。如果内置API的記憶體消耗,緩存行為或性能對于特定項目影響很大,或者項目必須運作特定于平台的代碼以實作其要求,那麼隻能定制下載下傳系統了。

使用UnityWebRequest或WWW.LoadFromCacheOrDownload可能不理想的情況示例:

  • 當需要對AssetBundle緩存進行細粒度控制時
  • 當項目需要實施自定義壓縮政策時
  • 當項目希望使用平台特定的API來滿足某些要求時,例如需要在背景傳輸資料。

    - 示例:使用iOS的背景任務API在背景下載下傳資料。

  • 如果AssetBundles必須通過SSL下載下傳,但是Unity沒有正确的SSL支援(如PC)。

1.2.3 内置緩存

Unity有一個内置的AssetBundle緩存系統,可用于緩存通過UnityWebRequest API下載下傳的AssetBundles,該API包含一個接受AssetBundle版本号作為參數的重載。此版本号不存儲在AssetBundle内部,并且不由AssetBundle系統生成。

緩存系統跟蹤傳遞給UnityWebRequest的最新版本号。當調用此API時傳入一個版本号,高速緩存系統通過比較版本号來檢查是否存在緩存的AssetBundle。如果這些數字比對,系統将加載緩存的AssetBundle。如果版本号不比對,或沒有緩存的AssetBundle,Unity将下載下傳一個新副本。這個新副本将與新版本号相關聯。

緩存系統中的AssetBundles僅由其檔案名來辨別,而不是由其下載下傳的完整URL辨別。這意味着具有相同檔案名的AssetBundle可以存儲在多個不同的位置,例如CDN(内容分發網絡,居然還可以有這種思路)。隻要檔案名稱相同,緩存系統就會将它們識别為相同的AssetBundle。

每個應用程式都要決定将版本号配置設定給AssetBundles的适當政策,并将這些版本号傳遞給UnityWebRequest。這些數字可能來自各種唯一辨別符,例如CRC值。請注意,雖然AssetBundleManifest.GetAssetBundleHash()也可用于此目的,但我們不建議使用此功能進行版本控制,因為它僅提供估算值,而不是真正的Hash值計算)。

有關更多詳細資訊,請參閱Unity手冊的“使用AssetBundles進行修補”部分。

在Unity 2017.1以後,緩存API已經擴充到提供更精細的控制,允許開發人員從多個緩存中選擇一個活動緩存。以前的Unity版本隻能修改Caching.expirationDelay和Caching.maximumAvailableDiskSpace來删除緩存的資源(Unity 2017.1中這些屬性保留在Cache類中)。

expirationDelay是自動删除AssetBundle之前必須經過的最小秒數。如果在此期間沒有通路AssetBundle,它将被自動删除。

maximumAvailableDiskSpace指定本地存儲空間量(以位元組為機關),這個量是指緩存在删除那些已經超過expirationDelay時間,沒有使用的AssetBundle之前,可以使用的空間量。達到限制時,Unity将删除最近最少打開的緩存中的AssetBundle(或通過Caching.MarkAsUsed标記為已使用)。 Unity會删除緩存的AssetBundles,直到有足夠的空間完成新的下載下傳為止。

1.2.3.1 緩存填充

由于AssetBundles使用檔案名作為辨別,是以可以使用應用程式附帶的AssetBundles“填充”緩存。 為此,請将每個AssetBundle的初始版本或基本版本存儲在/Assets/StreamingAssets /中。 該過程與“跟随項目釋出”部分中詳細介紹的過程相同。

第一次運作應用程式時,可以通過從Application.streamingAssetsPath加載AssetBundles來填充緩存。 此後,應用程式可以正常調用UnityWebRequest(UnityWebRequest也可用于最初從StreamingAssets路徑加載AssetBundles)。

1.2.4 自定義下載下傳器

編寫自定義下載下傳程式可以讓應用程式完全控制AssetBundles的下載下傳,解壓縮和存儲方式。 由于所涉及的工程工作不是必要的,是以我們隻為大型團隊推薦此方法。 編寫自定義下載下傳器時有四個主要考慮事項:

  • 下載下傳機制
  • 存儲位置
  • 壓縮類型
  • 修補

有關修補AssetBundles的資訊,請參閱使用AssetBundles修補部分。

1.2.4.1 下載下傳

對于大多數應用程式,HTTP是下載下傳AssetBundles最簡單的方法。 但是,實作基于HTTP的下載下傳程式并不簡單務。 自定義下載下傳程式必須避免過多的記憶體配置設定,過多的線程使用和過多的線程喚醒。 Unity的WWW類不适合的原因在這裡就不較長的描述了。

在編寫自定義下載下傳器時,有三個選項:

  • C#的HttpWebRequest和WebClient類
  • 自定義原生插件
  • Asset Store包

1.2.4.1.1 c#類

如果應用程式不需要使用HTTPS/SSL,那麼C#的WebClient類提供了下載下傳AssetBundles最簡單的機制。 它能夠将任何檔案直接異步下載下傳到本地存儲,而無需過多管理記憶體配置設定。

要使用WebClient下載下傳AssetBundle,請配置設定該類的一個執行個體,并将其傳遞給AssetBundle的URL以下載下傳和目标路徑。 如果需要對請求參數進行更多控制,可以使用C#的HttpWebRequest類編寫下載下傳程式:

  • 從HttpWebResponse.GetResponseStream擷取一個位元組流。
  • 在堆棧上配置設定一個固定大小的位元組緩沖區。
  • 從傳回流(reponse)中讀入緩沖區。
  • 使用C#的File.IO API或任何其他流式IO系統将緩沖區寫入磁盤。

1.2.4.1.2 Asset Store包

很多插件包提供了本地代碼的實作,以通過HTTP,HTTPS和其他協定下載下傳檔案。 在為Unity編寫自定義本機代碼插件之前,建議您先評估可用的Asset Store包。

1.2.4.1.3 自定義原生插件

編寫自定義原生插件是在Unity中下載下傳資料最耗時,但最靈活的方法。 由于程式設計時間花費高且技術風險高,隻有在沒有其他方法能夠滿足應用程式的要求時才推薦此方法。 例如,如果應用程式必須在Unity中沒有C#SSL支援的平台上使用SSL通信,則可能需要定制本機插件。

自定義本機插件通常會包裝目标平台的原生下載下傳API。 示例包括iOS上的NSURLConnection和Android上的

java.net.HttpURLConnection。 請查閱每個平台的本地文檔以擷取有關使用這些API的更多詳細資訊。

1.2.4.2 存儲

在所有平台上,Application.persistentDataPath指向一個可寫的位置,适合用于存儲在應用程式運作時保持的資料(不要誤解為不運作就會删除資料)。 在編寫自定義下載下傳器時,強烈建議使用Application.persistentDataPath的子目錄來存儲下載下傳的資料。

Application.streamingAssetPath不可寫,對于AssetBundle緩存來說是一個糟糕的選擇。 streamingAssetsPath的示例位置包括:

  • OSX:在.app包内; 不可寫。
  • Windows:在安裝目錄中(例如Program Files); 通常不可寫
  • iOS:在.ipa包内; 不可寫
  • Android:在.apk檔案中; 不可寫

1.3 資産配置設定政策

如何将項目資産劃分為AssetBundles并不簡單。 經常采用簡單的政策,比如将所有對象都單獨生成一個AssetBundle或僅使用一個AssetBundle,但這些解決方案具有明顯的缺點:

  • 擁有太少的AssetBundles …
    • 增加運作時記憶體使用量
    • 增加加載時間
    • 需要更大的下載下傳量
  • 擁有太多的AssetBundles …
    • 增加建構時間
    • 可能會使開發複雜化
    • 增加總下載下傳時間

關鍵的決定是如何将對象分組為AssetBundles。 主要戰略是:

  • 邏輯實體
  • 對象類型
  • 合并内容

有關這些分組政策的更多資訊可以在手冊中找到。

1.4 常見的坑

本節介紹使用AssetBundles項目中常見的幾個問題。

1.5.1 資産備援

Unity 5的AssetBundle系統将在對象建構到AssetBundle中時查找對象的所有依賴關系。此依賴關系資訊用于确定将包含在AssetBundle中的一組對象。

明确配置設定給AssetBundle的對象隻會建構到該AssetBundle中(就是建立了一個AssetBundleBuild)。當Object的AssetImporter的assetBundleName屬性設定為非空字元串時,對象将被“顯式配置設定”。這可以在Unity Editor中通過在對象的檢查器中選擇一個AssetBundle或從編輯器腳本中完成。

也可以将對象配置設定給AssetBundle,方法是将它們定義為AssetBundle building map的一部分,該map要與重載的BuildPipeline.BuildAssetBundles()函數一起使用,該函數接受一組AssetBundleBuild。

在AssetBundle中未明确配置設定的對象都将包含在所有包含對其引用的1個或多個對象的AssetBundle中(就是沒有在AssetBundleBuild數組中的,都将作為備援打包,包含在數組中的作為依賴打包,不會重複)。

例如,如果将兩個不同的對象配置設定給兩個不同的AssetBundles,但都具有對公共依賴項Object的引用,則該依賴Object将被複制到兩個AssetBundles中。重複的依賴關系也将被執行個體化,這意味着依賴關系對象的兩個副本将被視為具有不同辨別符的不同對象。這将增加應用程式AssetBundles的總大小。如果應用程式加載它的父項,這也會導緻Object的兩個不同副本被加載到記憶體中。

有幾種方法可以解決這個問題:

  • 確定建構到不同AssetBundles中的對象不共享依賴關系。任何共享依賴關系的對象都可以放置在同一個AssetBundle中,而不需要重複依賴關系。

    對于具有許多共享依賴的項目,此方法通常不可行。它導緻很多的AssetBundles為了友善和高效的使用,必須經常重建和重新下載下傳。

  • 分段AssetBundles,要確定不會同時加載由相同依賴的兩個AssetBundles。

    此方法可能适用于某些類型的項目,例如基于關卡的遊戲。但是,它仍然會不必要地增加項目的AssetBundles的大小,并增加建構時間和加載時間。

  • 確定所有依賴項資産都生成一個單獨的AssetBundles中。這完全消除了重複資産的風險,但也帶來了複雜性。應用程式必須跟蹤AssetBundles之間的依賴關系,并確定在調用任何AssetBundle.LoadAsset API之前加載了正确的AssetBundles。

通過位于UnityEditor名稱空間中的AssetDatabase API跟蹤對象依賴關系。正如命名空間所暗示的,這個API僅在Unity編輯器中可用,而不是在運作時。 AssetDatabase.GetDependencies可用于查找特定對象或資産的所有直接依賴關系。請注意,這些依賴關系可能有其自己的依賴關系。此外,AssetImporter API可用于查詢配置設定有任何特定對象的AssetBundle。

通過組合AssetDatabase和AssetImporter API,可以編寫一個編輯器腳本,以確定将所有AssetBundle的直接或間接依賴關系都生成了AssetBundle,或者共享同一個依賴的兩個資産都生成了AssetBundle。由于備援資産會産生的記憶體成本,建議所有項目都有這樣的腳本。

1.5.2 圖集重複

任何自動生成的圖集将被配置設定給包含從其生成圖集的Sprite對象的AssetBundle。如果Sprite對象被配置設定給多個AssetBundles,那麼圖集将不會隻配置設定給一個AssetBundle,而是多個。如果Sprite對象未配置設定給AssetBundle,則圖集也不會被配置設定給AssetBundle。

為了確定圖集沒有重複,請檢查标記在同一圖集中的所有Sprite配置設定到同一個AssetBundle。

請注意,在Unity 5.2.2p3及更早版本中,自動生成的圖集将永遠不會配置設定給AssetBundle。是以,它們将被包含在使用了該Sprite的任何AssetBundles中,以及任何引用其組成Sprite的AssetBundles中。由于這個問題,強烈建議所有使用Unity的sprite打包程式的Unity 5項目更新到Unity 5.2.2p4,5.3或任何更新版本的Unity。

1.5.3 Android紋理

由于Android的碎片化嚴重,通常需要将紋理壓縮為多種不同的格式。雖然所有Android裝置都支援ETC1,但ETC1不支援帶alpha通道的紋理。如果應用程式不需要OpenGL ES 2支援,則解決該問題的最簡單方法是使用所有Android OpenGL ES 3裝置支援的ETC2。

大多數應用程式需要在不支援ETC2的舊裝置上釋出。解決此問題的一種方法是使用Unity 5的AssetBundle變體(有關其他選項的詳細資訊,請參閱Unity的Android優化指南)。

要使用AssetBundle變體,所有無法使用ETC1進行壓縮的紋理必須分離為僅包含紋理的AssetBundles。接下來,使用特定于供應商的紋理壓縮格式(如DXT5,PVRTC和ATITC)建立相應的這些AssetBundles變體以支援Android中不支援非ETC2的裝置。對于每個AssetBundle變體,将包含的紋理的TextureImporter設定更改為适合Variant的壓縮格式。

在運作時,可以使用SystemInfo.SupportsTextureFormat API檢測對不同紋理壓縮格式的支援。并使用此資訊來選擇和加載包含以受支援格式壓縮的紋理的AssetBundle變體。

有關Android紋理壓縮格式的更多資訊可以在這裡找到。

1.5.4 iOS檔案句柄過度使用

Unity的目前版本不受此問題影響。

在Unity 5.3.2p2之前的版本中,Unity會在AssetBundle加載的整個過程中持有一個打開的檔案句柄。 這在大多數平台上都不是問題。 但是,iOS将程序可以同時打開的檔案句柄數限制為255.如果加載AssetBundle會導緻超出限制,則加載調用将失敗,并顯示“Too Many Open File Handles”錯誤。

對于嘗試将内容分成數百或數千個AssetBundles的項目,這是一個常見問題。

對于無法更新到更新檔版本的Unity的項目,臨時解決方案是:

  • 通過合并相關的AssetBundles來減少使用的AssetBundles的數量
  • 使用AssetBundle.Unload(false)關閉AssetBundle的檔案句柄,并手動管理加載的對象的生命周期

1.6 AssetBundle變體

AssetBundle系統的一個關鍵特性是引入了AssetBundle變體。變體的目的是允許應用程式調整其内容以更好地适應其運作時環境。當加載對象和解析執行個體ID引用時,變體允許不同的AssetBundle檔案中的不同UnityEngine.Objects顯示為“相同”對象。從概念上講,它允許兩個UnityEngine.Objects顯示為共享相同的檔案GUID和本地ID,并辨別實際的UnityEngine.Object以字元串變體ID加載。

這個系統有兩個主要用例:

  • 變體簡化了适用于指定平台的AssetBundles的加載。

    示例:建構系統可能會建立一個AssetBundle,其中包含适用于獨立DirectX11 Windows版本的高分辨率紋理和複雜着色器,以及适用于Android的具有較低保真度内容的第二個AssetBundle。在運作時,項目的資源加載代碼可以為其平台加載相應的AssetBundle Variant,并且傳遞到AssetBundle.Load API的對象名稱不需要更改。

  • 變體允許應用程式在同一平台上加載不同的内容,但使用不同的硬體。

    這是支援各種移動裝置的關鍵。在現實的應用程式中,iPhone 4都不能像最新的iPhone一樣顯示相同的清晰度。

    在Android上,AssetBundle 變體可用于解決裝置間螢幕縱橫比和DPI之間巨大的分割問題。

1.6.1 限制

AssetBundle 變體系統的一個關鍵限制是它需要使用不同的資産來建構變體。即使這些資産之間的唯一差異是其導入設定,也适用此限制。如果内置到變體A和變體B中的紋理之間的唯一差別是在Unity紋理導入器中選擇的特定紋理壓縮算法,但是變體A和變體B依然是完全不同的資産。這意味着變體A和變體B是磁盤上的兩個檔案。

這種限制使大型項目的管理複雜化,因為特定資産的多個副本必須儲存在源代碼管理中。當開發人員希望更改資産的内容時,必須更新資産的所有變體。這個問題沒有内置的解決方法。

大多數團隊都有他們自己的AssetBundle變體形式。這是通過建立AssetBundles來完成的,通過在檔案名後面添加了特定的字尾,以便識别給定AssetBundle所代表的特定變體。在建構這些AssetBundles時,通過代碼更改包含的資産的導入器設定。一些開發者已經擴充了他們的定制系統,以便能夠改變附屬于預制件的元件上的參數。

1.7 壓縮還是未壓縮?

是否壓縮AssetBundles需要一些重要的考慮因素,其中包括:

- 加載時間:從本地存儲或本地緩存加載時,未壓縮的AssetBundles比加載壓縮的AssetBundles要快得多。

  • 建構時間:在壓縮檔案時,LZMA和LZ4非常緩慢,Unity Editor按順序處理AssetBundles。具有大量AssetBundles的項目将花費大量的時間壓縮它們。
  • 應用程式大小:如果AssetBundles包含在應用程式中釋出,則壓縮它們将減少應用程式的總大小。或者,可以在安裝後下載下傳AssetBundles。
  • 記憶體使用情況:在Unity 5.3之前,所有Unity的解壓縮機制都要求在解壓縮之前将整個壓縮的AssetBundle加載到記憶體中。如果記憶體使用率很重要,請使用未壓縮或LZ4壓縮的AssetBundles。
  • 下載下傳時間:如果AssetBundles很大,或者使用者處于帶寬受限的環境中,例如在低速或資料連接配接上下載下傳,那麼壓縮可能需要。如果隻有幾兆位元組的資料通過高速連接配接傳送到PC,則可能會忽略壓縮。

1.7.1 Crunch壓縮

主要由使用Crunch壓縮算法的DXT壓縮紋理組成的Bundles應該被建構為未壓縮的。

1.8 AssetBundles和WebGL

Unity強烈建議開發人員不要在WebGL項目上使用壓縮的AssetBundles。

WebGL項目中的所有AssetBundle解壓縮和加載必須在主線程上進行。這是因為Unity的WebGL導出選項目前不支援工作線程。 AssetBundles的下載下傳使用XMLHttpRequest委托給浏覽器,XMLHttpRequest在Unity的主線程上執行。這意味着壓縮的AssetBundles在WebGL上加載非常昂貴。

如果您使用的是Unity 5.5或更高版本,請考慮避免使用LZMA作為您的AssetBundles,并使用LZ4進行壓縮,而不是按需解壓縮。如果您需要較小的壓縮大小,那麼LZ4可以提供,您可以配置您的Web伺服器以在HTTP協定層面(在LZ4壓縮之上)對檔案進行gzip壓縮。 Unity 5.6删除了LZMA作為WebGL平台的壓縮選項。