I’m working on a game where every vehicle has a number of modular components that can be equipped on it, which do anything from adding cargo capacity, placing weapons to the car’s body, or adding passive stat bonuses. Ordinarily I would do this by making some kind of base VehicleComponent class, and making every category of unique vehicle component its own child class. However, in messing around with the new JSON serializer I’ve found that unity still isn’t that great at serializing inheritance, and all my child classes are loaded in as their equivalent base class.
This seems like a gigantic problem, since inheritance hierarchies are incredibly useful, so I was wondering if there’s an established approach to getting around this that I’m unaware of? The best idea I’ve had thus far is to create one giant SerializedComponent struct that contains every possible piece of data that a component might have (payload info for freight components, weapon stats for weapon components, stat tables for boost components, etc), add an enumeration that specifies which type of component it is, and add some logic to my save/load game functions that look at the enumeration, create a new child class of the correct type, and populate it with only the specific parts of its SerializedComponent’s data that it wants. It feels really clumsy and awkward, but is that the general direction I want to be heading in?
You can’t rely on Unity’s serialization for anything. If you’re planning to have a save system, you have to design from the ground up with it in mind. This means maintaining a serializable structure that doesn’t rely on Unity’s serialization. That means not putting all your object data/logic in MonoBehaviours. Unfortunately, there’s no easy solution, especially if you’re retrofitting a saved game system onto an existing framework. In the past, when I’ve had to retrofit like that, I’ve practically had to tear my designs apart and start from scratch.
There’s a few different options for serialization out there for C#. I use SharpSerializer, which is fast and deals well with circular references and polymorphism. There’s no solution out there that I’m aware of that will safely serialize a GameObject and all its components, and even if there was, just taking a snapshot of the entire object isn’t the correct way to build a save file; it’ll create huge saves.
Then your problem just becomes giving that class the information to build its Unity representation when it’s loaded. I typically just give it a path to a prefab to instantiate.
Assuming I’m going for the simplest possible solution that won’t explode into a million pieces, does the following sound like a reasonable way to do it? Given the following example containers:
[System.Serializable]
public struct VehicleUpgrade
{
public string moduleName;
public float cargoCapacity;
public ECargoType cargoType;
public List<StatImprovement> statBoosts;
}
[System.Serializable]
public struct VehicleData
{
public string vehicleName;
public float speed;
public float acceleration;
}
[System.Serializable]
public struct SerializableData
{
MapData[100, 100] worldMap;
VehicleData playerVehicle;
}
Since everything is in a struct (and thus, doesn’t require references to gameobjects/monobehaviours to be retained), and since inheritance is never used, do you see any problems with expanding that logic to everything that needs to be retained, converting SerializableData to/from a JSON file to be retained in the game folder, and making the real workhorse some SavegameManager class whose job it is to generate the correct gameobjects/monobehaviours from data, and convert the same back to data?
I think you have some misunderstandings about the differences between a struct and a class. Neither one requires a reference to anything in the UnityEngine realm at all to be retained. There’s also no reason to not use inheritance. What you have will work as a basic structure, although expanding what objects you can save would mean having to correspondingly expand your SaveGameManager class. I would spend some time prototyping different approaches.
Hmm okay, thank you for clarifying! It’s likely incorrect, but for some reason I was under the impression that Unity couldn’t retain any classes unless they derived directly from monobehaviour or serializableobject, so sticking with the structs was a way to get around that by dumping everything into a value type