There is too much information that is missing in your question to give you a specific answer. so I’ll just post a checklist.
Since you are likely using (or should be using) Unity’s Serialization:
1) are you trying to serialize a user-defined struct?
don’t do it, turn the structs you plan to serialize into a classes cause the serializer simply doesn’t support it.
2) does the class inherit from ScriptableObject?
while you can make regular classes serializable, since you’re trying to use references and Lists you WANT to inherit from scriptableObject as it’ll properly keep the right connections.
3) Is the class the only class in the file?
Due to a quirk in the Serializer this is essential if you’re messing with polymorphic classes. not doing so will simply make it not work and it has aggravated tons of people before you into wasting hours into figuring why it doesn’t work for them. if you’re not using polymorphism then its not required, but I’d do it anyway.
4) does the class have a zero argument constructor? and are you only initalizing struct types in it?
this is why you can’t really serialize Monobehaviors since Monobehavior doesn’t let you define a constructor. Since the class is inheriting from ScriptableObject you should never call the constructor, call CreateInstance. The Constructor is called by Unity’s Serializer when its deserializing the data back into the application
5) does the class have an OnEnable()? and are you initalizing your serialized reference types in it?
after the constructor is called the serializer will populate all the values it was storing this is why you don’t initialize the reference types (any class references) in the constructor as the serializer is gonna overwrite it anyway and it would simply be wasted performance.
void OnEnable()
{
if( playerParty == null )
playerParty = new PlayerParty();
}
This way if the serializer had nothing to pass in we assume its because its a new object and so we initialize its data then.
6) does the class have a variable that can (directly or indirectly) reference its own type? can that variable be null?
consider the class:
[Serializable] public class LinkedListNode : ScriptableObject
{
[SerializedField] public string nodeName;
[SerializedField] public LinkedListNode previous;
[SerializedField] public LinkedListNode next;
LinkedListNode()
{
nodeName = "";
}
void OnEnable()
{
if(previous == null)
previous = ScriptableObject.CreateInstance<LinkedListNode>();
if(next == null)
next = ScriptableObject.CreateInstance<LinkedListNode>();
}
}
I just made an infinite recursion loop (sorta, eventually Unity would just give up). when the serializer loaded and if a node happened to be null… well by using the rules listed before you would simply create a new instance. the problem is that “previous” will then call it’s OnEnable() and previous.previous is null… so we create a new instance, but previous.previous.previous is… you get the picture. If you come across a self-reference where null is a valid value, then leave it null.
7) does the object have a class referencing it?
So far I’ve gave a checklist to follow so that when you try to save and load the data it does so correctly. Now you need make sure it’s referenced so that the garbage collect doesn’t delete it. either store the reference in a gameobject with a dontdestroyonload, or in code have it create an asset and save the asset via the AssetDatabase class (preferbly in the Resources folder so that the data is easy to load at runtime).
As an asset file
next I reccomend that you split the class that is to manage the data persistence (a GameMode class) from your gamecontroller class. The GameMode will be the same instance throughout the entire progression of the game, but controllers are usually more specific to the scene they are in. for example you usually aren’t using the same controllers in the menu or level select scenes as in the main game, but all those scenes may need information from the GameMode.
Finally the first half of my answer was about all the hoops you need to jump through to get Unity’s serialization to work and with the amount of support serializing ScriptableObject provide its usually worth the effort. Now one thing you should be aware of is the one major limitation to Unity’s Serialization system.
You can’t use Unity’s Serializer to save in the deployed build.
All the functions you’d used to save data with unity’s serialization are only availiable in the Editor namespace which is unavailable in a built game. as such while you can edit data in the gamemode you can’t save it during runtime. so its better to use the gamemode more as a blueprint of should be or always happen
So my typical solution (which I’ve already set you up for ) is to use the best of both worlds and save the path of the Gamemode asset that resides in the Resources folder. when you’re saveload class saves it simply serializes the path, (not the actual gamemode instance just the path ), not only is it less of a headache to program in .Net’s serialization its also has much better performance. And when you load you deserialize the string and run it through the Resources.Load and you have your GameMode settings back. If you need to save collection through .Net’s Serialization, Concrete arrays work just best.