持久化数据
[Serialization](序列化),可以用来将对象转化为字节流;仅对类,结构,枚举,委托的声明有效
Deserialization(反序列化),可以用来将字节流转换为对象。 Json和XML参考链接
PlayerPrefs
在游戏会话中储存和访问游戏存档。这个是持久化数据储存,比如保存游戏记录
函数
- DeleteAll
从游戏存档中删除所有key。请谨慎使用。 - DeleteKey
从游戏存档中删除key和它对应的值。 - GetFloat
如果存在,返回游戏存档文件中key对应的浮点数值。 - GetInt
如果存在,返回游戏存档文件中key对应的整数值。 - GetString
如果存在,返回游戏存档文件中key对应的字符串值。 - HasKey
如果key在游戏存档中存在,返回true。 - Save
写入所有修改参数到硬盘。 - SetFloat
设置由key确定的浮点数值。 - SetInt
设置由key键确定的整数值。 - SetString
设置由key确定的字符串值。
对于Save()函数:unity默认在程序退出时保存参数,以防游戏崩溃或过早退出,可能需要在游戏中合理的“检查点”编写PlayerPrefs。这个函数会将数据写入磁盘,可能会导致稍微的卡顿,因此不建议在实际的游戏过程中调用。
JSON
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,具有自我描述性。它基于JavaScript(Standard ECMA-262 3rd Edition - December 1999)的一个子集。
JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成(网络传输速度)。是一种与语言无关的发送和接收数据的常用格式,可以使用它来跨平台的传输数据。
- JSON序列化:对象--->JSON
- JSON反序列化:JSON--->对象
JSON语法
JSON 语法是 JavaScript 对象表示语法的子集。
- 数据在名称/值对中
- 数据由逗号分隔
- 花括号{ }保存对象
- 方括号[ ]保存数组
JSON 名称/值对
- JSON 数据的书写格式是:名称/值对。
- 名称/值对组合中的名称写在前面(在双引号中),值对写在后面(同样在双引号中),中间用冒号隔开;
- 例:"firstName":"John",这很容易理解,等价于这条 JavaScript 语句:firstName="Name"
JSON 值可以是:
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true 或 false)
- 数组(在方括号中[])
- 对象(在花括号中{})
- null
JSON结构
-
对象:对象在js中表示为“{}”括起来的内容,数据结构为 {key:value,key:value,...}的键值对的结构,在面向对象的语言中,key为对象的属性,value为对应的属性值,所以很容易理解,取值方法为 对象[key] 获取属性值,这个属性值的类型可以是 数字、字符串、数组、对象几种。
-
数组:数组在js中是中括号“[]”括起来的内容,数据结构为 [“c",“unity",“mono",...],取值方式和所有语言中一样,使用索引取值,字段值的类型可以是 数字、字符串、数组、对象几种。
LitJson介绍
LitJson是一个开源项目,比较小巧轻便,安装也很简单,在Unity里只需要把LitJson.dll放到Plugins文件夹下,并在代码的最开头添加 “Using LitJson”就可以了。
简单来说,LitJson的用途是实现Json和代码数据之间的转换,一般用于从服务器请求数据,得到返回的Json后进行转换从而在代码里可以访问。
Json生成、解析
- Json生成
//使用JsonWriter创建JSON复合字符串(包含对象和数组)
JsonWriter writer = new JsonWriter(); //创建JsonWriter对象
jsonWriter.WriteObjectStart();//开始写对象
jsonWriter.WritePropertyName("姓名");//写入键
jsonWriter.Write("wu");//写入值
jsonWriter.WritePropertyName("具体信息");//写入键
jsonWriter.WriteArrayStart();//开始写数组,数组做为键值对中的值
jsonWriter.Write("女");//写数组中的值
jsonWriter.Write("18");//写数组中的值
jsonWriter.WriteArrayEnd();//结束写数组
jsonWriter.WritePropertyName("其他信息");//写入键
jsonWriter.WriteObjectStart();//开始写对象,对象作为键值对的值
jsonWriter.WritePropertyName("年级");
jsonWriter.Write("一年级");
jsonWriter.WritePropertyName("学校");
jsonWriter.WriteArrayStart();
jsonWriter.Write("JSON字符串学习");
jsonWriter.WriteArrayEnd();//结束写数组
jsonWriter.WriteObjectEnd();//结束写对象
jsonWriter.WriteObjectEnd();//结束写对象
Debug.Log(jsonWriter);//直接输出json字符串
//--------------------------------------------------------
生成json对象
{"姓名":"wu","具体信息":["女","18"],"其他信息":{"年级":"一年级","学校":["JSON字符串学习"]}}
//使用JsonData创建JSON复合字符串(包含对象和数组)
JsonData jsonObject01 = new JsonData();//创建JsonData对象
jsonObject01["昵称"] = "囍";//为所创建的JsonData对象添加键值对;此时作为Json对象使用
jsonObject01["职业"] = "战士";
JsonData jsonArray = new JsonData();//创建JsonData对象
jsonArray.Add("攻击力");//为所创建的JsonData对象添加值;此时作为Json数组使用
jsonArray.Add(200);
jsonObject01["具体"] = jsonArray;//为Json对象添加键值对;值为一个Json数组
JsonData jsonObject02 = new JsonData();//创建JsonData对象
jsonObject02["性别"] = "男";//为所创建的JsonData对象添加键值对;此时作为Json对象使用
jsonObject01["其他信息"] = jsonObject02;//为Json对象添加键值对;值为一个Json对象
Debug.Log(jsonObject01.ToJson());//将jsonData对象转换为Json字符串后输出
//--------------------------------------------------
生成json对象
{"昵称":"囍","职业":"战士","具体":["攻击力",200],"其他信息":{"性别":"男"}}
//使用JsonMapper类将序列化的类对象转换为Json字符串
[System.Serializable]//可序列化特性
public class Save
{
public List<int> monsterTypeList = new List<int>();
public List<int> targetPosList = new List<int>();
public int shootNum = 0;
public int score = 0;
}
Save saveData = new Save();
string saveDataJson = JsonMapper.ToJson(saveData);//将序列化的对象转换为Json字符串
- Json解析
(JsonMapper类)相关API
JsonData data =JsonMapper.ToObject(string json) //把json数据转化成JsonData对象
data.ToJson(); //把JsonData对象转化成json数据
//------------------------------------------
JsonMapper.ToObject <T> (T t1) //把json数据转化成对应的类对象
JsonMapper.ToJson(object o) //把一个对象中的数据转化成json数据
string json01="{"姓名":"wu","具体信息":["女"],"其他":{"年级":"一年级","学校":["JSON字符串学习"]}}";
JsonData jsonData = JsonMapper.ToObject(json01);
Debug.Log(jsonData["姓名"].ToString()); //wu;通过键获取值
Debug.Log(jsonData["具体信息"][0].ToString());//女;通过键获取值后,通过索引获取数组中的值
Debug.Log(jsonData["其他"]["学校"][0].ToString());//JSON字符串学习;通过键获取值后,再次通过键获取值,再通过索引获取数组中的值
JsonUtility
Unity圣典
- JsonUtility.FromJson 使用Json表达式创建对象
C# => public static T FromJson(string json);
C# => public static object FromJson(string json, Type type);
json:json字符串;type:JSON表示的对象类型。
内部工具,此方法使用Unity序列化器;因此你创建的类型必须支持序列化。它必须是有序列化属性标记的类或结构。对象的域必须支持序列化。不支持的域和私有域或者非序列化属性标记的域一样,将会被忽略。仅支持普通的类和结构;从UnityEngine.Object派生的类(例如MonoBehaviour 或者ScriptableObject)是不支持的。如果JSON表示缺少任何字段,将为它们提供默认值(即T类型域会是默认值-它将不会被指定为字段初始化器的任何值,因为该对象的构造函数不会在反序列化期间执行)。该方法可以被底层线程调用。
using UnityEngine;
[Serializable]
public class PlayerInfo
{
public string name;
public int lives;
public float health;
public static PlayerInfo CreateFromJSON(string jsonString)
{
return JsonUtility.FromJson<PlayerInfo>(jsonString);
}
// 传入值:
// {"name":"Dr Charles","lives":3,"health":0.8}
// 返回的PlayerInfo对象
// name == "Dr Charles", lives == 3, and health == 0.8f.
}
- JsonUtility.FromJsonOverwrite 通过读取JSON字符串来覆盖对象中的数据。
C# => public static void FromJsonOverwrite(string json, object objectToOverwrite);
json:Json字符串;objectToOverwrite:要被重写数据的对象
它将JSON数据加载到一个现有对象中。这允许更新存储在类或对象中的值,而无需进行任何分配。
内部工具,这个方法使用Unity序列化器;因此,你传递的对象必须被序列化器支持:它必须是MonoBehaviour, ScriptableObject,或者应用了Serializable属性的普通类/结构。要覆盖的字段类型必须由序列化程序支持;不支持的字段将被忽略,私有字段、静态字段和应用了NonSerialized属性的字段也将被忽略。支持任何普通类或结构,以及MonoBehaviour或ScriptableObject派生的类。不支持其他引擎类型。仅在编辑器中,您可以使用EditorJsonUtility。FromJsonOverwrite覆盖其他引擎对象。如果一个对象的字段不存在JSON表示,该字段将保持不变。 在方法可以被底层线程调用。当该函数运行使你不该修改或重写该对象。
using UnityEngine;
public class PlayerState : MonoBehaviour
{
public string playerName;
public int lives;
public float health;
public void Load(string savedData)
{
return JsonUtility.FromJsonOverwrite(savedData, this);
}
// 传入输入值
// {"lives":3, "health":0.8}
// load函数将改变调用它的对象
// lives == 3 , health == 0.8
// 'playerName' 字段保持不变
}
- JsonUtility.ToJson 生成一个对象的公共字段的JSON字符串。
C# => public static string ToJson(object obj);
C# =>public static string ToJson(object obj, bool prettyPrint);
obj:要转为Json的对象;prettyPrint:如果为真,格式化输出以提高可读性。如果为false,将输出格式化为最小大小。默认是假的。
内部工具,这个方法使用Unity序列化器;因此,你传递的对象必须被序列化器支持:它必须是MonoBehaviour, ScriptableObject,或者应用了Serializable属性的普通类/结构。您希望包含的字段类型必须由序列化程序支持;不支持的字段将被忽略,私有字段、静态字段和应用了NonSerialized属性的字段也将被忽略。支持任何从MonoBehaviour 和ScriptableObject导出的普通类和结构。其他引擎类型是不支持的。仅在编辑器中你可以使用EditorJsonUtility.FromJsonOverwrite去重写其他引擎对象。 请注意,虽然可以将原始类型传递给该方法,但结果可能不是您所期望的;该方法将尝试序列化它们的公共实例字段,而不是直接序列化它们,结果生成一个空对象。类似地,将数组传递给该方法将不会生成包含每个元素的JSON数组,而是生成一个包含数组对象本身的公共字段(没有公共字段)的对象。要序列化数组或基本类型的实际内容,有必要将其包装在类或结构中。在方法可以被底层线程调用。当该函数运行使你不该修改或重写该对象。
using UnityEngine;
public class PlayerState : MonoBehaviour
{
public string playerName;
public int lives;
public float health;
public string SaveToString()
{
return JsonUtility.ToJson(this);
}
//该对象的值: playerName = "Dr Charles",lives = 3, health = 0.8f,
//返回值: {"playerName":"Dr Charles","lives":3,"health":0.8}
}
XML
扩展标记语言,用于标记电子文件使其具有结构性的标记语言。被设计用来传输和存储数据;结构化、存储以及传输信息。
-
XML有且只有一个根节点
-
XML指可扩展标记语言(Extensible Markup Language)。
-
XML是一种很像HTML的标记语言。
-
XML的设计宗旨是传输数据,而不是显示数据。
-
XML标签没有被预定义,你需要自行定义标签。
-
XML被设计为具有自我描述性。
-
XML是W3C的推荐标准。
-
图示:
XML语法规则
- 所有 XML 元素都须有关闭标签
<p>This is a paragraph</p>
- XML 标签对大小写敏感,例:标签 与标签 是两个不同的标签
<Message>这是错误的。</message>
<message>这是正确的。</message>
- XML 必须正确地嵌套
<b><i>This text is bold and italic</b></i> //错误
<b><i>This text is bold and italic</i></b> //正确
- XML 文档必须有根元素
<root>
<child>
<subchild>.....</subchild>
</child>
</root>
- XML 的属性值须加引号
<note date=08/08/2008> //错误
<to>George</to>
<from>John</from>
</note>
<note date="08/08/2008"> //正确
<to>George</to>
<from>John</from>
</note>
- XML 中的注释
<!-- This is a comment -->
XML命名规则
-
XML 元素必须遵循以下命名规则:
名称可以含字母、数字以及其他的字符
名称不能以数字或者标点符号开始
名称不能以字符 “xml”(或者 XML、Xml)开始
名称不能包含空格 -
可使用任何名称,没有保留的字词。
XmlDocument类
XmlDocument将XML视为树状结构,它装载XML文档,并在内存中构建该文档的树状结构。
创建XML
- XmlDocument:创建XmlDocument实例,
XmlDocument xmlDocument = new XmlDocument();
- CreateXmlDeclaration:声明头部文件:
XmlDeclaration head = xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", "");
- CreateElement:创建XML元素结点
XmlElement 节点名称 = xmlDocument.CreateElement(节点名称);
- AppendChild:将指定的节点添加到该节点的子节点列表的末尾
xmlDocument.AppendChild(head);
上一级结点节点.AppendChild(下一级节点);
- SetAttribute:设置属性
节点名称.SetAttribute(“属性名”,属性值);
- InnerText :获取或设置节点中的文本值及其所有子节点的串联值。
- Save :将 XML 文档保存到指定的位置
实例:
private void CreateXML()
{
string filePath = Application.streamingAssetsPath + "/Test.xml";
//检查xml是否存在,如果没有文件则创建
if(File.Exists(filePath)) return;
XmlDocument xmlDoc = new XmlDocument ();//创建xml实例
XmlElement root = xmlDoc.CreateElement("root_CharacterTemplateData");//创建根节点
XmlElement elmNew = xmlDoc.CreateElement("CharacterTemplateData");//创建一级子节点
elmNew.SetAttribute("id","0");//设置属性,属性名为id,值为0
elmNew.SetAttribute ("name","xml");//设置属性。属性名为name,值为xml
XmlElement jobID = xmlDoc.CreateElement("JobID");//创建二级节点,结点名为JobID
jobID.InnerText = "1";//设置二级节点内的文本值
elmNew.AppendChild(jobID);//在一级节点中追加二级节点
XmlElement jobMode = xmlDoc.CreateElement ("JobMode");//创建二级节点,结点名为JobMode
jobMode.InnerText = "none";//设置二级节点内的文本值
elmNew.AppendChild (jobMode);//在一级节点中追加二级节点
XmlElement initForce = xmlDoc.CreateElement ("InitForce");
initForce.InnerText = "1.1";
elmNew.AppendChild (initForce);
root.AppendChild(elmNew);//在跟节点中追加一级节点
xmlDoc.AppendChild(root);//在xml实例中追加根节点
xmlDoc.Save(filePath);//保存xml文件
}
//----------------------------------------------------
//结果:
<root_CharacterTemplateData>
<CharacterTemplateData id="0" name="xml">
<JobID>1</JobID>
<JobMode>none</JobMode>
<InitForce>1.1</InitForce>
</CharacterTemplateData>
</root_CharacterTemplateData>
添加XML
- Load:加载指定的 XML 数据。
XmlDocument 实例名称 = new XmlDocument ();//创建XmlDocument实例
XmlDocument 实例名称.Load(XML文件所在的文件路径包含文件名);
- SelectSingleNode:查找单一节点
XmlDocument 实例名称.SelectSingleNode(节点名称);
如果用于两个节点间的查找:例:B为A的结点,如果想要查找B;A结点.SelectSingleNode("B结点名称")
- SelectNodes:查找所有结点
XmlNodeList xmlNodeList = XmlNode对象.SelectNodes("PosAndType");
- GetElementsByTagName:通过结点名称查找元素;可以查找整个Xml中与string相同的结点。
XmlNodeList xmlNodeList = XmlDocument实例.GetElementsByTagName(结点名称);
- CreateElement:添加元素节点
XmlElement 二级节点= xmlDocument.CreateElement(节点名称)
实例:
private void AddXML()
{
//检测文件是否存在
if(!File.Exists(filePath)) return;
XmlDocument xmlDoc = new XmlDocument ();//创建XmlDocument实例
xmlDoc.Load(filePath);//通过文件路径加载XML文件
XmlNode root = xmlDoc.SelectSingleNode ("root_CharacterTemplateData");//查找根节点
XmlElement elmNew = xmlDoc.CreateElement("CharacterTemplateData");//要添加的新节点
elmNew.SetAttribute ("id","1");//设置属性
elmNew.SetAttribute ("name","name1");
XmlElement jobId = xmlDoc.CreateElement ("JobID");
jobId.InnerText = "1";//设置结点中的文本文件
elmNew.AppendChild (jobId);//进行结点的关联;将二级结点追加到一级节点下
XmlElement jobMode = xmlDoc.CreateElement ("JobMode");
jobMode.InnerText = "none";
elmNew.AppendChild (jobMode);
XmlElement initForce = xmlDoc.CreateElement ("InitForce");
initForce.InnerText = "2.2";
elmNew.AppendChild (initForce);
root.AppendChild (elmNew);//在根节点中添加新建的节点
xmlDoc.Save(filePath);//保存XML文件
}
//----------------------------------------------------
//结果:
<root_CharacterTemplateData>
<CharacterTemplateData id="0" name="xml">
<JobID>1</JobID>
<JobMode>none</JobMode>
<InitForce>1.1</InitForce>
</CharacterTemplateData>
<!-- 添加的部分 -->
<CharacterTemplateData id="1" name="name1">
<JobID>1</JobID>
<JobMode>none</JobMode>
<InitForce>2.2</InitForce>
</CharacterTemplateData>
<!-- 添加的部分 -->
</root_CharacterTemplateData>
更新XMl
- GetAttribute:获取属性
节点对象. GetAttribute(属性名称)
- SetAttribute:设置属性
节点对象.SetAttribute(“属性名”,属性值);
实例:
private void UpdateXML()
{
//检测文件
if(!File.Exists(filePath)) return;
//读取实例
XmlDocument xmlDoc = new XmlDocument ();
xmlDoc.Load (filePath);
//获取根节点下所有子节点
XmlNodeList nodeList = xmlDoc.SelectSingleNode("root_CharacterTemplateData").ChildNodes;
//获取结点名为root_CharacterTemplateData的所有子节点
foreach(XmlElement xe in nodeList)
{
//遍历所有子节点,节点名称为CharacterTemplateData并且属性id=0,修改属性
if (xe.Name == "CharacterTemplateData" && xe.GetAttribute ("id") == "0")
{
xe.SetAttribute ("id","1000");
//修改该节点的子节点,节点名称为JobID=2
xe.SelectSingleNode("JobID").InnerText = "2";
}
}
//保存
xmlDoc.Save(filePath);
}
//----------------------------------------------------
//结果:
<root_CharacterTemplateData>
<CharacterTemplateData id="1000" name="xml">//id更改
<JobID>2</JobID>//结点内文本值更改
<JobMode>none</JobMode>
<InitForce>1.1</InitForce>
</CharacterTemplateData>
<CharacterTemplateData id="1" name="name1">
<JobID>1</JobID>
<JobMode>none</JobMode>
<InitForce>2.2</InitForce>
</CharacterTemplateData>
</root_CharacterTemplateData>
删除XML
- ChildNodes:获取所用子节点
节点对象.ChildNodes;
- RemoveAttribute:删除节点属性
XmlElement对象.RemoveChild(XmlNode类型对象)
实例:
private void DeleteXML()
{
if (!File.Exists (filePath))
return;
XmlDocument xmlDoc = new XmlDocument ();
xmlDoc.Load (filePath);
XmlNodeList nodeList = xmlDoc.SelectSingleNode ("root_CharacterTemplateData").ChildNodes;
foreach(XmlElement xe in nodeList)
{
if (xe.Name == "CharacterTemplateData" && xe.GetAttribute ("id") == "1") {
xe.RemoveAttribute ("id");//移除节点属性值为id的结点
xe.SelectSingleNode ("JobID").InnerText = "";
}
}
xmlDoc.Save (filePath);
}
//----------------------------------------------------
//结果:
<root_CharacterTemplateData>
<CharacterTemplateData id="1000" name="xml">//id更改
<JobID>2</JobID>//结点内文本值更改
<JobMode>none</JobMode>
<InitForce>1.1</InitForce>
</CharacterTemplateData>
<CharacterTemplateData name="name1">
<JobID></JobID>
<JobMode>none</JobMode>
<InitForce>2.2</InitForce>
</CharacterTemplateData>
</root_CharacterTemplateData>