1. 程式人生 > >Save&Load--Unity存檔讀檔的學習總結

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:存檔和讀檔--要根據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