0

I have a problem with serialization, in Unity (using C#).

I have the following classes (the arrows indicate inheritance):

Vehicle (abstract) <- Car (abstract) <- Sedan

and

Vehicle (abstract) <- MotorBike

The vehicle class inherits from MonoBehavior.

I have some gameobjects (prefabs), that I attached the classed to. I only use the non abstract classes (i.e. MotorBike and Sedan).

My problem is this:

I want to serialize the data stored within these classes, to be able to save/load. (I use BinaryFormatter for this.) The problem is, MonoBehavior isn't serializable.

All I want is to serialize the data within the vehicle classes, and (upon loading the data) pass that to my Vehicle Factory class, to re-instantiate the prefabs, with the deserialized data. So, I'm not really interested in whatever is stored in the MonoBehavior part.

What I've tried so far:

Using a separate Data class, but this causes all kinds of problems and I would need to create a child data class for each of my Vehicle classes. And I would need to make the Vehicle class Generic, to indicate which Data class it needs to use, which would require me to use the Generic Type in every place I use the Vehicle type.

Also, I tried to make a SerializationSurrogate for MonoBehaviour, but this didn't work out, because there are numerous other Types that would need a surrogate. And since I'm not interested in the MonoBehaviour, but only in the Vehicle Class and it's child classes, this seemed to be too complex to try.

So, my question is: Does anybody know a way to handle this?

milosa
  • 833
  • 1
  • 8
  • 18
  • use an XmlSerializer, it does not need the class to be attributed with the serializable attribute. – Gusman Jun 02 '16 at 15:31
  • 1
    Serialization **basically doesn't work** in Unity. I would just forget about it. there's no real-world game situation where you actually need it. Just forget about it and move on. – Fattie Jun 02 '16 at 16:45
  • 1
    Programmer has perfectly explained the situation. – Fattie Jun 02 '16 at 16:45
  • 1
    You appear to be in the category "experienced programmer, trying Unity". In a sense what you are doing is totally wrong. There's no such thing as a "car" in Unity, and there never will be. Unity is incredibly not OO. Unity is more like using Photoshop than programming. Perhaps read this... http://stackoverflow.com/a/37243035/294884 – Fattie Jun 02 '16 at 16:47
  • 2
    That is true. In my studio very good c++ programmer started to work in my team as Unity3D developer - all I can say, even if his code is somehow good (you see his good programming skills), but is totaly not "Unity3D" way of coding. Using everywhere Interfaces, partial calsses definitions etc, makes unity3d project code very obscure. – Jerry Switalski Jun 02 '16 at 17:10
  • @JoeBlow Why do you say there is no real-world game situation where I would need serialization? How else am I supposed to save a game state? – milosa Jun 03 '16 at 11:32
  • Hi @user1939928 , just save it. Do what Programmer says below. I don't know how clear I can be: **Serialization basically doesn't work in Unity**. Is there something unclear about the sentence dude?! :-) If in Unity6, they finally fix serialization - I will put a message on here letting you know. OK? – Fattie Jun 03 '16 at 13:07
  • "Why do you say there is no real-world game situation where I would need serialization" Becuase there is no real-world game situation where you use serialization. Game engines ***are not OO and have utterly no connection in any way to inheritance, classes, or any ordinary programming concepts.*** There is no alignment between "stuff in classes on components" and "concepts you need to save". – Fattie Jun 03 '16 at 13:08

1 Answers1

3

I always find saving data from inherited classes hard. Your data classes should not inherit or derive from anything. A class inside another class is totally fine. The goal here is to have two classes for every vehicle. One that derives from MonoBehaviour(Object) and one that doesn't(data).

You use the Object class to perform action on the vehicles such as moving, collision decision and more. You use the data class to store information about each car/vehicle.

The example below uses Sedan to demonstrate what I am saying:

Attach the SedanObject.cs to the Sedan Prefab.

class SedanObject : MonoBehaviour
{
    float fuel;
    public void setFuel(float fuel)
    {
        this.fuel = fuel;
    }

    public float getFuel(float fuel)
    {
        return this.fuel;
    }

    void Start()
    {

    }

    void Update()
    {

    }
}

The Data class for Sedan:

[Serializable]
public class MotorBike
{

}

[Serializable]
public class Sedan
{
    public float fuel;
}

[Serializable]
public class Cars
{
    public List<Sedan> sedan;

    public Cars()
    {
        sedan = new List<Sedan>();
    }
}

[Serializable]
public class Vehicles
{
    public Cars cars;
    public MotorBike motorBike;

    public Vehicles()
    {
        cars = new Cars();
        motorBike = new MotorBike();
    }
}

At the end of the game, save the Vehicles settings. Every class/script inside Vehicles will be saved.

//Save on Exit
Vehicles vehicles = new Vehicles();
string vehicleJson = JsonUtility.ToJson(vehicles);
PlayerPrefs.SetString("vehicles", vehicleJson);
PlayerPrefs.Save();

When Game starts, load the Vehicles info from disk then instantiate Sedan Prefab based on how many sedans in the list.

//Load on Startup
string loadVehicleJson = PlayerPrefs.GetString("vehicles");
Vehicles loadVehicles;
loadVehicles = JsonUtility.FromJson<Vehicles>(loadVehicleJson);

//Process and instantiate sedan based on the loaded data amount/count
for (int i = 0; i < vehicles.cars.sedan.Count; i++)
{
    GameObject tempObjt = Instantiate(sedanPrefab);
    SedanObject sd = tempObjt.GetComponent<SedanObject>();

    //Send loaded settings to each vehicle script 
    sd.setFuel(vehicles.cars.sedan[i].fuel);
}

And if you want to add more Sedans later on:

//Create 3 Sedans
vehicles.cars.sedan.Add(new Sedan());
vehicles.cars.sedan.Add(new Sedan());
vehicles.cars.sedan.Add(new Sedan());

This is just a basic solution and can be extended. You can pass instance of each sedan data script to SedanObjectduring the for loop, just like I passed in loaded fuel to each instance. By passing each instance, information from the data script can be modified from each instance of SedanObject script during run-time. This can be done with xml too.

Community
  • 1
  • 1
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Thanks for your reply. I basically already tried this approach. The problem is, that I really need for the Data class to inherit from something. I've made an example [here](http://i.imgur.com/EuCdBvQ.jpg). When I want to store a `Sedan`, I need **all** data from the base class too. Also, the way you do it, causes duplicate code, right? I mean, you have a fuel property in both the SedanObject and SedanData classes. This wouldn't matter for one property, but it gets annoying when you have a lot of them. – milosa Jun 03 '16 at 11:24
  • 1
    Hi user, yes you have to duplicate some code. So what? Yes, your data classes can inherit as well. (Sort of in parallel.) – Fattie Jun 03 '16 at 13:04