Godot Tileset Collision — Physics Polygons from Tile Alpha
Drawing a collision shape on each of 47 tiles in Godot's TileSet editor is slow, and one pixel off is enough for a character to snag on a seam. The Tile workspace traces the shape from each tile's alpha, lets you drag the points that are wrong, and hands Godot a physics layer built by its own import script.
In Godot 4, tile collision lives in the TileSet: add a physics layer (its collision layer and mask), then give each tile one or more collision polygons; the points are measured from the tile's centre. Nerulio traces a polygon for every tile from its alpha (outline, exact rectangles or one box), lets you move the points, and its import script writes physics layer 0 with collision layer 1 and mask 1 and each polygon shifted by half a tile. Godot 4.7.2 read every polygon back point for point.

- Godot 4 TileSetVerified· Godot 4.7.2
- TiledVerified· Tiled 1.12.2
- Unity 6 Rule TileVerified· Unity 6000.5.3f1 + 2D Tilemap Extras 8.0.3
- LDtkPartly verified· LDtk 1.5.3 schema + loader
Physics layers, polygons and the tile centre
A TileSet has a list of physics layers, each with a collision layer, a collision mask and optionally a physics material (the Godot docs: TileSet inspector › Physics Layers › Add Element). Per layer every tile can hold several polygons (TileData.set_collision_polygon_points(layer, index, points)), each optionally one-way. A TileMapLayer turns the painted tiles into static collision as long as its collision_enabled is on (the default).
Nerulio keeps shapes in tile pixels with the origin at the tile's top-left, the way you draw them. Godot measures a tile's polygon from the tile centre, so the importer subtracts half the tile size from every point. A tool that skips this shift puts every shape half a tile up and left.
Three readings of the alpha (a pixel counts as solid when its alpha is above 0): Outline follows the solid edge along pixel borders and keeps only outer loops, so a hole becomes solid; Exact rectangles covers exactly the solid pixels with a few boxes, holes stay open; Box is one rectangle around everything. A long outline is simplified until it has at most 256 points.
- Physics layer
- A slot on the TileSet with collision layer, mask and material; tiles store polygons per slot.
- Collision polygon
- A list of points around the tile centre; everything inside counts as solid.
- Collision layer / mask
- Which layers the tiles are on, and which layers they detect; a body collides with the tiles when its mask includes their layer.
- Half-tile shift
- Top-left tile pixels minus (width ÷ 2, height ÷ 2) = Godot's centred coordinates.
Example: a 64 px ground tile, from pixels to Godot
64 × 64 tile, solid ground in the lower 40 px (pixel rows 24–63)
Nerulio, tile pixels (origin top-left) (0,24) (64,24) (64,64) (0,64)
half tile = (32, 32)
Godot, from the tile centre (-32,-8) (32,-8) (32,32) (-32,32)
importer TileData.set_collision_polygon_points(0, 0, points) physics layer 0, polygon 0
TileSet physics layer 0: collision_layer 1, collision_mask 1A CharacterBody2D stands on this tile only if its collision_mask includes layer 1; change the TileSet's layer numbers after import if your project uses others.
What it does
- Three readings of a tile's alpha: Outline polygon follows the solid edge, Exact rectangles covers only the solid pixels with axis-aligned boxes, Box wraps everything in one rectangle.
- Shapes live in tile pixels, snapped to whole pixels: drag a point, double-click an edge to add one, Alt+click to remove one. Every edit is one undo step.
- Show shapes of every tile draws all polygons over the sheet at once, so a gap between two neighbouring tiles shows up before you export.
- On export, the shapes you edited stay exactly as they are; the collision mode you pick fills in the tiles you never touched, from their alpha.
- nerulio_tileset_import.gd adds one physics layer (collision layer 1, mask 1) and writes each polygon onto its tile, shifted by half a tile because Godot measures tile shapes from the tile centre.
- Checked by reading the TileSet back in Godot 4.7.2: every polygon matched point for point — 49/49, 48/48 and 47/47 tiles on the corpus blob sets, 80/80 on a four-terrain dual-grid pack.
How to do it
- Drop the tileset and press Use this grid.
- Apply a layout or paint terrain bits; only tiles with bits can carry a shape.
- Press C, pick a tile, trace it as Outline polygon, Exact rectangles or Box, and drag any point that sits wrong.
- Open Export (Ctrl+E), tick Godot 4 and choose the collision mode for untouched tiles.
- In Godot, run nerulio_tileset_import.gd with File › Run and put the saved TileSet on a TileMapLayer.
Files you get
Collision travels inside the Godot files of the export:
| File | What it is for |
|---|---|
nerulio-tileset.json | physicsLayers (collision layer 1, mask 1, the mode used) and, per tile, collision: polygons in tile pixels. |
nerulio_tileset_import.gd | Adds the physics layer and writes each polygon with the half-tile shift; its Output line counts the polygons. |
nerulio-tileset.tres | Saved by Godot when the script runs: the TileSet with terrains and collision. |
Get the collision into Godot and test it
- In Nerulio's Export panel set Collision (Godot) to Outline polygon, Exact rectangles, Box or Only edited shapes. The default is None, which writes no physics layer at all — even for shapes you edited.
- Copy the PNG, JSON and script to the project root, run the script with File › Run, and check the Output line, for example
… 47 tiles, 188 peering bits, 47 collision polygons …. - Load
nerulio-tileset.tresinto aTileMapLayer's Tile Set property and paint. - Give your player body a
collision_maskthat includes layer 1 (or change physics layer 0's collision layer in the TileSet inspector to the layer your project uses). - Turn on Debug › Visible Collision Shapes and run the scene: the shapes are drawn over the tiles while the game runs.
- One-way platforms or a physics material: in the TileSet editor, Select mode, click the tile and edit its Physics Layer 0 section; press F there for a quick full-tile rectangle.
Check the result
- The script's Output line counts as many collision polygons as you expect (one per tile for Box and most Outline shapes).
- TileSet editor › Select mode › click a tile: the Physics Layer 0 section shows the polygon on the art.
- With Visible Collision Shapes on, the top edges of neighbouring ground tiles form one continuous line, and a tile with a gap shows the gap when you used Exact rectangles.
print($TileMapLayer.tile_set.get_physics_layer_collision_layer(0))prints 1.
Collision modes and what Godot receives
| Mode | Shape written | Holes | Good for |
|---|---|---|---|
| Outline polygon | The traced outer edge along pixel borders, one polygon per solid island | Filled (outer loop only) | Slopes, rounded ledges, irregular rock |
| Exact rectangles | A few axis-aligned rectangles that together cover exactly the solid pixels | Stay open | Tiles with a gap, L shapes, notched platforms |
| Box | One rectangle around every non-transparent pixel | Filled | Full blocks, crates, cheap physics |
| Only edited shapes | Your hand-edited polygons; other tiles get none | As you drew them | Sets where only a few tiles should collide |
| None | No physics layer at all | — | Decoration-only tilesets |
A fully opaque tile becomes one tile-sized square under Box and Exact rectangles. A long outline is simplified until the tile holds at most 256 points.
Troubleshooting
| Symptom | Likely cause | How to check | Fix |
|---|---|---|---|
| The TileSet has no physics layer | Collision (Godot) was left on None in the export | Output line: 0 collision polygons | Choose a collision mode and export again |
| The player falls through the tiles | The body's mask does not include layer 1, or the layer's collision_enabled is off | Inspector of the body and of the TileMapLayer | Add layer 1 to the mask, or set the TileSet's physics layer to your project's layer |
| Shapes sit half a tile up and left | The polygons came from a tool that wrote top-left coordinates without the shift | Visible Collision Shapes | Re-import with Nerulio's script, which applies the shift |
| A tile with an opening is fully solid | Outline keeps only the outer loop | The tile's polygon in the TileSet editor | Use Exact rectangles for that set, or edit that tile's points |
| The character catches on seams between tiles | Neighbouring polygons end at different heights by a pixel | Visible Collision Shapes, zoomed in on the seam | Use Box or Exact rectangles for full tiles, or drag the points level in the Studio's collision tool (C) |
| Some tiles have no shape | Only tiles with terrain bits are exported, and an alpha of 0 everywhere gives no shape | Studio: tiles without bits are dimmed | Paint bits for those tiles, or draw their shape by hand in Godot |
Other ways to do it
| Option | When it is the better choice |
|---|---|
| Draw the polygons in Godot's TileSet editor (F for a full rectangle, then add or remove points; tile property painting copies one shape onto many tiles) | Full blocks and a few slopes; no extra tool, but each shape is placed by hand. |
| Sprite collision instead of tiles: collision polygon generator | The shape belongs to a character or prop, not to a tile in a TileSet. |
| Unity Rule Tiles with Collider set to Grid: Unity Rule Tile | Your target is Unity; Nerulio's traced polygons are written for Godot only. |
Engine exports and how each was checked
"Verified" means the exported files were loaded by the real engine or tool on real CC0 assets and the result was compared with the source. Anything that was not loaded is labelled as such.
| Target | Files | Check |
|---|---|---|
| Godot 4 TileSet | PNG · nerulio-tileset.json · nerulio_tileset_import.gd | Verified Godot 4.7.2The included import script builds the TileSet with terrain peering bits; a map painted with set_cells_terrain_connect equals the Studio preview in every cell on 9 corpus sheets. |
| Tiled | .tsx Wang set · sample.tmx | Verified Tiled 1.12.2Tiled's own readers load every Wang ID as written and the sample map renders pixel-exact. The terrain brush itself was not driven. |
| Unity 6 Rule Tile | nerulio-ruletile.json · Editor/NerulioRuleTileImporter.cs | Verified Unity 6000.5.3f1 + 2D Tilemap Extras 8.0.3Blob and side sets: a painted Tilemap shows the predicted sprite in every cell. Corner / dual-grid sets are not exported to Unity. |
| LDtk | .ldtk (1.5.3) · rules · sample level | Partly verified LDtk 1.5.3 schema + loaderAccepted by the LDtk JSON schema and its official loader, and the rules re-run to the exported tiles; not opened in the LDtk app. |
Evidence
Layouts identified from the pixels alone on the tile corpus: 4 blob-47 templates and 2 Wang templates with high confidence, a real 64 px cave tileset with medium confidence — every tile's bits right. Godot 4.7.2 painted every test cell exactly as the Studio predicted (485/485 per blob set, 251/251 on a 4-terrain dual-grid pack); Tiled 1.12.2 read every Wang ID back; Unity 6000.5 matched on every painted cell of the blob and side sets.
Limits
- One physics layer, plain polygons: one-way flags, extra layers and physics materials are set in Godot afterwards.
- Holes are exact only with Exact rectangles.
- Tiles without terrain bits get no collision in the export.
- Tiled's terrain brush, the LDtk app and Godot's editor painter were not driven; the checks use the engines' readers and set_cells_terrain_connect.
- Unity gets blob and side sets only; several terrains become several Rule Tiles with no transitions between them.
- Isometric and hex tiles, animated tiles, occlusion and navigation layers are out of scope.
- Suggesting bits without a known layout needs a reference "full" tile; on side-only path templates the guess can fail until you select it.
Versions and sources
Checked in Godot 4.7.2 by reading the saved TileSet back: every polygon matched point for point after the half-tile shift — 49/49, 48/48 and 47/47 tiles on the corpus blob sets, 16/16 on the edge and corner templates, 80/80 on a four-terrain dual-grid pack. Editor steps follow the Godot 4.7 documentation.
Official documentation used
Questions
My character falls through the floor. Is the physics layer there?
The import script adds a physics layer only when at least one tile has a shape, and sets it to collision layer 1 and mask 1. Your body's collision mask has to include layer 1; if your project uses other layer numbers, change them on the TileSet after import.
Why does a tile with a hole come out fully solid?
Outline polygon keeps only the outer loop, and Godot treats the inside of every polygon as solid, so the hole fills in. Exact rectangles covers just the solid pixels, so the opening stays open.
Can I set one-way platforms or per-tile layers here?
No. The export writes one physics layer with plain polygons. One-way collision, extra layers and physics materials are set in Godot's TileSet editor once the set is imported.
Is collision also written for Unity, Tiled or LDtk?
No, collision goes to Godot only. The Unity Rule Tile, Tiled and LDtk exports carry the tiles and their rules without shapes, and tile collision inside Unity was not checked.
Is it free, and do I need an account?
Everything on these pages is free and works without an account. There is no watermark and no export that is held back.
Are my sprites uploaded?
No. The Studio decodes, packs and exports in this browser tab. Projects are autosaved in the browser (IndexedDB) and can be saved as a .nerulio file.
Does it need Aseprite or an engine plugin installed?
No. .aseprite files are read and written in the browser, and each engine bundle carries what that engine needs (for Godot, the import settings and scene; for Unity and the Godot TileSet, a small editor script).