存档与读档功能

举例:

  • 传统RPG游戏(仙剑、空之轨迹):

    1.角色信息(生命值,等级)

    2.道具信息(装备,药品)

    3.场景信息(场景名称、角色坐标)

    4.事件信息(任务相关)

  • 关卡类游戏:关卡的通关情况

  • 一些特殊的存档机制(漫漫长夜、亿万僵尸)

Unity中使用的存档方式

一、PlayerPrefs:数据持久化方案

官方手册

  1. 原理:采用键值对的方式对数据进行存储---保存在本地文件(不同操作系统存储路径不同)中,然后程序可以根据这个名称取出上次保存的数值)

  2. 储存类型:可以存储Int, Float, String类型的数据。

  3. 使用例子

//存储数据:
PlayerPrefs.SetInt("Index",1);
PlayerPrefs.SetFloat("Height",183.5f);
PlayerPrefs.SetString("Name","Tom");
//获取数据:
PlayerPrefs.GetInt("Index");

二、存储数据序列化

  • Serialization(序列化),可以用来将对象转化为字节流。
  • Deserialization(反序列化),可以用来将字节流转换为对象。
  • 常见的数据序列化方法:二进制方法,XML方法,JSON方法
  • 存储在:内存、文件、数据库--可存于本地或云

++下面以实例说明++:

Save类--用于保存信息

using System.Collections;
using System.Collections.Generic;
using UnityEngine; [System.Serializable]
public class Save{
//怪物位置
public List<int> livingTargetPositions = new List<int>();
//怪物类型
public List<int> livingMonsterTypes = new List<int>();
//得分情况
public int shootNum = 0;
public int score = 0;
}
  1. 二进制存储(Binary Formatter)
  • 序列化:新建或打开一个二进制文件,通过二进制格式器将对象写入该二进制文件。
  • 反序列化:打开待反序列化的二进制文件,通过二进制格式器将文件解析成对象。
