Jogo isométrico na Godot 4 com TileMap: guia prático

Aprenda a fazer um jogo isométrico na Godot 4 com TileMap: TileSet isométrico, Y-Sort para profundidade e movimento no grid com local_to_map.
Fazer um jogo isométrico na Godot 4 assusta no começo, mas o motor já traz tudo pronto no sistema de TileMap. Neste guia você vai configurar um TileSet isométrico, desenhar o mapa, resolver o clássico problema de "quem aparece na frente" com Y-Sort e mover um personagem clicando em qualquer célula do grid. Tudo com GDScript tipado e API real da Godot 4.x, sem gambiarra.
O que é perspectiva isométrica (2:1) e por que 2D vira 2.5D
Num jogo top-down a câmera olha o mundo direto de cima. O grid é quadrado, alinhado com a tela, e você só enxerga o topo das coisas. É simples, mas visualmente chapado.
No jogo isométrico a câmera fica inclinada num ângulo fixo. Cada tile deixa de ser um quadrado reto e vira um losango na proporção 2:1: para cada 2 pixels na horizontal, o losango sobe ou desce 1 pixel na vertical. Um tile de 128x64 é o padrão mais comum. Essa projeção deixa você ver o topo e as laterais dos objetos ao mesmo tempo, então casas, muros e árvores ganham volume. É o visual clássico de RPG tático, estratégia e tycoon (pense em SimCity, Diablo ou os Fallout antigos).
Tecnicamente ainda é 2D puro. Não existe eixo Z de verdade, só sprites empilhados de forma inteligente. Por isso muita gente chama de 2.5D. E é justamente essa "falsa profundidade" que traz o desafio de ordenação que a gente resolve mais pra frente.
Configurando o TileSet e a TileMapLayer como isométricos
Na Godot 4 o antigo nó TileMap foi dividido: agora você usa uma ou mais TileMapLayer, cada uma com seu próprio recurso TileSet. Se você nunca mexeu com esse sistema, vale primeiro entender o básico de usar TileMap na Godot 4 e depois voltar aqui para a parte isométrica.
O passo que muda tudo é o formato do grid. Selecione o recurso TileSet e ajuste:
- Tile Shape =
Isometric - Tile Layout =
Diamond Down(o mais usado) - Tile Size =
128 x 64(mantenha a proporção 2:1 da sua arte)
Depois arraste sua textura em losango para o TileSet, abra o editor de tiles e recorte cada célula respeitando o tamanho definido. A arte precisa já vir desenhada em perspectiva isométrica, porque a Godot só posiciona os losangos, ela não deforma o seu PNG.
Com isso pronto, adicione uma TileMapLayer na cena, aponte a propriedade Tile Set para o recurso que você criou e comece a pintar. Você vai notar que o cursor de desenho já aparece em formato de losango. Se os tiles ficarem sobrepostos errados ou com buracos, quase sempre é o Tile Size que não bate com o tamanho real do sprite.
Desenhando o mapa isométrico sem se perder no grid
O grid isométrico engana o olho. A célula (0, 0) fica no topo, e conforme você aumenta o X o losango vai para a direita e para baixo, enquanto o Y vai para a esquerda e para baixo. Não tente adivinhar coordenadas de cabeça: pinte no editor e deixe o motor calcular.
Para desenhar por código durante o jogo (gerar um mapa procedural de tycoon, por exemplo), use set_cell:
extends TileMapLayer
const SOURCE_ID: int = 0
const CHAO: Vector2i = Vector2i(0, 0)
func gerar_chao(largura: int, altura: int) -> void:
for x: int in range(largura):
for y: int in range(altura):
var celula: Vector2i = Vector2i(x, y)
set_cell(celula, SOURCE_ID, CHAO)
Aqui celula é a coordenada lógica no grid (não em pixels), SOURCE_ID é a fonte de tiles dentro do TileSet e CHAO é o atlas coord do tile específico. A TileMapLayer transforma tudo em losango na tela automaticamente.
O problema de profundidade: quem aparece na frente
Chegamos ao ponto que quebra a maioria dos projetos isométricos. Imagine um personagem parado atrás de uma casa. Se a Godot desenhar o personagem depois da casa, ele aparece por cima da parede, como um fantasma. Se desenhar antes, some corretamente atrás dela. A ordem de desenho precisa depender da posição no mundo.
A regra é simples: quem está mais embaixo na tela (Y maior) deve ser desenhado por cima, porque está mais perto da câmera. Fazer isso na mão é um inferno de comparações. A Godot resolve com Y-Sort, um recurso nativo do CanvasItem que reordena os filhos pela coordenada Y global a cada frame.
Para ativar:
- Na TileMapLayer, marque
Y Sort Enabledno inspetor. Ajuste oY Sort Originde cada tile alto (paredes, árvores) no editor do TileSet para a base do sprite. - No personagem (um
CharacterBody2DouNode2D), ativeY Sort Enabledno nó pai que contém tanto o mapa quanto os personagens, e ajuste oY Sort Origindo sprite para ficar nos pés dele.
O Y Sort Origin é o detalhe que quase todo mundo esquece: ele define qual ponto do sprite conta como "posição para ordenar". Nos pés do personagem e na base da parede, o resultado fica perfeito. Se você quer se aprofundar em como o motor decide a ordem e nos casos onde o Y-Sort falha, escrevi um passo a passo sobre ordenar profundidade em 2D com Y-Sort.
Convertendo tela para grid: local_to_map e map_to_local
Num jogo isométrico o jogador clica em losangos, não em quadrados, então você precisa traduzir a posição do mouse para uma célula. A TileMapLayer oferece dois métodos que fazem isso corretamente, respeitando a projeção isométrica:
local_to_map(pos: Vector2) -> Vector2i: recebe uma posição em pixels (no espaço local da layer) e devolve a coordenada da célula.map_to_local(celula: Vector2i) -> Vector2: faz o inverso, devolve o centro em pixels de uma célula.
O detalhe importante é converter o mouse do espaço global para o local da layer antes, com to_local:
extends TileMapLayer
func celula_sob_o_mouse() -> Vector2i:
var mouse_global: Vector2 = get_global_mouse_position()
var mouse_local: Vector2 = to_local(mouse_global)
var celula: Vector2i = local_to_map(mouse_local)
return celula
func _unhandled_input(evento: InputEvent) -> void:
if evento is InputEventMouseButton and evento.pressed:
var celula: Vector2i = celula_sob_o_mouse()
print("Cliquei na célula: ", celula)
Repare que nunca fazemos a conta do losango na mão. local_to_map já sabe que o grid é isométrico e retorna o Vector2i certo. Para desenhar um cursor de seleção em cima do tile apontado, é só o caminho contrário:
func destacar_celula(celula: Vector2i) -> void:
var centro_local: Vector2 = map_to_local(celula)
$Cursor.position = centro_local
Movimento de personagem no grid isométrico
Agora juntamos tudo: clicar numa célula e mover o personagem até lá. Em jogos de estratégia e tycoon o movimento costuma ser célula a célula (grid-based), não pixel livre. A ideia é converter a célula de destino para uma posição em pixels com map_to_local e interpolar o personagem até esse ponto.
Este script fica no personagem, que tem uma referência à TileMapLayer do chão:
extends CharacterBody2D
@export var mapa: TileMapLayer
@export var velocidade: float = 120.0
var destino: Vector2 = Vector2.ZERO
var movendo: bool = false
func _ready() -> void:
destino = global_position
func _unhandled_input(evento: InputEvent) -> void:
if evento is InputEventMouseButton and evento.pressed:
var mouse_local: Vector2 = mapa.to_local(get_global_mouse_position())
var celula: Vector2i = mapa.local_to_map(mouse_local)
definir_destino(celula)
func definir_destino(celula: Vector2i) -> void:
var centro_local: Vector2 = mapa.map_to_local(celula)
destino = mapa.to_global(centro_local)
movendo = true
func _physics_process(delta: float) -> void:
if not movendo:
return
var passo: float = velocidade * delta
global_position = global_position.move_toward(destino, passo)
if global_position.distance_to(destino) < 1.0:
global_position = destino
movendo = false
O fluxo é direto: o clique vira célula com local_to_map, a célula vira um ponto em pixels com map_to_local, e o move_toward desliza o personagem até lá em velocidade constante. Como o nó pai tem Y Sort Enabled, conforme o personagem anda ele passa automaticamente na frente ou atrás das casas, sem nenhuma linha extra de ordenação.
Para um movimento mais rico você pode calcular o caminho célula a célula com AStarGrid2D, que também aceita grids isométricos, ou trocar o move_toward por um Tween por passo do grid. Mas o esqueleto acima já dá um personagem funcional andando pelo mapa isométrico.
Próximos passos
Com TileSet isométrico, Y-Sort ligado e a dupla local_to_map / map_to_local, você tem a base de qualquer RPG, estratégia ou tycoon 2D na Godot. A partir daqui vale adicionar múltiplas camadas (chão, objetos, teto), colisão por célula e uma câmera com zoom. Se ainda estiver engatinhando no 2D do motor, comece pelo tutorial de fazer seu primeiro jogo 2D na Godot e depois volte para transformar aquele projeto quadrado num mundo isométrico de verdade.
O segredo é não brigar com o grid. Deixe a Godot fazer a conta dos losangos e concentre sua energia no que importa: o jogo em cima do mapa.
Perguntas frequentes
Qual a diferença entre isométrico e top-down na Godot?
No top-down o mundo é visto de cima, com o grid alinhado à tela (quadrados retos). No isométrico a câmera fica inclinada, os tiles viram losangos na proporção 2:1 e você enxerga a lateral e o topo dos objetos, dando sensação de profundidade sem 3D.
Como resolver o problema de sobreposição no isométrico?
Ative o Y-Sort. Na TileMapLayer marque Y Sort Enabled e nos personagens use o nó com Y-Sort ativo (propriedade Y Sort Origin no CanvasItem). A Godot passa a desenhar quem tem Y maior por cima, então objetos mais próximos da câmera aparecem na frente.
Como converter o clique do mouse para uma célula do grid isométrico?
Use local_to_map(). Pegue a posição do mouse com get_global_mouse_position(), converta para o espaço local da TileMapLayer com to_local() e passe para local_to_map(), que devolve a coordenada Vector2i da célula clicada.
Preciso desenhar os tiles já em formato de losango?
Sim. A arte precisa vir em perspectiva isométrica (2:1) e você configura o TileSet com Tile Shape = Isometric. A Godot cuida do posicionamento em losango, mas o desenho de cada sprite tem que respeitar a projeção.


