Godot TileMap V2 Migration
Migrates legacy TileMap system to Godot 4.3+ TileMapLayer architecture.
Breaking Changes
Godot 4.3 introduced a new TileMap system with breaking changes:
| Legacy (4.0-4.2) | New (4.3+) |
|---|---|
Single TileMap node | Multiple TileMapLayer nodes |
set_cell() method | set_cells_terrain_connect() |
| Cell-based coordinates | Layer-based organization |
| Single TileSet resource | Enhanced TileSet with layers |
update_bitmask_region() | Terrain system |
| Manual physics setup | Physics layers per tile |
UPON INVOCATION - START HERE
When this skill is invoked, IMMEDIATELY execute:
1. Verify Godot Project (5 seconds)
ls project.godot 2>/dev/null && echo "✓ Godot project detected" || echo "✗ Not a Godot project"
If NOT a Godot project:
- •Inform user this skill only works on Godot projects
- •STOP here
2. Detect TileMap Usage (10 seconds)
echo "=== Detecting TileMap Usage ===" echo "TileMap nodes in scenes:" grep -rn "type=\"TileMap\"" --include="*.tscn" . | wc -l echo "TileMap references in scripts:" grep -rn "TileMap" --include="*.gd" . | wc -l echo "set_cell calls:" grep -rn "set_cell\|get_cell" --include="*.gd" . | wc -l echo "update_bitmask calls:" grep -rn "update_bitmask" --include="*.gd" . | wc -l echo "tile_set property access:" grep -rn "\.tile_set" --include="*.gd" . | wc -l
3. Present Findings
Show the user:
=== TileMap V2 Migration Analysis === Project: [project name] Current: Legacy TileMap (Godot 4.0-4.2 style) TileMap usage detected: - TileMap nodes: X - TileMap references: X - set_cell/get_cell calls: X - update_bitmask calls: X - tile_set access: X Migration includes: ✓ TileMap → TileMapLayer node conversion ✓ set_cell() → set_cells_terrain_connect() ✓ TileSet resource upgrade ✓ Physics layer mapping ✓ Navigation layer conversion ✓ Custom data preservation ✓ Terrain system setup ✓ Git commit per layer ✓ Backup before changes ⚠️ WARNING: Godot 4.3+ required for new TileMapLayer system Would you like me to: 1. Proceed with migration (recommended) 2. Show detailed breakdown first 3. Migrate specific TileMaps only 4. Cancel
4. Wait for User Choice
- •If 1 (Proceed): Start Phase 2 immediately
- •If 2 (Details): Show file-by-file breakdown
- •If 3 (Selective): Ask which TileMaps to migrate
- •If 4 (Cancel): Exit skill
Phase 1: Analysis & Inventory
1.1 Create TileMap Inventory
# Find all TileMap nodes in .tscn files
grep -rn "type=\"TileMap\"" --include="*.tscn" . | while read line; do
file=$(echo "$line" | cut -d: -f1)
echo "Found TileMap in: $file"
done > /tmp/tilemap_scenes.txt
# Find all TileMap references in scripts
grep -rn "extends TileMap\|var.*TileMap\|: TileMap" --include="*.gd" . | while read line; do
file=$(echo "$line" | cut -d: -f1)
echo "TileMap usage in: $file"
done > /tmp/tilemap_scripts.txt
1.2 Analyze TileMap Structure
For each TileMap node:
# Extract TileMap configuration from .tscn grep -A20 "type=\"TileMap\"" scene.tscn | grep -E "tile_set|format|cell|layer"
1.3 Create Migration Plan
TileMap V2 Migration Plan: ========================= 1. Node Structure Changes (X TileMaps) - Convert TileMap → TileMapLayer nodes - Preserve layer order and names - Update scene hierarchy 2. API Migration (X scripts) - set_cell() → set_cells_terrain_connect() - get_cell() → get_cell_alternative_tile() - update_bitmask() → terrain system 3. TileSet Updates (X resources) - Upgrade TileSet format - Map physics layers - Configure navigation layers - Preserve custom data 4. Script Updates (X files) - Update TileMap references - Fix method calls - Update signal connections Total operations: X Estimated time: Auto Godot version required: 4.3+ Backup created: YES Rollback available: YES
Phase 2: TileMap Node Migration
2.1 Convert TileMap to TileMapLayer
Legacy Structure:
[node name="TileMap" type="TileMap"]
tile_set = ExtResource("1_qwerty")
format = 2
layer_0/name = "Ground"
layer_0/tile_data = PackedInt32Array(...)
layer_1/name = "Walls"
layer_1/tile_data = PackedInt32Array(...)
New Structure:
[node name="Ground" type="TileMapLayer" parent="."]
tile_map_data = PackedByteArray(...)
tile_set = ExtResource("1_qwerty")
[node name="Walls" type="TileMapLayer" parent="."]
tile_map_data = PackedByteArray(...)
tile_set = ExtResource("1_qwerty")
Transformation Process:
- •
Parse legacy TileMap
- •Extract layer names
- •Extract layer data
- •Extract TileSet reference
- •Extract position/z-index
- •
Generate TileMapLayer nodes
- •One node per layer
- •Name from layer name
- •Convert tile_data format
- •Preserve TileSet reference
- •
Update scene hierarchy
- •Remove TileMap node
- •Add TileMapLayer children
- •Update parent references
2.2 Tile Data Conversion
Legacy Format:
layer_0/tile_data = PackedInt32Array(0, 1, 0, 65536, 1, 0, ...)
New Format:
tile_map_data = PackedByteArray(0, 0, 0, 0, 1, 0, 0, 0, ...)
Note: Direct byte conversion requires Godot's internal format knowledge. Alternative approach:
# Migration script approach # 1. Load scene with legacy TileMap in Godot 4.3 # 2. Run migration tool script # 3. Save with new TileMapLayer format
Phase 3: API Migration
3.1 Method Call Updates
Detection:
grep -rn "set_cell\|get_cell\|update_bitmask" --include="*.gd" .
Common Mappings:
| Legacy API | New API |
|---|---|
set_cell(layer, coords, source_id) | set_cells_terrain_connect(coords_array, terrain_set, terrain) |
get_cell(layer, coords) | get_cell_alternative_tile(coords) |
get_cell_source_id(layer, coords) | get_cell_source_id(coords) |
update_bitmask_region(start, end) | Terrain system (set terrain bits) |
clear_layer(layer) | clear() on TileMapLayer |
erase_cell(layer, coords) | erase_cell(coords) |
3.2 Script Transformations
Example 1: Setting a cell
Before:
# Legacy TileMap API tilemap.set_cell(0, Vector2i(5, 3), 1) tilemap.set_cell(0, Vector2i(5, 4), 1) tilemap.set_cell(0, Vector2i(6, 3), 1)
After:
# New TileMapLayer API - Using terrain for multiple cells
ground_layer.set_cells_terrain_connect(
[Vector2i(5, 3), Vector2i(5, 4), Vector2i(6, 3)],
0, # terrain_set
1 # terrain_id
)
Example 2: Getting cell data
Before:
var source_id = tilemap.get_cell_source_id(0, coords) var atlas_coords = tilemap.get_cell_atlas_coords(0, coords)
After:
var source_id = ground_layer.get_cell_source_id(coords) var atlas_coords = ground_layer.get_cell_atlas_coords(coords)
Example 3: Autotile/Bitmask
Before:
tilemap.set_cell(0, coords, 1) tilemap.update_bitmask_region(Rect2i(0, 0, 20, 20))
After:
# Using terrain system ground_layer.set_cells_terrain_connect([coords], 0, 1) # Terrain automatically connects - no update_bitmask needed!
Phase 4: TileSet Resource Migration
4.1 Physics Layers
Legacy: Physics setup per tile in TileSet
New: Dedicated physics layers
# New TileSet setup in Godot 4.3 tileset.set_physics_layer_collision_layer(0, 1) tileset.set_physics_layer_collision_mask(0, 1) tileset.set_physics_layer_physics_material_override(0, physics_material)
4.2 Navigation Layers
Legacy: Navigation polygons per tile
New: Navigation layers system
# Enable navigation layer tileset.set_navigation_layer_enabled(0, true) # Set navigation polygon for specific tile var source_id = 1 var atlas_coords = Vector2i(0, 0) tileset.set_navigation_polygon(0, source_id, atlas_coords, navigation_polygon)
4.3 Custom Data Layers
New in 4.3: Custom data per tile
# Define custom data layer tileset.add_custom_data_layer() tileset.set_custom_data_layer_name(0, "tile_health") tileset.set_custom_data_layer_type(0, TYPE_INT) # Set data on specific tile tileset.set_custom_data(0, source_id, atlas_coords, 100)
Phase 5: Execution & Safety
5.1 Create Git Baseline
# Create backup tag git tag tilemap-v1-baseline-$(date +%Y%m%d-%H%M%S) # Commit any current changes git add . git commit -m "Baseline: Pre-TileMap V2 migration" || echo "No changes to commit"
5.2 Migration Execution
For each TileMap:
- •
Backup scene file
bashcp scene.tscn scene.tscn.backup
- •
Parse TileMap data
- •Extract layers
- •Extract tile data
- •Note TileSet reference
- •
Generate TileMapLayer nodes
- •Create node entries
- •Convert data format
- •Set properties
- •
Update scripts
- •Replace TileMap references
- •Update method calls
- •Fix signal connections
- •
Commit changes
bashgit add scene.tscn modified_scripts.gd git commit -m "Migrate: TileMap to TileMapLayer in scene.tscn - Converted TileMap node to TileMapLayer nodes - Updated script references - Migrated set_cell() calls - Preserved layer data"
5.3 Validation After Each Migration
# Check scene loads in Godot
godot --headless --quit-after 5 project.godot 2>&1 | tee test.log
# Check for errors
if grep -q "ERROR\|SCRIPT ERROR" test.log; then
echo "⚠️ Migration error detected!"
git reset --hard HEAD~1
echo "✓ Reverted to pre-migration state"
exit 1
fi
rm test.log
Phase 6: Post-Migration Setup
6.1 Terrain System Configuration
# Setup terrain for autotiling tileset.add_terrain_set() tileset.set_terrain_set_mode(0, TileSet.TerrainMode.CONNECT) # Add terrain tileset.add_terrain(0, 0) tileset.set_terrain_name(0, 0, "Ground") # Assign terrain to tiles # (Configure in Godot editor for best results)
6.2 Physics Layer Setup
# Configure physics layers per tile type tileset.set_physics_layer_collision_layer(0, 1) # Layer 1 tileset.set_physics_layer_collision_mask(0, 1) # Detect layer 1
6.3 Navigation Setup
# Enable navigation on specific tiles navigation_layer.enabled = true tileset.set_navigation_layer_enabled(0, true)
Examples
Example 1: Complete TileMap Migration
Before (Godot 4.2):
# level.tscn
[node name="TileMap" type="TileMap"]
tile_set = ExtResource("1_tileset")
format = 2
layer_0/name = "Ground"
layer_0/tile_data = PackedInt32Array(...)
layer_1/name = "Obstacles"
layer_1/tile_data = PackedInt32Array(...)
# level.gd
extends Node2D
@onready var tilemap: TileMap = $TileMap
func _ready():
# Set some cells
tilemap.set_cell(0, Vector2i(10, 5), 1)
tilemap.set_cell(0, Vector2i(11, 5), 1)
tilemap.update_bitmask_region(Rect2i(10, 5, 2, 1))
# Get cell info
var source_id = tilemap.get_cell_source_id(0, Vector2i(10, 5))
print("Cell source: ", source_id)
func generate_level():
for x in range(20):
for y in range(15):
tilemap.set_cell(0, Vector2i(x, y), randi() % 3)
tilemap.update_bitmask_region(Rect2i(0, 0, 20, 15))
After (Godot 4.3):
# level.tscn
[node name="Ground" type="TileMapLayer" parent="."]
tile_set = ExtResource("1_tileset")
tile_map_data = PackedByteArray(...)
[node name="Obstacles" type="TileMapLayer" parent="."]
tile_set = ExtResource("1_tileset")
tile_map_data = PackedByteArray(...)
# level.gd
extends Node2D
@onready var ground_layer: TileMapLayer = $Ground
@onready var obstacles_layer: TileMapLayer = $Obstacles
func _ready():
# Set cells using terrain
ground_layer.set_cells_terrain_connect(
[Vector2i(10, 5), Vector2i(11, 5)],
0, # terrain_set
1 # terrain_id
)
# Get cell info
var source_id = ground_layer.get_cell_source_id(Vector2i(10, 5))
print("Cell source: ", source_id)
func generate_level():
var coords_array = []
for x in range(20):
for y in range(15):
coords_array.append(Vector2i(x, y))
# Use terrain for efficient bulk placement
ground_layer.set_cells_terrain_connect(coords_array, 0, randi() % 3)
Example 2: Multiple Layer Management
Before:
# Access different layers
func set_ground_cell(coords: Vector2i, tile: int):
tilemap.set_cell(0, coords, tile)
func set_wall_cell(coords: Vector2i, tile: int):
tilemap.set_cell(1, coords, tile)
func clear_all():
tilemap.clear_layer(0)
tilemap.clear_layer(1)
After:
# Access different layers
func set_ground_cell(coords: Vector2i, tile: int):
ground_layer.set_cells_terrain_connect([coords], 0, tile)
func set_wall_cell(coords: Vector2i, tile: int):
obstacles_layer.set_cell(coords, tile)
func clear_all():
ground_layer.clear()
obstacles_layer.clear()
Success Criteria
Migration complete when:
- •✓ Zero TileMap nodes remain (converted to TileMapLayer)
- •✓ Zero
set_cell(layer, ...)calls remain - •✓ Zero
update_bitmaskcalls remain - •✓ All scripts use TileMapLayer references
- •✓ TileSet resources upgraded to new format
- •✓ Physics layers configured (if used)
- •✓ Navigation layers configured (if used)
- •✓ Custom data preserved (if any)
- •✓ All scenes load without errors
- •✓ Runtime behavior identical
Common Issues & Solutions
Issue 1: Layer index confusion
Problem: Old code used layer indices (0, 1, 2)
Solution: Replace with explicit layer references
# Before tilemap.set_cell(0, coords, tile) # Layer 0 # After ground_layer.set_cell(coords, tile) # Named layer
Issue 2: Bulk cell updates
Problem: update_bitmask_region() no longer exists
Solution: Use terrain system or set_cells_terrain_connect()
# Before
for x in range(width):
for y in range(height):
tilemap.set_cell(0, Vector2i(x, y), source_id)
tilemap.update_bitmask_region(Rect2i(0, 0, width, height))
# After
var coords_array = []
for x in range(width):
for y in range(height):
coords_array.append(Vector2i(x, y))
ground_layer.set_cells_terrain_connect(coords_array, 0, source_id)
Issue 3: Cell coordinate systems
Problem: Coordinate system differences between TileMap and TileMapLayer
Solution: Both use Vector2i, but ensure local vs global coordinates
# Both work similarly var local_coords = tilemap.local_to_map(position) # Legacy var local_coords = layer.local_to_map(position) # New
Rollback Procedure
If migration causes issues:
# Find baseline tag git tag | grep "tilemap-v1-baseline" # Reset to pre-migration git reset --hard <baseline-tag> # Or restore individual scene from backup cp scene.tscn.backup scene.tscn
Godot Version Requirements
Minimum: Godot 4.3+
Check version:
godot --version # Should show 4.3 or higher
Upgrade notes:
- •Open project in Godot 4.3 first (converts project.godot)
- •Then run this migration skill
- •Test thoroughly before committing
Integration with Other Skills
Use with:
- •
godot-modernize-gdscript- Update syntax before TileMap migration - •
godot-refactor- Clean up code after migration - •
godot-setup-navigation- Configure navigation on new TileMapLayer
Best practice workflow:
- •Run godot-modernize-gdscript (update syntax)
- •Run godot-migrate-tilemap (this skill)
- •Run godot-refactor (clean architecture)
- •Manual testing in Godot 4.3+
This skill handles the complex TileMap API changes in Godot 4.3, preserving your level data while upgrading to the modern TileMapLayer system.