Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/270.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Unity-保存非顺序级别的数据(二进制序列化)_C#_Unity3d_Serialization - Fatal编程技术网

C# Unity-保存非顺序级别的数据(二进制序列化)

C# Unity-保存非顺序级别的数据(二进制序列化),c#,unity3d,serialization,C#,Unity3d,Serialization,对于我的新游戏,我希望有一个系统,其中我有几个阶段,每个阶段都有一系列的级别 我想我可以在前一阶段的所有关卡都被清除后解锁下一阶段,但它并不真正适合游戏类型 问题是,我无法找到一种方法,让关卡在stage1_1、stage1_2内顺序解锁。。。不使用多个保存文件,因为在从另一个阶段解锁某个关卡后,列表将失去顺序。我目前正在将它们存储为列表。以下是当前的设置方式: public class SaveManager : MonoBehaviour { public static SaveManag

对于我的新游戏,我希望有一个系统,其中我有几个阶段,每个阶段都有一系列的级别

我想我可以在前一阶段的所有关卡都被清除后解锁下一阶段,但它并不真正适合游戏类型

问题是,我无法找到一种方法,让关卡在stage1_1、stage1_2内顺序解锁。。。不使用多个保存文件,因为在从另一个阶段解锁某个关卡后,列表将失去顺序。我目前正在将它们存储为列表。以下是当前的设置方式:

public class SaveManager : MonoBehaviour 
{
public static SaveManager manager;
public List<LevelData> levelData;
public GameStats gameStats;

private void Awake()
{
    if(manager == null)
    {
        DontDestroyOnLoad(gameObject);
        manager = this;
    } else if(manager != this)
    {
        Destroy(gameObject);
    }
}

private void OnEnable()
{
    levelData = GetLevelData();
    gameStats = GetGameStats();
}


public List<LevelData> GetLevelData()
{
    string saveFilePath = Application.persistentDataPath + "/levels.dat";
    if (File.Exists(saveFilePath))
    {
        BinaryFormatter bf = new BinaryFormatter();
        FileStream file = File.Open(saveFilePath, FileMode.Open);
        List<LevelData> data = bf.Deserialize(file) as List<LevelData>;

        file.Close();
        Debug.Log("loaded!");
        return data;

        //Debug.Log(Application.persistentDataPath + "/save.dat");
    } else
    {
        Debug.Log("set level defaults!");
        return SetLevelDefaults();

    }
}

public GameStats GetGameStats()
{
    string saveFilePath = Application.persistentDataPath + "/gamestats.dat";
    if (File.Exists(saveFilePath))
    {
        BinaryFormatter bf = new BinaryFormatter();
        FileStream file = File.Open(saveFilePath, FileMode.Open);
        GameStats data = bf.Deserialize(file) as GameStats;

        file.Close();
        Debug.Log("loaded!");
        return data;

        //Debug.Log(Application.persistentDataPath + "/save.dat");
    }
    else
    {
        Debug.Log("set stats defaults!");
        return SetStartingGameStats();

    }
}

public void SaveLevelData()
{
    string saveFilePath = Application.persistentDataPath + "/levels.dat";
    Save(saveFilePath, "levels");
}

public void SaveGameStats()
{
    string saveFilePath = Application.persistentDataPath + "/gamestats.dat";
    Save(saveFilePath, "stats");
}

public List<LevelData> SetLevelDefaults()
{
    // unlock level 1 and create the file
    List<LevelData> ld = new List<LevelData>();
    LevelData levelOne = new LevelData()
    {
        time = 0,
        stars = 0,
        unlocked = true
    };
    ld.Add(levelOne);
    return ld;

}

public GameStats SetStartingGameStats()
{
    return new GameStats()
    {
        money = 0
    };

}

public void Save(string saveFilePath, string type)
{
    BinaryFormatter bf = new BinaryFormatter();
    FileStream file = File.Open(saveFilePath, FileMode.Create);
    switch (type)
    {
        case "levels":
            bf.Serialize(file, levelData);
            break;
        case "stats":
            bf.Serialize(file, gameStats);
            break;
        default:
            break;
    }

    file.Close();
    Debug.Log("saved!");
}

}



[Serializable]
public class LevelData
{
public int time;
public int stars;
public bool unlocked;
}

[Serializable]
public class GameStats
{
public int money;
// todo add powerups
}
并从菜单中访问它们:

if(SaveManager.manager.levelData.Count > levelID && 
SaveManager.manager.levelData[levelID] != null)
    {
        LevelData levelData = SaveManager.manager.levelData[levelID];
        SetLevelTime(levelData.time);
        SetLevelStars(levelData.stars);

        // todo display best time
        if (levelData.unlocked)
            Unlock();
    }
这就是我的处境。这有点太混乱了,我没有意识到非顺序解锁级别的问题,所以我现在唯一的解决方案是在SaveManager stage1_levelData、stage2_levelData…中创建不同的变量,但听起来效率不高。我已经重写了两次,在这一点上真的没有什么想法了

