Unity实现数据存储和读取持久化(PlayerPrefs、序列化、JSON、XML)

22 篇文章 0 订阅

说来惭愧,之前甚至连这个都没学
用文件的形式存储,其实有很多种不同办法都能实现。
但是这学期时间有限,所以打算学习其中的四种,目前只是做demo,所以使用的方法都是小白级别的。

一、Unity自带-PlayerPrefs

简单介绍

存储读取数据只有对应的三种方法,非常方便。缺点是只能存储三种数据形式——int float string
使用就只有一种套路:

//在Player preference里面存入对应三种不同数据形式的键值对
PlayerPrefs.SetInt/SetFloat/SetString(String name,不同数据类型对应的值);
//在Player preference中查找对应键的值并返回
PlayerPrefs.GetInt/GetFloat/GetString(String name);

实现

在场景里面新建一个文本框,里面是我们要保存的数据,然后建立一个保存按钮和一个加载按钮。当按下空格的时候,里面的数字+1。
在这里插入图片描述

实现加分

书写ScoreAdd脚本,脚本随便挂载到一个物体上,并且把Text拖拽到脚本numText对象赋值;
使得按下空格的时候分数+1,

public class scoreAdd: MonoBehaviour
{
    private int num;
    public Text numText;
    private void Awake() {
        num = 0;
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)) {
            num++;
            Debug.Log("Score added");
            numText.text = num.ToString();
        }   
    }
}

在这里插入图片描述

实现保存和读取

为了让其他脚本能读取到num的数据,我们对scoreAdd脚本修改一下,把num改为public类型并且开启单例让其他脚本能访问到它。

public class scoreAdd : MonoBehaviour
{
    public static scoreAdd instance;
    
    public int num;
    public Text numText;
    private void Awake() {
        if (instance == null) {
            instance = this;
        }
    }
    void Update()
    {
        numText.text = num.ToString();
        if (Input.GetKeyDown(KeyCode.Space)) {
            num++;
            Debug.Log("Score added");
        }   
    }
}

然后我们建立新的脚本usePlayerPrefs,书写如下代码。

public class usePlayerPrefs : MonoBehaviour
{
    public void playerPrefsSave() {
        PlayerPrefs.SetInt("MyScore", scoreAdd.instance.num);
        Debug.Log("Score has saved");
    }
    public void playerPrefsLoad() {
        scoreAdd.instance.num = PlayerPrefs.GetInt("MyScore");
        Debug.Log("Score had loaded");
    }
}

然后也是随便拖到一个物体上
在inspector给Save按钮OnClick添加playerPrefsSave事件;
Load按钮添加playerPrefsLoad事件

然后运行游戏,先把数字加到任意一个数,(我选择8)
在这里插入图片描述
按下Save。
在这里插入图片描述
重新开始场景
在这里插入图片描述
按下Load按钮,发现数字变为8,(~ ̄▽ ̄)~
在这里插入图片描述

二、序列化和反序列化-二进制格式化

相比于PlayerPres,只需要创建一个类来存储数据,存储的时候将其序列化成二进制文件(序列化),读取的时候将其反序列为类。
并且由于是二进制格式存储,玩家比较难篡改(大概)。

简单介绍

1.序列化
Serialization
对象的状态信息转换为Unity可以存储文件的形式的自动化处理过程,或者简单来说是把对象转化为可传输的字节序列的过程。

2.文件
File
C#中的静态类,对文件进行操作:复制、创建、删除、移动(需要引入空间System.IO)

3.文件流
FileStream
对文件进行:读、写、关闭(需要引入空间System.IO)
反正就是需要什么操作就用什么类的函数

