11.20閒話-存檔

QEDQEDQED發表於2024-11-20

存檔

參考

使用沒有存檔的軟體,就像吃飯不給容器一般。

故存檔必然是極為重要的。

下面介紹Unity的幾種存檔方式。

程式碼出處

Part.1——PlayerPrefs

應該是最簡單的存檔方式。

但侷限性也是顯然的,只能儲存int, float, string 三種型別,就像在檔案中儲存了三個map <string, int / float / string>

PlayerPrefs.SetInt("田所浩二", 114514); // 存
PlayerPrefs.SetString("濤哥", "原神");

string players_id = PlayerPrefs.GetString("濤哥"); // 讀

Part.2——儲存資料序列化

分為三種:二進位制方法、XML方法、JSON方法,各有千秋。

大體就是

序列化 物件 -> 位元組流

反序列化 位元組流 -> 物件

先建立一個Save類,用於記錄需要儲存的東西

public class Save
{
    public List <string> HZOI_name = new List <string>();

    public int id;
}

1. 二進位制方式

使用二進位制檔案進行序列化與反序列化

private void Save_By_Bin()
{
    Save save = Create_Save();

    BinaryFormatter bf = new BinaryFormatter(); // 建立二進位制格式化程序

    FileStream fileStream = File.Create(Application.dataPath + "/StreamingFile" + "/byBin.txt"); // 建立檔案流

    bf.Serialize(fileStream, save);

    fileStream.Close(); // 關閉

    if (File.Exists(Application.dataPath + "/StreamingFile" + "/byBin.txt"))
    {
        Debug.Log("儲存成功");
    }
}

private void Load_By_Bin()
{
    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)bf.Deserialize(fileStream);

        fileStream.Close();

        Set_Game(save);

        Debug.Log("成功");
    }
    else
    {
        Debug.Log("What are you doing?");
    }
}

2. JSON方法

使用JSON進行序列化與反序列化

//JSON:存檔和讀檔
private void SaveByJson()
{
    Save save = Create_Save();
    string filePath = Application.dataPath + "/StreamingFile" + "/byJson.json";
    //利用JsonMapper將save物件轉換為Json格式的字串
    string saveJsonStr = JsonMapper.ToJson(save);
    //將這個字串寫入到檔案中
    //建立一個StreamWriter,並將字串寫入檔案中
    StreamWriter sw = new StreamWriter(filePath);
    sw.Write(saveJsonStr);

    sw.Close();

    if (File.Exists(Application.dataPath + "/StreamingFile" + "/byBin.txt"))
    {
        Debug.Log("儲存成功");
    }
}

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);//將儲存的資訊類初始遊戲
    
        Debug.Log("成功");
    }
    else
    {
        Debug.Log("What are you doing?");
    }
}

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), id, score)
    XmlElement id = xmlDoc.CreateElement("id");
    id.InnerText = save.id.ToString();
    root.AppendChild(id);

    xmlDoc.AppendChild(root);
    xmlDoc.Save(filePath);
    
    if (File.Exists(Application.dataPath + "/StreamingFile" + "/byBin.txt"))
    {
        Debug.Log("儲存成功");
    }
}


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 id = xmlDoc.GetElementsByTagName("id");
        int shootNumCount = int.Parse(id[0].InnerText);
        save.id = shootNumCount;

        SetGame(save);//將儲存的資訊類初始遊戲
        //TODO: 提示存檔不存在或為空,讀取成功

        Debug.Log("成功");
    }
    else
    {
        Debug.Log("What are you doing?");
    }
}

Part.3 總結

二進位制方法:簡單簡潔,但可讀性極差

JSON方法:可讀性好,感覺比較符合人類直覺

XML:冗長,可讀性好(?沒感覺可讀性太好,可能是我太蒻了嗚嗚嗚)

偶然發現