问题描述
我是 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(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 制作单个保存文件,每个级别都会在其中添加自己的变量数据。 但它不会添加数据,而是简单地替换以前的数据。那么列表中总是只有 1 个索引。 因此,当我加载时,变量来自上次播放的关卡!当我在玩完第一关后尝试玩另一关时,因为列表中只有 1 个索引,我得到了超出范围的错误。
我该如何解决这个问题? 抱歉,长篇大论!
感谢您的投入!
解决方法
首先,您真的需要每个级别都有自己的保存数据吗?因为如果您只需要在一个级别和另一个级别之间存储数据,我建议您使用某种 PlayerState 类来存储前一级别的数据。 但是,如果您确实需要存储每个级别的数据,那么我建议您使用 dictionary 而不是简单的列表。
这是我将如何做的一个例子
注意:我还没有测试过这段代码!
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();
}
}
然后是序列化器类
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
更改为您想要使用的任何标识符。