欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

unity文件存储与读写

程序员文章站 2022-05-17 15:47:23
...
最近做了一个小项目,遇到了一个问题,感觉比较经典,就想写下来,希望对其他人有所帮助
问题的报错就是这个.....
I/Unity   ( 8298): NullReferenceException: Object reference not set to an instan
ce of an object
I/Unity   ( 8298):   at Heathwork.Update () [0x00000] in <filename unknown>:0
Heathwork是一个挂载在gameobject上的脚本组件,在unity上测试一直都是好的,但是在手机上就是不行
一直报这个错,经过很多查询才知道,原来是我在untiy上使用了一个配置文件放在/assets/data下面,unity打包的时候
没有打进去,所以手机上是读不到这个文件的,我在Heathwork之中的start之中使用了这个文件,于是Heathwork没有实例化成功,
所以在update的时候才会报未将对象引用到对象的实例。于是我又去查了一下unity的打包机制,在此总结一下
1.unity(5.x)的资源打包过程
1.1 唯一的API
调用builgpipeline.BuildAssetBundles,引擎将自动根据资源的assetbundleName
属性批量打包,自动建立bundle以及资源之间的依赖关系
1.2 打包规则
在资源的inpector界面最下方可设置一个abName,每个abName(包含路劲)对应一个
bundle,即abName相同的资源会打在一个Bundle中,如果所以来的资源设置了不同的abName
则会与之建立依赖关系,避免出现冗余,
支持增量式发布,即在资源内容改变并重新打包时,会自动跳过内容未变的bundle。
1.3 新打包的选项
5.x 下默认开启三个选项(completeAssets,用于保证资源单的完备性,collectDependencies
,用于收集资源的依赖项,DeterministicAssetBundle,用于为资源维护固定id)之外
ForceRebuildAssetBundle
用于强制重打所有AssetBundle文件;
IgnoreTypeTreeChanges
用于判断AssetBundle更新时,是否忽略TypeTree的变化;
AppendHashToAssetBundleName
用于将Hash值添加在AssetBundle文件名之后,开启这个选项,可以直接通过文件名来判断哪些Bundle的内容进行了更新
(4.x下普遍需要通过比较二进制等方法来判断,但在某些情况下即使内容不变重新打包,Bundle的二进制也会变化)。

1.4 Manifest文件
在打包后生成的文件夹中,每个Bundle都会对应一个manifest文件,记录了Bundle的一些信息,
但这类manifest只在增量式打包时才用到;同时,根目录下还会生成一个同名manifest文件及其对应的Bundle文件,
通过该Bundle可以在运行时得到一个AssetbundleManifest对象,而所有的Bundle以及各自依赖的Bundle都可以通过该对象提供的接口进行获取。

1.5 Variant参数
在资源的Inspector界面最下方,除了可以指定abName,在其后方还可以指定Variant。打包时,Variant会作为
后缀添加在Bundle名字之后。相同abName,不同variant的Bundle中,资源必须是一一对应的,且他们在Bundle中的ID也是相同的,
从而可以起到相互替换的作用。
https://blog.uwa4d.com/archives/ABtopic_2.html
2.unity项目下的文件结构
2.1 GUID与fileID(本地ID)
   Unity会为每个导入到Assets目录中的资源创建一个meta文件,文件中记录了GUID,GUID用来记录资源之间的引用关系。
还有fileID(本地ID),用于标识资源内部的资源。资源间的依赖关系通过GUID来确定;资源内部的依赖关系使用fileID来确定。
2.2 MonoScripts
        一个MonoScripts含有三个字符串:程序库名称,类名称,命名空间。
构建工程时,Unity会收集Assets文件夹中独立的脚本文件并编译他们,组成一个Mono程序库。Unity会将Assets目录中的语言分开编译,Assets/Plugins目录中的脚本同理。Plugin子目录之外的C#脚本会放在Assembly-CSharp.dll中。而Plugin及其子目录中的脚本则放置在Assembly-CSharp-firstpass.all中。
这些程序库会被MonoScripts所引用,并在程序第一次启动时被加载。

2.3 Assets
为unity编辑器下的资源文件夹,unity编辑时的所有资源都将置于此文件夹内,
可以使用AssetDatabase.LoadAssetAtPath("Assets/x.txt"); 来获取资源对象
ps:只能在编辑器下使用,当项目打包后,游戏内无法运作,参数为包含Assets的文件全路径
并且需要文件后缀
Assets下的资源除特殊文件夹内 或者会被打入保内的场景引用的资源化,其余资源不会被打入包中,
2.4 Resources
资源载入
        Assets下的特殊文件夹,此文件夹内的资源将会在项目打包时,全部打入包内,并能通过以下方法获得对象:
