Unity中如何设置游戏存档方法归纳
前言众所周知,存档在许多游戏中已经成为了不可或缺的一部分,先不论联机的网络游戏,一些长流程游戏,比如黑帝斯,空洞骑士等(让我咕的万恶之源属于是),这样不存档让玩家连续花30+个小时甚至更久属实是让玩家坐牢,变成了班尼特福德之类的阴间游戏。这不仅对玩家是坐牢,对测试游戏的人更是一种全新的坐牢体验:要是要测试后面的关卡有没有bug,那就一定要把前面的打了,然后测出了bug游戏运行不了改了bug要重新来,想想就离谱。但如果拿特定的存档进去测试那肯定少了很多时间和精力。
综上所述,单机游戏存档的好处如下:
1.长流程游戏可以让玩家分几次体验,失败了也可以重新读档,减少了玩家的坐牢体验
2.测试时可以直接进入特定地点进行测试,不需要从头打起不断被自己的粪作ex到
3.肯定还有但我懒得想了
对此,Unity有这几种方法进行游戏存档:
PlayerPrefs的妙用
对于存档,Unity特别在脚本里提供了PlayerPrefs类的方法。它的方法是提供给玩家int,float,string三个类型进行储存与修改。每一个变量名都对应这一个值,也就是键值对,类似于字典类型。这三个类型的变量名和值都是直接储存在文件里的,所以可以达到储存轻量数据的可能。
相关代码也很简单,具体是:
PlayerPrefs.SetString("Name",name); //储存string类变量
PlayerPrefs.SetFloat("Name",name); //储存float类变量
PlayerPrefs.SetInt("Name",name); //储存int类变量
//这三个的意思是:创建名字叫Name的变量在PlayerPrefs里,
//同时他们的值和name相等(当然name也可以直接打float,int,string类型的常量)
Str=PlayerPrefs.GetString("Name"); //读取string类变量并存在Str里
I=PlayerPrefs.GetInt("Name"); //读取int类变量并存在I里
F=PlayerPrefs.GetFloat("Name"); //读取float类变量并存在F里
//如果不存在Name,那么会返回0
PlayerPrefs.DeleteAll(); //删除所有键和值
PlayerPrefs.DeleteKey("Name"); //删除Name的键和值
PlayerPrefs.HasKey("Name"); //判断是否存在Name这个键并返回true或者false
这些代码虽然很简单,但是只能存储三个类型而且不能做成一个类,所以只能用在一些简单的存储,比如简单变量和玩家设置里,比如获得金币钻石数量啊,开不开bgm啊(开了就setint为1,然后每次进游戏getint判断是不是1,是了就开)什么的。当然如果你硬要存储玩家数据也不是不可以,就是有种拿水果刀杀牛的感觉(别问我为什么不是鸡刀,你见过鸡刀吗),不太值得属于是。
注意:
因为是存在文件里,所以你Unity调试也是运行了PlayerPrefs的代码里的,所以在停止调试后PlayerPrefs的改变不会变回来捏
(通俗一点就是你调试时setint把0改成1,终止调试时那个值还是1而不是0辣,讲那么多干嘛)
序列化和反序列化
每个字单独我都认识,咋连起来就不认识了捏?容我细说:
概念
序列化(Serialization)和反序列化(Deserialization)
在Unity中,序列化的概念为:将[对象的状态信息]转化为的自动化处理过程
那反序列化自然是:将转化为[对象的状态信息]的自动化处理过程
从广义上来说,[对象的状态信息]可以被说成[对象],而就是[可传输的字节序列]。说白了,序列化就是将Unity的文件或者脚本(对象的状态信息)转化为二进制的形式(也就是一群读不懂的数字加字母),存储在数据库,内存或者文件中(Unity可以储存的形式),也就是游戏里的“保存游戏”按钮,而反序列化则是“读取游戏”。和其他的方法不同,这些数据会以二进制的形式存储在游戏的data.txt里。
序列化最简单的使用方式是让脚本内private变量的值在Inspector面板里显示出来。众所周知(???),脚本内的public变量都会经过序列化出现在inpector面板上,而private则不会。而要想显示出来,就要在命名上方加上,表示变量可被序列化(私以为其中原理应该是让Unity的Inspector面板保存数据)。具体如下:
private int i;
接下来将会介绍Unity为此特制的三个类,它们分别为:
File类,FileStream类和BinaryFormatter类
File类
文件类,即用来对文件进行基本操作,比如说创建、复制、剪切、打开等,可以直接赋值到后面的FileStream类
太长不看版:操作文件的
在使用该类前,请注意使用System.IO的命名空间(IO肯定是input,output辣),也就是:using System.IO
FileStream类
文件流类,即用来读取、写入操作系统文件的,可以进行读取、写入、保存、关文件流的功能,可以把文件转化为字节流的形式
注意:干完活记得把文件流关了,File类可以直接赋值FileStream类
在使用该类前,请注意使用System.IO的命名空间(IO肯定是input,output辣),也就是:using System.IO
综上所述呢,我们可以把文件看成包裹,对包裹本身的操作,比如创建个包裹,移动个包裹什么的就用File类,但是如果要对包裹里面的东西进行操作,那就得使用FileStream了。
BinaryFormatter类
Binary的意思是二进制,而Formatter则是格式化程序,那干什么自然不用我说了,就是以二进制的形式对对象进行序列化和反序列化。
在使用该类前,请注意使用System.Runtime.Serialization.Formatters.Binary的命名空间,也就是:using System.Runtime.Serialization.Formatters.Binary
应用
首先创建一个c#脚本命名为Save(名字随便啥都可以)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//直接告诉Unity这个类可被序列化
public class Save //不要MonoBehaviour,因为直接作为一个类,不需要挂物体上
{
public int coins;
public float playerPositionX;
public float playerPositionY;
}
接着,我们可以在另一个脚本里写下这些方法:
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
//命名空间肯定不止这么少,就是打出来提醒一下
public Save CreateSave(){ //创建一个Save对象存储当前游戏数据
Save save=new Save();
save.Coins=GameManager.Instance.coins;
save.playerPositionX=player.transform.position.x;
save.playerPositionY=player.transform.postion.y;
return Save;
}
public void SaveBySerialization(){
Save save=CreateSave();
//获取当前的游戏数据存在Save对象里
BinaryFormatter bf=new BinaryFormatter();
//创建一个二进制形式
FileStreamfs=File.Create(Application.persistentDataPath+"/Data.yj");
//这里指使用持久路径创建一个文件流并将其保存在Data.yj里(具体在哪就不打了,反正创建了)
//由于持久路径在Windows系统是隐藏的,所以无法找到Data.yj本身
//如果想看到,可以改成dataPath(就像下文json的代码里一样)
//文件后缀可以随便改,甚至是自定义的(比如我这里用了yj)
bf.Serialize(fs,save);
//将Save对象转化为字节
fs.Close();
//把文件流关了
}
这样,我们就完成了序列化(保存)的全过程:
创建一个二进制格式化对象->新建文件流->序列化Save对象->关闭文件流
接下来就是读取:
private void LoadByDeserialization(){
if(File.Exists(Application.persistentDataPath+"/Data.yj"))
//判断文件是否创建
{
BinaryFormatter bf=new BinaryFormatter();
FileStream fs=File.Open(Application.persistentDataPath+"/Data.yj",FileMode.Open);//打开文件
Save save=bf.Deserialize(fs) as Save;
//反序列化并将数据储存至save(因为返回变量类型不对,所以要强制转换为Save类
fs.Close();
//关文件流
GameManager.Instance.coins=save.coins;
player.transform.position=new Vector2(save.playerPositionX,save.playerPositionY);
//赋值
}else{
Debug.LogError("Data Not Found");
}
}
就这样,一个简单的游戏存档就做好了,这些方法可以挂载在按钮或者特定场景上,使得可以正常进行存档读档的操作。如果你想储存场景内的怪物数据的话,建议使用vector类型进行存储呢,比如说:
for(int i=0;i<save.isDead.Count;i++){
if(GameManager.instance.enemies==null){
if(!save.isDead)//如果敌人在我们保存之后死了
{
float enemyPosX=save.enemyPositionX;
float enemyPosY=save.enemyPositionY;
Enemy newEnemy=Instantiate(enemyPrefab,new Vector2(enemyPosX,enemyPosY),Quaternion.identity);
GameManager.Instance.enemies=newBat;
//把本该活着的敌人数据进行填充
}else{
float enemyPosX=save.enemyPositionX;
float enemyPosY=save.enemyPositionY;
Enemy newEnemy=Instantiate(enemyPrefab,new Vector2(enemyPosX,enemyPosY),Quaternion.identity);
}
}
}
新建的类也应该是:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//直接告诉Unity这个类可被序列化
public class Save //不要MonoBehaviour,因为直接作为一个类,不需要挂物体上
{
public int coins;
public float playerPositionX;
public float playerPositionY;
public List<float> enemyPositionX=new List<float>();
public List<float> enemyPositionY=new List<float>();
public List<bool> isDead=new List<bool>();
}
总结
序列化和反序列化看似很好解决了大量数据存储的问题,但是你要想啊,序列化以后的文件全是二进制,这鬼也看不懂啊(除了计算机捏),那修改也更是问题。那有没有什么序列化以后属于一个人能看懂的东西捏?这就要看:
另一种序列化方式——JSON
{json},搁着一看还以为是jvavscript(js),那它如何来实现存档读档呢?还是从概念入手把
概念
JSON
JSON,全称是JavaScript对象表示法(JavaScript Object Notation),它主要是用来在客户端和服务端进行交互数据的,它属于轻量级语言。它的格式以”键值对“的形式作为展示,而且键的名字可以自己定义,不理解的可以看看下面:
var Save={//大括号表示对象
&#34;coins&#34;:10,
&#34;playerPositionX&#34;:10.866,
&#34;playerPositionY&#34;:-21,
&#34;isDead&#34;: //中括号表示数组
}json由两部分构成:一个是键名(Key),也就是变量的名字,是String类型的;另一个就是值(value),它可以是int,bool,float,数组,甚至是对象类型。它们组合在一起,也就是上文所说的”键值对“(Key Value Pair),有点字典内味了哈(字典也是键值对啊kora)。而json文件本身也是String类型的文本。
看到这里也应该明白json和js的区别了(你认真的???):JavaScript是一种程序语言,而json则是一种数据格式,一种语法。因为它没有使用任何函数或者命令,仅仅是以文字为基础,易于阅读和编写,作为数据交换语言显然很适合。虽然JavaScript里自带方法可以让js和json互相转化,但本质上两者还是不属于一个东西。
虽然可以使用js可以和json互相转化,但是Unity的语言是C#啊。。。不过不用担心,Unity也有相应的方法让json可以和C#相互转化(JsonUtility类)。而下文也是通过这种方式实现的。
提示:其实可以通过库进行json数据的解析,比如.Net库的http://Json.Net和LitJson.dll。之所以使用JsonUtility类是因为它是Unity自带的,而且解析更快,产生的垃圾更少。
流写入类(StreamWriter类)
和FileSream类差不多,也是对系统文件进行写入的(没有读取功能捏),但是FileStream类读取的是字节数组,适用于非文本文件,所以不太适合读取String类型的JSON文件
注意:写完也记得把流关了
流读取类(StreamReader类)
和FileStream类差不多,也是对系统文件进行读取的(没有写入功能捏),但是FileStream类读取的是字节数组,适用于非文本文件,所以不太适合读取String类型的JSON文件
注意:写完也记得把流关了
就是复制粘贴改了的,略略略
应用
//不需要加别的命名空间,简直爽死(bushi
private void SaveByJSON()
{
Save save= CreateSave();
//创建一个Save实例存储游戏数据(CreateSave函数在上面)
String JsonString=JsonUtility.ToJson(save);
//将对象save转化为json字符串
//上面说了Json是string类型,所以命名string
StreamWriter sw=new StreamWriter(Application.dataPath+&#34;/Data.yj&#34;);
//persistentDataPath是隐藏文件的,所以你找不到Data.yj的所在地,
//而dataPath就不会隐藏,同时文件后缀也可以乱取
sw.Write(JsonString);
//将json字符串写入流参数
sw.Close();
//把流关了
}
private void LoadByJSON()
{
if(File.Exists(Application.dataPath+&#34;Data.yj&#34;))
//判断文件是否创建
{
StreamReader sr=new StreamReader(Application.dataPath+&#34;/Data.yj&#34;);
//从流中读取字符串
String JsonString=sr.ReadToEnd();
//ReadToEnd()方法可以读取从流当前位置到结尾的所有字符
//还有Read()方法,但是只读了一个字符,还有更多方法捏懒得打了
sr.Close();
//把流关了
Save save=JsonUtility.FromJson<Save>(JsonString);
//该方法属于泛型方法T,需要给出明确的类型定义,所以要写<Save>
GameManager.Instance.coins=save.coins;
player.transform.position=new Vector2(save.playerPosition.x,save.playerPositionY);
//属于是常规方式了
}
else{
Debug.LogError(&#34;File Not Found.&#34;);
}
}
就这样,我们使用json完成了游戏的存档和读档操作
小贴士:
json方法里的泛型和强制转换属于两码事,前者是一个方法,可以满足多个类型的实现(但是要使用<>标明类型)而后者只能完成一个类型,所以需要在前面加括号,或者使用as对错误的类型进行强制转换
总结
json看似完美的解决了文件可读性的问题(如果我们打开文件,看到的便是json本来的文本样式),但是json背后还是有着缺点:当在人机都需要识别数据时(比如说配置文件),json的可读性也会变得差,如果你要储存复杂场景,比如树的坐标,比如草的坐标,那一堆堆的数组表示的坐标肯定看得你想要坐牢。在这里提醒一下,当你打开json文件时,里面可不会有换行符一行行给你打的清楚,而是全部拧成了一团。那么,有没有一种文件保存类型,既可以在Unity里保存和读取数据,也可以具有良好的结构性和描述性呢?那就是最后一个我要介绍的最后一个类型:
XML
概念
XML
XML,意思是可扩展标记语言(eXtensible Markup Language)。为什么不叫EML呢?因为这样和eml(发邮件的文件后缀格式)重名了。。。
咳咳,言归正传,它专门被设计用来结构化“存储”和”传输“数据或者信息。它可以在不兼容的系统之间轻松交换数据,简化数据共享。而且,XML数据以纯文本方式进行存储(本质就是字符串),可以给任何阅读设备使用。
有些人听着可能觉得:这不就是html ?事实上两者确实是有区别的:html主要显示网站上的内容(文字、图片等),事实上,我们文章一开头网易云音乐放的真夜的歌(\真夜/\真夜/\真夜/\真夜/\真夜/\真夜/)(突发恶疾.jpg)就是通过html实现的:
阿巴<iframe frameborder=&#34;no&#34; border=&#34;0&#34; marginwidth=&#34;0&#34; marginheight=&#34;0&#34; width=330 height=86 src=&#34;//music.163.com/outchain/player?type=2&id=1399789328&auto=1&height=66&#34;></iframe>阿巴
在一个网站中,HTML负责网页内容的显示,CSS负责内容的颜色和排版,javascript负责进行一些交互功能的实现。(这些是前端的知识了属于是)
而XML相比于HTML,XML更在意里面存储的信息。比如说XML里面的标签可以自定义,而HTML都是预先定义好的,所以XML更能胜任这个存档的职能。
接下来就有点重要了:
为什么我们说XML相比于json更具有结构性呢?这是因为XML中的每个元素形成了一个文档树。我们可以创建一个玩家的根节点或者敌人的根节点,然后把这些对象应该拥有的变量通过AppendChild的方式附录到根节点上。这样人们想要看玩家的数据,只要找到玩家的节点一个个看就可以了,而不要像json一样挤在一起,难以分辨。下面应用里我手打的XML应该可以体现这一点。
XmlDocument类
用来保存,加载和解析XML文件,通过这个类可以对xml文件进行创建、保存、读取、添加以及删除。
不仅如此,这个类还有一个静态方法:xmlDocument.CreateElement(&#34;node&#34;);它可以在xml文档中创建一个“元素节点”(名字当然是自定义的,也就是标签),
注意:使用XmlDocument类前需要引入System.Xml命名空间哟~
应用
using System.Xml; //还是提醒一下
private void SaveByXML()
{
Save save=CreateSave();
XmlDocument xmlDocument=new XmlDocument();
#region CreateXML elements
XmlElement root=XmlDocument.CreateElement(&#34;Save&#34;);
//创建一个参数名为&#34;Save&#34;的xml元素,这个元素名字叫root
//这么取在脚本里就更好理解了
root.SetAttribute(&#34;FileName&#34;,&#34;File_01&#34;);
XmlElement coinElement=xmlDocument.CreateElement(&#34;Coins&#34;);
coinElement.InnerText=save.coins.ToString();
//创建金币保存的元素,并将金币数以字符串形式导入
root.AppendChild(coinElement);
//将coinElement这一元素附录至根节点root上
XmlElement playerPositionXElement=xmlDocument.CreateElement(&#34;PlayerPositionX&#34;);
playerPositionXElement.InnerText=save.playerPositionX.ToString();
root.AppendChild(playerPositionXElement);
//接下来就是重复步骤:创建节点、储存数据、将节点附录至根节点
XmlElement playerPositionYElement=xmlDocument.CreateElement(&#34;PlayerPositionY&#34;);
playerPositionYElement.InnerText=save.playerPositionY.ToString();
root.AppendChild(playerPositionYElement);
#endregion
xmlDocument.AppendChild(root);
//最后要把根节点附录在文件上,以便于文件可以保存
xmlDocument.Save(Application.dataPath+&#34;DataXML.yj&#34;);
//把数据保存在文件“DataXML.yj”里
if(File.Exists(Application.dataPath+&#34;DataXML.yj&#34;)){
Debug.Log(&#34;XML FILE SAVED&#34;);
}
}
接下来,如果你保存了并且打开了DataXML.yj,里面会是这样的:
<Save FileName=&#34;File_01&#34;>
<Coins>20</Coins>
<PlayerPositionX>2.5</PlayerPositionX>
<PlayerPositionY>-6</PlayerPositionY>
</Save>从这里我们就能看得很清楚了:在root节点(名字被我们命名为Save)下有三个子节点:Coins、PlayerPositionX、PlayerPositionY,它们都嵌套在了Save这个根节点下。相信通过这个更能理解XML文件的优越结构性了。
接下来就是读取:
private void LoadByXML()
{
if(File.Exists(Application.dataPath+&#34;/DataXML.yj&#34;))
{
Save save=new Save();
XmlDocument xmlDocument=new XmlDocument();
xmlDocument.Load(Applicatiom.dataPath+&#34;/DataXML.yj&#34;);
//创建并读取保存的XML文件
XmlNodeList coins=xmlDocument.GetElementsByTagName(&#34;Coins&#34;);
//寻找标签名称来找到保存在文件里的金币数
int coincount=int.Parse(coins.InnerText);
//MARKER 为什么是呢?因为如果标签名为Coins的有很多的话,就会重复
//所以返回的是List集合类型,第一个为,第二个为
//将String类的金币数通过Parse转化为int并存进变量里
save.coins=coincount;
XmlNodeList positionX=xml.DocumentXGetElementsByTagName(&#34;positionX&#34;);
float positionXcount=float.Parse(positionX.InnerText);
save.playerPositionX=positionXcount;
XmlNodeList positionY=xml.Document.GetElementsByTagName(&#34;positionY&#34;);
float positionYcount=float.Parse(positionY.InnerText);
save.playerPositionY=positionYcount;
GameManager.instance.coins=save.coins;
player.transform.position=new Vector2(save.playerPositionX,save.playerPositionY);
}
else
{
Debug.LogError(&#34;NOT FOUND FILE&#34;);
}
}
这样看XML可能还不太清楚,所以我打算在把敌人的数据也保存在XML里面,这样就得改成:
using System.Xml; //还是提醒一下
private void SaveByXML()
{
Save save=CreateSave();
XmlDocument xmlDocument=new XmlDocument();
#region CreateXML elements
XmlElement root=XmlDocument.CreateElement(&#34;Save&#34;);
//创建一个参数名为&#34;Save&#34;的xml元素,这个元素名字叫root
//这么取在脚本里就更好理解了
root.SetAttribute(&#34;FileName&#34;,&#34;File_01&#34;);
XmlElement coinElement=xmlDocument.CreateElement(&#34;Coins&#34;);
coinElement.InnerText=save.coins.ToString();
//创建金币保存的元素,并将金币数以字符串形式导入
root.AppendChild(coinElement);
//将coinElement这一元素附录至根节点root上
XmlElement playerPositionXElement=xmlDocument.CreateElement(&#34;PlayerPositionX&#34;);
playerPositionXElement.InnerText=save.playerPositionX.ToString();
root.AppendChild(playerPositionXElement);
//接下来就是重复步骤:创建节点、储存数据、将节点附录至根节点
XmlElement playerPositionYElement=xmlDocument.CreateElement(&#34;PlayerPositionY&#34;);
playerPositionYElement.InnerText=save.playerPositionY.ToString();
root.AppendChild(playerPositionYElement);
XmlElement enemy,enemyPositionX,enemyPositionY,isDead;
for(int i=0;i<save.enemyPositionX.Count;i++){
enemy=xmlDocument.CreateElement(&#34;Enemy&#34;);
enemyPositionX=xmlDocument.CreateElement(&#34;EnemyPositionX&#34;);
enemyPositionY=xmlDocument.CreateElement(&#34;EnemyPositionY&#34;);
isDead<span class="p">=xmlDocument.CreateElement(&#34;IsDead&#34;);
enemyPositionX.InnerText=save.enemyPositionX.ToString();
enemyPositionY.InnerText=save.enemyPositionY.ToString();
isDead.InnerText=save.isDead.ToString();
enemy.AppendChild(enemyPositionX);
enemy.AppendChild(enemyPositionY);
enemy.AppendChild(isDead);
root.AppendChild(enemy);
}
#endregion
xmlDocument.AppendChild(root);
//最后要把根节点附录在文件上,以便于文件可以保存
xmlDocument.Save(Application.dataPath+&#34;DataXML.yj&#34;);
//把数据保存在文件“DataXML.yj”里
if(File.Exists(Application.dataPath+&#34;DataXML.yj&#34;)){
Debug.Log(&#34;XML FILE SAVED&#34;);
}
}
而XML文档也会变成这个样子:
<Save FileName=&#34;File_01&#34;>
<Coins>20</Coins>
<PlayerPositionX>2.5</PlayerPositionX>
<PlayerPositionY>-6</PlayerPositionY>
<Enemy>
<EnemyPositionX>-2.1</EnemyPositionX>
<EnemyPositionY>2</EnemyPositionY>
<IsDead>True</IsDead>
</Enemy>
<Enemy>
<EnemyPositionX>-2.1</EnemyPositionX>
<EnemyPositionY>2</EnemyPositionY>
<IsDead>False</IsDead>
</Enemy>
<Enemy>
<EnemyPositionX>-0.1</EnemyPositionX>
<EnemyPositionY>5</EnemyPositionY>
<IsDead>True</IsDead>
</Enemy>
<Enemy>
<EnemyPositionX>2.1</EnemyPositionX>
<EnemyPositionY>2</EnemyPositionY>
<IsDead>False</IsDead>
</Enemy>
<Enemy>
<EnemyPositionX>-21</EnemyPositionX>
<EnemyPositionY>20</EnemyPositionY>
<IsDead>False</IsDead>
</Enemy>
</Save>这样看起来确实就很舒适了(相比于json文件)
在读取环节也要加一点东西:
private void LoadByXML()
{
if(File.Exists(Application.dataPath+&#34;/DataXML.yj&#34;))
{
Save save=new Save();
XmlDocument xmlDocument=new XmlDocument();
xmlDocument.Load(Applicatiom.dataPath+&#34;/DataXML.yj&#34;);
//创建并读取保存的XML文件
XmlNodeList coins=xmlDocument.GetElementsByTagName(&#34;Coins&#34;);
//寻找标签名称来找到保存在文件里的金币数
int coincount=int.Parse(coins.InnerText);
//MARKER 为什么是呢?因为如果标签名为Coins的有很多的话,就会重复
//所以返回的是List集合类型,第一个为,第二个为
//将String类的金币数通过Parse转化为int并存进变量里
save.coins=coincount;
XmlNodeList positionX=xml.DocumentXGetElementsByTagName(&#34;positionX&#34;);
float positionXcount=float.Parse(positionX.InnerText);
save.playerPositionX=positionXcount;
XmlNodeList positionY=xml.Document.GetElementsByTagName(&#34;positionY&#34;);
float positionYcount=float.Parse(positionY.InnerText);
save.playerPositionY=positionYcount;
if(enemy.Count!=0){
for(int i=0;i<enemy.Count;i++){
XmlNodeList enemyPositionX=xml.Document.GetElementsByTagName(&#34;EnemyPositionX&#34;);
float enemyPosX=float.Parse(enemyPositionX.InnerText);
save.enemyPositionX.Add(enemyPosX);
//由于对象save中敌人坐标声明为List集合,所以需要List.Add进行数值的添加
XmlNodeList enemyPositionY=xml.Document.GetElementsByTagName(&#34;EnemyPositionY&#34;);
float enemyPosY=float.Parse(enemyPositionY.InnerText);
save.enemyPositionY。Add(enemyPosY);
XmlNodeList isDead=xml.Document.GetElementsByTagName(&#34;IsDead&#34;);
}
}
GameManager.instance.coins=save.coins;
player.transform.position=new Vector2(save.playerPositionX,save.playerPositionY);
for(int i=0;i<save.isDead.Count;i++){
if(GameManager.instance.enemies==null){
if(!save.isDead)//如果敌人在我们保存之后死了
{
float enemyPosX=save.enemyPositionX;
float enemyPosY=save.enemyPositionY;
Enemy newEnemy=Instantiate(enemyPrefab,new Vector2(enemyPosX,enemyPosY),Quaternion.identity);
GameManager.Instance.enemies=newBat;
//把本该活着的敌人数据进行填充
}else{
float enemyPosX=save.enemyPositionX;
float enemyPosY=save.enemyPositionY;
Enemy newEnemy=Instantiate(enemyPrefab,new Vector2(enemyPosX,enemyPosY),Quaternion.identity);
}
}
}
}
else
{
Debug.LogError(&#34;NOT FOUND FILE&#34;);
}
}
总结
XML从个人观感上确实好了不少,可是你看着这代码。。。属实让人头皮发麻。毕竟你要在存储内容的基础上还要加上结构层次,多点代码量看上去也是理所应当的。
All In All
其实把对象储存进文件的方式还有很多,只是目前主流方式就是这四种了。PlayerPrefs可以较为简单的进行存档读档,但是却只能进行一些简单的操作(比如玩家设置等轻量数据),存储的数值也只局限于int,float,string三种类型,连List类都无法支持。序列化和反序列化看似可以解决数据的存储和读取,而且存档的内容也不至于让别人那么容易地进行篡改。但是改不了你自己也读不懂啊,而且还要加上很长的命名空间。json相对而言就不用加上一些复杂的命名空间,存储的文档也让人看得懂。但是储存的东西一下多了,你打开文档时,那不带换行的原始json文件又给你带来一个全新的坐牢体验。XML就很好的解决了文档可读性的问题,但是相对的代码量也提高了114514倍,命名空间也加了一个。关于这四种方式的解决问题,建议读者根据自己的实际情况进行选择。不过在我看来,json应该是最佳的选择把。。。
可以来我的博客坐坐:www.karmotrine.fun 好棒的帖子!写的真好,谢谢你(>﹏<) 听口音就是老二次元了,还是二次元懂二次元,生动有趣,通俗易懂
页:
[1]