//二进制方法:存档和读档
private void SaveByBin()
{
//序列化过程(将Save对象转换为字节流)
//创建Save对象并保存当前游戏状态
Save save = CreateSaveGO();
//创建一个二进制格式化程序
BinaryFormatter bf = new BinaryFormatter();
//创建一个文件流
FileStream fileStream = File.Create(Application.dataPath + "/StreamingFile" + "/byBin.txt");
//用二进制格式化程序的序列化方法来序列化Save对象,参数:创建的文件流和需要序列化的对象
bf.Serialize(fileStream, save);
//关闭流
fileStream.Close(); //如果文件存在,则显示保存成功
if (File.Exists(Application.dataPath + "/StreamingFile" + "/byBin.txt"))
{
//TODO: 提示保存成功
}
} private void LoadByBin()
{
if(File.Exists(Application.dataPath + "/StreamingFile" + "/byBin.txt"))
{
//反序列化过程
//创建一个二进制格式化程序
BinaryFormatter bf = new BinaryFormatter();
//打开一个文件流
FileStream fileStream = File.Open(Application.dataPath + "/StreamingFile" + "/byBin.txt", FileMode.Open);
//调用格式化程序的反序列化方法,将文件流转换为一个Save对象
Save save = (Save)bf.Deserialize(fileStream);
//关闭文件流
fileStream.Close(); SetGame(save); //将保存的信息类初始游戏
//TODO: 提示存档不存在或为空,读取成功 }
else
{
//TODO: 提示存档不存在或为空,读取失败
} }
  1. Json存储
  • JSON:是一种语言无关的发送和接收数据的常用格式。可以使用它来跨平台的传输数据。
  • JSON序列化:
graph LR
对象-->JSON
  • JSON反序列化:
graph LR
JSON-->对象
//JSON:存档和读档
private void SaveByJson()
{
Save save = CreateSaveGO();
string filePath = Application.dataPath + "/StreamingFile" + "/byJson.json";
//利用JsonMapper将save对象转换为Json格式的字符串
string saveJsonStr = JsonMapper.ToJson(save);
//将这个字符串写入到文件中
//创建一个StreamWriter,并将字符串写入文件中
StreamWriter sw = new StreamWriter(filePath);
sw.Write(saveJsonStr);
//关闭StreamWriter
sw.Close(); //TODO: 提示保存成功
} private void LoadByJson()
{
string filePath = Application.dataPath + "/StreamingFile" + "/byJson.json";
if(File.Exists(filePath))
{
//创建一个StreamReader,用来读取流
StreamReader sr = new StreamReader(filePath);
//将读取到的流赋值给jsonStr
string jsonStr = sr.ReadToEnd();
//关闭
sr.Close(); //将字符串jsonStr转换为Save对象
Save save = JsonMapper.ToObject<Save>(jsonStr);
SetGame(save);//将保存的信息类初始游戏
//TODO: 提示存档不存在或为空,读取成功
}
else
{
//TODO: 提示存档不存在或为空,读取失败
}
}

存储效果:

{
"livingTargetPositions":[0,1,2,4,5,8],
"livingMonsterTypes":[2,1,2,0,3,0],
"shootNum":18,
"score":9
}
  1. XML存储

XML:扩展标记语言,用于标记电子文件使其具有结构性的标记语言。

  • 可以用来标记数据、定义数据类型。
  • 序列化与反序列化的方式与二进制方法十分类似。

XML与Json的区别

//XML:存档和读档--要根据XML文件的结点结构进行操作
private void SaveByXml()
{
Save save = CreateSaveGO();
//创建XML文件的存储路径
string filePath = Application.dataPath + "/StreamingFile" + "/byXML.txt";
//创建XML文档
XmlDocument xmlDoc = new XmlDocument();
//创建根节点,即最上层节点
XmlElement root = xmlDoc.CreateElement("save");
//设置根节点中的值
root.SetAttribute("name", "saveFile1"); //创建XmlElement
XmlElement target;
XmlElement targetPosition;
XmlElement monsterType; //遍历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();
monsterType = xmlDoc.CreateElement("monsterType");
monsterType.InnerText = save.livingMonsterTypes[i].ToString(); //设置节点间的层级关系 root -- target -- (targetPosition, monsterType)
target.AppendChild(targetPosition);
target.AppendChild(monsterType);
root.AppendChild(target);
} //设置射击数和分数节点并设置层级关系 xmlDoc -- root --(target-- (targetPosition, monsterType), shootNum, score)
XmlElement shootNum = xmlDoc.CreateElement("shootNum");
shootNum.InnerText = save.shootNum.ToString();
root.AppendChild(shootNum); XmlElement score = xmlDoc.CreateElement("score");
score.InnerText = save.score.ToString();
root.AppendChild(score); xmlDoc.AppendChild(root);
xmlDoc.Save(filePath); if(File.Exists(Application.dataPath + "/StreamingFile" + "/byXML.txt"))
{
//TODO: 提示保存成功
}
} private void LoadByXml()
{
string filePath = Application.dataPath + "/StreamingFile" + "/byXML.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)
{
XmlNode targetPosition = target.ChildNodes[0];
int targetPositionIndex = int.Parse(targetPosition.InnerText);
//把得到的值存储到save中
save.livingTargetPositions.Add(targetPositionIndex); XmlNode monsterType = target.ChildNodes[1];
int monsterTypeIndex = int.Parse(monsterType.InnerText);
save.livingMonsterTypes.Add(monsterTypeIndex);
}
} //得到存储的射击数和分数
XmlNodeList shootNum = xmlDoc.GetElementsByTagName("shootNum");
int shootNumCount = int.Parse(shootNum[0].InnerText);
save.shootNum = shootNumCount; XmlNodeList score = xmlDoc.GetElementsByTagName("score");
int scoreCount = int.Parse(score[0].InnerText);
save.score = scoreCount; SetGame(save);//将保存的信息类初始游戏
//TODO: 提示存档不存在或为空,读取成功 }
else
{
//TODO: 提示存档不存在或为空,读取失败
}
}

存储效果:

<save name="saveFile1">
<target>
<targetPosition>0</targetPosition>
<monsterType>1</monsterType>
</target>
<target>
<targetPosition>1</targetPosition>
<monsterType>3</monsterType>
</target>
<target>
<targetPosition>3</targetPosition>
<monsterType>3</monsterType>
</target>
<target>
<targetPosition>4</targetPosition>
<monsterType>0</monsterType>
</target>
<target>
<targetPosition>6</targetPosition>
<monsterType>3</monsterType>
</target>
<target>
<targetPosition>7</targetPosition>
<monsterType>0</monsterType>
</target>
<shootNum>50</shootNum>
<score>29</score>
</save>
  1. 三种存储方式的对比
  • 二进制方法:简单,但可读性差。
  • XML:可读性强,但是文件庞大,冗余信息多。
  • JSON:数据格式比较简单,易于读写,但是不直观,可读性比XML差。
  1. 本地存储位置补充--关于Application.dataPath