然后我们进行存储和读取的时候,借助一个类。
存储的时候新建一个类的对象,在这个类里面存放需要存放的数据,并把其序列化为一个二进制文件(也可以是其他类型),然后放进一个路径(一般都是Applicaiton.persistentDataPath);
读取的时候调用反序列化函数把这个文件变为之前的类就好了,(需要强制转化为之前类的类型

要注意的是,需要引入命名空间 System,才能把这个类声明为可序列化。

实现

建立脚本Save
	using System;
/// <summary>
/// 用于序列化成二进制文件的类
/// </summary>
[Serializable]
public class Save 
{
   public int Score;
}
建立脚本useSerialization
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;

public class useSerialization : MonoBehaviour
{
    /// <summary>
    /// 创建一个保存了数据的类
    /// </summary>
    /// <returns></returns>
    Save createSave() {
        Save save = new Save();
        save.Score = scoreAdd.instance.num;
        return save;
    }
    public void serializationSave() {
        Save save = createSave();
        //Inheritance Object -> BinaryFormatter
        BinaryFormatter bf = new BinaryFormatter();
        //创建存档文件
        FileStream fs = File.Create(Application.persistentDataPath + "/Data.text");
        //将save变为fs
        bf.Serialize(fs, save);
        fs.Close();
    }
    public void serializationLoad() {
        if(File.Exists(Application.persistentDataPath + "/Data.text")) {
            BinaryFormatter bf = new BinaryFormatter();
            //打开对应路径的存档文件
            FileStream fs = File.Open(Application.persistentDataPath + "/Data.text",FileMode.Open);
            Save save = bf.Deserialize(fs) as Save;
            fs.Close();
            scoreAdd.instance.num = save.Score;
        } else {
            Debug.LogWarning("Not Found Save File");
        }
    }
}

和PlayerPrefs一样,同样让两个按钮的OnClick添加对应的save和load委托。

进行测试

同样加到8,按下save
在这里插入图片描述
重新开始
在这里插入图片描述
按下Load按钮,发现数字变为8,(~ ̄▽ ̄)~
在这里插入图片描述
还有另外一个会继续更新,先更到这


接下来是Json和XML

三、JSON

不要被看起来高大上的名字吓跑,在Unity上利用Json进行数据读写并不困难。
JSON全称 JavaScript Object Notation 也就是一种符号表示法。相比于序列化为二进制文件,JSON把对象存储为JSON文件,本质是字符串
在这里插入图片描述
具体规则自行查找。

简单介绍

JSON同样以键值对的形式表示数据,string类型的键, int、bool、float 数组、对象的值
利用JSON和利用序列化存档的本质类似:
存储的时候新建一个类的对象,在这个类里面存放需要存放的数据,并利用JsonUtility 把其转化为一个JSON规则的字符串,再利用 StreamWriter生成文件,最后放进一个路径(一般都是Applicaiton.persistentDataPath);
读取的时候借助JsonUtility把这个文件变为之前的类就好了(泛型方法)。

实现

建立脚本Save
using System;
/// <summary>
/// 用于序列化成二进制文件的类
/// </summary>
[Serializable]
public class Save 
{
    public int Score;
}
建立脚本useJson
using System.IO;
using UnityEngine;

public class useJson : MonoBehaviour
{
   /// <summary>
    /// 创建一个保存了数据的类
    /// </summary>
    /// <returns></returns>
    Save createSave() {
        Save save = new Save();
        save.Score = scoreAdd.instance.num;
        return save;
    }
    public void jsonSave() {
        Save save = createSave();

        //Inheritance Object -> Json
        string jsonString = JsonUtility.ToJson(save);

        //创建StreamWrite,作用类似于类似于FileStream,生成存档文件
        StreamWriter sw = new StreamWriter(Application.persistentDataPath + "/JsonData.txt");
        sw.Write(jsonString);
        sw.Close();

        Debug.Log("Use Json to save project successed");
     }

    public void jsonLoad() {
        if(File.Exists(Application.persistentDataPath + "/JsonData.txt")) {
            StreamReader sr = new StreamReader (Application.persistentDataPath + "/JsonData.txt");
            string JsonString = sr.ReadToEnd();
            sr.Close();

            Save save = JsonUtility.FromJson<Save>(JsonString);
            //接下来要做什么操作自己决定
            scoreAdd.instance.num = save.Score;
            Debug.Log("Json Load successed");
        } else {
            Debug.Log("Not found Json Save File");
        }
    }
}

和PlayerPrefs一样,同样让两个按钮的OnClick添加对应的save和load委托。

进行测试

加到8,按下save
在这里插入图片描述
重新开始
在这里插入图片描述
按下Load按钮,变成8了ヾ(✿゚▽゚)ノ

在这里插入图片描述

四、XML

简单介绍

XML:Extensible Markup Language: 可扩展标记语言
不同系统之间的数据传输和交换非常消耗时间,并且可能产生一些丢失数据的情况。而XML以纯文本格式进行存储,使得在不同系统之间交换数据方便。本质就是字符串
由于Unity没有自带解析XML的系统,所以我们需要借助Csharp的System.Xml来操作
存储的时候新建一个类的对象,在这个类里面存放需要存放的数据,然后构造一个XmlDocument类型的对象,设立一个根结点,对于需要存放的数据构造XmlElement类型的键值对,并且作为根结点的子结点,最后再将根结点作为对象的子结点,最后保存文件放进一个路径(一般都是Applicaiton.persistentDataPath);
读取的时候需要根据之前存放的键值对的值取到对应的数据。

实现

建立脚本Save

略,和前面一样

建立脚本useXML
using UnityEngine;
using System.IO;
using System.Xml;

public class useXML : MonoBehaviour
{
    /// <summary>
    /// 创建一个保存了数据的类
    /// </summary>
    /// <returns></returns>
    Save createSave() {
        Save save = new Save();
        save.Score = scoreAdd.instance.num;
        return save;
    }
    public void saveByXML() {
        Save save = createSave();
        XmlDocument xmlDocument = new XmlDocument();

        #region CreateXML elements

        XmlElement root = xmlDocument.CreateElement("Save");
        //也不知道有什么用
        root.SetAttribute("FileName", "File_01");

        XmlElement scoreElement = xmlDocument.CreateElement("Score");
        //设置元素值
        scoreElement.InnerText = save.Score.ToString();
        root.AppendChild(scoreElement);
        #endregion
        
        //将根结点和其子结点加入前面新建的XML文件
        xmlDocument.AppendChild(root);
        xmlDocument.Save(Application.dataPath + "/XMLData.text");
        if (File.Exists(Application.dataPath + "/XMLData.text")) {
            Debug.Log("XML file save successed");
        }
    }
    public void loadByXML() {
        if (File.Exists(Application.dataPath + "/XMLData.text")) {
            Save save = new Save();

            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(Application.dataPath + "/XMLData.text");

            //从XML中获得游戏数据
            XmlNodeList score = xmlDocument.GetElementsByTagName("Score");
            //score只有一个,所以是0号元素
            int number = int.Parse(score[0].InnerText);
            //获得数据赋予Save类
            save.Score = number;

            scoreAdd.instance.num = number;
        } else {
            Debug.Log("Load Failed");
        }
    }
}

进行测试

同样加到8,并且保存
在这里插入图片描述
重新启动
在这里插入图片描述
按下Load按钮,变成8了ヽ(゚∀゚)メ(゚∀゚)ノ
在这里插入图片描述
其实没有必要四个都会,精通一个就好了,现在觉得应该是json最适合目前的我。

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Unity 中,我们可以使用 JsonUtility 类来实现对象的序列化和反序列化。对于 Dictionary 类型的对象,我们可以通过将其转换为一个包含键值对的 List 类型对象,然后对 List 类型对象进行序列化和反序列化。 下面是一个示例代码: ```csharp using System.Collections.Generic; using UnityEngine; [System.Serializable] public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver { [SerializeField] private List<TKey> keys = new List<TKey>(); [SerializeField] private List<TValue> values = new List<TValue>(); // save the dictionary to lists public void OnBeforeSerialize() { keys.Clear(); values.Clear(); foreach (KeyValuePair<TKey, TValue> pair in this) { keys.Add(pair.Key); values.Add(pair.Value); } } // load dictionary from lists public void OnAfterDeserialize() { this.Clear(); for (int i = 0; i < keys.Count; i++) { this.Add(keys[i], values[i]); } } } [System.Serializable] public class MyData { public SerializableDictionary<string, int> myDict = new SerializableDictionary<string, int>(); } public static class JsonHelper { public static string ToJson<T>(T obj) { return JsonUtility.ToJson(obj); } public static T FromJson<T>(string json) { return JsonUtility.FromJson<T>(json); } } public class Example : MonoBehaviour { private MyData data = new MyData(); private void Start() { data.myDict.Add("key1", 1); data.myDict.Add("key2", 2); string json = JsonHelper.ToJson(data); Debug.Log(json); MyData loadedData = JsonHelper.FromJson<MyData>(json); Debug.Log(loadedData.myDict["key1"]); Debug.Log(loadedData.myDict["key2"]); } } ``` 在上面的示例代码中,我们定义了一个 SerializableDictionary 类来实现 Dictionary 的序列化和反序列化。在 MyData 类中使用了 SerializableDictionary 类型的成员变量 myDict。在 JsonHelper 类中,我们定义了 ToJson 和 FromJson 方法来将对象转换为 Json 字符串和从 Json 字符串中加载对象。在 Example 类中,我们创建了一个 MyData 对象,并向其中添加了两个键值对。我们将 MyData 对象转换为 Json 字符串并输出到控制台,然后从 Json 字符串中加载了一个新的对象,并输出了其中的两个值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值