Unity3D保存或加载每个级别及其自己的变量



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

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

#1:制作了一个静态类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(单行为(


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创建一个单独的保存文件,每个级别都会在其中添加自己的变量数据。但它不会添加数据,而是简单地替换以前的数据。那么列表中总是只有一个索引。因此,当我加载时,变量来自上次播放的级别!当我在玩完第一关后尝试玩另一关时,因为列表中只有1个索引,所以我会出现超出范围的错误。

我该如何处理?抱歉发了这么长的短信!

感谢您的投入!

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

下面是我将如何执行此操作的示例
注意:我还没有测试此代码

SaveGameManager类

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();
}
}

然后,Serializer类

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更改为想要使用的任何标识符。