Godot 4 tilemap collision: the TileMapLayer setup
Add collision to a TileMapLayer in Godot 4.3+: a TileSet physics layer, per-tile collision polygons, a CharacterBody2D mask that matches, and one-way platforms.
How to Add Tilemap Collisions in Godot 4A tile either collides or it does not, and the switch is not on the TileMapLayer. It is in
the TileSet: one physics layer plus a collision polygon on each tile. Two more things then
have to match — the layer's collision bits and the body's mask — or the map looks solid and
the player walks straight through it.
This is the current-node version of the tilemap collisions
video. The video was recorded before
TileMapLayer existed: Godot 4.3 split TileMap into one TileMapLayer per layer, and every
step below uses the 4.3+ node. The editor wording is from 4.8 beta 2 and the APIs used here
exist unchanged since 4.3.
1. Three pieces, not one switch
| Piece | Where it lives | What it decides |
|---|---|---|
| Physics layer | TileSet resource → Physics Layers |
which collision bits the tiles occupy |
| Collision polygon | TileData for one tile |
the shape that tile contributes |
| Body mask | your CharacterBody2D |
which bits the body scans for |
All three have to agree. A physics layer with no polygons collides with nothing. Polygons on a tile nobody painted do nothing. Correct polygons on layer 1 are invisible to a body whose mask does not include layer 1.
2. Create the TileSet and add a physics layer
- Add a
TileMapLayernode to the scene. - In the Inspector, set Tile Set → New TileSet, then set Tile Size to your tile size.
- Open the TileSet panel at the bottom of the editor and drag the tilesheet onto it. Answer Yes to create tiles automatically.
- Select the
TileMapLayeragain, unfold Tile Set → Physics Layers, and click Add Element. Godot creates layer 0 with collision layer 1. - Clear that layer's Collision Mask. A static floor never needs to detect anything; the mask matters only when the tilemap itself has to query bodies.
Writing the same layer from code is three calls:
var tileset := TileSet.new()
tileset.tile_size = Vector2i(32, 32)
tileset.add_physics_layer()
tileset.set_physics_layer_collision_layer(0, 1) # tiles occupy layer 1
tileset.set_physics_layer_collision_mask(0, 0) # tiles detect nothing3. Give the solid tiles a polygon
- In the TileSet panel, switch to the Select tool.
- Select the tiles that should be solid. Drag a rectangle, or hold
Shiftand click to add and remove tiles from the selection. - In the middle column, unfold Physics Layer 0 and use the three-dots menu above the
polygon editor: Reset to default tile shape, or press
F.
That gives each selected tile a rectangle covering the full cell. Polygon points are
tile-local and centered, so a full 32×32 cell is (-16, -16) to (16, 16), not (0, 0) to
(32, 32).
Select a single tile and drag points to fit a shape that is not a full square — a slope, a fence, the top of a table. Use the same three-dots menu to add or remove points.

The blue overlay above is Godot's own collision debug draw, not art added to the level. Every solid cell carries a full-cell polygon; the plank tile carries a thin polygon on its top edge only.
4. One-way platforms
To make a tile a platform the player can jump through from below:
- Select the platform tiles in the TileSet panel.
- In Physics Layer 0, expand Polygon 0.
- Enable One Way Collision.
A one-way polygon lets a body pass from one side and blocks it from the other. Keep the polygon along the surface the player should stand on — a thin strip at the top of the tile — rather than the full cell, and test the direction in a running scene: the editor's debug overlay draws the shape, not which side it blocks, and the point order is not a reliable way to reason about it.
5. Paint the level and check the layer
Paint the tiles onto the TileMapLayer in the 2D view. Two node properties decide whether the
map pushes back at all:
- Collision Enabled (
collision_enabled) is on by default. Turning it off gives you a decorative layer with identical tiles and no physics. - Collision Visibility Mode forces the debug shapes on, which is the fastest way to see whether a painted tile actually has a polygon.

