游戏中通常需要实现“保存进度”“读取进度”之类的功能,那么在开发中,就需要对数据进行存储和读取等工作。可以用PlayerPrefs实现对简单数据的处理以及用JSON实现对对象结构的数据的处理。
PlayerPrefs
PlayerPrefs是Unity自带的数据结构,位于UnityEngine命名空间下。它可以对整数、浮点数、字符串3种类型的数据进行存取。它是持久存储于设备上的,例如安卓,只要用户没有删除应用或者手动去清除应用数据,PlayerPrefs的数据就会一直保留。
整数的存取
一般脚本都会使用UnityEngine命名空间,所以可以在脚本里直接使用PlayerPrefs。
PlayerPrefs使用“键/值”配对的规则,如下:
1 | int num = 10; // 定义一个整型变量num |
第一行代码定义了一个整型变量num,第二行代码存储该变量。第一个参数Number传递了键,第二个参数num传递了这个键所要存储的值。
下面是如何读取这个整数:
1 | int num = PlayerPrefs.GetInt("Number"); |
浮点数的存取
浮点数的存取和整数类似,如下:
1 | float PI = 3.14f; // 定义一个浮点数 |
字符串的存取
字符串的存取也是一样的处理方法:
1 | string str = "Hello World!"; // 定义一个字符串 |
其他PlayerPrefs接口
PlayerPrefs还有一些其他的接口,如下所示:
1 | PlayerPrefs.Save(); // 保存PlayerPrefs数据 |
需要注意的是,3种数据结构的键是公用的,比如有一个键“Somekey”,先用它来存整数10,再用它来存字符串“Hello”,最后读取整数。那么结果是0,因为之前存的10被覆盖了。
属性访问器get和set
属性的访问器包含与获取(读取或计算)或设置(写)属性有关的可执行语句。访问器声明可以包含get或set访问器,或者两者均包含。简而言之,就是把一个变量拆成get读取部分和set写入部分,格式如下:
1 | private string name; |
上面代码中的get部分返回一个字符串,而set部分把value赋给name,用起来就跟普通的变量一样,例如:
1 | Name = "Hi"; |
任何类型的变量都可以使用属性访问器。
属性访问器与PlayerPrefs
思考一下就会发现,PlayerPrefs包含读写两部分,刚好跟get和set对应。用get和set包装一下,PlayerPrefs不就可以当作普通变量使用了吗?
确实是这样。两者配合后可以很方便地使用:
1 | public string Name |
一个自动保存的变量就这么创建出来了,是不是很方便呢?
JSON
JSON是一种轻量级的数据交换和存储格式,可以用于对数据的设备(如手机的本地存储)和向Web服务器上传,并且符合面向对象编程的思想。JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C、C++、C#、Java、JavaScript、Perl、Python等)。这些特性使JSON成为理想的数据交换语言,易于人阅读和编写,同时也易于机器解析和生成。
下面用JSON这个轻量级的数据格式来实现跨平台的存取数据功能。
JSON数据格式
JSON基本数据书写格式是:名称/值,如”name”:”李四”。JSON基本结构主要有以下两种。
对象
用{}包裹,用名称/值来表示对象中的一个属性,如下所示:
1 | public class Person |
对象Person john = new Person(“John”, 19);用JSON表示就是:
{“name”:”John”, “age”:”19”}
数组
用[]包裹,用“,”表示并列关系,如:
1 | {"people":[{"name":"John", "age":"19"}, {"name":"Tom", "age":"18"}]} |
前面我们知道对象john用JSON的表达,那么下面就来实现这个转换。这里用到了JsonFx,这是一个类对象和SON数据相互转换的动态链接库,网上有很多这样的库。在代码Person.cs中定义了Person类,在以下代码中创建了对象john,再将john转成JSON字符串,最后将JSON字符串转换回Person对象。
1 | public class Person |
1 | using UnityEngine; |
数据存储
Unity及其使用的Mono是跨平台的,符合NET框架,我们完全可以使用System.IO下的File.ReadAllText()和File.WriteAllTtext()这两个函数来实现数据的存取。
我们已经有了JSON字符串,下一步是得到存取的路径,如下代码所示:
1 | public static string GetDataPath() |
最后一步,将JSON数据写到该路径:
1 | File.WriteAllText(GetDataPath() + '/' + "FileName", Json_Text); |
至此,对数据存储功能实现的每一步都进行了讲解,数据存储功能的完整代码如下:
1 | using UnityEngine; |
运行一下,先点击保存按钮,然后刷新(Ctrl+R),发现在文件夹下多了刚才保存的data.txt文件,并且可以看到里面的内容。点击读取,在控制台里可以看到输出“John’s age is 19”。
数据加密
但是打开data.txt文件是可以直接看到文本内容的,十分不安全,需要对文本内容进行加密,加密算法有很多种,如RC2、RC4等。这里使用的是Rijndael算法。
Rijndael是.NET里包含的一个对称加密接口,在加密和解密时都使用相同的秘钥。位于System.Security.Crytography命名空间下。Rijndael算法符合AES堆成密码标准,秘钥长度为128、192、256位之一。
加密步骤如下:
(1)设置字符串秘钥并转化为byte数组。这里使用32的字符串转化为长度为32的byte数组,也就是256位的秘钥。
1 | static string key = "12348578902223367877723456789012"; |
(2)创建RijndaelManaged对象并设置参数。
1 | RijndaelManaged rDel = new RijndaelManaged(); |
(3)加密。
1 | // 将原始字符串转化成byte数姐 |
解密步骤如下:
(1)和加密共用同样的秘钥。
(2)创建RijndaelManaged对象并设置参数,和加密的第(2)步一致。
(3)解密。
1 | // 将加密后的字符串转化成byte数组 |
整个字符串加密与解密的完整代码如下所示。
1 | using UnityEngine; |