TileMap no Godot 4: Criar Mapas 2D do Zero

Tutorial de TileMap no Godot 4 do zero: TileSet, colisão por tile, camadas com TileMapLayer, autotiling com terrains e como pintar o mapa por código.
TileMap no Godot 4: Criar Mapas 2D do Zero
Quase todo jogo 2D que você já jogou foi montado com tiles: pedacinhos de imagem repetidos formando chão, parede, plataforma e decoração. O TileMap no Godot 4 é a ferramenta que faz isso, e ela mudou bastante em relação ao Godot 3. Quem chega com tutorial antigo na cabeça se perde no editor novo, e quem chega do zero esbarra em três coisas: onde configurar colisão, como organizar camadas e o que diabos é "terrain".
Esse tutorial monta o caminho completo: criar o TileSet, pintar o mapa, colocar colisão por tile, separar em camadas, ligar o autotiling e, no final, manipular tudo por código. Todo código aqui é GDScript do Godot 4.3 ou mais novo e roda como está.
TileMap ou TileMapLayer? A mudança do Godot 4.3
Antes de abrir o editor, um aviso que economiza confusão. Até o Godot 4.2, existia um node chamado TileMap que guardava várias camadas dentro de si. A partir do 4.3, esse node foi deprecado e cada camada virou um node próprio: o TileMapLayer.
Na prática:
- Godot 4.3+: use um node
TileMapLayerpra cada camada (chão, parede, decoração). É o jeito atual e é o que esse tutorial usa. - Projeto antigo com
TileMap: continua funcionando, e o editor oferece um botão pra converter pra TileMapLayers. Vale converter, a API nova é mais simples.
Se o tutorial que você está seguindo fala em set_layer_* ou em camadas dentro de um único node, ele é do 4.0 ao 4.2. Os conceitos valem, os nomes não.
Criando o TileSet
O TileSet é o recurso que diz quais tiles existem, de qual textura eles vêm e quais propriedades cada um carrega (colisão, terrain, dados customizados). O TileMapLayer só pinta o que o TileSet define.
O fluxo no editor:
- Adicione um node
TileMapLayerna cena. - No Inspector, no campo Tile Set, crie um New TileSet.
- Ainda no Inspector, ajuste o Tile Size pro tamanho do seu tile (16x16, 32x32, o que sua arte usar). Isso precisa bater com a spritesheet, senão tudo desalinha.
- Clique no recurso TileSet pra abrir o painel TileSet na parte de baixo do editor.
- Arraste sua spritesheet pra dentro desse painel. O Godot pergunta se quer criar os tiles automaticamente; aceite, ele fatia a imagem inteira no tamanho configurado.
Isso cria um atlas: uma fonte de tiles baseada numa única textura. Cada tile do atlas tem uma coordenada, tipo (0, 0) pro primeiro, (1, 0) pro vizinho da direita. Essas coordenadas importam depois, quando você for pintar por código.
Com o TileSet pronto, selecione o node TileMapLayer e abra o painel TileMap embaixo. Escolha um tile e pinte na viewport com o botão esquerdo. Botão direito apaga. Tem ferramenta de linha, retângulo e balde de tinta na barra do painel, e elas ajudam mais do que parecem: montar uma plataforma comprida com a ferramenta de linha leva um segundo.
Colisão por tile
Um mapa pintado é só imagem. Pro personagem andar em cima do chão em vez de atravessar, os tiles precisam de shapes de colisão, e no Godot 4 isso é configurado no próprio TileSet, uma vez só. Todo tile pintado daquele tipo já nasce com a colisão junto.
Primeiro, crie a camada de física no recurso:
- Selecione o TileMapLayer e clique no recurso TileSet no Inspector.
- Em Physics Layers, clique em Add Element.
- Configure Collision Layer e Collision Mask dessa camada, igual você faria num StaticBody2D. Se o seu player colide com a camada 3 ("cenário", por exemplo), os tiles ficam na layer 3.
Depois, desenhe a shape em cada tile que deve ser sólido:
- No painel TileSet, troque pro modo Select.
- Clique no tile (o tile de chão, por exemplo).
- Nas propriedades do tile, expanda Physics > Physics Layer 0.
- Desenhe o polígono de colisão sobre o tile. Pra tile cheio, use o retângulo padrão que cobre o tile inteiro; pra rampa ou meia-altura, desenhe o polígono na mão.
Dica de fluxo: dá pra selecionar vários tiles de uma vez segurando Shift e aplicar a mesma colisão em todos. Num tileset com 50 tiles sólidos, isso transforma meia hora de clique em um minuto.
Por baixo dos panos, o TileMapLayer gera corpos estáticos pra esses tiles. Pro seu CharacterBody2D, é parede e chão como outro qualquer: is_on_floor() e move_and_slide() funcionam sem nenhum código extra.
Camadas: organizando o mapa com TileMapLayer
Mapa de verdade não é uma camada só. O arranjo clássico é separar por função:
Mapa (Node2D)
├── Chao (TileMapLayer) # terreno base, com colisão
├── Paredes (TileMapLayer) # obstáculos, com colisão
├── Decoracao (TileMapLayer) # grama, flores, sem colisão
└── Frente (TileMapLayer) # o que desenha NA FRENTE do player
Como cada camada é um node, a ordem na árvore define a ordem de desenho: o que está mais embaixo na árvore desenha por cima. Player entre Decoracao e Frente fica atrás dos topos das árvores e na frente do chão, que é o efeito de profundidade que quase todo top-down quer.
Todas as camadas podem apontar pro mesmo recurso TileSet. Você configura colisão e terrain uma vez e cada camada usa o que precisa.
Duas propriedades do TileMapLayer que valem conhecer:
- Y Sort Enabled: liga ordenação por Y, pra jogo top-down onde quem está mais "abaixo" na tela desenha na frente. Ligue no TileMapLayer e no node do player, e coloque os dois sob um pai com Y sort ligado também.
- Enabled: desliga a camada inteira em runtime. Útil pra esconder uma camada de "segredos" até o jogador achar a passagem.
Autotiling com terrains
Pintar mapa tile por tile escolhendo a borda certa, o canto certo e a quina interna certa é tortura. O autotiling resolve: você diz "isso aqui é grama" e o Godot escolhe sozinho qual variação de tile usar pra borda casar com os vizinhos. No Godot 4 isso se chama terrain (era "autotile" no Godot 3).
A configuração tem duas partes. Primeiro, criar o terrain set no recurso TileSet:
- Com o TileSet selecionado no Inspector, expanda Terrain Sets e clique em Add Element.
- Em Mode, deixe Match Corners and Sides, que é o modo mais comum pra terreno de plataforma e top-down.
- Dentro do terrain set, adicione um terrain com nome e cor (ex: "Grama", verde).
Segundo, marcar quais tiles pertencem ao terrain e como eles se conectam:
- No painel TileSet, modo Select, selecione os tiles que formam o conjunto (centro, bordas, cantos).
- Em Terrains, configure Terrain Set = 0 e Terrain = 0 em cada tile.
- Agora vem a parte que define tudo: pinte os peering bits de cada tile. É aquela grade sobreposta no tile onde você marca quais lados e cantos "são grama". O tile de centro tem tudo marcado; o tile de borda esquerda tem tudo menos o lado esquerdo; e assim vai.
Os peering bits são o contrato: "esse lado meu conecta com terrain igual". Com eles pintados, vá no painel TileMap do TileMapLayer, aba Terrains, escolha o terrain e desenhe. O Godot resolve borda, canto e quina interna em tempo real, inclusive corrigindo os vizinhos do que você acabou de pintar.
Um aviso honesto: configurar os peering bits do primeiro terrain set dá trabalho, uns 15 a 30 minutos pra um conjunto completo de 47 tiles (o famoso "blob"). Mas é trabalho que você faz uma vez por tileset e economiza em todas as horas de level design depois. Se a sua arte segue o layout padrão de autotile (a maioria dos packs de asset segue), a marcação é mecânica.
Manipulando o TileMap por código
Editor resolve mapa desenhado à mão. Pra geração procedural, destruição de terreno ou qualquer mapa que muda em runtime, você fala com o TileMapLayer por GDScript.
As três funções centrais:
@onready var chao: TileMapLayer = $Chao
func _ready():
# set_cell(coordenada_do_mapa, id_da_fonte, coordenada_no_atlas)
# Fonte 0 = o primeiro atlas do TileSet.
# Vector2i(0, 0) = o tile no canto superior esquerdo da spritesheet.
chao.set_cell(Vector2i(5, 10), 0, Vector2i(0, 0))
# Apagar um tile:
chao.erase_cell(Vector2i(5, 10))
# Ler o que tem numa célula:
var atlas_coords = chao.get_cell_atlas_coords(Vector2i(5, 10))
# Retorna Vector2i(-1, -1) se a célula está vazia.
Repare que tudo trabalha com Vector2i, coordenadas do grid, não pixels. Pra converter entre os dois mundos existem local_to_map() e map_to_local(). O caso clássico, descobrir em qual tile o jogador clicou:
func _unhandled_input(event):
if event is InputEventMouseButton and event.pressed:
var clique_local = chao.to_local(get_global_mouse_position())
var celula = chao.local_to_map(clique_local)
chao.erase_cell(celula) # clicou, quebrou o bloco
Um exemplo pequeno de geração procedural, um chão com altura variando:
func gerar_chao():
var altura = 10
for x in range(60):
# Varia a altura de leve, sem buraco impossível.
altura += randi_range(-1, 1)
altura = clampi(altura, 6, 14)
# Preenche da superfície até o fundo do mapa.
for y in range(altura, 20):
chao.set_cell(Vector2i(x, y), 0, Vector2i(0, 0))
Isso pinta tudo com o mesmo tile, o que fica feio. Pra gerar já com o autotiling aplicado, use a versão de terrain:
func gerar_chao_com_terrain():
var celulas: Array[Vector2i] = []
var altura = 10
for x in range(60):
altura = clampi(altura + randi_range(-1, 1), 6, 14)
for y in range(altura, 20):
celulas.append(Vector2i(x, y))
# terrain_set 0, terrain 0: o Godot escolhe borda e canto sozinho.
chao.set_cells_terrain_connect(celulas, 0, 0)
O set_cells_terrain_connect() aplica as mesmas regras de peering bits que o pincel de terrain do editor. É o motivo de valer a pena configurar terrain mesmo em jogo procedural.
Custom data: dados por tile
Última peça que destrava muita mecânica: cada tile pode carregar dados seus. Tile de lava causa dano, tile de gelo escorrega, tile de areia desacelera. No TileSet, em Custom Data Layers, crie uma camada (ex: nome dano, tipo int) e preencha o valor por tile no modo Select. No código:
func dano_do_tile_sob_o_player(pos_global: Vector2) -> int:
var celula = chao.local_to_map(chao.to_local(pos_global))
var data = chao.get_cell_tile_data(celula)
if data:
return data.get_custom_data("dano")
return 0
Isso evita o anti-padrão de espalhar Area2D em cada tile perigoso do mapa. O dado mora no tile, o código pergunta pro tile.
Fechando
O TileMap no Godot 4 se resume a uma divisão de responsabilidade: o TileSet define o que cada tile é (textura, colisão, terrain, dados), e cada TileMapLayer define onde os tiles estão. Quando isso clica, o resto é consequência: colisão configurada uma vez vale pro mapa inteiro, terrain configurado uma vez pinta o nível em minutos, e a mesma estrutura serve pra mapa desenhado à mão ou gerado por código.
Meu conselho prático: pegue um tileset gratuito (o Kenney tem dezenas), monte uma cena com três camadas, configure colisão e um terrain set completo. É uma tarde de trabalho e cobre 90% do que você vai usar em qualquer jogo 2D daqui pra frente. A parte chata é a marcação de peering bits; a parte viciante é pintar um nível inteiro em dez minutos depois disso.