而且据我所知,字典不能进行二进制序列化,对吗

希望有人能给我指出正确的方向:
提前谢谢

您可能更愿意使用like,例如

private LevelData[,] levelData = new LevelData[X,Y];
在哪里

X:阶段数量 Y:每个阶段的级别数量 稍后,您可以使用访问特定条目,例如

LevelData[2,4].unlocked = true;
现在你解锁了第三阶段的第五关

不过,我已经填充了整个数组,而不仅仅是添加您已经通过的级别。与其保留空条目并进行比较,不如添加附加标志

public bool Completed;
并且已经使用有效的LevelData条目初始化了整个数组,请参见下面的示例。这避免了空引用,另外还从一开始就将必要的存储内存保持相同的大小,从而在您第一次序列化时就已经保留了所需的存储内存

SaveManager.manager.levelData.Add(nextLevelData);
宁愿成为

SaveManager.manager.levelData[stageID, levelID].unlocked = true;
不创建LevelData的新实例

将成为

SaveManager.manager.levelData[stageID, levelID].Completed
优点:

您可以轻松地迭代此数组并检查值

在此之前,我建议使用两个输入StageID和LevelID。由于在多维数组中,所有条目都是长度相同的数组,因此可以使用一个平面索引轻松计算这两个值:

// EXAMPLE FOR INITIALLY FILLING THE ARRAY
for(var i = 0; i < StageAmount * LevelsPerStageAmount; i++)
{
    // using integer division
    var stageID = i / LevelsPerStageAmount;

    // using modulo 
    // (starts over from 0 after exceeding LevelsPerStageAmount - 1)
    var levelID = i % LevelsPerStageAmount;

    var newLevelEntry = new LevelData;

    newLevelEntry.Completed = false;
    newLevelEntry.stars = -1;
    newLevelEntry.time = -1;
    newLevelEntry.unlocked = false;

    SaveManager.manager.levelData[stageID, levelID] = newLevelEntry;
}
所以你仍然可以使用一个整体的平面指数-你只需要记住如何计算它

在内存中,这样的数组仍然是平面格式,因此可以简单地:

注意:对于以后添加更多级别和阶段的更新,您可能需要将存储的数据反序列化到临时数组中,然后将临时数组中的值复制到您现在实际更大的数组中

什么对字典不利


没有反序列化词典的标准方法。通常必须将键和值序列化为单独的列表。可以在两个不同的文件中或使用自定义类型执行此操作。然后,您可以决定是存储键值对列表,还是存储两个键值列表。无论哪种方式,这都有点麻烦。

二进制数据是一个字节[]。字典的值可以是字节[]。在您的案例中,字典的键可以是一个字符串,它是级别和属性。您可能需要枚举级别A、枚举级别A=1、枚举级别B=2、枚举级别C=3、枚举级别D=4,它们可以有一个名称,并且可以将值转换为数字,以便您可以比较代码中是否大于某个级别。谢谢,我将尝试一下。我想知道为什么在许多关于相似主题的帖子中,他们总是反对连载词典。哇,非常感谢你的解释!我甚至不知道这是可能的。还有一个问题,因为您提到了在以后添加阶段和级别,我可能会在发布后这样做。这种解决方案是否能够处理具有不同级别的阶段?例如,如果我有一个特殊的阶段,与其他阶段不同,具有提供独特挑战的级别,它可能只有少数而不是40左右。您提到它们的长度相同,所以我想知道在这种情况下保留的(尽管未使用的)空间是否会太多。您可以在独立于数组的第二个列表中序列化它们,或者在当前数组中使用一个常用项,但不使用其余元素
SaveManager.manager.levelData[stageID, levelID].Completed
// EXAMPLE FOR INITIALLY FILLING THE ARRAY
for(var i = 0; i < StageAmount * LevelsPerStageAmount; i++)
{
    // using integer division
    var stageID = i / LevelsPerStageAmount;

    // using modulo 
    // (starts over from 0 after exceeding LevelsPerStageAmount - 1)
    var levelID = i % LevelsPerStageAmount;

    var newLevelEntry = new LevelData;

    newLevelEntry.Completed = false;
    newLevelEntry.stars = -1;
    newLevelEntry.time = -1;
    newLevelEntry.unlocked = false;

    SaveManager.manager.levelData[stageID, levelID] = newLevelEntry;
}
// For example if you want to keep a continues level name
var overallFlatIndex = stageID * LevelsPerStageAmount + levelID;
using System.Runtime.Serialization.Formatters.Binary;  

...

LevelData[,] levelData = new LevelData[X,Y]{ .... };
BinaryFormatter bf = new BinaryFormatter();  
MemoryStream ms = new MemoryStream();  
bf.Serialize(ms, levelData);