zoukankan      html  css  js  c++  java
  • Save&Load--Unity存档读档的学习总结

    存档与读档功能

    举例:

    • 传统RPG游戏(仙剑、空之轨迹):
      1.角色信息(生命值,等级)
      2.道具信息(装备,药品)
      3.场景信息(场景名称、角色坐标)
      4.事件信息(任务相关)

    • 关卡类游戏:关卡的通关情况

    • 一些特殊的存档机制(漫漫长夜、亿万僵尸)

    Unity中使用的存档方式

    一、PlayerPrefs:数据持久化方案

    官方手册

    1. 原理:采用键值对的方式对数据进行存储---保存在本地文件(不同操作系统存储路径不同)中,然后程序可以根据这个名称取出上次保存的数值)

    2. 储存类型:可以存储Int, Float, String类型的数据。

    3. 使用例子

    //存储数据:
    PlayerPrefs.SetInt("Index",1); 
    PlayerPrefs.SetFloat("Height",183.5f); 
    PlayerPrefs.SetString("Name","Tom");
    //获取数据:
    PlayerPrefs.GetInt("Index");
    

    二、存储数据序列化

    • Serialization(序列化),可以用来将对象转化为字节流。
    • Deserialization(反序列化),可以用来将字节流转换为对象。
    • 常见的数据序列化方法:二进制方法,XML方法,JSON方法
    • 存储在:内存、文件、数据库--可存于本地或云

    ++下面以实例说明++:

    Save类--用于保存信息

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    [System.Serializable]
    public class Save{
        //怪物位置
        public List<int> livingTargetPositions = new List<int>();
        //怪物类型
        public List<int> livingMonsterTypes = new List<int>();
        //得分情况
        public int shootNum = 0;
        public int score = 0;
    }
    
    
    1. 二进制存储(Binary Formatter)
    • 序列化:新建或打开一个二进制文件,通过二进制格式器将对象写入该二进制文件。
    • 反序列化:打开待反序列化的二进制文件,通过二进制格式器将文件解析成对象。
    //二进制方法:存档和读档
        private void SaveByBin()
        {
            //序列化过程(将Save对象转换为字节流)
            //创建Save对象并保存当前游戏状态
            Save save = CreateSaveGO();
            //创建一个二进制格式化程序
            BinaryFormatter bf = new BinaryFormatter();
            //创建一个文件流
            FileStream fileStream = File.Create(Application.dataPath + "/StreamingFile" + "/byBin.txt");
            //用二进制格式化程序的序列化方法来序列化Save对象,参数:创建的文件流和需要序列化的对象
            bf.Serialize(fileStream, save);
            //关闭流
            fileStream.Close();
    
            //如果文件存在,则显示保存成功
            if (File.Exists(Application.dataPath + "/StreamingFile" + "/byBin.txt"))
            {
                //TODO: 提示保存成功
            }
        }
    
        private void LoadByBin()
        {
            if(File.Exists(Application.dataPath + "/StreamingFile" + "/byBin.txt"))
            {
                //反序列化过程
                //创建一个二进制格式化程序
                BinaryFormatter bf = new BinaryFormatter();
                //打开一个文件流
                FileStream fileStream = File.Open(Application.dataPath + "/StreamingFile" + "/byBin.txt", FileMode.Open);
                //调用格式化程序的反序列化方法,将文件流转换为一个Save对象
                Save save = (Save)bf.Deserialize(fileStream);
                //关闭文件流
                fileStream.Close();
    
                SetGame(save); //将保存的信息类初始游戏
                //TODO: 提示存档不存在或为空,读取成功
    
            }
            else
            {
                //TODO: 提示存档不存在或为空,读取失败
            }
    
            
        }
    
    1. Json存储
    • JSON:是一种语言无关的发送和接收数据的常用格式。可以使用它来跨平台的传输数据。
    • JSON序列化:
    graph LR
    对象-->JSON
    
    • JSON反序列化:
    graph LR
    JSON-->对象
    
    //JSON:存档和读档
        private void SaveByJson()
        {
            Save save = CreateSaveGO();
            string filePath = Application.dataPath + "/StreamingFile" + "/byJson.json";
            //利用JsonMapper将save对象转换为Json格式的字符串
            string saveJsonStr = JsonMapper.ToJson(save);
            //将这个字符串写入到文件中
            //创建一个StreamWriter,并将字符串写入文件中
            StreamWriter sw = new StreamWriter(filePath);
            sw.Write(saveJsonStr);
            //关闭StreamWriter
            sw.Close();
    
            //TODO: 提示保存成功
        }
    
        private void LoadByJson()
        { 
            string filePath = Application.dataPath + "/StreamingFile" + "/byJson.json";
            if(File.Exists(filePath))
            {
                //创建一个StreamReader,用来读取流
                StreamReader sr = new StreamReader(filePath);
                //将读取到的流赋值给jsonStr
                string jsonStr = sr.ReadToEnd();
                //关闭
                sr.Close();
    
                //将字符串jsonStr转换为Save对象
                Save save = JsonMapper.ToObject<Save>(jsonStr);
                SetGame(save);//将保存的信息类初始游戏
                //TODO: 提示存档不存在或为空,读取成功
            }
            else
            {
                //TODO: 提示存档不存在或为空,读取失败
            }
        }
    

    存储效果:

    {
        "livingTargetPositions":[0,1,2,4,5,8],
        "livingMonsterTypes":[2,1,2,0,3,0],
        "shootNum":18,
        "score":9
    }
    
    1. XML存储

    XML:扩展标记语言,用于标记电子文件使其具有结构性的标记语言。

    • 可以用来标记数据、定义数据类型。
    • 序列化与反序列化的方式与二进制方法十分类似。

    XML与Json的区别

    //XML:存档和读档--要根据XML文件的结点结构进行操作
        private void SaveByXml()
        {
            Save save = CreateSaveGO();
            //创建XML文件的存储路径
            string filePath = Application.dataPath + "/StreamingFile" + "/byXML.txt";
            //创建XML文档
            XmlDocument xmlDoc = new XmlDocument();
            //创建根节点,即最上层节点
            XmlElement root = xmlDoc.CreateElement("save");
            //设置根节点中的值
            root.SetAttribute("name", "saveFile1");
    
            //创建XmlElement
            XmlElement target;
            XmlElement targetPosition;
            XmlElement monsterType;
    
            //遍历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();
                monsterType = xmlDoc.CreateElement("monsterType");
                monsterType.InnerText = save.livingMonsterTypes[i].ToString();
    
                //设置节点间的层级关系 root -- target -- (targetPosition, monsterType)
                target.AppendChild(targetPosition);
                target.AppendChild(monsterType);
                root.AppendChild(target);
            }
    
            //设置射击数和分数节点并设置层级关系  xmlDoc -- root --(target-- (targetPosition, monsterType), shootNum, score)
            XmlElement shootNum = xmlDoc.CreateElement("shootNum");
            shootNum.InnerText = save.shootNum.ToString();
            root.AppendChild(shootNum);
    
            XmlElement score = xmlDoc.CreateElement("score");
            score.InnerText = save.score.ToString();
            root.AppendChild(score);
    
            xmlDoc.AppendChild(root);
            xmlDoc.Save(filePath);
    
            if(File.Exists(Application.dataPath + "/StreamingFile" + "/byXML.txt"))
            {
                //TODO: 提示保存成功
            }
        }
    
    
        private void LoadByXml()
        {
            string filePath = Application.dataPath + "/StreamingFile" + "/byXML.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)
                    {
                        XmlNode targetPosition = target.ChildNodes[0];
                        int targetPositionIndex = int.Parse(targetPosition.InnerText);
                        //把得到的值存储到save中
                        save.livingTargetPositions.Add(targetPositionIndex);
    
                        XmlNode monsterType = target.ChildNodes[1];
                        int monsterTypeIndex = int.Parse(monsterType.InnerText);
                        save.livingMonsterTypes.Add(monsterTypeIndex);
                    }
                }
                
                //得到存储的射击数和分数
                XmlNodeList shootNum = xmlDoc.GetElementsByTagName("shootNum");
                int shootNumCount = int.Parse(shootNum[0].InnerText);
                save.shootNum = shootNumCount;
    
                XmlNodeList score = xmlDoc.GetElementsByTagName("score");
                int scoreCount = int.Parse(score[0].InnerText);
                save.score = scoreCount;
    
                SetGame(save);//将保存的信息类初始游戏
                //TODO: 提示存档不存在或为空,读取成功
    
            }
            else
            {
                //TODO: 提示存档不存在或为空,读取失败
            }
        }
    

    存储效果:

    <save name="saveFile1">
      <target>
        <targetPosition>0</targetPosition>
        <monsterType>1</monsterType>
      </target>
      <target>
        <targetPosition>1</targetPosition>
        <monsterType>3</monsterType>
      </target>
      <target>
        <targetPosition>3</targetPosition>
        <monsterType>3</monsterType>
      </target>
      <target>
        <targetPosition>4</targetPosition>
        <monsterType>0</monsterType>
      </target>
      <target>
        <targetPosition>6</targetPosition>
        <monsterType>3</monsterType>
      </target>
      <target>
        <targetPosition>7</targetPosition>
        <monsterType>0</monsterType>
      </target>
      <shootNum>50</shootNum>
      <score>29</score>
    </save>
    
    1. 三种存储方式的对比
    • 二进制方法:简单,但可读性差。
    • XML:可读性强,但是文件庞大,冗余信息多。
    • JSON:数据格式比较简单,易于读写,但是不直观,可读性比XML差。
    1. 本地存储位置补充--关于Application.dataPath
  • 相关阅读:
    分区硬盘Lvm 折腾小记
    添加源ubuntu_x64 安装 Adobe Reader
    停止标记NYOJ 一个简单的数学题 南工330停止标记
    读控制台HDU 1788 Chinese remainder theorem again 数论读控制台
    对象方法PHP中魔术方法的用法对象方法
    指针修饰C语言const修饰符探秘指针修饰
    输入整数角谷步数 你听说过角谷猜想吗? 任意的正整数,比如 5, 我们从它开始,如下规则计算: 如果是偶数,则除以2,如果是奇数,则乘以3再加1. 如此循环,最终必会得到“1” !输入整数
    根节点左边POJ 1456 Supermarket根节点左边
    代码功能【OpenGL】初识OpenGL4.0代码功能
    终点节点NYOJ115 城市平乱终点节点
  • 原文地址:https://www.cnblogs.com/JoharWong/p/9867394.html
Copyright © 2011-2022 走看看