在字典中添加新值会破坏旧的保存文件

时间:2018-08-02 17:48:51

标签: c#

我正在使用一种简单的方法来对保存文件进行序列化和反序列化,如下所示

//Object that is being stored
[System.Serializable]
public class GameData{
    public int units;
    public int scanRange;
    public int gains;
    public int reputation;
    public int clicks;
    public Dictionary<string,bool> upgradesPurchased;
    public Dictionary<string,bool> upgradesOwned;
    public Dictionary<string,bool> achievementsEarned;

    public GameData(int units_Int,int scan_Range,int gains_Int,int reputation_Int,int clicks_Int,Dictionary<string,bool> upgrades_Purchased,Dictionary<string,bool> upgrades_Owned,Dictionary<string,bool> achievements_Earned){
        units = units_Int;
        scanRange = scan_Range;
        gains = gains_Int;
        reputation = reputation_Int;
        clicks = clicks_Int;
        upgradesPurchased = upgrades_Purchased;
        upgradesOwned = upgrades_Owned;
        achievementsEarned = achievements_Earned;
    }
}

//Method that handles saving the object
public void SaveFile(){
        string destination = Application.persistentDataPath + DATA_FILE;
        FileStream file;

        if (File.Exists (destination)) {
            file = File.OpenWrite (destination);
        } else {
            file = File.Create (destination);
        }

        GameData data = new GameData (GameManager.Instance.units,GameManager.Instance.scanRange,GameManager.Instance.gains,GameManager.Instance.reputation,GameManager.Instance.clicks,UpgradeManager.Instance.upgradesPurchased,UpgradeManager.Instance.upgradesOwned,AchievementManager.Instance.achievementsEarned);
        BinaryFormatter bf = new BinaryFormatter ();
        bf.Serialize (file, data);
        file.Close ();

        NotificationsBar.Instance.ShowNotification ("Game saved success");
    }

  //Method that loads the object
  public void LoadFile(){
        string destination = Application.persistentDataPath + DATA_FILE;

        FileStream file;

        if (File.Exists (destination)) {
            file = File.OpenRead (destination);
        } else {
            UpgradeManager.Instance.FirstLoad ();
            return;
        }

        BinaryFormatter bf = new BinaryFormatter ();
        GameData data = (GameData)bf.Deserialize (file);
        file.Close ();

        GameManager.Instance.units = data.units;
        GameManager.Instance.scanRange = data.scanRange;
        GameManager.Instance.gains = data.gains;
        GameManager.Instance.reputation = data.reputation;
        GameManager.Instance.clicks = data.clicks;

        UpgradeManager.Instance.upgradesPurchased = data.upgradesPurchased;
        UpgradeManager.Instance.upgradesOwned = data.upgradesOwned;

        AchievementManager.Instance.achievementsEarned = data.achievementsEarned;

        Debug.Log ("Units: " + data.units);
    }

这里有很多代码,但是每个人都可以清楚地看到整个系统的样子

因此,此方法的问题是在将新值添加到传递给GameData UpgradeManager.Instance.upgradesPurchased的字典中时,在字典key not present in dictionary中搜索数据时会出现错误

我的分析是,由于添加了新值,因此字典中存在一个偏移值,从该位置开始放置新值以及该位置曾经存在的值

当我第一次写出代码时,字典会自动填充新的值并覆盖旧的数据

对于我的意思的直观表示,可以说您进行了2次升级

Upgrade1,Upgrade2

现在已保存

现在代码更改了,您进行了3次升级

升级1,升级3,升级2

我认为会发生的是将新值添加到保存中

所以我不确定为什么会这样。...

1 个答案:

答案 0 :(得分:1)

虽然我看不到问题的确切原因,但我建议如下:

首先,将您的保存/加载逻辑从GameData类中删除,并将其放入SaveDataManager类中,这样您就可以分隔责任。

从那里,您可以将GameData类简化为struct,使序列化/反序列化更加容易。

然后在您的主游戏类中,每当需要加载游戏时,您都可以按照以下方式进行操作:

SaveGameManger sgManager = new SaveGameManager(file);
gameData = sgManager.LoadGame()

这将使您的代码更易于维护,如果不能解决您的问题,则查找起来会容易得多。

此外,它还允许您构建单元测试以验证加载和保存逻辑的完整性。


我没有机会对其进行测试,但是您分离并重构的代码看起来像这样(尽管它需要添加一些验证检查,但不行):

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace  temp
{

    public class GameLoop
    {
        private SaveGameManager sgManager;
        private GameData data;
        private bool isPlaying;

        public GameLoop()
        {
            sgManager = new SaveGameManager("MYSAVELOCATION");
            data = sgManager.LoadGame();
            isPlaying = true;
        }

        private void PlayGame()
        {
            while (isPlaying)
            {
                //All of your game code
            }
        }



    }

    public class SaveGameManager
    {
        private string saveFile;
        private BinaryFormatter formatter;

        public SaveGameManager(string file)
        {
            saveFile = file;
            formatter = new BinaryFormatter();
        }

        public GameData LoadGame()
        {
            using (StreamReader reader = new StreamReader(saveFile))
            {
                return (GameData)formatter.Deserialize(reader.BaseStream);
            }
        }

        public void SaveGame(GameData data)
        {
            using (StreamWriter writer = new StreamWriter(saveFile))
            {
                formatter.Serialize(writer.BaseStream, data);
            }
        }
    }

    [Serializable]
    public struct GameData
    {
        public int units;
        public int scanRange;
        public int gains;
        public int reputation;
        public int clicks;
        public Dictionary<string, bool> upgradesPurchased;
        public Dictionary<string, bool> upgradesOwned;
        public Dictionary<string, bool> achievementsEarned;
    }



}

我真的会考虑使用您的string键进行升级,以支持枚举……更不容易出错。