Save&Load--Unity存档读档的学习总结的更多相关文章

  1. Unity - 存读档机制简析

    本文旨在于简要分析Unity中的两种存档机制,即:PlayerPrefs数据持久化方法及Serialization数据序列化方法 较比与源项目,我另加了JSON方法.XML方法等及一些Unity设置, ...

  2. 【UE4 C++】 SaveGame 存档/读档

    创建 SaveGame 类 继承自 USaveGame UCLASS() class TIPS_API USimpleSaveGame : public USaveGame { GENERATED_B ...

  3. Unity开发之存档和读档的三种实现方式

    此文内容源自siki学院视频,仅供学习!视频链接地址:http://www.sikiedu.com/course/129 工程使用Unity 2017.3.0f3 (64-bit) 老司机读博客,了解 ...

  4. Unity3D — —存读档【转载】

    详细可参考此篇博文: Unity序列化之XML,JSON--------合成与解析 简单例子(SiKi学院教程): using System.Collections; using System.Col ...

  5. 读Flask源代码学习Python--config原理

    读Flask源代码学习Python--config原理 个人学习笔记,水平有限.如果理解错误的地方,请大家指出来,谢谢!第一次写文章,发现好累--!. 起因   莫名其妙在第一份工作中使用了从来没有接 ...

  6. OpenGL——外部读档+异常报错

    从外部读取shader文件: 先添加Shader类: 再创建vertexSource.txt和fragmentSource.txt两个文件: 如图填入shader: 在shader.h宣告: 在sha ...

  7. webpack搭建vue项目开发环境【文档向学习】

    为何有这篇文章 各个社区已经有无数篇帖子介绍如何使用webpack搭建前端项目,但无论是出于学习webpack的目的还是为了解决工作实际需要都面临着一个现实问题,那就是版本更新.别人的帖子可能刚写好版 ...

  8. 【未解决】CImage::Save / Load 导致“线程 0xc224 已退出,返回值为 1 (0x1)”

    不知道这个返回值意味着什么,反正只要用到Save/Load就会出现这个情况. 这个链接:http://forums.codeguru.com/showthread.php?354017-The-thr ...

  9. 从官方文档去学习之FreeMarker

    一.前言 上一篇 <从现在开始,试着学会用官方文档去学习一个技术框架>提倡大家多去从官方文档学习技术,没有讲到具体的实践,本篇就拿一个案例具体的说一说,就是FreeMarker,选择这个框 ...

随机推荐

  1. Java 最全异常讲解

    1. 导引问题 实际工作中,遇到的情况不可能是非常完美的.比如:你写的某个模块,用户输入不一定符合你的要求.你的程序要打开某个文件,这个文件可能不存在或者文件格式不对,你要读取数据库的数据,数据可能是 ...

  2. 11_for语句的使用

    for是一种循环结构 go语言中,for语句结构: for 初始语句; 条件语句; 迭代后语句 { 代码体 } 例子: package main import "fmt" impo ...

  3. Python——面向对象编程

    面向对象编程是一种程序设计思想,把对象作为程序的基本单元,一个对象里面包含了数据以及操作数据的方法,称为类的属性和方法. 基础-定义.实例化.实例属性 用class来定义对象,类名首字母大写,如果这个 ...

  4. win命令获取外网ip

    win命令: chcp 65001 curl https://ip.cn bat: @echo offchcp 65001 && curl https://ip.cnpause 链接: ...

  5. POJ-2104 K-th Number CDQ分治

    题目传送门 题意:给你一个序列,长度为n,m次询问,询问一段区间的第k大. 题解:CDQ分治,对整个值域进行分治.每次取一个mid, 计算出整个区间内mid <= 的数目,如果 num > ...

  6. jQuery - 01. jQuery特点、如何使用jQuery、jQuery入口函数、jQuery和DOM对象的区别、jQuery选择器、

    this指的是原生js的DOM对象 .css(“”):只写一个值是取值,写俩值是赋值 window.onload   ===   $(document).ready(); $(“”):获取元素   标 ...

  7. 【Offer】[29] 【顺时针打印矩阵】

    题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字. 例如,如果输入如下矩阵:  则依次打印出数字1,2,3,4,8,12, ...

  8. Linux系统下安装zookeeper教程

    环境: 1.VMware® Workstation 12 Pro 2.CentOS7 3.zookeeper-3.4.6 安装步骤 1.下载zookeeper 本文使用的zookeeper下载地址如下 ...

  9. Mysql 获取当月和上个月第一天和最后一天的SQL

    Mysql 获取当月和上个月第一天和最后一天的SQL #获取当前日期select curdate(); #获取当月最后一天select last_day(curdate()); #获取本月的第一天se ...

  10. MySQL和mybatis查询相关

    0.mybatis的xml文件头 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapp ...