繁体   English   中英

Unity3D 保存或加载每个关卡及其自己的变量

[英]Unity3D Save or Load each Level and their own variables

我是 stackoverflow 和 Unity3D 的新手,所以如果我做错了,我很抱歉。

所以目前,我正在制作一款益智游戏。 它有50个不同的级别。 我需要为他们每个人保存 3 或 4 个变量。 例如,当级别 1 被清除时,我希望它存储 (int)hitCounts、(bool)cleared、(int)bestHitCounts。 我不想使用 playerPrefs,因为我不希望它可以从盒子外面读取。 我希望将其转换为二进制文件。 这是我所拥有的:

#1:制作了一个 static class TGameDat

[System.Serializable]
public class TGameDat 
{
    public int tGameDatInt;
    public bool tGameDatBool;
    public int tSceneIndex;

    public TGameDat (TPlayer player)
    {
        tGameDatInt = player.tInt;
        tGameDatBool = player.tBool;
        tSceneIndex = player.tScene;
    }
}

#2:然后制作 Tplayer(monobehaviour)



public class TPlayer : MonoBehaviour
{
    public int tInt = 0;
    public bool tBool = false;
    public int tScene;
    public List<TPlayer> TestGameDatList = new List<TPlayer>();

    private void Start()
    {
        TSceneMaker();
    }

    public void TSceneMaker()
    {
        tScene = SceneManager.GetActiveScene().buildIndex;
    }

    public void TNextScene()
    {
        SceneManager.LoadScene(tScene + 1);
    }

    public void TPreviousScene()
    {
        SceneManager.LoadScene(tScene - 1);
    }

    public void TSaveVariables()
    {
        TSave.TSavePlayer(this);
        TestGameDatList.Add(this);
        Debug.Log("saved");
        Debug.Log(tInt + " " + tBool + " " + tScene);
    }

    public void TLoadVariables()
    {
        List<TGameDat> data = TSave.TLoadPlayer(this);
        Debug.Log("loaded. data count = " + data.Count + " tSceneIndex " + tScene);

                tInt = data[0].tGameDatInt;
                tBool = data[0].tGameDatBool;
                tScene = data[0].tSceneIndex;
    }
}

#3:最后我创建了一个保存和加载系统:


public static class TSave
{
    public static void TSavePlayer (TPlayer player)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        List<TGameDat> data = new List<TGameDat>();
        string path = Application.persistentDataPath + "/Tsave_" + player.tScene + ".fun";

        if(File.Exists(path))
        {
            FileStream stream = File.Open(path, FileMode.Open);
            data.Add(new TGameDat(player));
            formatter.Serialize(stream, data);
            stream.Close();
        }
        else
        {
            FileStream stream = File.Create(path);
            data.Add(new TGameDat(player));
            formatter.Serialize(stream, data);
            stream.Close();
        }
    }

    public static List<TGameDat> TLoadPlayer(TPlayer player)
    {
        string path = Application.persistentDataPath + "/Tsave_" + player.tScene + ".fun";
        if(File.Exists(path))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            FileStream stream = File.Open(path, FileMode.Open);
            List<TGameDat> data = new List<TGameDat>();
            
            data = formatter.Deserialize(stream) as List<TGameDat>;
            stream.Close();
            return data;
        }
        else
        {
            Debug.LogError("Save file not found in " + path);
            return null;
        }
    }
}

所以,这是我的问题:

1:当前情况下,每个场景编译一个二进制文件。 所以最后会堆积一堆二进制文件……好像50个,我有50个场景……是不是太多了?

2:当然我试过用List制作一个单独的保存文件,每个级别都会在里面添加自己的变量数据。 但它不会添加数据,而是简单地替换以前的数据。 那么 List 中总是只有 1 个索引。 因此,当我加载时,变量来自上次播放的关卡,而当我在玩完第一个关卡后尝试玩另一个关卡时,因为列表中只有 1 个索引。 我超出范围错误。

我该如何处理? 对不起,很长的文字!

感谢您的投入!

首先,你真的需要每个关卡都有自己的存档数据吗? 因为如果您只需要存储一个级别和另一个级别之间的数据,我建议您使用某种 PlayerState class 来存储上一级别的数据。 但是如果你真的需要存储每个级别的数据,那么我建议你使用字典而不是简单的列表。

这是我将如何做的一个例子
注意:我还没有测试过这段代码!

SaveGameManager class

public string SavePath => Application.persistentDataPath + "/save.dat";

public static SaveGameManager Instance; // Singleton pattern
private Dictionary<string, TGameDat> gameData;

private void Awake()
{
    if (Instance != null)
    {
        Destroy(this.gameObject);
        return;
    }

    // Singleton initialization
    Instance = this;
    // Keep the object when changing scenes
    DontDestroyOnLoad(this.gameObject);
    LoadGameData();
}

public TGameDat GetGameData(string key)
{
    if (gameData.TryGetValue(key, out TGameDat data))
    {
        return data;
    }

    Debug.Log($"Unable to find saved data with key {key}");
    return null;
}

public void SetGameData(string key, TGameDat data)
{
    // Sets a value with given key and save it to file
    gameData[key] = data;
    SaveGameData();
}

public void SaveGameData()
{
    Serializer.SaveBinaryFile(gameData, SavePath);
}

public void LoadGameData()
{
    var savedData = Serializer.LoadBinaryFile(SavePath);
    if (savedData != null)
    {
        gameData = (Dictionary<string, TGameDat>)savedData;
    }
    else
    {
        // Creating and saving new data because we can't found 
        // any that already stored in path
        gameData = new Dictionary<string, TGameDat>();
        SaveGameData();
    }
}

然后,串行器 class

public static void SaveBinaryFile(object data, string path)
{
    using (var stream = new FileStream(path, FileMode.Create))
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(stream, data);
    }
}

public static object LoadBinaryFile(string path)
{
    if (!File.Exists(path))
    {
        // Trying to load a file that does not exist
        return null;
    }

    using (var stream = new FileStream(path, FileMode.Open))
    {
        var formatter = new BinaryFormatter();
        return formatter.Deserialize(stream);
    }
}

然后你可以像这样使用它

public TGameDat data;

public void TSaveVariables()
{
    SaveGameManager.Instance.SetGameData(level.id, this.data);
}

public void TLoadVariables()
{
    var savedData = SaveGameManager.Instance.GetGameData(level.id);
    if (savedData != null)
    {
        this.data = savedData;
    }
    else
    {
        // We don't have any save file for this level yet
        savedData = new TGameDat();
    }
}

您可以将level.id更改为您想要使用的任何标识符。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM