Efficiently serializing large collision grid

I am currently making a Tile-Map class in Unity with a custom editor. This TileMap currently only contains a 2-D grid, with each tile being either collidable or non-collidable. At the moment, my TileMap class looks something like:

public class TileMap : MonoBehaviour {
    public int cols = 100;
    public int rows = 100;
    public bool[] collisionGrid;
}

The collisionGrid variable is a flattened 2D array of size cols * rows, and represents each grid space on the 2D grid. If collisionGrid[r * cols + c] = true, then that grid space is impassable.

My TileEditor script is a class that extends Editor. It gets the mouse position and, if the mouse is clicked, sets the corresponding tile to true.

void OnSceneGUI(){
    //Get Mouse position and appropriate tile index
    if(e.type == EventType.MouseDown || e.type == EventType.MouseDrag){
        tMap.collisionGrid[tileIndex] = true;
    }
    EditorUtility.SetDirty(tMap);
}

My problem is that, while this does what it is supposed to do, the editor slows to almost a halt. Clicking and dragging to add new collidable positions is next to impossible, and even attempting to pan the camera has noticeable delay.

There are several things I have tried already to resolve this issue:

  1. Changing the dimensions from 100 x 100 to, say, 20 x 20, then everything works smoothly. However, I specifically want to make larger tile maps, And I feel like 100 x 100 is hardly a demanding size to work with.

  2. Removing the “EditorUtility.SetDirty(tMap)”. However, If I do this, Unity will not persist my collisionGrid properties between sessions. I need it to persist.

  3. Moving the “EditorUtility.SetDirty(tmap)” inside of the “if” condition. This resolves the problem of regular Unity functionality (like panning and zooming) being sluggish, but doesn’t resolve the core problem, that editing the grid of collision values is so slow as to be unusable.

So my question is, what am I doing wrong? How can I edit a moderately sized grid in real-time and have it persist? Is it a case of calling EditorUtility.SetDirty() sometimes, but not every frame? Or something smarter than that?

You have a few avenues to explore:

  • You could try targeting the SerializedProperty of the collision by using

    // when setting

    var tileProperty = serializedObject.FindProperty(“collisionGrid”).GetArrayElementAtIndex(tileIndex);
    tileProperty.boolValue = true;

    // to apply

    serializedObject.ApplyModifiedProperties();

I’m not sure, but this may be faster, and only copy the changed properties of the object.

  • You could add a cached version of the array that the editor changes when working, and then try to make it call “SetDirty” only after the user has been idle for more than 3 (or whatever) seconds.

    You can ensure the cached field exists only in the editor using #if UNITY_EDITOR preprocessor blocks. You can make the TileMap choose to draw from the cached field first before the actual values when in the editor.

    Setting a timer in the Editor could be accomplished in a few ways. A simple way would be to add a listener to EditorApplication.update that looks for when Editor.timeSinceStartup - idleStartTime > 3.0d, then copies the cache and calls EditorUtility.SetDirty(targetObject) if it needs to. Update idleStartTime to EditorApplication.timeSinceStartup every time the mouse is down.

Well, when calling SetDirty you mark your array as “dirty” so it will be reserialized to the assetfile where its parent class is actually contained. 100 x 00 are actually 10000 bool values (or 10 kB).

Of course you should only call SetDirty when you actually changed something. So put the call inside that if statement.

Additionally you should implement some kind of enable / diable for your editor. Otherwise it will always execute the check while you have your object selected.