Multidimensional array filled by Custom Editor is empty on Play Mode

Hello friends!

I'm generating a grid through custom editor and it works great, but the array looks empty during Play Mode. Here's the code:

[CustomEditor(typeof(BattleMapManager))]
public class MapManagerEditor : Editor{

    public override void OnInspectorGUI(){

        BattleMapManager mapManager = (BattleMapManager)target;
        base.OnInspectorGUI();

        if (GUILayout.Button("Generate")){

            mapManager.GenerateGrid();
        }
    }
}
public class BattleMapManager : MonoBehaviour {

    Tile[,] grid;

    public void GenerateGrid() {

        grid = new Tile[gridSize.x, gridSize.y];

        //code to fill the grid by Instantiating Tile objects
    }
}

I've done a lot of research on this but to be frank and didn't quite understand how this serialization works, I've tried plenty of solutions and none of them worked. I even read some places that multidimensional arrays can't be serialized, which could be what's causing my problems.

So I humbly ask for your advice, what would be the best way to handle this? Should I just fill it on scene Start?

Thanks in advance!

It's complicated.

2D arrays are definitely not directly supported... but you can get there by arrays of objects with arrays in them.

Are you aware that Unity has a Tilemap solution?

https://learn.unity.com/tutorial/introduction-to-tilemaps#

Yeah, I tried playing around with that but it had some limitations so I ended up building my own grid system, which works great.

So I’m right in my assumption that I need to serialize the field in order to “save” it outside Play Mode?
I could serialize 1D arrays and mash them together afterwards, the code would be something like this, right? https://discussions.unity.com/t/635708/5

But I guess it would be easier to just do FindObjectsWithTag(“Tile”) on scene start instead?

I'm a bit of a scrub with regards to serialization; I tend to keep it simple, just never push it far.

I'm gonna summon @spiney199 who likely has a great deal more constructive things to say on the subject of serialization.

I cannot say, but the thing you want to consider is where you are doing this editing, where you are "adding value" or authoring content in your game. Is this just to find stuff at runtime? Then sure, just find all the things. Or is it to let you edit it, modify it, and save it in its new form or shape?


There are a few things I have to manually adjust in a few individual tiles of each map (such as setting a element for each... fire, water, etc), so I built a tool to generate me the tiles in Editor and I just adjust the ones I have to.

It works great, but it would be even better if those were already stored in the grid[,] for me to use in Play Mode since I've already done that during generation. It would not be a problem to grab them on load, but it's duplicated step.

I have been summoned!

If you want to serialise a 2d array you have a few options.

1: Make some kind of wrapper class that contains the information in a manner in which Unity can serialise, and provides an interface for a 2d style array. To do this you'd pretty much need a collection of (another) wrapper class, which in turn have their own collection. Ergo, the first collection is your rows, the collection in each of the wrappers represent the columns.

2: Use the ISerializationCallbackReceiver interface: https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html

This is similar to the first method, as you still need to store the data in a manner in which Unity can serialise, but you can use this to swap the Unity-serialisable data into an actual 2d array to make interfacing with it a bit cleaner.

3: Odin serialisation: https://github.com/TeamSirenix/odin-serializer

Odin serialisation supports 2d arrays. The github package doesn't include any inspector stuff (that's what the paid asset is for), but you can still used the SerializedX classes to be able to serialise stuff Unity normally can't, and provide your own custom inspector if required. Just note that Odin serialisation and prefabs don't get along right now.

As additional notes, custom editors and property drawers being able to draw types do not make them serialisable. Unity can and only ever will serialise the data it is able to, outlined in the docs: https://docs.unity3d.com/Manual/script-Serialization.html

Even the Odin SerializedMonobehaviour and similar types just use the ISerializationCallbackReciever to convert a byte array to the actual non-Unity serialisable data and back.

1 Like

@spiney199 thank you for the detailed response! Now I will need some time to digest all this stuff... (and try to learn more about serialization in general).

I only skimmed through, so I apologize for that in advance, but I noticed you kept using the notation for a multidimensional array. My instincts tell me that you will have more trouble with that than using a jagged array (just an array of arrays.) Or, you can make a single array and use it exactly the same as a multidimensional array by handling the multidimensional indexing logic yourself, converting a multidimensional index into a single integer that indexes into the correct spot in the one big array. That's how a multidimensional array works anyway. I tend not to use C# multidimensional arrays in general.


Yeah, I could, but the multidimensional array delivered the best solution and very simple implementation :)
Until now, at least... haha.

After a lot of research and better understanding how serialization works, I ended up doing just what you said, converted my 2D array into a 1D array, hence allowing it to be serialized. So the code to retrieve the values went...

From:

Tile tile = grid[x,y];

To:

Tile tile = grid[(y * gridSize.x) + x];

Not as elegant but still simple and effective, and allows me to store all the stuff I need with my Editor script.

1 Like