2D tile-based level editor performance

For my current project (2D platformer) I need to implement a tile-based level editor. I’ve come to the conclusion that it would be better to have the needed grid of tiles in the scene view than drawing it in an EditorWindow with Unity - Scripting API: GUI.DrawTextureWithTexCoords.

The reasons for this are partly design-based (e.g. I’ll later need to add a functionality to be able to place items/objects/units in the level which should be easier by using the existing scene view).

One important problem however is performance. I’ve noticed that the method mentioned above absolutely destroys performance in the EditorWindow when I draw an entire level with the theoretical maximum number of tiles I want to draw which is 1000 * 500 tiles where 1 tile has 1 background layer and 1 foreground layer while ingame. Further each tile can have (in theory) up to 20 properties enabled (e.g. wall, platform, water, damage-area, etc) which need to be represented by a graphic while in the editor. In practice a majority will have 0 properties active (air) or 1 (mostly walls) and only a minor amount of tiles will have a few more (2-5) properties active at once.

Now for the tiles to be displayed in the scene view I understand I’d need to create quads, set the material of each quad and set the UV coordinates to display the correct tile from the texture altas.

In a previous project I used Unity - Scripting API: Mesh.CombineMeshes to reduce the number of meshes to improve perfomance, however that was in an already running game. Since in the current project this is for a level editor though where it needs to be possible to edit tiles fluently I’m not sure this will be possible here. However, I also know from previous projects that having 100050022 = 11 million quads as single gameobjects (with MeshFilter and MeshRenderer components) will also vaporize my performance.

So what are my options here?

Use the Unity tile system, or one of the existing ones on the asset store. Just to pick one totally at random, SpriteTile allows various properties for tiles, and while it uses an editor window rather than the scene view, it allows placing GameObjects directly in the level editor. (Also has a preview function using the scene view in case that’s not enough.) Levels can be insanely big without affecting speed since only tiles that would be visible to the camera are drawn.

–Eric

Thanks for the quick reply!

I had a look at the tile system (Unity - Manual: Tilemaps) which seems to go in the right direction.

I noticed that sprites with sprite mode “multiple” which have been sliced in the sprite editor get split up into seperate textures. Is this really the most efficient approach? I would’ve thought that putting them in a texture atlas would be better…

Currently I have about 90 texture atlasses with a size of 256256 pixels in which there are 1212 tiles each with a size of 2020 pixels. Since Unity allows larger textures than that I had planned to later combine 8.58.5 texture atlasses into a single texture atlas of 20482048 pixels with 102102 tiles of 20*20 pixels each. This would result in about 1.25 texture atlasses to minimize the amount of drawcalls to the graphics card.

I’m also not completely happy with the behaviour of some of the brushes in the Tile Palette and although I’ve seen that it’s possible to script brushes (Unity - Manual: Scriptable Brushes) I’m not entirely convinced that it’ll be possible to make them fit the needs of my project (e.g. regarding the mentioned tile properties, interaction with my custom EditorWindow, etc.).

Would it maybe be possible to recreate some of the base functionalities of the Tile Palette (e.g. the grid, drawing tiles) myself efficiently or is there too much internal optimization involved in the existing tool?

Sprites are auto-atlased.

–Eric

As in, multiple smaller atlases are put into larger altases and get referenced automatically internally? Is there maybe some documentation on this?

Also, I don’t suppose having 11 million gameobjects with a SpriteRenderer component instead of MeshFilter and a MeshRenderer component would improve performance, would it?

Edit: And if I’m not mistaken this would rather remove the possibility to combine meshes instead, making performance worse?

11 million GameObjects would be a disaster. As for sprite atlases, Unity - Manual: Sprite Packer Sprites are auto-batched.

–Eric

Yeah, that’s what I thought.

Is it possible then to use the same methods which the Tile system uses to draw the tiles for a custom tile system? Or are those methods all internal? But if so, could it be replicated efficiently?

The two primary methods for usable tile systems are 1) create meshes using the Mesh class or 2) use one sprite per tile, but only enough to fill the screen, and recycle sprites as needed when the camera moves.

–Eric

This would require though that the camera cannot zoom (or at least not too much), right? While I thought about this approach for ingame as well, it would probably be a restriction for the scene view camera so that it won’t be able to view the entire level at once.

Since you mention “primary methods”, does that mean there are other methods as well?

Nope, camera zooming is fine, you dynamically create more sprites as needed. As for other methods, there are an infinite number of ways to solve problems.

–Eric

If I dynamically create more sprites as needed when zooming I’ll eventually end up at 11 million sprites again though.

If you zoomed out far enough to see the whole level, yes, but that’s something you would prevent.

–Eric

Would it then not be better to use a mesh with, say, 100*100 tiles as a chunk object (and possibly hide said object in the hierarchy to remove overhead for the end-user of the level editor)?

At 1000500 tiles with 22 layers this would then create “only” 1100 GameObjects with 4100*100 = 40,000 vertices (to stay below the vertex limit of 65,353).

This is a suggestion as well as a question.

I have not built a level editor in Unity as of yet, but I have in another platform (Adobe). Previously I built the editor with one “map” sprite whose graphic I built using getPixel/setPixel. This was very fast because the engine didn’t need to worry about the overhead of 1 sprite verse 2000+ sprites. Thus, this is my suggestion Sprite.texture.GetPixels/SetPixels. Then only redraw the parts of the texture that change. In the case of scrolling you just grab all the pixels minus a row/column and dump it to new x,y coor and then render the new row/column. Only expensive when initial load, scaling, or some sort of fill.

My question is, how would this perform in Unity? Does Unity handle this level pixel manipulation very quickly? I assume performance here would be pretty high depending on exact implementation.

I thought about this approach as well but I think it would get expensive (since I have free movement instead of it being locked to tiles). This would make it necessary to either constantly move the sprite or repaint it (almost) every frame.

I’ve done some experimenting myself in the meantime and came to the conclusion that the way forward will probably be to have an array of vertices which get recalculated and applied to a single mesh:


The green rect represents the area which the camera sees. To make sure there are no gaps at the borders the blue rect represents the overdraw area. The red line is the camera offset from V3.zero.

So basically every time the position of the camera changes more than the overdraw distance it’ll trigger an UpdateGeometry method to loop over all tiles which are within the blue border or even just touch it. Within this loop the coordinates of the corners of tiles get saved into the the vertex array as 2 triangles. Afterwards I just need to assign the array to the mesh.