本文旨在于简要分析Unity中的两种存档机制,即:PlayerPrefs数据持久化方法Serialization数据序列化方法

较比于源项目,我另加了JSON方法、XML方法等及一些Unity设置,更便于读者在使用中理解Unity的存档机制。核心脚本为Game.cs


一、PlayerPrefs 数据持久化方法

  1. 存储原理:采用键值对(key与value)的方法,将游戏数据储存到本地,是一种Unity自带的储存方法。
  2. 储存类型:仅支持int、float、string三种
  3. 储存地址:详见官方文档 PlayerPrefs - Unity Documentation
  4. 读写示例:
//项目内未展示该用法,但以下代码即为常规用法
//新建存档
PlayerPrefs.SetInt("Score", 20);
PlayerPrefs.SetFloat("Health", 100.0F);
PlayerPrefs.SetString("Name",m_PlayerName); //检验存档信息
if(!PlayerPrefs.HasKey("Name"))
return; //读取存档
socre = PlayerPrefs.GetInt("Score");
health = PlayerPrefs.GetFloat("Health");
m_PlayerName = PlayerPrefs.GetString("Name"); //删除存档
PlayerPrefs.DeleteKey("Score");
  • 优缺点:虽然以这种方式存储游戏数据方便快捷,但是当数据量庞大以后,键值对的大量创建使用,不仅脚本控制繁琐,也有可能造成资源的浪费。因此,只建议对一些基础数据,例如图像设置、声音设置等采用该方法存储。

二、Serialization 序列化方法

  1. 存储原理:将对象(Object)转换为数据流(stream of bytes),再经过文件流存储到本地的方法。

    • 对象(Object):可以是Unity中的任何文件或是脚本
    • 数据流(stream of bytes):
  2. 序列化反序列化:
    • Serialization:对象-->数据流
    • Deserialization:数据流-->对象
  3. 序列化的方法:
    • 二进制方法
    • JSON方法
    • XML方法

1. 二进制存储(Binary Formatter):

//存档信息的类:
[System.Serializable]
public class Save
{
public int hits = 0;
public int shots = 0;
public List<int> livingTargetPositions = new List<int>();
public List<int> livingTargetsTypes = new List<int>();
} //设置游戏数值
public void SetGame(Save save)
{
hits = save.hits;
shots = save.shots; for (int i = 0; i < save.livingTargetPositions.Count; i++)
{
int position = save.livingTargetPositions[i];
Target target = targets[position].GetComponent<Target>();
target.ActivateRobot((RobotTypes)save.livingTargetsTypes[i]);
target.GetComponent<Target>().ResetDeathTimer();
}
} //存档函数:
public void SaveGame()
{
//1. 序列化过程
//创建save对象保存游戏信息
Save save = CreateSaveGameObject();
string filePath = Application.dataPath + "/gameSaveBySerialize.save"; //2. 创建二进制格式化程序及文件流
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(filePath); //3. 将save对象序列化到file流
bf.Serialize(file, save);
file.Close();
} //读档函数:
public void LoadGame()
{
string filePath = Application.dataPath + "/gameSaveBySerialize.save"; //1. 检验目标位置是否有存档
if (File.Exists(filePath))
{
//2. 创建二进制格式化程序,打开文件流
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(filePath, FileMode.Open); //3. 将file流反序列化到save对象
Save save = (Save)bf.Deserialize(file);
file.Close(); //从save对象读取信息到本地
SetGame(save);
}
else
Debug.Log("No gamesaved!");
}

2. JSON方法:

/*
* 注意:使用JSON存档方法需要用到LitJson库,LitJson.dll文件可在项目Assets目录下找到。
* 使用方法:将LitJson.dll拖拽到个人项目Assets目录下即可
*/ //JSON存档函数:
public void SaveAsJson()
{
//1. 创建save对象保存游戏信息
Save save = CreateSaveGameobject();
string path = Application.dataPath + "/gameSaveByJson.json"; //2. 利用JsonMapper将save对象转换为Json格式的字符串
string saveJsonStr = JsonMapper.ToJson(save); //3. 创建StreamWriter,将Json字符串写入文件中
StreamWriter sw = new StreamWriter(path);
sw.Write(saveJsonStr);
sw.Close();
} //JSON读档函数:
public void LoadAsJson()
{
string path = Application.dataPath + "/gameSaveByJson.json"; //1. 检验目标位置是否有存档
if(File.Exists(path))
{
//2. 创建一个StreamReader,用来读取流
StreamReader sr = new StreamReader(path); //3. 将读取到的流赋值给jsonStr
string jsonStr = sr.ReadToEnd();
sr.Close(); //4. 将字符串jsonStr转换为Save对象
Save save = JsonMapper.ToObject<Save>(jsonStr); //从save对象读取信息到本地
SetGame(save);
}
else
Debug.Log("No gamesaved!");
}

JSON存档格式:

{
"livingTargetPositions":[0,1,2,4],
"livingTargetsTypes":[2,2,2,1],
"hits":1,
"shots":8
}

3. XML方法:

