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
uinteach; - one mesh with a quad for every tile, and a
MeshRendererto draw it; - its colliders: several
EdgeCollider2Dcomponents or onePolygonCollider2Dfor 2D, oneMeshColliderfor 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:
- The map bounds are recalculated.
- Every dirty chunk rebuilds its mesh. Brush tiles resolve their autotiling here.
- Chunks left empty are destroyed.
- Every chunk whose colliders are dirty rebuilds them.
OnMeshUpdatedis 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 Typeset toNone. Its chunks skip the collider pass, and pathfinding ignores it. - Write in bulk, rebuild once. From code, write all your tiles and call
UpdateMeshorUpdateMeshImmediateonce. 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 TypetoNoneand untickEnable Undo. Restore both when you finish, and the colliders rebuild in one pass.