關於 web service, unity, blogger 等軟體工程筆記

Unity 載入外部檔案機制之整理筆記

Edit icon 沒有留言
Unity

最近看到社團一些詢問 Unity 載入外部設定檔的問題,因此整理若要在 Unity 中實作外部載入的功能,有哪些機制可以使用以及比較。

Resources

從 Unity 1.x 就存在的機制,現在許多 Unity 書籍都會把這一個方式寫進書裡,但從 Unity 官方講師以及演講分享,Resources 在現今已經是個過時的機制,強烈建議不要大量使用這個,它帶來許多的缺點與問題:

  • 遊戲啟動會變慢 (slow on startup)
  • 占用更多個記憶體花費 (high memory cost)

雖然它的運作機制相當簡單,只需要將 Assets 放入到任何資料夾名稱為 Resources 即可,且程式碼寫法也相當簡潔:

using UnityEngine;

public class ResourceLoadExample : MonoBehaviour
{
   void Start ()
   {
      // 載入文字檔位於 /Assets/Resources/example.txt
      var txt = Resources.Load<TextAsset>("example");
      Debug.Log(txt.text);
   }
}   

但能不要使用就不要使用,更多請見官方文件

File IO

這應該算是 .Net framework 的實作功能,開啟檔案讀取其內容資料,唯一要注意的各式平台 (Platform) 的支援與權限,例如 WebGL 平台就無法使用,而手機平台則可能需要取得檔案讀取的權限。

using System.IO;
using UnityEngine;

public class FileLoadExample : MonoBehaviour
{
  void Start ()
  {
  var path = @"C:\example.txt";
  var txt = File.ReadAllText(path);
  Debug.Log(txt);
  }
}

更多可參考 MSDN 文件

WWW or UnityWebRequest

WWW 是一個簡易實作機制,能存取放置在遠端伺服器 (remote server) 上的檔案 (抑或是本機電腦, local),目前支援的 protocol 有以下幾種:

  • http://
  • https://
  • file://

Unity 在 5.x 正式推出 UnityWebRequest,相較於 WWW 功能更加強大,能支援更多 HTTP 的方法 (GET/ POST/ PUT/ DELETE/ HEAD/ OPTIONS 等等),建議改用使用 UnityWebRequest

UnityWebRequest 其程式碼寫法,搭配 Coroutine 機制也是相當簡潔:

using System.Collections;
using UnityEngine.Networking;
using UnityEngine;

public class UnityWebRequestLoadExample : MonoBehaviour
{
   void Start ()
   {
      this.StartCoroutine(this.LoadTxt());
   }

   IEnumerator LoadTxt()
   {
      // 或者是讀取檔案系統 var url = @"file://C:\example.txt";
      var url = "https://example.com";
      var request = UnityWebRequest.Get(url);
      var download = new DownloadHandlerBuffer();
      request.downloadHandler = download;
      yield return request.Send();
      Debug.Log(download.text);
   }
}

之前也為此機制寫篇筆記 Unity WebRequest,更多可參考官方文件

Other Networking Methods

如果以上透過 HTTP 機制不符合所需,那是而透過其他 Protocol 進行網路資料傳輸,那麼就得自己根據需求,自己找一套函數庫 (Library) 來使用,亦或是使用 Berkeley socket 從頭開始刻交換方式囉。

Streaming Assets

嚴格來說這不是載入機制的一種,而是 Unity 其中一項的建置打包機制。若 Assets 是被放置在 StreamingAssets (注意大小寫) 資料夾中時,專案發布會將其 Assets 複製到建置專案,透過 Application.streamingAssetsPath 取得該資料夾路徑,之後透過 File IO 或者是 UnityWebRequest 來讀取該資料 (WebGL 平台應該使用 UnityWebRequest):

using System.IO;
using UnityEngine;

public class StreamingAssetLoadExample : MonoBehaviour
{
   void Start ()
   {
      // 載入文字檔放置於 /Assets/StreamingAssets/example.txt
      var path = Application.streamingAssetsPath + "/example.txt";
      var txt = File.ReadAllText(path);
      Debug.Log(txt);
   }
}

但唯一要注意的是 Android 平台,StreamingAssets 將會打包成 jar,必須先透過解壓縮才能取得其原始資料,更多細節請參考官方文件

AssetBundles

同上,這也不算是載入的機制,而也是另一種 Unity 建置打包的機制。在建置階段將多個 Assets (Asset 可以是設定的文字檔) 打包成一個或是多個 AssetBundle,在執行階段透過 AssetBundle.LoadFromFile 利用 File IO 從本機檔案系統讀取 (例如 Stream Assets),或者是透過 UnityWebRequest 從遠端伺服器載入 AssetBundle,並讀取其內容取得所需的 Asset:

using System.Collections;
using UnityEngine.Networking;
using UnityEngine;

public class AssetBundleLoadExample : MonoBehaviour
{
   void Start ()
   {
       this.StartCoroutine(this.LoadTxt());
   }

   IEnumerator LoadTxt()
   {
      var url = "http://example.com/example.assetbundle";
      var request = UnityWebRequest.GetAssetBundle(url);
      yield return request.Send();
      var ab = DownloadHandlerAssetBundle.GetContent(request);
      var txt = ab.LoadAsset<TextAsset>("example.txt");
      Debug.Log(txt.text);
   }
}

但若使用 AssetBundle 的機制來加載資源,不像是前面幾種方式直接修改完設定檔即可,還需要經過一個打包成 AssetBundle 的步驟,若沒有建立自動化流程,更新時會相當麻煩痛苦。

更多關於 AssetBundles 機制可參考官方文件

小結與評比

將以上機制根據幾項因素考量來評比:

  • 各平台支援:是否支援大部分的平台
  • 本機讀取:是否能存取本機的檔案系統拿到資料 (local storage)
  • 雲端讀取:是否透過 HTTP 向雲端伺服器拿到資料 (remote storage)
  • 發布更新:對於已發佈的遊戲,是否容易更新其設定檔資料
  • 其他備註:其他該機制的註解
Mechanism 各平台支援 本機讀取 雲端讀取 發布更新 其他備註
Resources ✕ (需重新建置專案) 不建議使用
UnityWebRequest 本機使用 file://,雲端使用 http:// 或者是 https:// 讀取資源
File IO △ (需額外實作更新機制) 平台受限,WebGL 不能用,手機平台可能需要拿到權限
Mechanism 資源放置位置
Streaming Assets 只能在本機
AssetBundles 本機或是雲端

最後整理兩種常見的外部資源使用上的經驗:

  • 遊戲參數設定檔 評估遊戲本身架構,以及是否有網路存取能力而定,否則一般單機版本使用 Stream Assets 似乎就綽綽有餘,更甚至捨棄外部的參數檔載入,而是直接將參數寫在遊戲中,利用 ScriptableObject 或者是 MonoBehaviour 來儲存實驗,讓編輯人員直接在 Editor 裏頭編輯參數並且測試遊戲。

    至於外部參數檔的檔案格式選擇,不管是 CSV, INI, XML, YAML, JSON 或者要包成 AssetBundles 等等,那又是另外一個很長的故事了。

  • 遊戲資源 (貼圖、模型、或是場景等等) 一定是採用 AssetBundle 搭配 UnityWebRequest 來實在載入機制較為方便簡單。至於 AssetBundles 要放在雲端伺服器上,抑或是玩家本機資料夾,那就看各個專案架構評估而定了。

沒有留言: