TileMapLayer (Godot 4.3+)
Godot 4.3 a ajouté le nœud TileMapLayer et rendu TileMap obsolète. Chaque TileMapLayer représente une seule couche, donc un niveau avec sol, murs et décoration sera composé de trois nœuds TileMapLayer frères partageant un même TileSet. N'écrivez pas de nouveau code utilisant TileMap, et n'utilisez jamais l'API Godot 3 (set_cellv, world_to_map, set_cell(x, y, tile_id)).
L'API des cellules
Une cellule est identifiée par des coordonnées entières de carte (Vector2i). Une tuile est identifiée par trois valeurs :
source_id: l'ID d'une source dans leTileSet(une texture d'atlas ou une collection de scènes). La première source est généralement0; vérifiez dans l'éditeur TileSet ou avec la valeur de retour deTileSet.add_source().atlas_coords: la colonne et la ligne de la tuile dans cet atlas, en tuiles, pas en pixels.alternative_tile:0sauf si vous avez créé des alternatives retournées ou pivotées.
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 quand la cellule est vide
var atlas := get_cell_atlas_coords(Vector2i(3, 4)) # Vector2i(-1, -1) quand vide
print(source_id, atlas)
erase_cell(Vector2i(3, 4))
print(get_used_cells().size(), " cellules, limites ", 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 retourne le centre de la cellule dans l'espace local de cette couche.
return to_global(map_to_local(coords))
Points importants :
set_cell(coords)avec les arguments par défaut efface la cellule (source_idvaut par défaut-1). Passez toujourssource_idetatlas_coordslorsque vous placez une tuile.- Définir des
atlas_coordsqui n'existent pas dans l'atlas enregistre la cellule sans erreur (get_cell_source_id()retourne la source), etget_cell_tile_data()affichera plus tardThe TileSetAtlasSource atlas has no tile at (x, y).et retourneranull. local_to_map()etmap_to_local()fonctionnent dans l'espace local de la couche. Convertissez avecto_local()/to_global()lorsque la couche est déplacée ou mise à l'échelle.get_used_cells_by_id(source_id, atlas_coords)trouve toutes les cellules utilisant une tuile particulière.- Les modifications sont regroupées jusqu'à la fin de l'image. Appelez
update_internals()si vous avez besoin que les collisions ou les tuiles de scène soient mises à jour immédiatement.
Données personnalisées par tuile
Ajoutez une couche de données personnalisées dans le TileSet (Inspecteur > TileSet > Couches de données personnalisées, par exemple damage de type int), peignez les valeurs dans l'éditeur TileSet, puis lisez-les :
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() retourne null pour les cellules vides et pour les tuiles de collection de scènes, donc vérifiez avant d'appeler des méthodes dessus.
Terrains (autotiling)
Les masques de bits autotile de Godot 3 sont devenus des ensembles de terrains. Dans le TileSet, ajoutez un ensemble de terrains et un terrain, puis peignez les bits de voisinage de chaque tuile dans l'éditeur TileSet. En code, passez toutes les cellules que vous voulez remplir :
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() choisit les tuiles pour que les bords se connectent ; set_cells_terrain_path() est pour les routes et rivières dessinées comme un chemin. Si le résultat montre des tuiles incorrectes ou manquantes, les bits de voisinage dans le TileSet sont incomplets ; le code ne peut pas corriger cela.
Collision
Les tuiles n'ont de collision que si le TileSet possède une couche physique (Inspecteur TileSet > Couches physiques > Ajouter un élément) et que la tuile a un polygone de collision sur cette couche (éditeur TileSet, sélectionnez la tuile, Physique > Couche physique 0 ; appuyez sur F pour un rectangle pleine tuile). La propriété collision_enabled de la couche est activée par défaut. Les couches physiques portent également collision_layer et collision_mask, définis sur le TileSet, pas sur le nœud.
Pour trouver quelle tuile un CharacterBody2D a touchée, utilisez le point de collision. get_coords_for_body_rid() retourne les coordonnées d'un quadrant physique, pas d'une cellule, sauf si physics_quadrant_size est 1 (Godot 4.5+ fusionne jusqu'à 16 x 16 cellules en un seul corps par défaut) :
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:
# Avancez légèrement dans la tuile, car le point de contact est sur son bord.
var inside := collision.get_position() - collision.get_normal()
var coords := level.local_to_map(level.to_local(inside))
print("Touche la tuile ", coords)
Construire un TileSet en code
Utile pour les niveaux procéduraux et pour les tests qui ne peuvent pas utiliser l'éditeur TileSet. Les points du polygone de collision sont relatifs au centre de la tuile.
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))
Migration depuis TileMap
Dans l'éditeur : sélectionnez le TileMap, ouvrez le panneau inférieur TileMap, cliquez sur l'icône de boîte à outils et choisissez "Extraire les couches TileMap en tant que nœuds TileMapLayer individuels". Ensuite, mettez à jour les scripts : chaque méthode de TileMap qui prenait un index de layer en premier argument devient une méthode sur le nœud de cette couche, sans l'index.
| Ancien code | 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) |
Erreurs courantes
- Passer des positions en pixels à
set_cell(). Convertissez d'abord aveclocal_to_map(). - Appeler
set_cell(coords, tile_index)avec un seul nombre : le deuxième argument est l'ID de source, etatlas_coordsvaut par défautVector2i(-1, -1), ce qui efface la cellule. - Attendre des collisions d'un TileSet sans couche physique, ou de tuiles sans polygone peint.
- Utiliser
get_coords_for_body_rid()avec la valeur par défaut dephysics_quadrant_sizeet obtenir une coordonnée de quadrant. - Appeler des méthodes sur
get_cell_tile_data()sans vérificationnull. - Ajouter un nœud
TileMappour un nouveau travail parce que des tutoriels plus anciens le font. Utilisez unTileMapLayerpar couche.