这一篇只能说写了一部分,并没有把Unity里数据持久化的操作讲完整,之后可能是学到一点就记一点的模式。
1 概念
数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称。
人话版:将游戏数据存储到硬盘,硬盘中数据读取到游戏中,也就是传统意义上的存盘。
2 PlayerPrefs
2.1 概念
PlayerPrefs是Unity提供的一个可以用于存储读取玩家数据的公共类。
2.2 操作
1 存储
PlayerPrefs的数据存储类似于键值对,一个键对应一个值。它提供了3种数据存储方式:int,float,string。
键:string。
值:int,float,string,对应3种API。
PlayerPrefs.SetInt("Age", 18);
PlayerPrefs.SetFloat("Height", 100);
PlayerPrefs.SetString("Name", ",manqi");
需要注意的是,直接调用API只会把数据存储到内存中。当游戏结束时,才会自动把数据存到硬盘中。
如果游戏不是正常结束(崩溃等),数据是不会存到硬盘中的。为了能立刻将数据存储到硬盘中,避免丢失的情况,我们可以用PlayerPrefs.Save()。
PlayerPrefs.Save(); //将数据立刻存储到硬盘中
然而我们可以看到PlayerPrefs是有局限性的,它只能存储3种类型的数据,如果有其他类型需求,只能降低精度或者转变类型(如bool的true存储为int的1)。
另外,需要注意,如果同一键名存储,无论数据类型是否变化,都会将原来的数据覆盖。(所以我们要保证key的唯一性。
PlayerPrefs.SetInt("Age",18);
PlayerPrefs.SetString("Age","18");
2 读取
用int举例:
//如果没有对应键,会传出默认值,如int-0。
int age = PlayerPrefs.GetInt("Age");
//如果找不到对应键,会传出函数第二个参数。
int age = PlayerPrefs.GetInt("Age",100);
判断数据是否存在:
if(PlayerPrefs.HasKey("Age")){
print("存在");
}
3 删除
//删除指定键值对
PlayerPrefs.DeleteKey("Age");
PlayerPrefs.DeleteAll();
2.3 Windows存储位置
PlayerPrefs存储在HKEY_CURRENT_USER\Software[公司名][产品名] 项下的注册表中。(其中公司名和产品名是“File->Build Setting->Project Settings->Player”中设置的名称。
但我们会发现,当我们直接修改注册表内数据信息时,游戏内读取到的数据也会对应改变,这是用PlayerPrefs存储信息的缺陷。针对这个缺陷,我们可以进行加密或者是采取其他的数据存储方式。
2.4 优缺点分析
优点:简单易懂方便操作。
缺点:①能直接存储的数据类型有限,复杂类型的存储需要自己实现;②数据安全性低,容易被修改。
适用范围:适合用来存储暂时性数据,如玩家偏好设定。
2.5 自定义类的存储
自定义类的存储我们可以利用PlayerPrefs的SetString API以及JsonUtility。
方法类文章来源:https://www.toymoban.com/news/detail-521836.html
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class SaveSystem{
public static void SaveByPlayerPrefs(string key,object data) {
var json = JsonUtility.ToJson(data);
PlayerPrefs.SetString(key, json);
PlayerPrefs.Save();
}
public static string LoadFromPlayerPrefs(string key) {
return PlayerPrefs.GetString(key, null) ;
}
}
调用类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour {
//增加可序列化特性才能被JsonUtility转换
[System.Serializable]
class SaveData {
public string playerName;
public string playerLevel;
public string playerCoin;
public Vector3 playerPosition;
}
const string PLAYER_DATA_KEY = "PlayerData";
public string playerName;
public string playerLevel;
public string playerCoin;
public Vector3 playerPosition;
//存档
SaveData SavingData() {
var savaData = new SaveData();
savaData.playerName = playerName;
savaData.playerLevel = playerLevel;
savaData.playerCoin = playerCoin;
savaData.playerPosition = playerPosition;
return savaData;
}
void SaveByPlayerPrefs() {
SaveSystem.SaveByPlayerPrefs(PLAYER_DATA_KEY, SavingData());
}
//读档
void LoadFromPlayerPrefs() {
var json = SaveSystem.LoadFromPlayerPrefs(PLAYER_DATA_KEY);
var saveData = JsonUtility.FromJson<SaveData>(json);
}
void LoadData(SaveData saveData) {
playerName = saveData.playerName;
playerLevel = saveData.playerLevel;
playerName = saveData.playerName;
playerPosition = saveData.playerPosition;
}
//删除
void DeletePlayerDataPrefs() {
PlayerPrefs.DeleteKey(PLAYER_DATA_KEY);
}
}
3 JSON
3.1 概念
在Unity中,JSON(JavaScript Object Notation)是一种常见的数据交换格式。Unity内置了JsonUtility类,可以用于将对象序列化为JSON字符串或将JSON字符串反序列化为对象。
3.2 JsonUtility
JsonUtility是Unity内用来处理Json数据的类,相关API:JsonUtility
1 JsonUtility.ToJson
第一个参数必须支持Unity序列化,具体是否能被序列化见【3.3】。
第二个参数指的是是否转换为更适合阅读的Json文本格式。
var json = JsonUtility.ToJson(object);
var json = JsonUtility.ToJson(object,bool);
另外,需要注意的是,如果data为空:
bool data;
print(JsonUtility.ToJson(data));
此时控制台只会打印出一个{},因为Json序列化的对象是数据本身,而data本身没有存储任何数据,此时Json会尝试序列化,但它会发现序列化的内容为空,因此只会打印出一个{}。
如果我们希望Json能正确转换字段,我们可以直接转换封装的类本身。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ToJsonTest : MonoBehaviour{
[System.Serializable] class Player {
public string playerName;
public bool isDie;
public int health;
}
void Start(){
Player player = new Player();
print(JsonUtility.ToJson(player));
}
}
2 JsonUtility.FromJson
public static T FromJson(string json);
3 JsonUtility.FromJsonOverwrite
转换后的数据覆盖第二个参数传入的object。
public static void FromJsonOverwrite(string json,object objectToOverwrite);
3.3 支持/不支持Unity序列化
简单的判断是否能被Unity序列化的方法:
将类型标记为public或者在前面标上[SerializeField]特性,看该类型是否能在Inspector窗口显示。
PS:JsonUtility类只支持序列化和反序列化C#对象,不支持序列化和反序列化Unity的组件和对象。
具体的:
1 字段
2 类
3.4 基于Json的简易存档系统
方法类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public static class SaveSystem{
public static void SaveByJson(string saveFileName,object data) {
var json = JsonUtility.ToJson(data);
//Application.persistentDataPath:Unity提供的一个存储永久数据的路径:不同平台上路径不同。
//Path.Combine:合并传入的路径。
var path = Path.Combine(Application.persistentDataPath, saveFileName) ;
//File.WriteAllText方法用于写入字符串类型的数据,而Json是字符串类型。
File.WriteAllText(path, json);
}
public static T LoadFromJson<T>(string saveFileName) {
var path = Path.Combine(Application.persistentDataPath, saveFileName);
var json = File.ReadAllText(path);
var data = JsonUtility.FromJson<T>(json);
return data;
}
public static void DeleteSaveFile(string saveFileName) {
var path = Path.Combine(Application.persistentDataPath, saveFileName);
File.Delete(path);
}
}
调用类
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class Test : MonoBehaviour{
//增加可序列化特性才能被JsonUtility转换
[System.Serializable]class SaveData {
public string playerName;
public string playerLevel;
public string playerCoin;
public Vector3 playerPosition;
}
const string PLAYER_DATA_KEY = "PlayerData";
const string PLAYER_DATA_FILE_NAME = "PlayerData";
public string playerName;
public string playerLevel;
public string playerCoin;
//存档
SaveData SavingData() {
var saveData = new SaveData();
saveData.playerName = playerName;
saveData.playerLevel = playerLevel;
saveData.playerCoin = playerCoin;
saveData.playerPosition = transform.position;
return saveData;
}
void SaveByJson() {
SaveSystem.SaveByJson(PLAYER_DATA_FILE_NAME, SavingData());
}
//读档
void LoadFromJson() {
var saveData = SaveSystem.LoadFromJson<SaveData>(PLAYER_DATA_FILE_NAME);
LoadData(saveData);
}
void LoadData(SaveData saveData) {
playerName = saveData.playerName;
playerLevel = saveData.playerLevel;
playerCoin = saveData.playerCoin;
transform.position = saveData.playerPosition;
}
//删除
void DeletePlayerDataFiles() {
SaveSystem.DeleteSaveFile(PLAYER_DATA_FILE_NAME);
}
}
3.6 优缺点分析
优点:①更符合人类的阅读习惯;②广泛使用,适用多编程语言;③轻量级,易于网络传输与机器解析和生成。
缺点:①支持的数据类型有限(因为是通过Unity的序列化程序实现的;②数据安全性低。
适用范围:
联网:①优秀的网络数据交换载体;②云存档。
本地存储:①非敏感而需要大量读取和修改的数据,如Mod;②玩家的偏好设置。
4 XML
4.1 概念
在Unity中,XML(Extensible Markup Language)是一种常见的数据交换格式。Unity提供了System.Xml命名空间下的类库,可以用于将XML数据序列化为对象或将对象反序列化为XML数据。
<?xml version="1.0" encoding="UTF-8"?>
<student>
<student1 name="椎名">
<age>5</age>
<sex>man</sex>
</student1>
<student2 name="mikan">
<age>10</age>
<sex>woman</sex>
</student2>
<student3 name="みかん">
<age>15</age>
<sex>woman</sex>
</student3>
</student>
4.2 基于XML的简易存档系统
请注意,由于Unity默认不支持使用XmlSerializer类,因此我们需要在Assets文件夹中创建一个空的XML文件夹,并将MonoXml.dll文件从Windows的.NET Framework文件夹中拷贝到该文件夹中。
方法类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Xml.Serialization;
public static class SaveSystem {
//存储
public static void SaveByXml(string saveFileName, object data) {
//创建XmlSerializer对象,传入待序列化数据的类型。
var serializer = new XmlSerializer(data.GetType());
//创建FileStream对象,指定文件路径和操作模式。
string path = Path.Combine(Application.persistentDataPath, saveFileName);
var stream = new FileStream(path, FileMode.Create);
//序列化数据,并写入文件
serializer.Serialize(stream, data);
stream.Close();
}
//读取
public static T LoadFromXml<T>(string saveFileName) {
//创建XmlSerializer对象,传入待反序列化数据的类型
var serializer = new XmlSerializer(typeof(T));
//创建FileStream对象,指定文件路径和操作模式。
string path = Path.Combine(Application.persistentDataPath, saveFileName);
var stream = new FileStream(path, FileMode.Create);
//从文件中反序列化数据,并转换为指定类型
var data = (T)serializer.Deserialize(stream);
stream.Close();
return data;
}
//删除
public static void DeleteSaveFile(string saveFileName) {
var path = Path.Combine(Application.persistentDataPath, saveFileName);
File.Delete(path);
}
}
调用类文章来源地址https://www.toymoban.com/news/detail-521836.html
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour {
//增加可序列化特性才能被转换
[System.Serializable]
class SaveData {
public string playerName;
public string playerLevel;
public string playerCoin;
public Vector3 playerPosition;
}
public string playerName;
public string playerLevel;
public string playerCoin;
public Vector3 playerPosition;
const string PLAYER_DATA_FILE_NAME = "PlayerData";
SaveData SavingData() {
var savaData = new SaveData();
savaData.playerName = playerName;
savaData.playerLevel = playerLevel;
savaData.playerCoin = playerCoin;
savaData.playerPosition = playerPosition;
return savaData;
}
//存档
void SaveByXML() {
SaveSystem.SaveByXml(PLAYER_DATA_FILE_NAME,SavingData());
}
//读档
void LoadFromJson() {
var saveData = SaveSystem.LoadFromXml<SaveData>(PLAYER_DATA_FILE_NAME);
LoadData(saveData);
}
void LoadData(SaveData saveData) {
playerName = saveData.playerName;
playerLevel = saveData.playerLevel;
playerCoin = saveData.playerCoin;
transform.position = saveData.playerPosition;
}
//删除
void DeletePlayerDataFiles() {
SaveSystem.DeleteSaveFile(PLAYER_DATA_FILE_NAME);
}
}
到了这里,关于【Unity学习日记03】数据持久化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!