哈喽大家好,你的橙哥突然出现~
本系列博客地址:传送门
上节我们讲了 PlayerPrefs,及如何构建一个基础的数据存储系统,
今天我们来加深一步,让学到的东西可以实际用在项目上。
一、JsonUtility 的用法
首先我们先学习一下后续会用到的API知识。
1、JsonUtility.ToJson("")
示例:
JsonUtility.ToJson(),接受一个object类的参数(可以是类、可以是string、int等)用来传递需要转换的对象,并且返回一个JSON格式的字符串。
2、JsonUtility.ToJson("" , true)
JsonUtility.ToJson的第二个重载,便是加上true或是false。
不加默认为false。
加上true,转换后的Json字符串更符合我们的阅读习惯的。
下面是加和不加的对比:
1️⃣不加true,转换后的字符串
[Serializable]
class MyData
{
public string name;
public int level;
}
private void Start()
{
MyData myData = new MyData();
myData.name = "skode";
myData.level = 1;
print(JsonUtility.ToJson(myData));
}
2️⃣加true,转换后的字符串
[Serializable]
class MyData
{
public string name;
public int level;
}
private void Start()
{
MyData myData = new MyData();
myData.name = "skode";
myData.level = 1;
print(JsonUtility.ToJson(myData,true));
}
3、JsonUtility.FromJson()
它的作用和ToJson()相反,
是将JSON格式的字符串转换成Unity序列化程序支持的对象。
使用案例:
[Serializable]
class MyData
{
[NonSerialized] public string name;
public int level;
}
private void Start()
{
MyData myData = new MyData();
myData.name = "skode";
myData.level = 1;
string jsonValue = JsonUtility.ToJson(myData);
MyData jsonClass = JsonUtility.FromJson<MyData>(jsonValue); //将json字符串转化为类。
}
4、JsonUtility.FromJsonOverwrite( “” , object )
它接受一个JSON格式的字符串,以及一个object类型数据。
它的用法和FromJson()类似。
不同之处在于当数据转换完成之后,FromJsonOverwrite原有的数据会被转换后的数据所覆写,不像FromJson一样,需要自己取用里面的数值。
如果对象的某个字段没有 JSON 表示形式,则该字段保持不变。
使用场景:
比如一款游戏有多个存档,例如《动物之星》存档,可以存很多档。
那当我们还原某个档时,就需要这个函数了。
直接读取该档的json,解析后直接覆盖掉程序的值,不需要像JsonUtility.FromJson一样,得到数据后还需要挨个去赋值,费时费力。
二、什么样的类可以被转成JSON
如果之前就用过Json的同学,可能发现了一个事情。
就有的时候,想把一个类转成Json,可转换后的结果,只有一个空的大括号,里面什么也没有。
那这是为什么呢?
1、为什么有时候类无法转换成Json
简单一句话就是:
可以在Inspector面板显示的属性,就能转换成Json。
如果类不能,就加上[Serializable],完成序列化。
只要能显示,就能转化。
同时值得注意的是:
ToJson()函数会尝试序列化传入数据中可序列化数据,而不是序列化整个传入的数据。
意思就是,传入的类,如果这个类中有数据标记了[NonSerialized],或者private、const等特性,这些特性是不能在层级面板显示出来的,就不能被序列化。
2、哪些类型的对象支持Unity序列化?
1️⃣、可被序列化的字段
在字段中,只有公有字段,以及添加了序列化字段特性的字段才支持Unity序列化。
2️⃣、可被序列化的类
继承自Mono的类也可被序列化。
这就意味着这些类,不需要加[Serializable]特性,使用时传入this、或者它的实例即可。
3️⃣、其他不能被序列化的类型
这里还要注意一下,一些类型的数据也是不支持Unity序列化的。比如:
- 字典
- 多维数组
即使加了[Serializable]也没用。在层级面板依旧显示不出来。
那么如何存储这些不支持序列化的数据呢?
- 那我们字典可以拆开分成两个部分:键的列表以及值的列表。
- 多维数组的话,则拆开成相应的多个一维数组。分别进行存储就行了。
三、构建一个基于JSON的存档系统
最后我们来构建一个基于JSON的存档系统。
1、编写存档工具脚本
using System;
using System.IO;
using UnityEngine;
public static class SaveSystem
{
/// <summary>
/// 存档
/// </summary>
/// <param name="saveFileName">文件名,后缀可以是任何字符。</param>
/// <param name="data">保存了数据的类、int、string等数据</param>
public static void SaveByJson(string saveFileName, object data)
{
var json = JsonUtility.ToJson(data);
//Application.persistentDataPath,支持全平台,提供一个存储永久数据的路径。
//路径随发布平台的不同而变更。
var path = Path.Combine(Application.persistentDataPath, saveFileName);
try
{
//將string写入到saveFileName文件
//未存在,便创建。已存在,便覆写
File.WriteAllText(path, json);
Debug.Log("Save Success" + path);
}
catch (Exception e)
{
Debug.LogError(e);
}
}
/// <summary>
/// 读档
/// </summary>
/// <param name="saveFileName">保存的Json文件名全称</param>
/// <returns></returns>
public static T LoadFromJson<T>(string saveFileName)
{
var path = Path.Combine(Application.persistentDataPath, saveFileName);
try
{
var json = File.ReadAllText(path);
var data = JsonUtility.FromJson<T>(json);
return data;
}
catch (Exception e)
{
Debug.LogError(e);
return default;
}
}
/// <summary>
/// 删除存档
/// </summary>
/// <param name="saveFileName"></param>
public static void DeleteSaveFile(string saveFileName)
{
var path = Path.Combine(Application.persistentDataPath, saveFileName);
try
{
File.Delete(path);
}
catch (Exception e)
{
Debug.LogError(e);
}
}
}
2、使用存档工具的示例
按下A键,进行存档。按下B键,进行读档。
using System;
using UnityEngine;
public class SaveTest : MonoBehaviour
{
[Serializable]
class MyData
{
public string name;
public int level;
}
private void Update()
{
//存档示例
if (Input.GetKeyDown(KeyCode.A))
{
MyData myData = new MyData();
myData.name = "skode";
myData.level = 1;
SaveSystem.SaveByJson("我的存档.skode", myData);
}
//读档示例
if (Input.GetKeyDown(KeyCode.B))
{
MyData value = SaveSystem.LoadFromJson<MyData>("我的存档.skode");
print(value.name);
print(value.level);
}
}
}
3、小提示
或许我们要用到删除存档的功能,但每次去掉方法挺麻烦的。
Unity也提供了删除所有PlayPrefs的功能:
Edit - Clear All PlayPrefs,便可清除所有PlayPrefs。
好啦,这就是本章所有内容,
后续我会出关于Unity存档系统商业化的教程,欢迎关注!
如果你有 技术的问题 或 项目开发
都可以加下方联系方式
和我聊一聊你的故事