unity -- 存档与读档

不同于unity自带的PlayerPrefs的字典式存储,本文主要说明三种文件存储的具体实现方式(项目原型基于siki案例–存档与读档,类似于打砖块的小游戏),即:二进制方法、XML以及JSON。

三种存储方式的对比

一、二进制方式存储
存储简单,但基本没有可读性。
二、XML
可读性强,但文件庞大,冗余信息多。
三、JSON
数据格式比较简单,易于读写,但是不直观,可读性比XML差。

三种方式存储的预先准备

首先得创建一个Save类存储相关数据并标记为可序列化,本例具体代码如下:
(新建一个C#脚本,将继承类删除即可开始编写Save类,Save类用于存储需要存储的数据)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class Save
{
    public List<int> activeMonsterPosition = new List<int>();
    public List<int> activeMonsterType = new List<int>();

    public int shootNum;
    public int score;
}

一、二进制方式的存档与读档

需要添加两个命名空间
1、using System.IO; //用于文件的读写
2、using System.Runtime.Serialization.Formatters.Binary; //用于创建二进制格式化程序

存档代码如下:

private void SaveByBin()
    {
        //创建存储类对象
        Save save = CreateSaveGo();
        //创建二进制格式化程序
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        //创建文件流(create参数为存储路径,注意路径前的"/"以及标明文件名称和格式)
        FileStream fileStream = File.Create(Application.dataPath + "/StreamingFile" + "/byBin.txt");
        //用二进制格式化程序的序列化方法来序列化save对象 
        binaryFormatter.Serialize(fileStream, save);
        //关闭文件流
        fileStream.Close();
    }

读档代码如下:

private void LoadByBin()
    {
        if (File.Exists(Application.dataPath + "/StreamingFile" + "/byBin.txt")) //检测是否存在读档信息
        {
            //创建二进制格式化程序
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            //创建文件流打开读档信息
            FileStream fileStream = File.Open(Application.dataPath + "/StreamingFile" + "/byBin.txt", FileMode.Open);
            //调用二进制格式化程序的反序列化方法,将文件流转化为Save对象
            Save save = (Save)binaryFormatter.Deserialize(fileStream); //返回值为object类型,需要强制转换为需要的类型
            //关闭文件流
            fileStream.Close();
            //将读取到的游戏属性设置为游戏当前属性(根据游戏需要自行编写)
            SetGame(save);
        }
    }

二、JSON方式的存档与读档

需要添加两个命名空间
1、using System.IO; //用于文件的读写
2、using LitJson;//用于json数据的转换(需要自行导入LitJson.dll文件)

存档代码如下:

 private void SaveByJson()
    {
        //保存当前游戏状态,创建save对象(CreateSaveGo函数根据保存数据需要编写)
        Save save = CreateSaveGo();
        //保存文件路径(美化代码)
        string filePath = Application.dataPath + "/StreamingFile" + "/byJson.json";//文件类型可以时.json也可以是.txt
        //通过JsonMapper.ToJson方法将save对象中的数据转化为json字符串保存下来
        string saveJsonStr = JsonMapper.ToJson(save);//using LitJson;
        //创建StreamWriter写入流(注意需要一个string参数代表路径)
        StreamWriter streamWriter = new StreamWriter(filePath);
        //将转化后的字符串写入目标文件
        streamWriter.Write(saveJsonStr);
        //关闭StreamWriter
        streamWriter.Close();
    }

读档代码如下:

读档代码逻辑就是存档的反向逻辑。

 private void LoadByJson()
    { 
        //string保存路径(美化代码)
        string filePath = Application.dataPath + "/StreamingFile" + "/byJson.json";
        //创建文件读取流(注意new对象时需要string类型参数代表路径)
        StreamReader streamReader = new StreamReader(filePath);
        //读取文件中所有的字符串(ReadToEnd代表读取到最后,即读取所有)
        string jsonStr = streamReader.ReadToEnd();
        //将读取到的字符串通过JsonMapper.ToObject转换为Save类型的对象(JsonMapper.ToObject<类型>(路径);)
        Save save = JsonMapper.ToObject<Save>(jsonStr);//using LitJson;
        //设置游戏状态,根据游戏需要自行编写
        SetGame(save);
    }

三、XML方式的存档与读档

需要添加两个命名空间
1、using System.IO; //用于文件的读写
2、using System.Xml; //用于创建XML文档

存档代码如下:

xml虽然有系统自带的类库,但是所有的结点需要自己添加,相比其它两种方法要麻烦,但过程逻辑不复杂,了解自己编写的Save类即可轻松编写。
总体思路是 XmlDocument ( root ( target( …… ) – shootNum – score ) ) 【括号代表层级关系,"–"代表同级】

 private void SaveByXml()
    {
        //保存当前游戏状态,创建save对象(CreateSaveGo函数根据保存数据需要编写)
        Save save = CreateSaveGo();
        //保存文件路径(美化代码)
        string filePath = Application.dataPath + "/StreamingFile" + "/byXml.txt";
        //总思路:XmlDocument (    root (  target(  ……  ) -- shootNum -- score  )   )
        //括号代表层级关系,"--"代表同级
        //创建Xml文档
        XmlDocument xmlDocument = new XmlDocument();
        //创建xml根节点
        XmlElement root = xmlDocument.CreateElement("save");
        //设置根结点的值(名字的名称 ,名字)
        root.SetAttribute("name", "saveFile1");

        //创建xml元素(每个元素都包含名称 和 内部包含的值)
        XmlElement target;
        XmlElement targetPosition;
        XmlElement monsterType;

        //循环赋值
        for (int i = 0; i < save.activeMonsterPosition.Count; i++)
        {
            //设置xml元素的名称
            target = xmlDocument.CreateElement("target");
            targetPosition = xmlDocument.CreateElement("targetPosition");
            monsterType = xmlDocument.CreateElement("monsterType");

            //设置xml元素内部包含的值(即存储的字符串)
            targetPosition.InnerText = save.activeMonsterPosition[i].ToString();
            monsterType.InnerText = save.activeMonsterType[i].ToString();

            //设置层级关系(root -> target -> ( targetPosition -- monsterType ) )
            root.AppendChild(target);
            target.AppendChild(targetPosition);
            target.AppendChild(monsterType);
        }

        //创建xml元素并设置名称
        XmlElement shootNum = xmlDocument.CreateElement("shootNum");
        XmlElement score = xmlDocument.CreateElement("score");
        //设置xml元素内部包含的值(即存储的字符串)
        shootNum.InnerText = save.shootNum.ToString();
        score.InnerText = save.score.ToString();

        //设置层级关系(xmlDocument -> root -> ( shootNum -- score ))
        root.AppendChild(shootNum);
        root.AppendChild(score);
        xmlDocument.AppendChild(root);

        //将xml文档保存到指定路径
        xmlDocument.Save(filePath);
    }

读档代码如下:

总体思路: XmlDocument -> XmlNodeList -> XmlNode ( -> XmlNode .ChildNodes[……]) -> InnerText

  private void LoadByXml()
    {
        //保存文件路径(美化代码)
        string filePath = Application.dataPath + "/StreamingFile" + "/byXml.txt";
        //当保存文件存在时进行读档
        if (File.Exists(filePath))
        {
            //创建Save保存类对象
            Save save = new Save();
            //创建xml文档
            XmlDocument xmlDocument = new XmlDocument();
            //加载指定路径的xml文档
            xmlDocument.Load(filePath);

            //通过结点名来获取结点,返回值为XmlNodeList类型(存储XmlNode的List)
            XmlNodeList targets = xmlDocument.GetElementsByTagName("target");
            //当存在target时进行遍历
            if (targets.Count != 0)
            {
                //foreach遍历获得的list -- targets
                foreach (XmlNode target in targets)
                {
                    //获得list中的结点(对应位置参考存储时的位置)
                    XmlNode targetPosition = target.ChildNodes[0];
                    //直接获得该结点的innerText值(注意转化类型)
                    int targetPositionIndex = int.Parse(targetPosition.InnerText);
                    //给save对象赋值
                    save.activeMonsterPosition.Add(targetPositionIndex);

                    //获得list中的结点(对应位置参考存储时的位置)
                    XmlNode monsterType = target.ChildNodes[1];
                    //直接获得该结点的innerText值(注意转化类型)
                    int monsterTypeIndex = int.Parse(monsterType.InnerText);
                    //给save对象赋值
                    save.activeMonsterType.Add(monsterTypeIndex);
                }
            }

            //通过结点名来获取结点,返回值为XmlNodeList类型(存储XmlNode的List)
            XmlNodeList shootNumCount = xmlDocument.GetElementsByTagName("shootNum");
            //注意当XmlNodeList中的XmlNode没有子节点的时候可以直接获得XmlNode的InnerText
            int shootNum = int.Parse(shootNumCount[0].InnerText);
            //给save对象赋值
            save.shootNum = shootNum;

            //通过结点名来获取结点,返回值为XmlNodeList类型(存储XmlNode的List)
            XmlNodeList scoreCount = xmlDocument.GetElementsByTagName("score");
            //注意当XmlNodeList中的XmlNode没有子节点的时候可以直接获得XmlNode的InnerText
            int score = int.Parse(scoreCount[0].InnerText);
            //给save对象赋值
            save.score = score;

            //将读取到的游戏属性设置为游戏当前属性(根据游戏需要自行编写)
            SetGame(save);
        }
    }

本例中的CreateSaveGo()函数和SetGame()函数

CreateSaveGo():

   private Save CreateSaveGo()
    {
        Save save = new Save();
        //遍历每一个目前存活的目标,逐个保存
        foreach (GameObject targetGo in targetGos)
        {
            TargetManager targetManager = targetGo.GetComponent<TargetManager>();
            if (targetManager.activeMonster != null)
            {
                save.activeMonsterPosition.Add(targetManager.targetPosition);
                int type = targetManager.activeMonster.GetComponent<MonsterManager>().monsterType;
                save.activeMonsterType.Add(type);
            }
        }
        //保存UI属性
        save.shootNum = UIManager._instance.shootNum;
		save.score = UIManager._instance.score;
        return save;
    }

SetGame():

    private void SetGame(Save save)
    {
        foreach (GameObject target in targetGos)
        {
            target.GetComponent<TargetManager>().UpdateMonster();//先将所有怪物清空
        }
        for (int i = 0; i < save.activeMonsterPosition.Count; i++)
        {
            int position = save.activeMonsterPosition[i];
            int type = save.activeMonsterType[i];

            targetGos[position].GetComponent<TargetManager>().ActiveMonsterByType(type);//激活指定位置的怪物
        }
        //设置UI
        UIManager._instance.shootNum = save.shootNum;
        UIManager._instance.score = save.score;

        DePause();//取消暂停状态、关闭菜单
    }

你可能感兴趣的:(unity)