Hello. I am trying to build a safe game functionality. I am able to serialize a serializable class I wrote, but I am not able to figure out how to serialize that class, it the class has child object.
When I run the save method, this line: info.AddValue(“Coins”, saveObject.playerInventory.coins);
produces a null reference exception (saveObject.playerInventory is null). If I exclude the playerInventory object from the save, it works fine. I have found examples of how to do this when it is just one object, but I have been unable to find any examples of nested objects. Can this be done? Am I going about this the wrong way?
Here are my classes:
[CreateAssetMenu(fileName = "New Save Object", menuName = "Save Object")]
[System.Serializable]
public class SaveObject : ScriptableObject
{
public Inventory playerInventory;
public string sceneName;
}
[CreateAssetMenu(fileName = "New Inventory", menuName = "Inventory/Inventory")]
[System.Serializable]
public class Inventory : ScriptableObject
{
public Item currentItem;
public List<Item> Items = new List<Item>();
public int coins;
public FloatValue PlayerHealth;
public FloatValue PlayerMana;
public FloatValue PlayerStrength;
public FloatValue PlayerXP;
public IntegerValue PlayerLevel;
public FloatValue PlayerGold;
public PlayerPosition PlayerPostion;
public void RemoveInventoryItem(Item item)
{
Items.Remove(item);
}
public void addCoins(int coin)
{
coins = coins + coin;
}
public void removeCoins(int coin)
{
coins = coins - coin;
}
public void AddHealth(int points)
{
if (PlayerHealth.RuntimeValue < PlayerHealth.MaxValue)
{
PlayerHealth.RuntimeValue += points;
}
else
{
PlayerHealth.RuntimeValue = PlayerHealth.MaxValue;
}
}
public void AddStamina(int points)
{
if (PlayerStrength.RuntimeValue < PlayerStrength.MaxValue)
{
PlayerStrength.RuntimeValue += points;
}
else
{
PlayerStrength.RuntimeValue = PlayerStrength.MaxValue;
}
}
public void AddMana(int points)
{
if (PlayerStrength.RuntimeValue < PlayerStrength.MaxValue)
{
PlayerStrength.RuntimeValue += points;
}
else
{
PlayerStrength.RuntimeValue = PlayerStrength.MaxValue;
}
}
}
And my serialize object:
public class SavePlayer : Singleton<SavePlayer>
{
[SerializeField] public SaveObject playerSaveObject;
public void SaveGameState(string saveName, string sceneName)
{
string SaveFolder = Application.persistentDataPath + "/saves/";
string SavePath = SaveFolder + saveName + ".save";
BinaryFormatter formatter = GetBinaryFormatter();
if (!Directory.Exists(SaveFolder))
{
Directory.CreateDirectory(SaveFolder);
}
FileStream file = File.Create(SavePath);
playerSaveObject.sceneName = sceneName;
formatter.Serialize(file, playerSaveObject);
file.Close();
}
public void LoadGameState(string SavePath)
{
if (!File.Exists(SavePath))
{
Debug.LogErrorFormat("File does not exist {0}", SavePath);
}
BinaryFormatter formatter = GetBinaryFormatter();
FileStream file = File.Open(SavePath, FileMode.Open);
try
{
object save = formatter.Deserialize(file);
SaveObject saveObj = (SaveObject)save;
playerSaveObject.sceneName = saveObj.sceneName;
}
catch (Exception ex)
{
Debug.LogErrorFormat("Failed to load file at {0}", ex);
}
finally
{
file.Close();
}
}
public BinaryFormatter GetBinaryFormatter()
{
BinaryFormatter formatter = new BinaryFormatter();
SurrogateSelector surrogateSelector = new SurrogateSelector();
SavePlayerObjectSerializationSurrogate SavePlayerObjectSerializationSurrogate = new SavePlayerObjectSerializationSurrogate();
surrogateSelector.AddSurrogate(typeof(SaveObject), new StreamingContext(StreamingContextStates.All), SavePlayerObjectSerializationSurrogate);
formatter.SurrogateSelector = surrogateSelector;
return formatter;
}
}
public class SavePlayerObjectSerializationSurrogate : ISerializationSurrogate
{
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
SaveObject saveObject = (SaveObject)obj;
info.AddValue("scene_name", saveObject.sceneName);
info.AddValue("coins", saveObject.playerInventory.coins);
}
public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
SaveObject saveObject = (SaveObject)obj;
saveObject.sceneName = (string)info.GetValue("scene_name", typeof(string));
saveObject.playerInventory.coins = (int)info.GetValue("coins", typeof(int));
return obj;
}
}