TileMapLayer (Godot 4.3+)
Godot 4.3 añadió el nodo TileMapLayer y dejó obsoleto TileMap. Cada TileMapLayer es una capa, por lo que un nivel con suelo, paredes y decoración son tres nodos TileMapLayer hermanos que comparten un TileSet. No escribas código nuevo contra TileMap, y nunca uses la API de Godot 3 (set_cellv, world_to_map, set_cell(x, y, tile_id)).
La API de celdas
Una celda se identifica por coordenadas enteras de mapa (Vector2i). Una baldosa se identifica por tres valores:
source_id: el ID de una fuente en elTileSet(una textura de atlas o una colección de escenas). El primer atlas suele ser0; comprueba en el editor de TileSet o el valor de retorno deTileSet.add_source().atlas_coords: la columna y fila de la baldosa dentro de ese atlas, en baldosas, no en píxeles.alternative_tile:0a menos que hayas creado alternativas volteadas o rotadas.
extends TileMapLayer
const SOURCE_ID := 0
const GRASS := Vector2i(0, 0)
const WALL := Vector2i(1, 0)
func _ready() -> void:
for x in range(10):
set_cell(Vector2i(x, 5), SOURCE_ID, GRASS)
set_cell(Vector2i(3, 4), SOURCE_ID, WALL)
var source_id := get_cell_source_id(Vector2i(3, 4)) # -1 cuando la celda está vacía
var atlas := get_cell_atlas_coords(Vector2i(3, 4)) # Vector2i(-1, -1) cuando está vacía
print(source_id, atlas)
erase_cell(Vector2i(3, 4))
print(get_used_cells().size(), " celdas, límites ", get_used_rect())
func cell_at_global(global_point: Vector2) -> Vector2i:
return local_to_map(to_local(global_point))
func center_of_cell(coords: Vector2i) -> Vector2:
# map_to_local devuelve el centro de la celda en el espacio local de esta capa.
return to_global(map_to_local(coords))
Hechos importantes:
set_cell(coords)con los argumentos por defecto borra la celda (source_idpor defecto es-1). Siempre pasasource_idyatlas_coordsal colocar una baldosa.- Establecer
atlas_coordsque no existen en el atlas almacena la celda sin error (get_cell_source_id()devuelve la fuente), yget_cell_tile_data()más tarde registraThe TileSetAtlasSource atlas has no tile at (x, y).y devuelvenull. local_to_map()ymap_to_local()trabajan en el espacio local de la capa. Convierte conto_local()/to_global()cuando la capa se mueve o escala.get_used_cells_by_id(source_id, atlas_coords)encuentra todas las celdas que usan una baldosa.- Los cambios se agrupan al final del frame. Llama a
update_internals()si necesitas que la colisión o las baldosas de escena se actualicen inmediatamente.
Datos personalizados por baldosa
Añade una capa de datos personalizados en el TileSet (Inspector > TileSet > Custom Data Layers, por ejemplo damage de tipo int), pinta valores en el editor de TileSet, luego léelos:
extends TileMapLayer
func damage_at(coords: Vector2i) -> int:
var data := get_cell_tile_data(coords)
if data == null:
return 0
return data.get_custom_data("damage")
get_cell_tile_data() devuelve null para celdas vacías y para baldosas de colección de escenas, así que comprueba antes de llamar a métodos sobre él.
Terrenos (autotiling)
Las máscaras de bits de autotile de Godot 3 se convirtieron en conjuntos de terrenos. En el TileSet, añade un conjunto de terrenos y un terreno, luego pinta los bits de conexión de cada baldosa en el editor de TileSet. En código, pasa todas las celdas que quieras rellenar:
extends TileMapLayer
const TERRAIN_SET := 0
const DIRT := 0
func paint_room(rect: Rect2i) -> void:
var cells: Array[Vector2i] = []
for x in range(rect.position.x, rect.end.x):
for y in range(rect.position.y, rect.end.y):
cells.append(Vector2i(x, y))
set_cells_terrain_connect(cells, TERRAIN_SET, DIRT)
set_cells_terrain_connect() elige baldosas para que los bordes se conecten; set_cells_terrain_path() es para caminos y ríos dibujados como un recorrido. Si el resultado muestra baldosas incorrectas o faltantes, los bits de conexión en el TileSet están incompletos; el código no puede solucionarlo.
Colisión
Las baldosas colisionan solo cuando el TileSet tiene una capa física (Inspector de TileSet > Physics Layers > Add Element) y la baldosa tiene un polígono de colisión en esa capa (editor de TileSet, selecciona la baldosa, Physics > Physics Layer 0; presiona F para un rectángulo de baldosa completa). La propiedad collision_enabled de la capa está activada por defecto. Las capas físicas también llevan collision_layer y collision_mask, establecidos en el TileSet, no en el nodo.
Para encontrar qué baldosa tocó un CharacterBody2D, usa el punto de colisión. get_coords_for_body_rid() devuelve las coordenadas de un cuadrante físico, no de una celda, a menos que physics_quadrant_size sea 1 (Godot 4.5+ fusiona hasta 16x16 celdas en un cuerpo por defecto):
extends CharacterBody2D
@export var level: TileMapLayer
func _physics_process(delta: float) -> void:
velocity += get_gravity() * delta
move_and_slide()
for i in get_slide_collision_count():
var collision := get_slide_collision(i)
if collision.get_collider() == level:
# Avanza ligeramente dentro de la baldosa, porque el punto de contacto está en su borde.
var inside := collision.get_position() - collision.get_normal()
var coords := level.local_to_map(level.to_local(inside))
print("Tocando baldosa ", coords)
Construir un TileSet en código
Útil para niveles procedurales y para pruebas que no pueden usar el editor de TileSet. Los puntos del polígono de colisión son relativos al centro de la baldosa.
extends Node2D
func _ready() -> void:
var tile_set := TileSet.new()
tile_set.tile_size = Vector2i(16, 16)
tile_set.add_physics_layer()
var source := TileSetAtlasSource.new()
source.texture = load("res://tiles.png") as Texture2D
source.texture_region_size = Vector2i(16, 16)
source.create_tile(Vector2i(0, 0))
var source_id := tile_set.add_source(source)
var data := source.get_tile_data(Vector2i(0, 0), 0)
data.add_collision_polygon(0)
data.set_collision_polygon_points(0, 0, PackedVector2Array([
Vector2(-8, -8), Vector2(8, -8), Vector2(8, 8), Vector2(-8, 8),
]))
var layer := TileMapLayer.new()
layer.tile_set = tile_set
add_child(layer)
layer.set_cell(Vector2i(0, 0), source_id, Vector2i(0, 0))
Migrar desde TileMap
En el editor: selecciona el TileMap, abre el panel inferior TileMap, haz clic en el icono de la caja de herramientas y elige "Extract TileMap layers as individual TileMapLayer nodes". Luego actualiza los scripts: cada método de TileMap que tomaba un índice de layer ahora es un método en el nodo de esa capa, sin el índice.
| Código antiguo | TileMapLayer |
|---|---|
tile_map.set_cell(0, coords, source_id, atlas_coords) | ground.set_cell(coords, source_id, atlas_coords) |
tile_map.get_cell_source_id(1, coords) | walls.get_cell_source_id(coords) |
tile_map.get_used_cells(0) | ground.get_used_cells() |
tile_map.set_layer_enabled(2, false) | decoration.enabled = false |
tile_map.set_layer_z_index(1, 5) | walls.z_index = 5 |
Godot 3 set_cellv(pos, id), world_to_map(p), map_to_world(c) | set_cell(coords, source_id, atlas_coords), local_to_map(p), map_to_local(c) |
Errores comunes
- Pasar posiciones de píxeles a
set_cell(). Convierte primero conlocal_to_map(). - Llamar a
set_cell(coords, tile_index)con un solo número: el segundo argumento es el source ID, yatlas_coordspor defecto esVector2i(-1, -1), lo que borra la celda. - Esperar colisiones de un TileSet que no tiene capa física, o de baldosas sin polígono pintado.
- Usar
get_coords_for_body_rid()con elphysics_quadrant_sizepor defecto y obtener una coordenada de cuadrante. - Llamar a métodos sobre
get_cell_tile_data()sin comprobar si esnull. - Añadir un nodo
TileMappara trabajo nuevo porque tutoriales antiguos lo hacen. Usa unTileMapLayerpor capa.