首页 > 解决方案 > 如何添加新数据并保留旧的现有数据

问题描述

我几乎完成了我的手机游戏的制作,并且我有一个使用视频中显示的内容的 DATA 脚本。我有一个列表,其中包含玩家可以完成的不同挑战的值。我将如何更新游戏,以便在保留旧数据的同时添加更多挑战。

(挑战数据基本包含是否完成,距离完成还有多远)

我看过这个指南,但我不太明白。我是序列化的新手。

先感谢您 :)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

[System.Serializable]
public class XMLManager : MonoBehaviour {

    public static XMLManager dataManagement;

    public gameData data;


    void Awake()
    {
        //File.Delete(Application.dataPath + "/StreamingFiles/XML/item_data.xml");
        System.Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");
        dataManagement = this;
        DontDestroyOnLoad(gameObject);

    }


    public void SaveData()
    {
        XmlSerializer serializer = new XmlSerializer(typeof(gameData));
        System.Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");
        FileStream stream = new FileStream(Application.dataPath + "/StreamingFiles/XML/item_data.xml", FileMode.Create);
        serializer.Serialize(stream, data);
        stream.Close();
    }
    public void LoadData()
    {
        System.Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");
        if (File.Exists(Application.dataPath + "/StreamingFiles/XML/item_data.xml"))
        {
            XmlSerializer serializer = new XmlSerializer(typeof(gameData));
            FileStream stream = new FileStream(Application.dataPath + "/StreamingFiles/XML/item_data.xml", FileMode.Open);
            data = serializer.Deserialize(stream) as gameData;
            stream.Close();

        }
        else
        {
            print("SaveData");
            SaveData();
        }


    }
}

[System.Serializable]
public class gameData
{


    public List<ChallengeStatus> Challenges;
    public int HighScore;
    public int CoinsCollected;
    public List<bool> Unlocked;
    public int GamesPlayed;
    public int currentChallenge;

}
[System.Serializable]
public class ChallengeStatus
{


    public int id;
    public string title;
    public int current;
    public int max;
    public int reward;
    public bool completed;
    public bool claimed;



}

标签: c#unity3d

解决方案


First you should have a look at Unity XML Serialization and use proper attributes. You don't totally need them (except maybe the [XmlRoot]) but they let you customize your Xml file. If not provided Unity uses the variable names and uses sub-elements instead of attributes. However afaik this works only for primitives (int, float, string, bool, etc) and lists of them not for your own class ChallengeStatus. So at least for the list of your class you have to provide attributes:

[System.Serializable]
[XmlRoot("GameData")]
public class GameData
{
    [XmlArray("Challenges")]
    [XmlArrayItem("ChallengeStatus)]
    public List<ChallengeStatus> Challenges;

    //...
}

Now I don't really understand why you need to keep the old XML file when saving a new one but if you want to keep the current file I would add an int FileCounter .. ofcourse not in the same XML file ;) Might be e.g. via PlayerPrefs or a second simple text file only including the number or something similar.

Note it is better/saver to use Path.Combine for concatenate systempaths) - the overload taking an array of strings requires .Net4. Something like

private string FilePath 
{
    get
    {
        //TODO first load / read the FileCounter from somewhere

        var paths = {
        Application.dataPath, 
        "StreamingFiles", 
        "XML", 

        // here you get from somewhere and use that global FileCounter
        string.Format("item_data_{0}.xml", FileCounter)};

        return Path.Combine(paths);
    }
}

Than you can increase that global FileCounter everytime you save the file.

public void SaveData()
{
    //TODO somehow increase the global value
    // store to PlayerPrefs or write a new file or ...
    FileCounter += 1;

    System.Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");   

    // better use the "using" keyword for streams
    // use the FilePath field to get the filepath including the filecounter
    using(FileStream stream = new FileStream(FilePath, FileMode.Create))
    {
        XmlSerializer serializer = new XmlSerializer(typeof(gameData))
        serializer.Serialize(stream, data);
    }
}

And read the file with the current FileCounter without increasing it

public void LoadData()
{
    System.Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");

    if (File.Exists(FilePath))
    {
        // better use a "using" block for streams
        // use the FilePath field to get the filepath including the filecounter
        using(FileStream stream = new FileStream(FilePath, FileMode.Open))
        {
            XmlSerializer serializer = new XmlSerializer(typeof(gameData));
            data = serializer.Deserialize(stream) as gameData;
        }
    }
    else
    {
        print("SaveData");
        SaveData();
    }
}

Hint 1:
As soon as you provide a constructor for your classes e.g.

public GameData(List<ChallengeStatus> challenges)
{
    Challenges = challenges;
}

than you always have to also provide a default constructor (even if it does nothing)

public GameData(){ }

Hint 2:
You should always initialize your lists:

public class GameData
{
    public List<ChallengeStatus> Challenges = new List≤ChallangeStatus>();
    //...

    public List<bool> Unlocked = new List<bool>();

    //...
}

Hint 3:
Btw you don't need that [System.Serializable] for XmlManager since it inherits from MonoBehaviour which already is serializable anyway.


推荐阅读