Saving Unique Instances of Classes

Discussion and help for Easy Save 3
Post Reply
norbs
Posts: 2
Joined: Thu Oct 06, 2022 5:43 pm

Saving Unique Instances of Classes

Post by norbs »

Hi, ES3 has been working great for us so far, thank you for all your hard work on it!

My question is about storing specific instances of classes, we noticed some behavior that we keep running up against and not sure if there's some other solution we might not know about.

We're making a city builder game, so you can imagine we have a 2D array storing instances of a 'Cell' class, which holds data about the type of building on any given point on the map.

Now imagine we have workers who get assigned to work on a cell that might be a farm. So we set a Cell member variable on that worker to point to the instance of the Cell object that is in our 2D grid array.

Now we save both the grid and the workers. After we load the game, I notice that doing a comparison of objects between the Cell in the grid array and the Cell ref stored in the person are not equal. e.g. `worker.farm == grid.getcell(0,0)` would return false if the farm they work at is in grid 0,0. But that statement returned true before we saved the game.

It makes sense to me that when loading, ES3 just looks and sees that the worker has a Cell and it has some data, so it creates a new instance of a Cell for them and sets it on them. But it would be amazing if we had a way on load for the worker to get the same copy of the Cell instance that's in the grid.

We're working around this for now by doing a bunch of lookups with grid coordinates and conversion back and forth but we keep running into this issue (this is just one example).

Thanks in advance for your help!
User avatar
Joel
Moodkie Staff
Posts: 4849
Joined: Wed Nov 07, 2012 10:32 pm

Re: Saving Unique Instances of Classes

Post by Joel »

Hi there, and thanks for getting in contact.

This is because it's not possible to get a persistent reference to a non-UnityEngine.Object class because it's generated at runtime, meaning the instance will change between sessions.

Generally the only way around this is to find some way of uniquely referencing your Cells. The way that Unity suggests is the same approach as you're taking at the moment. I.e. making fields store the grid coordinates rather than a reference to the object itself.

Another approach which is possible with Easy Save (and may or may not be easier for you), is to only perform this conversion at the point serialization/deserialization. This way you wouldn't need to change any fields or how you access those Cells.

To do this you can create an ES3Type which gets the reference rather than creates a new instance. The process for this would be as follows:
  1. Go to Tools > Easy Save 3 > Types, search for your Cell class and select Create ES3Type Script.
  2. Go to Assets/Plugins/Easy Save 3/Types/, and open the ES3UserType_Cell.cs script which was created here.
  3. Modify the protected override void WriteObject(object obj, ES3Writer writer) method so that it looks up the Cell in your grid array and writes the X,Y coordinates.
  4. Modify the protected override object ReadObject<T>(ES3Reader reader) method so that it reads the X,Y coordinates and then gets the Cell at those coordinates in your grid array, or creates a new instance if it doesn't exist.
These two methods might look something like this:

Code: Select all

protected override void WriteObject(object obj, ES3Writer writer)
{
    var instance = (Cell)obj;
    
    var grid = GetGrid();
    int x, y;
    grid.GetCoordinates(instance, out x, out y);

    writer.WriteProperty("x", x);
    writer.WriteProperty("y", y);
    writer.WriteProperty("myField", instance.myField, ES3Type_int.Instance);
    writer.WriteProperty("myOtherField", instance.myOtherField, ES3Type_string.Instance);
}

Code: Select all

protected override object ReadObject<T>(ES3Reader reader)
{
    int x = reader.ReadProperty<int>();
    int y = reader.ReadProperty<int>();

    var grid = GetGrid();
    var instance = grid.GetCell(x, y);

    if (instance == null)
    {
        instance = new Cell();
        grid[x, y] = instance;
    }

    ReadObject<T>(reader, instance);
    return instance;
}
This of course relies on your Grid being saved and loaded before anything else, otherwise the instances won't exist yet.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
norbs
Posts: 2
Joined: Thu Oct 06, 2022 5:43 pm

Re: Saving Unique Instances of Classes

Post by norbs »

Hey Joel,

Thanks for the reply, appreciate the insights, I'll take a look at this approach and see if it makes sense. I've got some serialization/deserialization logic that does more or less what you describe, but might be more convenient if I bake it into an ES3Type as you describe.

Cheers!
Post Reply