Skip to content

How It Works

This page explains how a tilemap is stored and drawn, and what that means for large maps: where the time goes, what you can do about it, and why some edits cost more than they look like they should. Read it when a map grows past a few hundred cells on a side, or before you write a generator.

Chunks

An STETilemap stores no tiles itself. The map is divided into blocks of 60 by 60 cells, called chunks, and each block that holds at least one tile is a child GameObject of the tilemap with a TilemapChunk component. A chunk owns:

  • the tile data of its 3,600 cells, one uint each;
  • one mesh with a quad for every tile, and a MeshRenderer to draw it;
  • its colliders: several EdgeCollider2D components or one PolygonCollider2D for 2D, one MeshCollider for 3D;
  • the tile colors, if any have been painted.

Chunks are created when a tile is first written into their area and removed when a rebuild finds them empty. They are hidden in the hierarchy. To see them, tick Show Tile Chunks under Advanced Options in the tilemap's Map tab. With the tilemap selected, the scene view outlines each chunk in red.

The size is the constant STETilemap.k_chunkSize, and it is 60 because a mesh with 16 bit indices holds at most 65,535 vertices. A tile takes 4 vertices, and a tile that a brush splits into four subtiles takes 16: 60 by 60 cells at 16 vertices is 57,600. Every saved tilemap is laid out in chunks of that size, so the value is fixed.

What that means for drawing

Each visible chunk is its own renderer, so the number of draws grows with the number of chunks on screen, not with the number of tiles. Chunks outside the camera are culled by Unity like any other renderer. A 512x512 map is 81 chunks; a screen showing a few of them draws a few meshes.

All chunks of a tilemap share its material. Tint color and atlas texture reach each chunk through a MaterialPropertyBlock, which is rebuilt only when one of them changes, so a tilemap nobody is editing costs almost nothing per frame. Animated brush tiles are the exception: each chunk that holds one checks its animation every frame in LateUpdate and uploads new UVs when a frame advances.

What that means for saving

Meshes are never saved. A scene or prefab holds only the tile data, and every chunk builds its mesh when it is enabled. That keeps files small. In a measurement on 2026-09-23 a 512x512 map saved to a 2.5 MB scene here, against 68.7 MB for Unity's own Tilemap holding the same map.

The price is paid at load. Building the 81 chunk meshes of a 512x512 map took 273 ms in the same round of measurements, taken before version 1.7.7 made the per chunk rebuild about twice as fast (from 3.5 ms to 1.7 ms a chunk).

All figures on this page come from an i7-10700K running the Unity 2022.3.62f2 editor, in edit mode.

Deferred rebuilds

SetTile, SetTileData, Erase and the paint tools only change tile data and mark the chunk dirty. The rebuild runs later, in UpdateMeshImmediate, either because you called it or because UpdateMesh() set a flag that the tilemap's Update picks up. It goes like this:

  1. The map bounds are recalculated.
  2. Every dirty chunk rebuilds its mesh. Brush tiles resolve their autotiling here.
  3. Chunks left empty are destroyed.
  4. Every chunk whose colliders are dirty rebuilds them.
  5. OnMeshUpdated is raised.

The colliders wait for every mesh because a brush can change neighbouring tile ids while it refreshes, and the colliders are built from those ids.

Autotiling is deferred too

Writing a brush tile does not pick its tile. It clears a "needs refresh" bit on the cell and on each of its eight neighbours whose brush autotiles with it. The brush's Refresh runs in step 2 for every cell with the bit clear, reads the neighbours, chooses a tile and sets the bit again.

A neighbour in another chunk gets its bit cleared through the tilemap, which marks that chunk dirty as well. So a brush tile on a chunk edge rebuilds two chunks, or four at a corner. Collider edges work the same way, because a solid tile hides the edge it shares with a solid neighbour.

When the map bounds change size, every brush tile in the map is scheduled to refresh, because a brush with the TilemapBounds autotiling mode treats the map edge as a neighbour. The refresh happens as part of each chunk's next mesh rebuild.

Why a small edit costs a whole chunk

A chunk rebuilds its mesh by walking all 3,600 cells and regenerating every quad, whatever changed. One tile changed costs about the same as a full chunk. That is the floor under every brush stroke, and it does not depend on the size of the map.

Measured on 2026-09-23 during 1.7.7 development, before the chunk rebuild speedup mentioned above, one tile changed on a built map cost:

Map Colliders on Renderer only
128x128 5.71 ms 3.55 ms
512x512 6.00 ms 3.86 ms
1024x1024 7.03 ms 4.67 ms

The mesh half of those figures has since halved. The small growth with map size is the rebuild visiting every chunk to check whether it is dirty.

Bulk writes are where the design pays off. Writing every cell of a 512x512 map from code took 305 ms, and 128x128 took 19.7 ms, measured on version 1.7.7 without the mesh build. Unity's own SetTile took 728.6 ms and 44.3 ms for the same maps on the same machine.

Undo in the editor

Every paint operation records an undo step for the chunks it touches. With 1.7.7, undoing a fill rebuilds only the chunks whose data actually changed. Undoing a 10,000 tile fill on a 1024x1024 map dropped from 1,086 ms to 51 ms with that change.

Recording is not free on every map. On a 256x256 map, a 10,000 cell fill that creates no new chunk took 56 ms to record and 340 ms to undo, because Unity compares and restores each changed cell.

If painting a huge map feels slow, untick Enable Undo under Advanced Options in the Map tab. Paint operations then record nothing and cannot be undone, so turn it back on when you are done.

Advice for large maps

  • Split layers by purpose. Put decoration on its own tilemap with Collider Type set to None. Its chunks skip the collider pass, and pathfinding ignores it.
  • Write in bulk, rebuild once. From code, write all your tiles and call UpdateMesh or UpdateMeshImmediate once. See Scripting.
  • Spread generation over frames. Rebuild cost is per dirty chunk, so a generator that fills and rebuilds one region per frame never stalls on the whole map.
  • Hold back tile prefabs while generating. Prefab instances are created at write time. See Tile Prefabs.
  • Keep prefab tiles sparse. A chunk looks its prefab instances up one by one on every rebuild, so a chunk packed with prefab tiles rebuilds slower.
  • Delay what you do not need yet. A tilemap whose GameObject starts inactive builds its meshes when you activate it, not when the scene loads.
  • While painting a very large map, set Collider Type to None and untick Enable Undo. Restore both when you finish, and the colliders rebuild in one pass.