Skip to content
Development
Skill

/godot-tilemap

Build and edit tile-based 2D levels in Godot 4.7 with TileMapLayer and TileSet: paint layers, set up collision/navigation/custom-data on tiles, autotile with terrain sets, and read/write cells from code (set_cell, get_cell_tile_data, local_to_map). Use when working with

From plugin
awesome-gamedev-agent-skills
6.3k77 skills

Claude Code

$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill godot-tilemap --agent claude-code

SKILL.md

godot-tilemap.SKILL.md
name: godot-tilemap
description: >
  Godot 4.7 tile maps with TileMapLayer and TileSet: atlas sources, data layers, terrain
  autotiling and set_cell or local_to_map at runtime. Use for 2D tile levels in Godot, tiles with
  no collision, a click on the wrong cell or porting a deprecated TileMap. Not for Unity
  (`unity-tilemap-2d`), tile art (`create-game-assets`) or generation (`procedural-gen`).

Godot TileMap (4.7 TileMapLayer)

Author tile levels with `TileMapLayer` + `TileSet`, attach per-tile collision and data, autotile with terrains, and edit cells at runtime. Targets **Godot 4.7**, where `TileMapLayer` replaces the deprecated `TileMap` node (see Version notes).

When to use

  • 2D levels on a tile grid, configuring a `TileSet` (collision, navigation, custom data,

terrains), or reading/writing tiles from code.

  • Migrating a `TileMap` node (one node, many layers) to `TileMapLayer` nodes.

**When *not* to use:** moving the player across tiles → `godot-2d-movement`; general bodies and raycasts → `godot-physics`; generation algorithms → `procedural-gen`; level design practice → `level-design`; 3D grids → `godot-3d-essentials` (GridMap).

Core workflow

1. **One `TileMapLayer` per layer** (background, walls, foreground), grouped under a `Node2D`. All layers share one `TileSet` saved as an external `.tres`. 2. **Build the TileSet.** The TileSet editor is click-only, so when you cannot drive the editor, generate the `.tres` with a headless tool script (Pattern 1) and keep that script in the repo. Otherwise, ask the user to make the change in the editor and report the source id, atlas coordinates and layer names. 3. **Add data layers on the TileSet**: physics layers (collision polygons plus their collision layer/mask bits), navigation layers, occlusion, and typed custom data layers (`damage: int`, `is_ladder: bool`). 4. **Place tiles from code** with `set_cell`, `set_cells_terrain_connect` or `set_cells_terrain_path`. The cells in a `.tscn` are an encoded `tile_map_data` byte array, so never edit them by hand; write a generator script or ask the user to paint in the editor. 5. **Turn on per-layer collision/navigation** with `collision_enabled` / `navigation_enabled` on each `TileMapLayer`. 6. **Check before you finish.**

  • Run the `godot-nodes-scenes` skill's `scripts/check_godot_project.py <project-dir>`

to catch a TileSet `.tres` whose texture or script path is broken.

  • Prove the tiles behave with a headless test (Pattern 4): on a fresh checkout run

`godot --headless --path . --import` first so the tile texture is imported, then `godot --headless --path . --script res://tests/test_tiles.gd`, and read the exit code. With no Godot binary (`godot`, `godot4` or `$GODOT_BIN`), ask the user to run it and paste the output. For a visual check, ask the user to run `godot --path . --debug-collisions` and report whether walls show shapes.

Patterns

1. Generate a TileSet `.tres` in code

# res://tools/make_tileset.gd
# Run: godot --headless --path . --script res://tools/make_tileset.gd
extends SceneTree

func _initialize() -> void:
    var tile_set := TileSet.new()
    tile_set.tile_size = Vector2i(16, 16)
    tile_set.add_physics_layer()
    tile_set.set_physics_layer_collision_layer(0, 1 << 1)   # bit 2 ("walls")
    tile_set.add_custom_data_layer()
    tile_set.set_custom_data_layer_name(0, "damage")
    tile_set.set_custom_data_layer_type(0, TYPE_INT)

    var atlas := TileSetAtlasSource.new()
    atlas.texture = load("res://art/tiles.png")
    atlas.texture_region_size = Vector2i(16, 16)
    tile_set.add_source(atlas, 0)                  # add before creating tiles
    atlas.create_tile(Vector2i(2, 1))              # column 2, row 1 of the texture
    var wall := atlas.get_tile_data(Vector2i(2, 1), 0)
    wall.add_collision_polygon(0)
    wall.set_collision_polygon_points(0, 0, PackedVector2Array([
        Vector2(-8, -8), Vector2(8, -8), Vector2(8, 8), Vector2(-8, 8)]))  # cell-centred
    wall.set_custom_data("damage", 0)

    var err := ResourceSaver.save(tile_set, "res://tiles/world_tileset.tres")
    print("saved tileset: ", error_string(err))
    quit(0 if err == OK else 1)

Add the source to the TileSet before `create_tile`, so the tile's data knows the TileSet's physics and custom data layers. Polygon points are relative to the cell centre.

2. Mouse position to a cell, then read custom data

extends TileMapLayer

func _unhandled_input(event: InputEvent) -> void:
    if event is InputEventMouseButton and event.pressed:
        # event.position is in viewport pixels and ignores Camera2D scroll/zoom.
        var local_event := make_input_local(event) as InputEventMouseButton
        var cell := local_to_map(local_event.position)
        var data := get_cell_tile_data(cell)       # TileData or null
        if data:
            var dmg: int = data.get_custom_data("damage")
            print("Cell %s deals %d damage" % [cell, dmg])

3. Place, erase and autotile at runtime

func place_wall(cell: Vector2i) -> void:
    set_cell(cell, 0, Vector2i(2, 1))     # source 0, atlas tile at column 2, row 1

func dig(cell: Vector2i) -> void:
    erase_cell(cell)                      # same as set_cell(cell, -1)

func fill_with_grass(cells: Array[Vector2i]) -> void:
    set_cells_terrain_connect(cells, 0, 0, true)   # terrain set 0, terrain 0

4. Headless test: a placed wall has collision and data

# res://tests/test_tiles.gd
extends SceneTree

func _initialize() -> void:
    var layer := TileMapLayer.new()
    layer.tile_set = load("res://tiles/world_tileset.tres")
    root.add_child(layer)
    layer.set_cell(Vector2i(0, 0), 0, Vector2i(2, 1))
    var data := layer.get_cell_tile_data(Vector2i(0, 0))
    var failed := 0
    if data == null:
        failed += 1
        push_error("wall tile missing: wrong source id or atlas coords?")
    elif
Read more
@gamedev-skills
Ships withawesome-gamedev-agent-skills

<img src="docs/assets/awesome-gamedev-agent-skills-banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.

Stars on GitHub

Momentum

stars in total
6.3k
Star history of gamedev-skills/awesome-gamedev-agent-skills on GitHub, a chart by star-history.com

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills12d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react3d252k
@facebook@facebookInstall Skill

Questions about godot-tilemap.

Which coding agents can use godot-tilemap?
godot-tilemap installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.