I have a fairly complex game which involves a lot of ScriptableObject instances at runtime. I have one ScriptableObject which manages all of these instances. I was hoping that I could somehow save the game state by just saving this managing object, using BinaryFormatter, but I only get this error:
SerializationException: Type UnityEngine.ScriptableObject in assembly UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null is not marked as serializable.
Here’s how I’m doing it:
[System.Serializable]
public class GameData : ScriptableObject {
// ... other stuff
public static void Save(GameData data) {
Debug.Log("Saving game...");
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(SavePath);
bf.Serialize(file, data);
file.Close();
}
}
No, you can’t. The ScriptableObject can’t be serialized with the BinaryFormatter. If you want to use the BinaryFormatter you have to use normal classes (not derived from anything (except System.object)). Normal classes can have the Serializable attribute and you can serialize them with the normal .NET serialization system. However, almost all Unity types are not supported as non of them are marked as serializable, not even the Vector3 or Color structs.
One solution is the Unity serializer originally by Mike Talbot (aka WhyDoIDoIt). He implemented a serialization system to serialize whole scenes including gameobjects, scripts, custom classes. However it doesn’t seem to be continued anymore.
Since around Unity 5.3 (i think) we have the JsonUtility which can serialize almost the same things Unity can serialize at edit time. This includes most Unity valuetype types like Vector2/3/4, Quaterions, Color values, … However it can’t serialize “references” as JSON doesn’t even have a concept for that. It also can’t serialize UnityEngine.Object derived classes with the exception of ScriptableObjects. In addition it’s possible to use “FromJsonOverwrite” to deserialize and overwrite the values of MonoBehaviour components.
You can create and inner serilializable class to hold you data:
public abstract class Variable<T> : BaseVariable {
[Serializable]
private class VariableData<TV> : VariableData
{
public TV Value;
}
public T DefaultValue;
public T RuntimeValue { get; set; }
protected void OnEnable()
{
RuntimeValue = DefaultValue;
}
public override VariableData GetData()
{
return new VariableData<T>
{
Value = RuntimeValue
};
}
public override void LoadFromData(VariableData data)
{
RuntimeValue = ((VariableData<T>) data).Value;
}
}
I use serialize objects for saving data I’m going to use in my game. one holds a list of all weapons with their associated game object, mesh, material and am icon I’m going to implement into gui. theoretically you can make your levels out of scripts le objects. anything that you want. it can be altered during runtime and saved to JSON and or binary like any other script that is serialized. it’s great for things you want to be the same everytime you load your game…
Put all your scriptable objects into a list that you want to serialize and save, then make a function and a serializable public class that will take all the variables from those scriptable objects, use a forloop to loop trough the scriptable object array and store each variable in a variable array within the public serializable class. Then save the variable arrays in a file. Let me lay out the code, it’s easier to explain that way.
public class LoadSaveFunctions : MonoBehaviour
{
//===============================================
//Item = your Scriptable object
//inventory == your array of scriptable objects
//===============================================
//You'll have to put scriptable objects into inventory[]
//I don't know your specific setup, for this i'll use my
//own scriptable obj called "Item"
//===============================================
public List<Item> inventory;
public int maxItems = 50;
void DefineScriptableOBJArray()
{
inventory[0] = "Your scriptable obj";
inventory[2] = "Your scriptable obj";
inventory[3] = "Your scriptable obj";
//........how ever many you want to define
}
//===============================================
//Below is, Saving each classes variables from the inventory array
//into a file.
//===============================================
public void SaveSlot()
{
BinaryFormatter Ibf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/Inventory1.dat");
InventorySaver toSave = new InventorySaver();
//Now loop, and save each scriptable objects variables
//into the serialized class's variable arrays
for (int i = 0; i < inventory.Count; i++)
{
if (inventory*.itemName != null)*
//=============================================== //Below is, Loading each class variables from save file //into your inventory array. //=============================================== private void LoadSlot() { if (File.Exists(Application.persistentDataPath + “/Inventory1.dat”)) { BinaryFormatter bf = new BinaryFormatter(); FileStream file = File.Open(Application.persistentDataPath + “/Inventory1.dat”, FileMode.Open); InventorySaver toLoad = (InventorySaver)bf.Deserialize(file); file.Close();
//Now loop, and load each item variables, from the //saved variable arrays for (int i = 0; i < inventory.Count; i++) { if (toLoad.saveitemName != null) { inventory_.itemName = toLoad.saveitemName*; inventory.itemID = toLoad.saveitemID; inventory.itemDam = toLoad.saveitemDam; }*_
if (i == maxItems) { break; } } } } }
//===================================== //Below is, The public serializable class I said to add //NOT in Mono. This will be the variable arrays that you will //serialize & save/load //=====================================
[System.Serializable] public class InventorySaver { //size of array = maxItems public string[] saveitemName = new string[50]; public int[] saveitemID = new int[50]; public float[] saveitemDam = new float[50]; }