//XML存储
public void SaveAsXml()
{
Save save = CreateSaveGameObject(); //创建XML文件的存储路径
string filePath = Application.dataPath + "/gameSaveByXML.txt"; //创建XML文档
XmlDocument xmlDoc = new XmlDocument(); //创建根节点,即最上层节点
XmlElement root = xmlDoc.CreateElement("save"); //设置根节点中的值
root.SetAttribute("name", "saveFile1"); //创建XmlElement
XmlElement target;
XmlElement targetPosition;
XmlElement targetType; //遍历save中存储的数据,将数据转换成XML格式
for (int i = 0; i < save.livingTargetPositions.Count; i++)
{
target = xmlDoc.CreateElement("target");
targetPosition = xmlDoc.CreateElement("targetPosition"); //设置InnerText值
targetPosition.InnerText = save.livingTargetPositions[i].ToString();
targetType = xmlDoc.CreateElement("targetType");
targetType.InnerText = save.livingTargetsTypes[i].ToString(); //设置节点间的层级关系 root -- target -- (targetPosition, monsterType)
target.AppendChild(targetPosition);
target.AppendChild(targetType);
root.AppendChild(target);
} //设置射击数和分数节点并设置层级关系
XmlElement shots = xmlDoc.CreateElement("shoots");
shots.InnerText = save.shots.ToString();
root.AppendChild(shots); XmlElement hits = xmlDoc.CreateElement("hits");
hits.InnerText = save.hits.ToString();
root.AppendChild(hits); xmlDoc.AppendChild(root);
xmlDoc.Save(filePath); if (File.Exists(Application.dataPath + "/gameSaveByXML.txt"))
{
Debug.Log("Saving as XML");
}
} //XML读取
public void LoadAsXml()
{
string filePath = Application.dataPath + "/gameSaveByXML.txt";
if (File.Exists(filePath))
{
Save save = new Save(); //加载XML文档
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(filePath); //通过节点名称来获取元素,结果为XmlNodeList类型
XmlNodeList targets = xmlDoc.GetElementsByTagName("target"); //遍历所有的target节点,并获得子节点和子节点的InnerText
if (targets.Count != 0)
{
foreach (XmlNode target in targets)
{
//把得到的值存储到save中
XmlNode targetPosition = target.ChildNodes[0];
int targetPositionIndex = int.Parse(targetPosition.InnerText);
save.livingTargetPositions.Add(targetPositionIndex); XmlNode targetType = target.ChildNodes[1];
int targetTypeIndex = int.Parse(targetType.InnerText);
save.livingTargetsTypes.Add(targetTypeIndex);
}
} //得到存储的射击数和分数
XmlNodeList shoots = xmlDoc.GetElementsByTagName("shoots");
int shootNumCount = int.Parse(shoots[0].InnerText);
save.shots = shootNumCount; XmlNodeList hits = xmlDoc.GetElementsByTagName("hits");
int hitsCount = int.Parse(hits[0].InnerText);
save.hits = hitsCount; SetGame(save);
}
else
{
Debug.Log("No game saved!");
}
}

XML存档格式:

<save name="saveFile1">
<target>
<targetPosition>0</targetPosition>
<targetType>2</targetType>
</target>
<target>
<targetPosition>1</targetPosition>
<targetType>2</targetType>
</target>
<target>
<targetPosition>2</targetPosition>
<targetType>2</targetType>
</target>
<target>
<targetPosition>3</targetPosition>
<targetType>2</targetType>
</target>
<shoots>13</shoots>
<hits>3</hits>
</save>

三、总述

无论是数据持久化方法还是序列化方法都可以实现Unity的存档机制。数据持久化方法操作方便,适用于数值较少的小项目。序列化方法的存档格式较为规范,其中二进制方法操作简单,但可读性差;JSON方法存档格式规范易读,具有一定的可读性;XML方法操作繁琐,但是存档格式可读性强,JSON和XML存档都可以用文本读取便于查看。

综上所述,Unity存档机制众多,但还应按照个人项目需求选择合适的存档方法。


四、参考

最新文章

  1. 学习AOP之JAVA的代理机制
  2. 拿到添加对象的id号方法
  3. 如何在magento后台增加一个自定义订单状态
  4. vue.js插件使用(01) vue-resource
  5. .NET DLL 保护措施详解(五)常规条件下的破解
  6. python脚本工具-1 制作爬虫下载网页图片
  7. BZOJ 1688: [Usaco2005 Open]Disease Manangement 疾病管理
  8. js获取键盘的keyCode-------Day42
  9. JAVA 原始国际化例子
  10. python模块之time和datetime
  11. 《Linux Device Drivers》 第十七章 网络驱动程序——note
  12. Android.mk编译APK范例
  13. Java调用本地接口
  14. java.util.NoSuchElementException: None.get的解决方法
  15. Python连接SQLite数据库
  16. Android-Gradle(三)
  17. (7)udp-socket
  18. 手机访问电脑端Wampserver2.4-x64服务
  19. vue请求java服务端并返回数据
  20. 使用 Spring Boot 搭建一套增删改查(无多余代码)

热门文章

  1. SpringBoot_@valid_参数校验
  2. 再谈C#装箱和拆箱操作
  3. NLP(五) 词性标注和文法
  4. Badboy参数化 - Add Variable(循环使用不同的关键字进行搜索)
  5. [python]标准比较运算符
  6. CodeChef - QRECT Rectangle Query CDQ分治
  7. 用C#实现的几种常用数据校验方法整理(CRC校验;LRC校验;BCC校验;累加和校验)
  8. eql框架。
  9. 自定义Hive UDAF 实现相邻去重
  10. 【Offer】[48] 【最长不含重复字符的子字符串】