Resources.Load("fileName");

注意:函数内的参数为相对于Resource目录下的文件路径与名称,不包含后缀。Assets目录下可以拥有任意路径及数量的Resources文件夹,在运行时,Resources下的文件路径将被合并。
例:Assets/Resources/test.txt与 Assets/TestFloder/Resources/test.png在使用Resource.Load("test")载入时,将被视为同一资源,只会返回第一个符合名称的对象。如果使用Resource.Load(“test”)将返回text.txt;
如果在Resources下有相同路径及名称的资源,使用以上方法只能获得第一个符合查找条件的对象,使用以下方法能或得到所有符合条件的对象:
Object[] assets = Resources.LoadAll("fileName");
TextAsset[] assets = Resources.LoadAll("fileName");
在工程进行打包后,Resource文件夹中的资源将进行加密与压缩,打包后的程序内将不存在Resource文件夹,故无法通过路径访问以及更新资源
意思就是打包无法进行更改了
2.5 StreamingAssets
概述
        StreamingAssets文件夹为流媒体文件夹,此文件夹内的资源将不会经过压缩与加密,原封不动的打包进游戏包内。在游戏安装时,StreamAssets文件件内的资源将根据平台,移动到对应的文件夹内。StreamingAssets文件夹在Android与IOS平台上为只读文件夹.
    你可以使用以下函数获得不同平台下的StreamingAssets文件夹路径:
Application.streamingAssetsPath
    请参考以下各平台下StreamingAssets文件夹的等价路径,Application.dataPath为程序安装路径。Android平台下的路径比较特殊,请留意此路径的前缀,在一些资源读取的方法中是不必要的(AssetBundle.LoadFromFile,下详)
Application.dataPath+"/StreamingAssets"//Windows OR MacOS
Application.dataPath+"/Raw" //IOS
"jar:file://"+Application.dataPath+"!/assets/" //Android
在pc跟mac中可以有读写权限,但是移动端只支持读取操作
ps:也可以在Assets下直接创建其他文件夹  也会被打进项目中,使用application.datapath来访问,但是
在移动端是没有访问权限的!!!!

2.6 AssetBundle
概述
        AssetBundles let you stream additional assets via the WWW class and instantiate them at runtime. AssetBundles are created via BuildPipeline.BuildAssetBundle.
AssetBundle是Unity支持的一种文件储存格式,也是Unity官方推荐的资源存储与更新方式,它可以对资源(Asset)进行压缩,分组打包,动态加载,以及实现热更新,
但是AssetBundle无法对Unity脚本进行热更新,因为其需要在打包时进行编译。
它的位置就在 你随便点击一个perfab 在inspector中 最下面的那个就是assetBundle
总结几点:
1.Resources 打包会被进入,但是会被加密,不能进行更改,可存放一些固定配置的文件,通过
Resources.Load("fileName"); 或者使用textAsset拖入脚本中使用
2.直接在Assets中放入文件
使用application.dataPath+"fileName" 来读取  但是这个在移动端是没有访问权限的
3.StreamingAssets
在项目中创建这个文件夹之后,
可以使用Application.streamingAssetsPath 来读取文件 也可以使用Application.dataPath来读取
Application.dataPath使用的话必须分清楚平台路径
#if UNITY_ANDROID   //安卓 
string filepath= "jar:file://" + Application.dataPath + "!/assets/"+"Filename"; 
#elif UNITY_IPHONE  //iPhone 
string filepath=Application.dataPath + "/Raw/"+"Filename"; 
#elif UNITY_STANDALONE_WIN || UNITY_EDITOR  //windows平台和web平台 
string filepath = "file://" + Application.dataPath + "/StreamingAssets/"+"Filename"; 
#end
实验做出来的是只能www去读  IO无法读取内容 在手机上
4.使用Application.persistentDataPath 来操作文件(推荐)
此文件存在手机沙盒中,项目中是找不到的,
在移动端使用服务器下载文件后 保存在这个文件 使用md5对比下载更新新资源
如果没有服务器 可以通过文件流的形式在本地读取然后写入Application.persistentDataPath中,
再通过Application.persistentDataPath 来读取操作文件
ps:在移动端可以进行任意文件操作,同时IOS会被icloud备份

