--- name: godot-migrate-tilemap version: 1.0.0 displayName: "Godot TileMap V2 Migration" description: > Migrates legacy TileMap (Godot 4.0-4.2) to new TileMapLayer system (Godot 4.3+). Handles TileMap node conversion, API migration (set_cell to set_cells_terrain_connect), TileSet resource updates, physics layer mapping, navigation layer conversion, and custom data preservation. Essential for upgrading Godot 4.2 projects to 4.3+. author: "Asreonn" license: MIT category: game-development type: tool difficulty: advanced audience: [developers, migrators] keywords: - godot - tilemap - tilemaplayer - migration - godot43 - tileset - terrain - navigation - physics platforms: [macos, linux, windows] repository: https://github.com/asreonn/godot-superpowers homepage: https://github.com/asreonn/godot-superpowers#readme filesystem: read: - "${PROJECT_ROOT}/**/*.gd" - "${PROJECT_ROOT}/**/*.tscn" - "${PROJECT_ROOT}/**/*.tres" - "${PROJECT_ROOT}/project.godot" write: - "${PROJECT_ROOT}/**/*.gd" - "${PROJECT_ROOT}/**/*.tscn" - "${PROJECT_ROOT}/**/*.tres" deny: - "**/.env*" - "**/secrets*" - "**/*.key" behavior: timeout: 600 retry: 2 cache: true interactive: true --- # 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) ```bash 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) ```bash 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 ```bash # 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: ```bash # 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:** ```ini [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:** ```ini [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:** 1. **Parse legacy TileMap** - Extract layer names - Extract layer data - Extract TileSet reference - Extract position/z-index 2. **Generate TileMapLayer nodes** - One node per layer - Name from layer name - Convert tile_data format - Preserve TileSet reference 3. **Update scene hierarchy** - Remove TileMap node - Add TileMapLayer children - Update parent references --- ### 2.2 Tile Data Conversion **Legacy Format:** ```ini layer_0/tile_data = PackedInt32Array(0, 1, 0, 65536, 1, 0, ...) ``` **New Format:** ```ini tile_map_data = PackedByteArray(0, 0, 0, 0, 1, 0, 0, 0, ...) ``` **Note:** Direct byte conversion requires Godot's internal format knowledge. Alternative approach: ```gdscript # 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:** ```bash 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: ```gdscript # 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: ```gdscript # 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: ```gdscript var source_id = tilemap.get_cell_source_id(0, coords) var atlas_coords = tilemap.get_cell_atlas_coords(0, coords) ``` After: ```gdscript 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: ```gdscript tilemap.set_cell(0, coords, 1) tilemap.update_bitmask_region(Rect2i(0, 0, 20, 20)) ``` After: ```gdscript # 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 ```gdscript # 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 ```gdscript # 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 ```gdscript # 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 ```bash # 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: 1. **Backup scene file** ```bash cp scene.tscn scene.tscn.backup ``` 2. **Parse TileMap data** - Extract layers - Extract tile data - Note TileSet reference 3. **Generate TileMapLayer nodes** - Create node entries - Convert data format - Set properties 4. **Update scripts** - Replace TileMap references - Update method calls - Fix signal connections 5. **Commit changes** ```bash git 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 ```bash # 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 ```gdscript # 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 ```gdscript # 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 ```gdscript # 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):** ```gdscript # 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):** ```gdscript # 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:** ```gdscript # 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:** ```gdscript # 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_bitmask` calls 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 ```gdscript # 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() ```gdscript # 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 ```gdscript # 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: ```bash # Find baseline tag git tag | grep "tilemap-v1-baseline" # Reset to pre-migration git reset --hard # Or restore individual scene from backup cp scene.tscn.backup scene.tscn ``` --- ## Godot Version Requirements **Minimum:** Godot 4.3+ **Check version:** ```bash 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:** 1. Run godot-modernize-gdscript (update syntax) 2. Run godot-migrate-tilemap (this skill) 3. Run godot-refactor (clean architecture) 4. 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.**