The level above is three painted rows: grass floor, a brick wall four cells tall, and a plank platform. The steps in this article are what make the floor solid.
6. Make the body collide
The tiles sit on physics layer 1. The body has to scan layer 1 and be something else itself:
- Add a
CharacterBody2Dwith aCollisionShape2Dsized to the character. - Set Collision Layer to 2 and Collision Mask to 1.
The mask is the one that matters. A body with mask 0 falls through a fully collided floor and logs no error — there is nothing to collide with as far as that body is concerned. If several kinds of object share the ground, give each its own layer bit and have the player's mask include all of them.
7. Move it
extends CharacterBody2D
@export var speed := 220.0
@export var jump_velocity := -600.0
var gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity.y += gravity * delta
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = jump_velocity
velocity.x = Input.get_axis("ui_left", "ui_right") * speed
move_and_slide()is_on_floor() is true only after move_and_slide() has resolved a contact, so gravity needs
the is_on_floor() guard. Without it the body accumulates downward velocity while standing
still, and the first frame it leaves the ground it drops like a stone.
8. Jump up through a one-way platform, drop back down
Rising through a one-way polygon works on its own: the body passes, then lands on it on the way down. To drop through on purpose, switch off the layer bit for a moment:
func drop_through() -> void:
set_collision_mask_value(1, false)
await get_tree().create_timer(0.15).timeout
set_collision_mask_value(1, true)set_collision_mask_value(1, false) clears bit 1, which is the bit the tiles occupy. The
timer has to be long enough for the body to clear the polygon — 0.1–0.2 s at normal gravity.
Two warnings: do not leave the bit off if the player can cancel the drop mid-air, and do not
disable the whole mask if other layers (enemies, triggers) share it.
9. Read the tile under the player
Collision resolves movement; it does not tell you what the player is standing on. Ask the layer directly:
func surface_below(layer: TileMapLayer) -> StringName:
var cell := layer.local_to_map(layer.to_local(global_position) + Vector2(0, 16))
var data := layer.get_cell_tile_data(cell)
if data == null:
return &""
return data.get_custom_data("surface")local_to_map() takes layer-local coordinates, so convert the global position first with
to_local(). get_cell_tile_data() returns the tile's TileData — the same object that holds
the collision polygon — which is where a custom data layer such as surface lives. The layer
has to exist first: add it under Tile Set → Custom Data Layers → Add Element, and set the
value per tile in the TileSet panel. On a TileSet without a layer named surface, the call
logs TileSet has no layer with name: surface and returns null.
10. When tiles are not solid
| Symptom | Cause | Fix |
|---|---|---|
| Player falls through everything | body mask does not include the tile layer | set mask bit 1 (or the bit the layer uses) |
| Player falls through one tile type only | that tile has no polygon | select it in the TileSet panel, press F |
| Level is solid but the debug shapes are missing | collision_enabled off, or collision debug hidden |
re-enable the property, turn on visible collision shapes |
| One-way platform blocks from below | One Way Collision off, or the polygon covers the full cell | enable it, and keep the polygon on the standing surface |
| Player sticks to walls | gravity applied while on the floor, or a mask that ignores walls | guard gravity with is_on_floor(); check layers |
| Nothing collides after an editor restart | the TileSet was edited but not saved | save the TileSet resource and re-run |
11. What this unlocks
The layer is now a physics surface. The next step most 2D games take is putting objects on it: snapping furniture, props, or buildings to TileMap cells converts the same cells into placement targets, and the top-down game guide puts movement, camera, and collision in the order that avoids rewrites.
If you would rather not write placement, preview, and commit rules yourself, Grid Placement is the paid addon that builds on this layer. Nothing on this page needs it.
How this was checked
Every claim above was exercised on Godot 4.8 beta 2 with a headless test scene, not typed from memory:
- the body falls and lands on the grass tile's polygon at the expected height;
- a brick wall stops horizontal movement at the cell edge;
- a one-way plank lets the body rise through it, then holds it on the way down;
collision_enabled = falsedrops the same body through the same floor;local_to_map()plusget_cell_source_id()return the painted cell.
The TileSet, polygons, and one-way flag in that test are built with the same calls the editor
makes, so a failure in the editor steps shows up in the test.
Download the demo project (6 KB) and run
the same scene yourself; the zip contains tests/test.tscn, its expected output, and the two
screenshots above come from that project.