Unity - 存读档机制简析

2023-05-16

本文旨在于简要分析Unity中的两种存档机制,即:PlayerPrefs数据持久化方法Serialization数据序列化方法

较比于源项目,我另加了JSON方法、XML方法等及一些Unity设置,更便于读者在使用中理解Unity的存档机制。核心脚本为Game.cs

  • 源项目地址:How to Save and Load a Game in Unity - raywenderlich
  • 个人项目地址:BattleSave - SouthBegonia

1688704-20191002093232581-2052765411.jpg

1688704-20191002093241521-1652804985.jpg


一、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存档机制众多,但还应按照个人项目需求选择合适的存档方法。


四、参考

  • PlayerPrefs - Unity Documentation
  • How to Save and Load a Game in Unity - raywenderlich
  • 对于PlayerPrefs学习以及储存的研究 - 果vinegar
  • Save&Load Unity存档读档的学习总结 - JoharWong
  • C#中File和FileStream的用法 - 忆汐辰
  • Application.dataPath - Unity Documentation

转载于:https://www.cnblogs.com/SouthBegonia/p/11616825.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • python画矩形函数drawrectangle_基于python,OPenCv中基本的绘图函数

    一 OpenCv中 python接口的基本的绘图函数 xff11 xff0d 用于绘制直线的 cv2 line 函数 xff12 xff0d 用于绘制椭圆的 cv2 ellipse 函数 xff13 xff0d 用于绘制矩形的 cv2 re
  • java堆和栈分别存什么类型_栈、堆、方法区分别存储什么内容

    栈中保存基本数据类型的值和对象以及基础数据的引用 xff1b 堆中存储的全部是对象 xff0c 每个对象都包含一个与之对应的class的信息 xff1b 方法区被所有的线程所共享 xff0c 方法区包含所有的class和static变量 堆
  • 手把手教程2: keil如何向MDK中加入头文件和库文件

    华大MCU如何向MDK中加入头文件和库文件 开发板请点击 xff1a https item taobao com item htm spm 61 a2oq0 12575281 0 0 50111debhMzSwz amp ft 61 t a
  • html转微信公众号,通过htmlparse动态抓取微信公众号的文章

    真的是很久没有更新我的博客了 xff0c 刚开始 xff0c 我还真的不知道爬虫是什么东西 xff0c 但是由于项目需要 xff0c 老板要我做个简单的爬虫 xff0c 就是去人家的微信公众号去把里面的文章动态的抓取下来 xff0c 当听到
  • 超声波测距传感器初识之HC-SR04

    1 HC SR04初识 HC SR04超声波测距模块可提供2cm 400cm的非接触式距离感测功能 xff0c 测距精度可达高到3mm xff1b 模块包括超声波发射器 接收器和控制电路 其基本工作原理采用IO口TRIG触发测距 xff0c
  • 2.3 使用ARDUINO控制MC20进行GPRS的TCP通讯

    需要准备的硬件 MC20开发板 1个 https item taobao com item htm id 61 562661881042GSM GPRS天线 1根 https item taobao com item htm id 61 5
  • ubuntu16.04下编译ceres-solver

    一 编译环境 ubuntu16 04 二 准备工作之安装必要的库 2 1安装cmake sudo apt get install cmake 2 2 安装google glog 43 gflags sudo apt get install
  • 四轴飞行器1.5 各种PID对比分析及选择

    原创文章 xff0c 欢迎转载 xff0c 转载请注明出处 这篇文章主要介绍我对PID的理解 xff0c 以及选择PID算法的过程 一 PID的理解和学习过程 二 飞控的PID效果 先上个飞控PID的响应的视频 xff1a 介绍在后面 地址
  • micropython安装ros_ROS2 树莓派SBC镜像安装

    下载并安装Ubuntu映像文件 TurtleBot3 1 转到Ubuntu旧版本 2 在远程PC上下载ubuntu 18 04 3 preinstalled server arm64 43 raspi3 img xz 3 将Ubuntu映像
  • Error "Client wants topic A to have B, but our version has C. Dropping connection."

    ROS problem 出现这个问题的原因是话题上的消息类型和订阅节点指定的消息类型不匹配 转载于 https www cnblogs com lvchaoshun p 7811123 html
  • unity利用A*算法进行2D寻路

    找了份新工作之后 xff0c 忙的要死 xff0c 都没时间鼓捣博客了 xff0c 深深的感受到资本家的剥削 xff0c 端午节连粽子都没有 xff0c 每天下班累得跟条咸鱼一样 可能就是 刚好最近忙里偷闲 xff0c 就来写写unity在
  • 工信部划分数据中心规模等级

    C114讯 2013年1月14日上午消息 为落实 国务院关于加快培育和发展战略性新兴产业的决定 xff0c 满足社会 信息化 水平不断提高的要求 xff0c 促进我国数据中心 xff0c 特别是大型数据中心的合理布局和健康发展 xff0c
  • VS2010 error RC2135: file not found

    VS2010 C 43 43 win32 DLL 工程 xff0c 添加 rc 文件 xff0c 编辑 String Table 默认情况下英文版本的 rc 文件能够顺序编译通过 xff0c 为了让工程支持多语言 xff0c 将字符串修改为
  • mavlink协议移植问题

    mavlink协议移植问题 mavlink源代码是一个代码库 xff0c 使用的时候只需要将mavlink h头文件包含到工程项目中即可 mavlink通信协议是无状态的连接 xff0c 一般采用心跳消息跟踪系统是否存在 请确保每60 30
  • 光流传感器 定位精度_光流传感器其它方面的应用

    光流传感器可以通过在一定的时间内拍摄两张不同的照片 进而计算出物体运动的速度 光流是一种简单实用的图像运动表达方式 通常定义为一个图像序列中的图像亮度模式的表观运动 光流法检测运动物体的基本原理是 xff1a 给图像的每一个像素点赋予一个速
  • 6.28-机器人模拟器Gazebo基础

    gazebo基础学习 前言 在算法人员开发出可以真机使用的算法之前进行仿真学习机器人物理仿真器的基本使用 xff0c 包括创建场景 xff0c 制作ROS控制接口等 目录 gazebo基础学习 前言 目录 参考 学习记录 基础 安装gaze
  • Poco C++库网络模块例子解析2-------HttpServer

    下面程序取自 Poco 库的Net模块例子 HTTPServer 下面开始解析代码 include 34 Poco Net HTTPServer h 34 继承自TCPServer 实现了一个完整的HTTP多线程服务器 include 34
  • 【0928 | Day 39】事务(精讲)

    目录 一 事务 1 mysql如何控制事务 xff1f 2 默认事务开启的作用是什么 xff1f 3 事务的其他打开方式 xff1f 二 事物的四大特性 一 事务 在mysql中 xff0c 事务其实是一个最小的不可分割的工作单元 xff0
  • Unity项目 - DeathtrapDungeon死亡地牢

    目录 游戏原型项目演示绘图资源代码实现注意事项技术探讨参考来源 游戏原型 死亡地牢是一款 2D Roguelike 的地牢冒险游戏 手握利刃 xff0c 斩杀怪物 xff0c 在凶险的地牢内生存下去 但注意 xff0c 敌人也并非善茬 xf
  • Unity - 存读档机制简析

    本文旨在于简要分析Unity中的两种存档机制 xff0c 即 xff1a PlayerPrefs数据持久化方法及Serialization数据序列化方法 较比于源项目 xff0c 我另加了JSON方法 XML方法等及一些Unity设置 xf