Issue with binary deserializing on a spesific scene

Hi,
I am working on a 2D platformer game and I am having some issues with deserializing a binary file.


I have stored game-settings like volume if a binary file to preserve the data between scenes and game launches. Every time a scene starts the game reads the binary file and sets the volume.


The setup I have now worked before, but suddely I noticed that it had stopped working when I was updating the volume slider UI.


This process of changing volume, switching scenes shutting down and starting up the game works perfectly fine and preserves the volume level every time, except when exiting my level_1 scene. It seems that the file gets reset somehow, but only when I exit that one scene through a “quit to main menu” button.


If the binary formatter cant find the file it will return null and the game will set the volume to a deafult volume. I tried to diagnose the problem by sending an error message to the console if the binary formatter didn’t find the file, but I never got that error message, meaning that the file was found. But, in the receiving script that gets the deserialized data in a class, I tested If the class was null, and it was.

The volume is saved every time the volume sliders are changed and not when exiting a scene.


Does this mean that the file is there but the value somehow isn’t?


Here is a video showcasing the problem


This is the code that runs when I “exit to main menu”

The function executed when the button is pressed:

public void quitGame ()
    {
        pauseMenuUI.SetActive(false);
        Time.timeScale = 1f;
        GameIsPaused = false;
        levelManager.LoadLevelIndex(0);
    }

And the Level Manager:

public void LoadLevelIndex(int sceneIndex)
    {
        SceneManager.LoadScene(sceneIndex);
    }

And this is the Game Settings script, Constructor and Binary Formatter:

Game Settings:

using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.UI;

public class GameSettings : MonoBehaviour {

    public AudioMixer audioMixer;
    public float deafultVolume;
    public Slider masterVolumeSlider;
    public float volume;

    private void Start()
    {
        LoadGameSettings();

        if (masterVolumeSlider != null)
        {
            masterVolumeSlider.value = volume;
        } else
        {
            audioMixer.SetFloat("volume", volume);
        }
    }

    public void SetVolume (float newVolume)
    {
        audioMixer.SetFloat("volume", newVolume);
        volume = newVolume;
        SaveGameSettingsData();
    }

    public void LoadGameSettings()
    {
        GameSettingsConstructor data = SaveGameSettings.LoadGameSettingsData();

        if (data != null)
        {
            volume = data.volume;
            Debug.Log(data.volume); -------This outputs the volume level every time a scene loads exept the main menu when loaded form Level_1
        }
        else
        {
            volume = deafultVolume;
            Debug.Log("class is null"); -----This runs every time the main menu is loaded form Level_1
        }
    }

    public void SaveGameSettingsData()
    {
        SaveGameSettings.SaveGameSettingsData(this);
    }

}

Constructor class:

using UnityEngine;

[System.Serializable]
public class GameSettingsConstructor
{

    public float volume;

    public GameSettingsConstructor (GameSettings game)
    {
        volume = game.volume;
    }

}

Binary Formatter:

using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public static class SaveGameSettings
{

    public static void SaveGameSettingsData(GameSettings game)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        string path = Application.persistentDataPath + "/game.data";
        FileStream stream = new FileStream(path, FileMode.Create);

        GameSettingsConstructor data = new GameSettingsConstructor(game);

        formatter.Serialize(stream, data);
        stream.Close();
    }

    public static GameSettingsConstructor LoadGameSettingsData()
    {
        string path = Application.persistentDataPath + "/game.data";
        if (File.Exists(path))

        {
            BinaryFormatter formatter = new BinaryFormatter();
            FileStream stream = new FileStream(path, FileMode.Open);

            GameSettingsConstructor data = formatter.Deserialize(stream) as GameSettingsConstructor;
            stream.Close();

            return data;
        }
        else
        {
            Debug.Log("Save file not found in " + path); ------------This message never shows
            return null;
        }
    }

}

This was not easy to explain, but hopfully you understand.
Thanks in advance for any answers!

I’m not 100% sure but as far as i remember the BinaryFormatter (as well as any .NET serialization class) requires the serialized classes to have a public parameterless constructor. Your class “GameSettingsConstructor” doesn’t have one.

Note that this is probably a C# detail you might not know. A class that has no explicit constructor declared, always has a parameterless constructor by default. However as soon as you create any explicit constructor, the implicit constructor will no longer be available. Maybe in the past you didn’t have any constructor and once you added your constructor the deserialization doesn’t work anymore.

Apart from those things i wouldn’t recommend using the BinaryFormatter for pretty much anything. The format is strictly bound to the actual class type, assembly and its fields. Any changes to the class would break any already serialized data. Some use the binary formatter because they think it’s a more secure and / or compact format. Though this isn’t true. The format (the .NET remoting protocol) is well known and specified, so there is no security involved. It’s also a quite verbose format. It only pays off when you have a lot similar data. For just a few values the file is about 10 times larger than the actual data.

In most cases there are other solutions. For really small data you may just want to use the BinaryWriter / BinaryReader to manually write / read your data to a file. You are in full control what you write to the file. The most flexible solution is to serialize your data as JSON. You can use Unity’s JsonUtility for this. It’s much more robust against changes to the data (addition / removal of fields).

Finally I’d like to mention that “GameSettingsConstructor” is a horrible and confusing class name ^^. It should be either GameSettingsData or GameSettingsWrapper or something similar.