- Home /
The question is answered, right answer was accepted
Serialization failed.
I used the following code to save and then load data. The save works well and the highScore ends up in savedGameData:
{
savedGameData.highScore = gameController.GetComponent<GameController>().highScore;
}
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/saveGame.sav");
bf.Serialize(file, savedGameData);
file.Close();
}
The load part does not because after deserialization, highScore is 0. I confirmed that it is also 0 in savedGameData when I attached the code to Unity.
if (File.Exists(Application.persistentDataPath + "/saveGame.sav"))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/saveGame.sav", FileMode.Open);
GameStats savedGameData = (GameStats)bf.Deserialize(file);
file.Close();
}
The code also includes:
public GameStats GetSavedGameData()
{
return savedGameData;
}
}
[System.Serializable]
public class GameStats
{
public float highScore;
}
What did I do wrong here?
Answer by Bunny83 · Apr 24, 2020 at 05:43 AM
Well, Are you actually sure about what value you saved? Can you share your "saveGame.sav" file?
Once again I highly recommend to not use the BinaryFormatter for such things. The generated file for such trivial data has huge overhead. Most who choose the BinaryFormatter just want a "non-readable" / not modifiable save. Both is no achieved that way. It's a little bit harder but quite trivial. I highly recommend to just use a human readable format like JSON and if you want to make it non readable, just use a simple "scramble" / "unscramble" once you're ready to ship your game. You could simply use base64 encoding which makes it equally difficult to read / modify. However a simple xor of the data with a fix key would be much more difficult to break than the BinaryFormatter. That way it would make debugging much easier since you can simply disable the obfuscation until you actually ship the game.
The BinaryFormatter uses a well known format (the MS remoting protocol). It's in general not hard to read or modify. It's also quite verbose. But the biggest issue is that it's very strict and doesn't allow any change in the structure of the saved data. So a new version where you changed some fields of your class would completely break older saves.
Regardless if you really want to stick to it you should first make sure what value you actually serialized. Again, if you can share a sample saveGame.sav file I can tell you what value is stored inside.
Hi,
I am sending the saveGame file. Now that you mention it, I don't think it saved anything because when I opened it with notepad I did not see any numbers that would represent the highScore. But I do know that the highScore did get written into SaveGame because I saw it there when debugging. So this means that something is wrong with
{
savedGameData.highScore = gameController.GetComponent<GameController>().highScore;
}
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/saveGame.sav");
bf.Serialize(file, savedGameData);
file.Close();
}
Well, as far as I can tell the highscore in the file is 300 if I'm not mistaken ^^ Though I just quickly glanced over it with a hex editor. What do you actually do with your deserialited value? In all the code you've posted so far so don't do anything with the deserialized GameStats instance. Is it possible that you have declared a variable called "savedGameData" inside your class and you just hide that variable through your local variable? So this line:
GameStats savedGameData = (GameStats)bf.Deserialize(file);
should be this line:
savedGameData = (GameStats)bf.Deserialize(file);
Though that's hard to tell since we don't see your whole script.
edit
Here's the essential part of your save file in hex and where the actual value is stored
// 05 01000000 0947616D655374617473 01000000
// | | | |
// | | | +- member count == 1
// | | +- length prefixed type name(9 characters) == "GameStats"
// | +- Object ID == 1
// +- record type 5 == Class with member and types
// 096869676853636F7265 00 0B 02000000 00009643 0B
// | | | | | +- End ot stream
// | | | | +- actual float value 0x43960000 == 300
// | | | +- library ID (was defined earlier; essentially
// | | | references "Assembly-CSharp")
// | | +- primitive type enum (11 == float)
// | +- member type (0 == primitive type)
// +- length prefixed member name (9 characters) == "highScore"
As I said, well documented and relatively easy to read, even without knowing anything about the code. The format is self describing. The whole definition of the class and it's members is in the data.
I made the correction you indicated and it works. To answer your question, highScore gets deserialized on Start and then goes to the highScore Text display.
Big thanks!