For my new game I'd like to have a system in which I have several stages, and each of them has a bunch of levels.
I thought I could have the next stage be unlocked once all the previous stage's levels are cleared, but it doesn't really fit the type of game.
The problem is that I cannot figure out a way to have the levels unlock sequentially within the stage (stage1_1, stage1_2...) without using multiple save files, as the list would go out of order after unlocking a level from another stage. I am currently storing them as a list. Here is how it's currently set up:
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
}
Then, when a level is completed:
// only unlock the next level if it's not been unlocked yet
if (SaveManager.manager.levelData.Count - 1 == id)
SaveManager.manager.levelData.Add(nextLevelData);
SaveManager.manager.SaveLevelData();
And to access them from the menu:
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();
}
And that's my situation. It's a little (too) messy, I did not realize the issue with unlocking levels non-sequentially, so my only solution at this point would be to create different variables in SaveManager (stage1_levelData, stage2_levelData...), but it doesn't sound efficient. I already rewrote it twice and really ran out of ideas at this point.
Also as far as I understand dictionaries cannot be binary serialized, am I correct?
Hopefully someone can point me to the right direction :) Thanks in advance!