Unity3D移動平臺動態讀取外部檔案全解析
前言:
一直有個想法,就是把工作中遇到的坑通過自己的深挖,總結成一套相同問題的解決方案供各位同行拍磚探討。眼瞅著2015年第一個工作日就要來到了,小匹夫也休息的差不多了,尋思著也該寫點東西活動活動大腦和手指了。那麼今天開始,小匹夫會記錄一些平時工作中遇到的坑,以及小匹夫的應對方法,歡迎各位拍磚討論。那麼今天主要討論一下Unity3D在移動端如何動態的讀取外部檔案,比如csv(txt),xml一類的檔案。主要涉及的問題,就是PC端上本來測試的好好的東西,到了移動端就不能用了,所以要討論一下PC端和移動端的區別,那麼下一個問題自然而然的就是移動端的資源路徑(要討論一下Resources、StreamingAssets、AssetBundle、PersistentDataPath),最後一步就是找到了資源如何讀取(這裡也會具體到對應的幾種情況,即Resources、StreamingAssets、AssetBundle),主要的思路就是這樣啦。對嘞,前言部分還是要祝各位看官新的一年身體健康,升職加薪。
假如我想在editor裡動態讀取檔案
實際的遊戲開發中,其實有相當一部分靜態資料是可以放在客戶端的,所以勢必會產生要動態讀取這些檔案的需求,比如csv(其實就是文字檔案),xml等等。我相信大家不管是用win還是用mac來做unity3d的開發,都一定要先在editor中去實現基本的功能,在具體到各個移動平臺上去除錯。所以作為要讀取外部檔案的第一步,顯然我們要先在editor也就是pc上實現這個功能。
下面給各位舉一個讀取xml的例子,也是我在以前的一篇文章《自己動手之使用反射和泛型,動態讀取XML建立類例項並賦值》中使用過的,動態讀取一個xml檔案並動態生成一個類。
下面是我們用來做例子的xml檔案,Test.xml:
<?xml version="1.0" encoding="UTF-8"?> <test> <name>chenjd</name> <blog>http://www.cnblogs.com/murongxiaopifu/</blog> <organization>Fanyoy</organization> <age>25</age> </test>
我們就可以很任性的把這個檔案隨便丟在一個地方,只要你能指定對它的地址。例如我還把它放在那篇文章中的地址Assets/xml-to-egg/xml-to-egg-test/資料夾下(的確很任性)
下面我們實現在PC上讀取這個檔案內容的程式碼:
//讀取xml測試 using UnityEngine; using System.Collections; using EggToolkit; using System.Xml.Linq; public class Test : MonoBehaviour { // Use this for initialization void Start () { XElement result = LoadXML("Assets/xml-to-egg/xml-to-egg-test/Test.xml");//任性的地址 Debug.Log(result.ToString()); } // Update is called once per frame void Update () { } private XElement LoadXML(string path) { XElement xml = XElement.Load(path); return xml; } }
結果如下:
結果是讀取成功了。但是你以為到這一步就成功了,那就錯了。因為這樣的程式碼到移動端是行不通的,至少2處可以被罵sb:
- 醉人的地址,地址引數那樣寫就不用考慮跨平臺了。所以這個sb點引出的問題就是在移動端unity3d找不到目標檔案。
- 使用的還是pc上傳統的一套讀取資源的做法,沒有使用unity3d提供的方法,所以可能導致的問題是找得到檔案但是沒有正確的讀取檔案內容。
以上用紅色標出的問題,便是小匹夫想到的可能出現的問題,也是下文要討論的內容。那麼我們首先來看看資源路徑在各個平臺上的不同之處吧。
移動平臺的資源路徑問題
想要讀取一個檔案,自然首先要找到這個檔案,下面小匹夫首先會總結一下unity3d中存在的各個地址,之後再總結一下各個地址在各個移動平臺中的對應位置。
Unity3D中的資源路徑
Application.dataPath | 此屬性用於返回程式的資料檔案所在資料夾的路徑。例如在Editor中就是Assets了。 |
Application.streamingAssetsPath | 此屬性用於返回流資料的快取目錄,返回路徑為相對路徑,適合設定一些外部資料檔案的路徑。 |
Application.persistentDataPath | 此屬性用於返回一個持久化資料儲存目錄的路徑,可以在此路徑下儲存一些持久化的資料檔案。 |
Application.temporaryCachePath | 此屬性用於返回一個臨時資料的快取目錄。 |
android平臺
Application.dataPath | /data/app/xxx.xxx.xxx.apk |
Application.streamingAssetsPath | jar:file:///data/app/xxx.xxx.xxx.apk/!/assets |
Application.persistentDataPath | /data/data/xxx.xxx.xxx/files |
Application.temporaryCachePath | /data/data/xxx.xxx.xxx/cache |
IOS平臺
Application.dataPath | Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data |
Application.streamingAssetsPath | Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data/Raw |
Application.persistentDataPath | Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Documents |
Application.temporaryCachePath | Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Library/Caches |
從上面的3張表格,我們可以看到 dataPath和streamingAssetsPath的路徑位置一般是相對程式的安裝目錄位置,而persistentDataPath和temporaryCachePath的路徑位置一般是相對所在系統的固定位置。那麼現在明確了unity3d中各個地址在不同平臺上的含義,下一個問題就來了,也就是我打包之後的資源要怎麼和這些地址對應上呢?要知道在pc的editor裡預設的資原始檔存放的路徑就是Assets啊,為何又會派生出那麼多路徑呢?那麼就帶著這個疑問,和小匹夫一起進行下文的內容吧。
簡單介紹一下unity3d中資源的處理種類(歡迎拍磚):
小匹夫遇到過的大體就是如下幾種了,Resources、StreamingAssets、AssetBundle、PersistentDataPath,下面簡單分析一下。
Resources:
是作為一個Unity3D的保留資料夾出現的,也就是如果你新建的資料夾的名字叫Resources,那麼裡面的內容在打包時都會被無條件的打到釋出包中。它的特點簡單總結一下就是:
- 只讀,即不能動態修改。所以想要動態更新的資源不要放在這裡。
- 會將資料夾內的資源打包整合到.asset檔案裡面。因此建議可以放一些Prefab,因為Prefab在打包時會自動過濾掉不需要的資源,有利於減小資源包的大小。
- 主執行緒載入。
- 資源讀取使用Resources.Load()。
StreamingAssets:
要說到StreamingAssets,其實和Resources還是蠻像的。同樣作為一個只讀的Unity3D的保留資料夾出現。不過兩者也有很大的區別,那就是Resources資料夾中的內容在打包時會被壓縮和加密。而StreamingAsset資料夾中的內容則會原封不動的打入包中,因此StreamingAssets主要用來存放一些二進位制檔案。下面也同樣做一個簡單的總結:
- 同樣,只讀不可寫。
- 主要用來存放二進位制檔案。
- 只能用過WWW類來讀取。
AssetBundle:
關於AssetBundle的介紹已經有很多了。簡而言之就是把prefab或者二進位制檔案封裝成AssetBundle檔案(也是一種二進位制)。但是也有硬傷,就是在移動端無法更新指令碼。下面簡單的總結下:
- 是Unity3D定義的一種二進位制型別。
- 最好將prefab封裝成AseetBundle,不過上面不是才說了在移動端無法更新指令碼嗎?那從Assetbundle中拿到的Prefab上掛的指令碼是不是就無法執行了?也不一定,只要這個prefab上掛的是本地指令碼,就可以。
- 使用WWW類來下載。
PersistentDataPath:
看上去它只是個路徑呀,可為什麼要把它從路徑裡面單獨拿出來介紹呢?因為它的確蠻特殊的,這個路徑下是可讀寫。而且在IOS上就是應用程式的沙盒,但是在Android可以是程式的沙盒,也可以是sdcard。並且在Android打包的時候,ProjectSetting頁面有一個選項Write Access,可以設定它的路徑是沙盒還是sdcard。下面同樣簡單的總結一下:
- 內容可讀寫,不過只能執行時才能寫入或者讀取。提前將資料存入這個路徑是不可行的。
- 無內容限制。你可以從StreamingAsset中讀取二進位制檔案或者從AssetBundle讀取檔案來寫入PersistentDataPath中。
- 寫下的檔案,可以在電腦上檢視。同樣也可以清掉。
好啦,小匹夫介紹到這裡,各位看官們是不是也都清楚了一些呢?那麼下面我們就開始最後一步了,也就是如何在移動平臺如何讀取外部檔案。
移動平臺讀取外部檔案的方法
上文小匹夫之所以要介紹Resources、StreamingAssets、AssetBundle、PersistentDataPath這四個東東,就是因為讀取外部資源的操作所涉及到的東西無外乎這幾種。既然是用Unity3D來開發遊戲,那麼自然要使用Unity3D規定的操作方式,而不是我們在PC上很原始的那種操作方式來操作咯。否則就會像本文一開始所演示的那樣,寫出移動端無法使用的很傻的程式碼來。
下面小匹夫就分別實現一下利用Resources、StreamingAssets、AssetBundle來讀取的過程。
Resources:
首先我們新建一個Resources目錄,並且將上面我們用到的Test.xml複製一份到這個資料夾中。如圖:
然後我們通過Resources的讀取方法來讀取Test.xml的內容。並且呼叫GUI將xml的內容繪製出來。
//用Resources讀取xml using UnityEngine; using System.Collections; using EggToolkit; using System.Xml.Linq; using System.Xml; public class Test : MonoBehaviour { private string _result; // Use this for initialization void Start () { LoadXML("Test"); } // Update is called once per frame void Update () { } private void LoadXML(string path) { _result = Resources.Load(path).ToString(); XmlDocument doc = new XmlDocument(); doc.LoadXml(_result); } void OnGUI() { GUIStyle titleStyle = new GUIStyle(); titleStyle.fontSize = 20; titleStyle.normal.textColor = new Color(46f/256f, 163f/256f, 256f/256f, 256f/256f); GUI.Label(new Rect(400, 10, 500, 200), _result,titleStyle); } }
結果如圖:
OK,Resources讀取外部資源目標達成!!
下面我們繼續,這次則是使用StreamingAssets來操作。
StreamingAssets:
同Resources一樣,我們要新建一個StreamingAssets的資料夾來存放我們的Test.xml檔案。如圖:
不過前文已經說了,StreamingAssets資料夾內的東西並不會被壓縮和加密,而是放進去什麼就是什麼,所以一般是要放二進位制檔案的,這裡小匹夫僅僅做一個演示,各位在實際操作中切記不要直接把資料檔案放到這個目錄中打包。
using UnityEngine; using System.Collections; using EggToolkit; using System.Xml.Linq; using System.Xml; using System.IO; public class Test : MonoBehaviour { private string _result; // Use this for initialization void Start () { StartCoroutine(LoadXML()); } // Update is called once per frame void Update () { } /// <summary> /// 如前文所述,streamingAssets只能使用www來讀取, /// 如果不是使用www來讀取的同學,就不要問為啥讀不到streamingAssets下的內容了。 /// 這裡還可以使用persistenDataPath來儲存從streamingassets那裡讀到內容。 /// </summary> IEnumerator LoadXML() { string sPath= Application.streamingAssetsPath + "/Test.xml"; WWW www = new WWW(sPath); yield return www; _result = www.text; } void OnGUI() { GUIStyle titleStyle = new GUIStyle(); titleStyle.fontSize = 20; titleStyle.normal.textColor = new Color(46f/256f, 163f/256f, 256f/256f, 256f/256f); GUI.Label(new Rect(400, 10, 500, 200), _result,titleStyle); } }
結果如圖:
OK,StreamingAssets讀取外部資源目標達成!!
下面我們繼續,最後則是使用AssetBundle來操作。
AssetBundle:
來到AssetBundle,這裡就和上面兩個不一樣了。首先我們要把我們的檔案Test.xml打成AssetBundle檔案,由於小匹夫使用的是小米3作為測試機,所以AssetBundle的平臺選擇為Andorid。
如圖,我們建立了一個AssetBundle檔案,並命名為TextXML。並且按照二進位制檔案放入StreamingAssets資料夾中的慣例,將這個AssetBundle檔案放入StreamingAssets資料夾。
那麼下面就是從AssetBudle中讀取Test.xml的內容咯。直接上程式碼:
//從AssetBundle中讀取xml using EggToolkit; using System.Xml.Linq; using System.Xml; using System.IO; public class Test : MonoBehaviour { private string _result; // Use this for initialization void Start () { LoadXML(); } // Update is called once per frame void Update () { } void LoadXML() { AssetBundle AssetBundleCsv = new AssetBundle(); //讀取放入StreamingAssets資料夾中的bundle檔案 string str = Application.streamingAssetsPath + "/" + "TestXML.bundle"; WWW www = new WWW(str); www = WWW.LoadFromCacheOrDownload(str, 0); AssetBundleCsv = www.assetBundle; string path = "Test"; TextAsset test = AssetBundleCsv.Load(path, typeof(TextAsset)) as TextAsset; _result = test.ToString(); } void OnGUI() { GUIStyle titleStyle = new GUIStyle(); titleStyle.fontSize = 20; titleStyle.normal.textColor = new Color(46f/256f, 163f/256f, 256f/256f, 256f/256f); GUI.Label(new Rect(400, 10, 500, 200), _result,titleStyle); } }
結果如圖:
OK,AssetBundle讀取外部資源目標也達成了!!
補充:
在此統一回答一下在評論和qq上有同學提出的一個問題:安卓上Application.persistentDataPath的內容貌似不是匹夫你表裡的那個呀?在本文的評論裡小匹夫已經回覆過了,其實文中也說過
但是在Android可以是程式的沙盒,也可以是sdcard。並且在Android打包的時候,ProjectSetting頁面有一個選項Write Access,可以設定它的路徑是沙盒還是sdcard。
下面上圖好啦:
相關文章
- Java 最佳化:讀取配置檔案 "萬能方式" 跨平臺,動態獲取檔案的絕對路徑Java
- Java動態指令碼Groovy讀取配置檔案Java指令碼
- 移動跨平臺開發深度解析
- 動態引用外部的Javascript指令碼檔案JavaScript指令碼
- 安卓平臺Flutter啟動過程全解析安卓Flutter
- Unity3D動態載入FBX檔案Unity3D
- 動態引入外部javascript檔案程式碼例項JavaScript
- js動態載入外部css檔案程式碼JSCSS
- 移動端跨平臺開發的深度解析
- 利用外部表讀取告警日誌檔案
- 移動檔案
- Maui 讀取外部檔案顯示到Blazor中UIBlazor
- 阿里雲移動研發平臺EMAS:2月產品動態阿里
- 後臺動態配置config檔案
- 阿里雲移動研發平臺EMAS4月產品動態阿里
- 通過讀取properties檔案動態生成對資料庫的連線資料庫
- 讀取xml檔案 解析雙層xmlXML
- MySQL讀取配置檔案的順序、啟動方式、啟動原理MySql
- 移動端上傳分測平臺
- springboot 執行 jar 包讀取外部配置檔案Spring BootJAR
- 系統快取全解析5:檔案快取依賴快取
- 03 #### 讀取靜態檔案-圖片
- 動態引入js檔案使用隨機數防止讀取快取資料程式碼例項JS隨機快取
- vue+node全棧移動商城【6】-node介面配置檔案Vue全棧
- 3285.如何通過連結移動外部檔案到空間(步驟)
- 獵豹移動要做全球TOP3的移動廣告平臺
- 《React Native跨平臺移動應用開發》讀後鬼扯React Native
- 乾貨 | 移動安全管理多場景全解析
- 12c 聯機狀態移動資料檔案
- hybird跨平臺移動app開發APP
- 移動平臺的技術演變
- SQLServer移動資料檔案SQLServer
- 線上移動資料檔案
- ORACLE移動資料檔案Oracle
- python——批次移動檔案Python
- Laravel 支援動態多 env 配置讀取Laravel
- 資原始檔讀取_檔案_VC技術文章_VC源動力
- C#移動跨平臺開發(2)Xamarin移動跨平臺解決方案是如何工作的?C#