5.Application.temporaryCachePath
操作方式跟Application.persistentDataPath一样  但是不能被icloud备份


以上都是理论部分......废话不多说 来点干货
void Start () {
		text = (TextAsset)Resources.Load ("test2");
		testtxt = text.text;
		this.gameObject.GetComponent<Text> ().text = testtxt;
	}

	testtxt = text.text;
		this.gameObject.GetComponent<Text> ().text = testtxt;

public TextAsset text;
	public string testtxt;
	// Use this for initialization
	void Start () {

		//string path = Application.streamingAssetsPath+"/test3.txt";
		//或者使用下面的filepath 也是可以的
		#if UNITY_ANDROID   //安卓  
			string filepath= "jar:file://" + Application.dataPath + "!/assets/"+"/test3.txt";  
		#elif UNITY_IPHONE  //iPhone  
			string filepath=Application.dataPath + "/Raw/"+"/test3.txt";  
		#elif UNITY_STANDALONE_WIN || UNITY_EDITOR  //windows平台和web平台  
			string filepath = "file://" + Application.dataPath + "/StreamingAssets/"+"/test3.txt";  
		#endif 

		testtxt = Loadfile(filepath);
		this.gameObject.GetComponent<Text> ().text = testtxt+"  "+filepath;
	}
	
	// Update is called once per frame
	void Update () {
		
	}
	public string Loadfile(string path){
		StreamReader sr = null;
		try{
			sr = File.OpenText(path);

		}catch(Exception e){
			return null;

		}
		string line;
		//字符分割就不做了 可以用sr.ReadLine();
		line = sr.ReadToEnd ();
		sr.Close ();
		sr.Dispose ();
		return line;
	}


public TextAsset text;
	public string testtxt;
	// Use this for initialization
	void Start () {
		#if UNITY_ANDROID   //安卓  
			string path = Application.streamingAssetsPath+"/test4.txt";
		#elif UNITY_STANDALONE_WIN || UNITY_EDITOR  //windows平台和web平台  
			string path ="file://"+ Application.streamingAssetsPath+"/test4.txt";
		#endif

		StartCoroutine (readwww (path));
	}
	
	// Update is called once per frame
	void Update () {
		
	}
	IEnumerator readwww(string path){
		//Debug.Log (path);
		WWW www = new WWW (path);
		yield return www;
		testtxt = www.text;
		//Debug.Log ("!!!"+testtxt);
		this.gameObject.GetComponent<Text> ().text = testtxt+path;
	}
	public string Loadfile(string path){
		StreamReader sr = null;
		try{
			sr = File.OpenText(path);

		}catch(Exception e){
			return null;

		}
		string line;
		//字符分割就不做了 可以用sr.ReadLine();
		line = sr.ReadToEnd ();
		sr.Close ();
		sr.Dispose ();
		return line;
	}

public TextAsset text;
	public string testtxt;
	// Use this for initialization
	void Start () {
		string txt = "测试手机沙盒的读与写";
		string path = Application.persistentDataPath+"/test5.txt";
		write (path, txt);
		testtxt = Loadfile (path);
		this.gameObject.GetComponent<Text> ().text = testtxt+path;
	}
	
	// Update is called once per frame
	void Update () {
		
	}
	public void write(string path,string text){
		StreamWriter sr = null;
		FileInfo file = new FileInfo (path);
		if (!file.Exists) {
			sr = file.CreateText ();
		}else{
			sr = file.AppendText ();
		}
		sr.Write (text);
		sr.Close ();
		sr.Dispose ();
	}
	public string Loadfile(string path){
		StreamReader sr = null;
		try{
			sr = File.OpenText(path);

		}catch(Exception e){
			return null;

		}
		string line;
		//字符分割就不做了 可以用sr.ReadLine();
		line = sr.ReadToEnd ();
		sr.Close ();
		sr.Dispose ();
		return line;
	}

这里分别是五个模式下的核心代码运行之后的效果如图
第二张图则是在手机上运行结果
我把工程项目文件也传上来了,有兴趣可以下载看看
  • unity文件存储与读写
            
    
    博客分类: c#历程 unity游戏文件读写unity打包 
  • 大小: 83.2 KB
  • unity文件存储与读写
            
    
    博客分类: c#历程 unity游戏文件读写unity打包 
  • 大小: 54 KB