6-31 Using AssetBundles Natively And UnityWebRequest
本章要來介紹在Unity使用AssetBundle的四種方法。
第二種方式:AssetBundle.LoadFromFile
第三種方式:WWW.LoadfromCacheOrDownload
針對LoadfromCacheOrDownload,再提一個可添加的參數CRC,這是用來檢查網路下載的檔案是否有錯誤的檢查碼。
Unity產生AssetBundle的時候,也會附加一個manifest文件。如不清楚mainfest文件,可以參考之前寫的文章。
6-30 AssetBundle Option And Manifest File
https://3dactionrpg.blogspot.com/2018/06/6-30-assetbundle-option-and-manifest.html
其中就有一個欄位是該AssetBundle的CRC檢查碼。
第四種方式:UnityWebRequest’s DownloadHandlerAssetBundle
- AssetBundle.LoadFromMemory
- AssetBundle.LoadFromFile
- WWW.LoadfromCacheOrDownload
- UnityWebRequest’s DownloadHandlerAssetBundle (Unity 5.3 or newer)
第一種是透過記憶體載入AssetBundle,比方說從網路上下載的檔案是bytes陣列,可以直接使用LoadFromMemoryAsync轉換成AssetBundle。Unity官方不建議使用此API,原理上至少會佔用三倍AssetBundle的容量於記憶體內。
第二種是從本地載入AssetBundle。如AssetBundle為LZ4壓縮格式,則只會載入 AssetBundle 的檔頭,其餘的資料仍留存在硬碟上,將會基於「按需求 (on-demand)」的模式來載入。
第三種是從網路上下載AssetBundle後,在本地緩存並載入。*注意:從Unity 2017.1開始,第三種方法只是包裝自UnityWebRequest。 因此,使用Unity 2017.1或更高版本的開發人員應改用UnityWebRequest。* 與UnityWebRequest不同,每次調用此API都會產生一個新的工作線程。 因此,在移動設備等內存有限的平台上,每次只能使用此API下載一個AssetBundle,以避免內存高峰。如果需要下載超過5個AssetBundle,請使用代碼創建和管理下載隊列,以確保只有少量AssetBundle下載正在同時運行。
第四種是從Unity 5.3以後才支援的功能,用來處理從網路上下載的AssetBundle。使用DownloadHandlerAssetBundle下載時,下載的數據會傳輸到固定大小的緩衝區,然後根據不同的配置方式,決定將緩衝的數據放到暫存空間或AssetBundle緩存空間。 以上操作都以native-code形式進行,消除了擴展heap的風險。
上述解說來自這篇官方文檔,也推薦大家閱讀這篇文檔,會獲得非常大的幫助:
第一種方式:AssetBundle.LoadFromMemory
第二種方式:AssetBundle.LoadFromFile
第三種方式:WWW.LoadfromCacheOrDownload
針對LoadfromCacheOrDownload,再提一個可添加的參數CRC,這是用來檢查網路下載的檔案是否有錯誤的檢查碼。
Unity產生AssetBundle的時候,也會附加一個manifest文件。如不清楚mainfest文件,可以參考之前寫的文章。
6-30 AssetBundle Option And Manifest File
https://3dactionrpg.blogspot.com/2018/06/6-30-assetbundle-option-and-manifest.html
其中就有一個欄位是該AssetBundle的CRC檢查碼。
第四種方式:UnityWebRequest’s DownloadHandlerAssetBundle
需要注意的是,經由UnityWebRequest下載的AssetBundle並不會存到本地的暫存區,跟WWW. LoadfromCacheOrDownload不一樣。主要理念是讓使用者自己管理下載後的AssetBundle,可以透過request.downloadHandler.data取得資料流的byte陣列,再使用File.WriteAllBytes寫入到本地的儲存體內。
以下提供四種方法的測試用程式碼,我統一都寫在LoadAssetBundle.cs腳本中,並都含有註解,請大家自行參考嘍~
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using UnityEngine.Networking; public class LoadAssetBundle : MonoBehaviour { [SerializeField] string loadPath; [SerializeField] string assetName; [SerializeField] Transform assetTransform; [SerializeField] string urlPath; void Start () { // LoadFromMemory (); // StartCoroutine (LoadFromMemoryAsync()); // LoadFromFile (); // StartCoroutine (LoadFromFileAsync()); // StartCoroutine (LoadFromCacheOrDownload()); StartCoroutine (LoadFromUnityWebRequest()); } // 第一種方式(同步) void LoadFromMemory(){ // 加載AssetBundle AssetBundle assetBundle = AssetBundle.LoadFromMemory(File.ReadAllBytes(loadPath)); // 從AssetBundle取得想要的資源 GameObject Building_a = assetBundle.LoadAsset(assetName); // 產生資源 Instantiate(Building_a, assetTransform.position, assetTransform.rotation); } // 第一種方式(異步) IEnumerator LoadFromMemoryAsync(){ // 異步加載 AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync (File.ReadAllBytes(loadPath)); // 等待加載完成 yield return request; AssetBundle assetBundle = request.assetBundle; // 從AssetBundle取得想要的資源 GameObject Building_a = assetBundle.LoadAsset (assetName); // 產生資源 Instantiate(Building_a, assetTransform.position, assetTransform.rotation); } // 第二種方式(同步) void LoadFromFile(){ // 加載AssetBundle AssetBundle assetBundle = AssetBundle.LoadFromFile(loadPath); // 從AssetBundle取得想要的資源 GameObject Building_a = assetBundle.LoadAsset (assetName); // 產生資源 Instantiate(Building_a, assetTransform.position, assetTransform.rotation); } // 第二種方式(異步) IEnumerator LoadFromFileAsync(){ // 異步加載 AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync (loadPath); // 等待加載完成 yield return request; AssetBundle assetBundle = request.assetBundle; // 從AssetBundle取得想要的資源 GameObject Building_a = assetBundle.LoadAsset (assetName); // 產生資源 Instantiate(Building_a, assetTransform.position, assetTransform.rotation); } // 第三種方式 IEnumerator LoadFromCacheOrDownload(){ // 先檢查Cache是否正常 while (Caching.ready == false) { // 暫停一個Frame yield return null; } // 使用WWW下載資源,參數中的1為版本號 // 可使用版本號決定是否更新Cache中的AssetBundle WWW www = WWW.LoadFromCacheOrDownload (urlPath, 1); yield return www; // 如下載時發生問題,會記錄於error中 if (string.IsNullOrEmpty(www.error) == false) { Debug.Log (www.error); // 跳出Corutine yield break; } AssetBundle assetBundle = www.assetBundle; // 從AssetBundle取得想要的資源 GameObject Building_a = assetBundle.LoadAsset (assetName); // 產生資源 Instantiate(Building_a, assetTransform.position, assetTransform.rotation); } // 第四種方式(用來取代WWW方法,需引用UnityEngine.Networking) IEnumerator LoadFromUnityWebRequest(){ // 使用UnityWebRequest不會有緩存,需要自行管理下載後的AssetBundle UnityWebRequest request = UnityWebRequest.GetAssetBundle(urlPath); yield return request.SendWebRequest (); // 取得AssetBundle(方法1) AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(request); // 取得AssetBundle(方法2) // AssetBundle assetBundle = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle; // 從AssetBundle取得想要的資源 GameObject Building_a = assetBundle.LoadAsset (assetName); // 產生資源 Instantiate(Building_a, assetTransform.position, assetTransform.rotation); } void SaveToLocal(UnityWebRequest request, string savePath){ File.WriteAllBytes (savePath, request.downloadHandler.data); } }
撰寫好以後,將LoadAssetBundle放進一個Empty GameObject中,並指定相關參數。
接著來測試看看吧,比較特別需要注意的是,如果在遊戲執行才開始從Server上下載的話,就會出現如下圖的情況。進入遊戲後,場景中沒有任何東西。
過一段時間,下載完成後才會出現。這樣的話當然是非正常狀態,建議大家應要在遊戲場景載入前將檔案下載到本地端。
留言
張貼留言