UNITY_資源路徑與加載外部文件
https://www.tuicool.com/articles/qMNnmm6
https://blog.csdn.net/appppppen/article/details/51396256
https://unity3d.com/cn/learn/tutorials/topics/best-practices/resources-folder
https://blog.csdn.net/qq_18995513/article/details/51958906
背景:
有許多靜態數據是放在客戶端中的,比如csv/xml文件,是需要動態讀取文件的
實例: 動態讀取一個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>
將此xml文件隨意放在某路徑下:Assets/aa/bb/Test.xml
使用代碼讀取文件內容
void Start() { XElement result = LoadXML("Assets/aa/bb/Test.xml"); } void LoadXML(string path) { XElement xml = XElement.Load(path); return xml; }
讀取成功。
問題1. 路徑和地址。在移動端是找不到文件的。
問題2. 使用的是PC上傳統的一套讀取資源的做法,而沒有使用unity3d提供的方法。
可能導致找得到文件但是沒能正確地讀取文件內容
移動平台的資源路徑:
Application.dataPath: 程序的數據文件所在文件夾。在Editor中就是Assets
安卓: /data/app/xxx.xxx.xxx.apk
iOS: Application/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data
Application.streamingAssetsPath: 流數據的緩存目錄,為相對路徑,適合設置一些外部數據文件
安卓: jar:file:///data/app/xxx.xxx.xxx.apk/!/assets
iOS: Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data/Raw
Application.persistentDataPath: 持久化數據存儲目錄的路徑,可用於存儲一些持久化的數據文件
安卓: /data/data/xxx.xxx.xxx/files
iOS: Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Documents
Application.temporaryCachePath: 臨時數據的緩存目錄
安卓: /data/data/xxx.xxx.xxx/cache
iOS: Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Library/Caches
dataPath和streamingAssetsPath一般是相對程序的安裝目錄位置
persistentDataPath和temporaryCachePath一般是與系統有關的固定位置
那么,打包之后的資源的路徑如何與這些地址對應上呢?
Unity中的資源處理種類:
Resources:
如果有文件夾名為Resources,則里面的內容會無條件地在打包時打包集成到.asset文件中
可以放置一些Prefab,因為Prefab在打包時會自動過濾掉不需要的資源,有利於減小資源包
1. 只讀。不能動態修改,會動態更新的資源不要放在這里
2. 主線程加載
3. 使用Resources.Load()加載資源
StreamingAssets:
與Resources類似
區別為:Resources文件夾中的內容在打包時會被壓縮和加密,而StreamingAssets中的內容則原封不動地打包
一般用來存放一些二進制文件
1. 只讀,不能動態修改
2. 主要存放二進制文件
3. 只能通過WWW類讀取
AssetBundle:
將prefab或二進制文件封裝成AssetBundle文件(也是二進制文件)
缺點: 在移動端無法更新腳本
總結:
1. 該二進制文件(AssetBundle文件) 是Unity3D定義的一種二進制類型
2. 最好將Prefab封裝成AssetBundle,但是在移動端無法更新腳本
3. 使用WWW類讀取
PersistentDataPath:
這是一個路徑(可讀寫)
在iOS上就是應用程序的沙盒;
在安卓上可以是程序的沙盒或sdCard -- 在安卓打包時,ProjectSetting的選項WriteAccess可設置路徑是沙盒還是sdcard
總結:
1. 內容在運行時可讀寫,提前將數據存入這個路徑是不可行的
2. 無內容類型的限制
3. 寫下的文件可以在電腦上查看,同樣也可以在電腦中清除
移動平台讀取外部文件的方法:
使用Unity3D規定的操作方式來讀取外部資源:Resources/ StreamingAssets/ AssetBundle
Resources:
新建Resources目錄,在目錄中創建文件Test.xml(之前在背景中提到的那個文件)
通過Resources的方法來讀取Test.xml中的內容。
public class Test: MonoBehaviour { private string _result; void Start(){ LoadXML("Test"); } private void LoadXML(string path){ _result = Resources.Load(path).ToString(); XmlDocument doc = new XmlDocument(); doc.LoadXml(_result); } }
StreamingAssets:
新建StreamingAssets文件夾並存放Test.xml文件
(StreamingAssets文件夾中的文件不會被壓縮或加密,所以一般是要放二進制文件的,這里放xml只是做一個演示,實際操作中切記不要直接把數據文件放到該目錄中)
public class Test : MonoBehaviour { private string _result; void Start(){ StartCoroutine(LoadXML()); } IEnumerator LoadXML(){ string path = Application.streamingAssetsPath; WWW www = new WWW(path); yield return www; _result = www.text; } }
AssetBundle:
比較麻煩,需要先把Test.xml打包成AssetBundle文件
創建好AssetBundle文件並命名為TestXML后,因為ab文件是二進制文件,因此放入StreamingAssets文件夾。
public class Test: MonoBehaviour { private string _result; void Start(){ LoadXML(); } void LoadXML(){ AssetBundle assetBundleCsv = new AssetBundle(); // 讀取放入StreamingAssets文件夾中的ab文件 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(); } }
PersistentDataPath:
只有在運行時才能讀寫,例如通過網絡下載資源存放在PersistantDataPath中
與StreamingAssets的讀取很類似,但要注意通過www類加載PersistentDataPath必須使用file://協議實現加載
public class Test: MonoBehaviour{ private string _result; void Start(){ StartCoroutine(LoadXML()); } private void LoadXML(){ string path = "file://" + Application.persistentDataPath + "/test.xml"; WWW www = new WWW(path); yield return www; _result = www.text; } }
深入Resources.Load()
Recommendation: DO NOT USE IT.
Reasons:
1. Use of the Resources folder makes fine-grained memory management more difficult;
2. Improper use of Resources folders will increase applicaiton startup time and the length of builds
The increase of the number of Resources folders makes the management of the "Resources Assets" more difficult;
3. The Resources system degrades a project's ability to deliver custom content to specific platforms and eliminates the possibility of incremental content upgrades
AssetBundle Variants are Unity's primary tool for adjusting content on a per-device basis
Proper uses:
Two specific use cases where Resources system can be helpful
1. The ease of the Resources folder makes it an excellent system to rapidly prototype.
But when a project moves into full production, the use of Resources system should be eliminated.
2. When the content is:
Generally required throughout a project's lifetime
Not memory-intensive
Not prone to patching, or does not vary across platforms/ devices
Used for minimal bootstrapping
比如: 持有預制體的MonoBehaviour單例、包含第三方配置數據的ScriptableObject容器等
Resources的卸載:
Resources資源類型的加載方式只有一種,但卸載有多種。
1. Resources.UnloadAsset(Object assetToUnload)
從內存中卸載(非GameObject類型的資源???),會將內存中已加載的資源卸載掉
2. Destroy(obj)
僅用於卸載(GameObject類型???)的資源的克隆體
3. DestroyImmediately(obj)
卸載GameObject類型的資源,會將內存中已加載資源及其克隆體卸載;
但該方法只能用在非編輯模式下,否則會報錯,提示改為DestroyImmediately(obj, true),
然而編輯模式下使用該函數會連文件夾里的原始Asset一並刪除。
官方推薦的卸載方法為:
public static AsyncOperation UnloadUnusedAssets()
異步檢索資源如果沒有被使用才會卸載。
被全局變量引用的資源會導致一直無法釋放。