本文旨在于简要分析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存档机制众多,但还应按照个人项目需求选择合适的存档方法。


四、参考

Unity - 存读档机制简析的更多相关文章

  1. Linux VFS机制简析(二)

    Linux VFS机制简析(二) 接上一篇Linux VFS机制简析(一),本篇继续介绍有关Address space和address operations.file和file operations. ...

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

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

  3. Linux VFS机制简析(一)

    Linux VFS机制简析(一) 本文主要基于Linux内核文档,简单分析Linux VFS机制,以期对编写新的内核文件系统(通常是给分布式文件系统编写内核客户端)的场景有所帮助. 个人渊源 切入正文 ...

  4. Linux内存管理机制简析

    Linux内存管理机制简析 本文对Linux内存管理机制做一个简单的分析,试图让你快速理解Linux一些内存管理的概念并有效的利用一些管理方法. NUMA Linux 2.6开始支持NUMA( Non ...

  5. Binder机制简析(三)

    注册Service Service组件运行在Server进程中,首先要将Service注册到Service Manager中,再启动一个Binder线程池来等待和处理Client的通信请求. 注册过程 ...

  6. Linux内核poll/select机制简析

    0 I/O多路复用机制 I/O多路复用 (I/O multiplexing),提供了同时监测若干个文件描述符是否可以执行IO操作的能力. select/poll/epoll 函数都提供了这样的机制,能 ...

  7. Magento 缓存机制简析

    在知道缓存机制前,首先需要知道,Magento的路由机制,这边就不做赘述了,百度一大堆. 下面一个简单的缓存生效流程: A:首先在页面开始时,Magento在app\code\core\Mage\Co ...

  8. linux-2.6.38poll机制简析(以tiny6410按键中断程序为基础)

    一.应用程序 /* struct pollfd { int fd; //文件描述符 short events; //表示请求检测的事件 short revents; //表示检测之后返回的事件 }; ...

  9. DPDK多核多线程机制简析

    DPDK通过在多核设备上,创建多个线程,每个线程绑定到单独的核上,减少线程调度的开销,以提高性能. DPDK的线程分为控制线程和数据线程,控制线程一般绑定到MASTER核上,主要是接受用户配置,并传递 ...

随机推荐

  1. HDU 6052

    题意略. 思路:我们单独考虑每种颜色的贡献,颜色c对答案的贡献 == 含有c的矩形个数,这就是在考查我们计数的能力,暴力可过. #include<bits/stdc++.h> #defin ...

  2. springBoot日志框架自动配置与原理

    1.日志框架 小张:开发一个大型系统: ​ 1.System.out.println(""):将关键数据打印在控制台:去掉?写在一个文件? ​ 2.框架来记录系统的一些运行时信息: ...

  3. JAVA 泛型中的通配符 T,E,K,V,?

    前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据 ...

  4. 大型互联网公司分布式ID方案总结

    ID是数据的唯一标识,传统的做法是利用UUID和数据库的自增ID,在互联网企业中,大部分公司使用的都是Mysql,并且因为需要事务支持,所以通常会使用Innodb存储引擎,UUID太长以及无序,所以并 ...

  5. MySQL5.7.27报错[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated

    mysql5.7.27在运行更新语句时出现如下情况,mysql5.6之前没有这种情况出现. of ORDER BY clause is not in GROUP BY clause and conta ...

  6. ST算法 Sliding Window algorithm template

    ST算法(Sliding Window):A easy way to slove the substring problems algorithm template to slove substrin ...

  7. JMeter特点&性能测试工具选型的原则&模拟压力的原理

    1.JMeter自身的特点 1)开源.轻量级.更适合自动化和持续集成(100M左右,LoadRunner 4G左右) 2)学习难度大 3)资料少.全英文 2. 性能测试工具选型的原则 1)成本 a.工 ...

  8. Codeforces 939 D Love Rescue

    Love Rescue 题意:Valya 和 Tolya 是一对情侣, 他们的T恤和头巾上都有小写字母,但是女朋友嫌弃男朋友上T恤上的字不和她的头巾上的字一样,就很生气, 然后来了一个魔法师, 它可以 ...

  9. CodeForces 1105E Helping Hiasat 最大独立集

    Helping Hiasat 题解: 如果我们把连续的2出现的人都相互连边的话, 题目就是问最大独立集的答案是多少. 求最大独立集可以将图变成反图, 然后求最大团. 代码: #include<b ...

  10. 不权威的国产CPU发展历程

    最近进行了一些国产化相关工作 趁着周末有时间,自己整理一下这段时间的学习内容. 毕竟不是处理器和芯片的业内人士,里面多有纰漏,请谅解. 希望可以作为入门学习的简单知识. 1.0 远古时代 unix 世 ...