unity 如何使用文件流讀取streamingassets下的資源
目的:讀取streamingassets下的文件中指定的一段字節(jié)
已知:文件中的起始位置,和需要讀取的長(zhǎng)度
1.android下讀取
1.1 不能直接使用C#的FileStream,讀取失敗
var buffer = new byte[size]; FileStream stream = File.OpenRead(path); stream.Read(buffer , pos, size);
報(bào)錯(cuò):
IsolatedStorageException: Could not find a part of the path "/jar:file:/data/app/com.xxx.xxxx-1/base.apk!/assets/xxx.pack".
1.2 可以使用Unity原生接口與Android交互
主要過(guò)程:
Java
public class XXXPlugin extends UnityPlayerNativeActivity { protected AssetManager assetManager; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); assetManager = getAssets(); } //返回字節(jié)數(shù)組 public byte[] LoadBytes(String path,int offset,int len) { //可以緩存起來(lái),不需每次都o(jì)pen InputStream inputStream = assetManager.open(path); try { byte buf[] = new byte[len]; inputStream.reset(); //注意skip、read的可靠性 inputStream.skip(offset); inputStream.read(buf,0,len) inputStream.close(); return buf; } catch (IOException e) { Log.v ("unity", e.getMessage()); } return null; } }
注意skip、read的可靠性,每次調(diào)用不一定能返回正確長(zhǎng)度,可能需多次調(diào)用。
參考how-does-the-skip-method-in-inputstream-work
C#:
public static byte[] read_streamingpath_bytes(string path,IntPtr ptr, int pos, int size) { using (AndroidJavaClass cls = new AndroidJavaClass("com.XXX.XXXPlugin";) ) { AndroidJavaObject m_AndroidJavaObject = cls.GetStatic<AndroidJavaObject>("mainActivity"); byte[] s = m_AndroidJavaObject.Call<byte[]>("LoadBytes", path, pos, size); return s; } return null; }
這種方法是在Java分配內(nèi)存。
1.3 更靈活的方法,使用JNI,可以從C#傳遞指針到C++
在Android Studio中生成庫(kù)libNativeLib.so文件,參考NativeReadBytes
C++
#include "com_XXX_NativeHelper.h" #include <android/asset_manager_jni.h> #include <android/asset_manager.h> #include <string> #include <jni.h> #include <stdint.h> #ifdef __cplusplus extern "C" { #endif static AAssetManager *assetManager = nullptr; JNIEXPORT void JNICALL Java_com_XXX_NativeHelper_SetAssetManager (JNIEnv *env, jobject jobj, jobject jassetManager) { assetManager = AAssetManager_fromJava(env, jassetManager); } JNIEXPORT int32_t JNICALL ReadAssetsBytesWithOffset(uint32_t pathKey, char* fileName, unsigned char** result, int32_t offset, int32_t length){ if(assetManager == nullptr){ return -1; } AAsset* asset = asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_UNKNOWN); if(asset == nullptr){ return -1; } off_t size = AAsset_getLength(asset); if(size > 0){ try { AAsset_seek(asset, offset, SEEK_SET); AAsset_read(asset, *result, length); }catch (std::bad_alloc){ *result = nullptr; return -1; } } AAsset_close(asset); return (int32_t)length; } #ifdef __cplusplus } #endif
Java
//XXXPlugin.java public class XXXPlugin extends UnityPlayerNativeActivity { static{ System.loadLibrary("NativeLib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mainActivity = this; //設(shè)置AssetManager, C++中要使用 NativeHelper.SetAssetManager(getAssets()); } } //NativeHelper.java public class NativeHelper { public static native void SetAssetManager(AssetManager assetManager); }
C#
public class ReadNativeByte { #if UNITY_ANDROID [DllImport("NativeLib")] public static extern int ReadAssetsBytesWithOffset(uint pathKey,string name, ref IntPtr ptr, int offset, int length); #endif }
2.IOS下讀取
可以直接在C#下讀取,StreamAsseting只有讀的權(quán)限,用OpenRead
byte[] bytes = new byte[len]; FileStream stream = File.OpenRead(path); stream.Seek(offset, SeekOrigin.Begin); stream.Read(bytes, 0, len);
補(bǔ)充:unity中 在移動(dòng)平臺(tái)各種讀寫文件夾存路徑整理 如 StreamingAssets 等文件夾 各個(gè)路徑在各種平臺(tái)的文件路徑
1:Resources 文件夾 少用
//資源卸載 /* * Resources.UnloadAsset(obj):卸載非 GameObject類型的資源,會(huì)將內(nèi)存中已加載資源及其克隆體卸載。 Destroy(obj):僅用于卸載GameObject類型的資源的克隆體。 DestroyImmediately(obj):卸載GameObject類型的資源,會(huì)將內(nèi)存中已加載資源及其克隆體卸載,但該方法只能用在非編輯模式下,否則會(huì)報(bào)錯(cuò)提示改為DestroyImmediately(obj, true),然而編輯模式下使用該函數(shù)會(huì)連文件夾里的原始Asset一并刪除。 */
2:StreamingAssets
在移動(dòng)端也是只可讀的不能寫入數(shù)據(jù) 主要用來(lái)存放二進(jìn)制文件。
//安卓下這兩個(gè)文件夾路徑相同 //Application.streamingAssetsPath = jar:file:///data/app/com.xxx.xxx-1.apk!/assets/ == "jar:file://"+Application.dataPath+"!assets/" //Application.dataPath+"!assets/" = /data/app/com.xxx.xxx-1.apk!assets/ //Ios下 //Application.dataPath + "/Raw/" == @"file:///" + Application.streamingAssetsPath + "/" //Editor WIN //@"file:///" + Application.streamingAssetsPath + "/" //"file:///" + Application.dataPath + "/StreamingAssets" + "/"
private string path = string.Empty; public string GetSAPath() { //安卓平臺(tái) 加文件名 #if UNITY_ANDROID && !UNITY_EDITOR path = Application.streamingAssetsPath + "/" #elif UNITY_IPHONE && !UNITY_EDITOR path = @"file:///" + Application.streamingAssetsPath + "/"; #elif UNITY_STANDLONE_WIN||UNITY_EDITOR path = @"file:///" + Application.streamingAssetsPath + "/"; #endif return path; }
3:Application.persistentDataPath
這個(gè)目錄可讀可寫 一般存本地關(guān)卡等
用于存檔 直接使用 打包之前是沒(méi)有這個(gè)目錄的,直到應(yīng)用程序在手機(jī)上安裝完畢才有這個(gè)目錄。
該文件存在手機(jī)沙盒中,因?yàn)椴荒苤苯哟娣盼募?/h3>
1.通過(guò)服務(wù)器直接下載保存到該位置,也可以通過(guò)Md5碼比對(duì)下載更新新的資源
2.沒(méi)有服務(wù)器的,只有間接通過(guò)文件流的方式從本地讀取并寫入Application.persistentDataPath文件下,然后再通過(guò)
Application.persistentDataPath來(lái)讀取操作。
注:在Pc/Mac電腦 以及Android跟Ipad、ipone都可對(duì)文件進(jìn)行任意操作,另外在IOS上該目錄下的東西可以被iCloud自動(dòng)備份。
Application.persistentDataPath + "/tempDic", "testXml"
對(duì)應(yīng)存儲(chǔ)路徑
Windows應(yīng)用商店應(yīng)用程序:
application.persistentdatapath指向%userprofile%\appdata\local\packages\<productName>\localstate ios:application.persistentdatapath指向/var/mobile/containers/data/application/<guid>/documents
android:application.persistentdatapath指向大多數(shù)設(shè)備上的/storage/emulated/0/android/data/<packagename>/文件(有些舊手機(jī)可能指向SD卡上的位置,如果存在),該路徑使用android.content.context.getexternalfilesdir解析
4:Application.temporaryCachePath
來(lái)操作文件 同上但是 此屬性用于返回一個(gè)臨時(shí)數(shù)據(jù)的緩存目錄(不會(huì)備份并且清空緩存會(huì)清掉)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
C#使用CryptoStream類加密和解密字符串的實(shí)現(xiàn)
CryptoStream設(shè)計(jì)用于在內(nèi)容以流的形式輸出到文件時(shí)加密和解密內(nèi)容,本文主要介紹了C#使用CryptoStream類加密和解密字符串的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01C#使用TimeSpan時(shí)間計(jì)算的簡(jiǎn)單實(shí)現(xiàn)
這篇文章主要給大家介紹了關(guān)于C#使用TimeSpan時(shí)間計(jì)算的相關(guān)資料,以及通過(guò)一個(gè)實(shí)例代碼給大家介紹了C#使用timespan和timer完成一個(gè)簡(jiǎn)單的倒計(jì)時(shí)器的方法,需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06C#實(shí)現(xiàn)讀取USB轉(zhuǎn)串口參數(shù)并顯示在ComboBox
在很多應(yīng)用程序中,尤其是那些需要與外部硬件通信的程序中,自動(dòng)檢測(cè)和讀取串口參數(shù)是一個(gè)非常有用的功能,下面我們就來(lái)看看如何在C#中實(shí)現(xiàn)這一功能吧2024-01-01WinForm實(shí)現(xiàn)為ComboBox綁定數(shù)據(jù)源并提供下拉提示功能
這篇文章主要介紹了WinForm實(shí)現(xiàn)為ComboBox綁定數(shù)據(jù)源并提供下拉提示功能,是非常實(shí)用的功能,需要的朋友可以參考下2014-08-08C#調(diào)用Python程序傳參數(shù)獲得返回值
C# 調(diào)用 Python 程序有多種方式,本文主要介紹了4種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02