Como Fazer um Jogo de Plataforma no Godot (Tutorial Completo)

Como fazer um jogo de plataforma no Godot 4 do zero: movimento, gravidade, pulo, colisao com tilemap e coletaveis em GDScript tipado, passo a passo.
Como Fazer um Jogo de Plataforma no Godot (Tutorial Completo)
Saber como fazer um jogo de plataforma no Godot e quase um rito de passagem. E o primeiro projeto de quase todo mundo, e por bom motivo: ele junta os pilares de qualquer jogo 2D (movimento, gravidade, colisao, coletaveis e fim de fase) num escopo que da pra terminar. Neste tutorial a gente monta um platformer funcional do zero no Godot 4, peca por peca, com GDScript tipado e explicado. No final voce vai ter um personagem que corre, pula, bate no cenario, pega moedas, morre no espinho e chega na bandeira de fim de fase.
Vou assumir que voce ja instalou o Godot 4 e abriu um projeto 2D vazio. O resto a gente constroi junto. Cada bloco de codigo aqui roda como esta, sem API inventada.
Configurando o personagem com CharacterBody2D
O coracao do personagem e o node CharacterBody2D. Ele e o no que o Godot 4 recomenda pra quem e controlado pelo jogador: voce mesmo define a velocity a cada quadro e chama move_and_slide() pra resolver as colisoes. Diferente do RigidBody2D, ele nao sai voando por conta de forcas fisicas, e isso e exatamente o que a gente quer num plataforma, controle total.
Monte a cena assim:
Player (CharacterBody2D)
├── CollisionShape2D
└── Sprite2D (ou AnimatedSprite2D)
Na CollisionShape2D, escolha uma forma de capsula ou retangulo que envolva o sprite. A capsula desliza melhor em quina e evita travar no chao. No Sprite2D, jogue a imagem do seu personagem.
Antes de programar qualquer coisa, abra Project Settings > Input Map e crie tres acoes: mover_esquerda, mover_direita e pular. Mapeie as setas (ou A/D) e a barra de espaco. Sem isso o Godot reclama de acao inexistente quando o script rodar.
Movimento horizontal com Input.get_axis
Com a cena pronta, anexe um novo script ao node Player. A primeira mecanica e andar pros lados. O jeito limpo de ler "esquerda menos direita" num valor so e o Input.get_axis(), que devolve -1.0, 0.0 ou 1.0 dependendo de qual tecla esta pressionada.
extends CharacterBody2D
const VELOCIDADE: float = 200.0
func _physics_process(delta: float) -> void:
var direcao: float = Input.get_axis("mover_esquerda", "mover_direita")
if direcao != 0.0:
velocity.x = direcao * VELOCIDADE
else:
velocity.x = move_toward(velocity.x, 0.0, VELOCIDADE)
move_and_slide()
Repare em dois pontos. Primeiro, tudo vive dentro de _physics_process, nunca de _process. O passo de fisica e fixo (60 vezes por segundo por padrao), entao o movimento fica igual em qualquer maquina. Segundo, quando o jogador solta a tecla, em vez de zerar a velocidade de uma vez eu uso move_toward(velocity.x, 0.0, VELOCIDADE), que reduz a velocidade suavemente ate parar. Ja da uma desacelerada bem mais agradavel que o liga-desliga cru.
Esse movimento e o minimo viavel. Se voce quiser deixar a corrida com peso de verdade (aceleracao, friccao e controle no ar separados), eu cubro isso em detalhe no tutorial de movimento de personagem em plataforma 2D. Pra esse jogo, o basico ja serve.
Gravidade e queda
Por enquanto o personagem flutua. Falta a gravidade puxar ele pra baixo quando nao tem chao. No Godot 4 a forma recomendada e pegar a gravidade global do projeto e aplicar so quando o personagem nao esta apoiado.
extends CharacterBody2D
const VELOCIDADE: float = 200.0
var gravidade: float = ProjectSettings.get_setting("physics/2d/default_gravity")
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity.y += gravidade * delta
var direcao: float = Input.get_axis("mover_esquerda", "mover_direita")
if direcao != 0.0:
velocity.x = direcao * VELOCIDADE
else:
velocity.x = move_toward(velocity.x, 0.0, VELOCIDADE)
move_and_slide()
A gravidade vem do ProjectSettings, entao se voce ajustar a gravidade global do projeto o personagem acompanha sem mexer no codigo. Multiplicar por delta transforma o valor numa taxa por segundo, o que mantem a queda consistente. E note: no Godot o eixo Y cresce pra baixo, entao somar em velocity.y empurra o corpo pro chao.
O is_on_floor() so funciona depois que o move_and_slide() rodou pelo menos uma vez, porque e ele quem detecta o contato. Por isso a checagem fica no inicio do quadro usando o resultado do quadro anterior, e o move_and_slide() fica sempre por ultimo.
Pulo com is_on_floor
Agora o pulo. A regra de ouro do plataforma: o personagem so pode pular quando esta no chao. O is_on_floor() resolve isso. Junte ele com o input numa unica condicao e pronto.
const FORCA_PULO: float = -400.0
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity.y += gravidade * delta
if Input.is_action_just_pressed("pular") and is_on_floor():
velocity.y = FORCA_PULO
var direcao: float = Input.get_axis("mover_esquerda", "mover_direita")
if direcao != 0.0:
velocity.x = direcao * VELOCIDADE
else:
velocity.x = move_toward(velocity.x, 0.0, VELOCIDADE)
move_and_slide()
A FORCA_PULO e negativa porque subir e ir no sentido contrario ao crescimento do Y. Quanto mais negativo o valor, mais alto o pulo. O Input.is_action_just_pressed dispara so no quadro em que a tecla foi apertada, entao segurar a barra de espaco nao gera pulo continuo, exatamente o que a gente quer.
Esse e o pulo simples. Mais pra frente eu mostro como deixar ele mais gostoso, mas primeiro o personagem precisa de um chao pra pisar.
Colisao com o cenario via TileMap
Sem cenario, o personagem cai pra sempre. Hora de montar o nivel com um TileMapLayer, que e o jeito do Godot 4 desenhar fases com blocos repetidos e ja resolver a colisao junto.
Adicione um node TileMapLayer na cena principal (a mesma que tem o Player). No Inspector dele, crie um novo TileSet e arraste a sua imagem de tiles (o spritesheet de blocos). O Godot fatia a imagem na grade que voce definir.
O passo que todo iniciante esquece: dar colisao aos blocos. No painel do TileSet, va na aba de fisica (Physics Layers), adicione uma camada de fisica e depois, tile por tile, pinte a area de colisao de cada bloco solido. Isso diz ao Godot quais celulas barram o personagem. Os blocos so de decoracao voce deixa sem colisao.
Com isso feito, desenhe o chao e as plataformas com a ferramenta de pintura do TileMapLayer. Nao precisa escrever nenhuma linha de codigo de colisao: o move_and_slide() do CharacterBody2D ja bate nas celulas marcadas automaticamente. Rode o jogo. O personagem agora corre, pula e para no chao de verdade.
Um coletavel simples com Area2D
Todo plataforma tem o que coletar: moedas, gemas, frutas. Pra isso a gente usa Area2D, um node que detecta sobreposicao sem empurrar nada. Ele e perfeito pra "encostou, pegou".
Crie uma cena nova com a raiz sendo um Area2D, e dentro dela uma CollisionShape2D (um circulo pequeno serve) e um Sprite2D com a arte da moeda. Salve como moeda.tscn. Anexe este script na raiz:
extends Area2D
func _ready() -> void:
body_entered.connect(_ao_entrar_corpo)
func _ao_entrar_corpo(corpo: Node2D) -> void:
if corpo.is_in_group("player"):
queue_free()
O sinal body_entered dispara quando um corpo fisico (o nosso CharacterBody2D) entra na area. Eu conecto ele no _ready() e, quando o corpo que entrou esta no grupo player, removo a moeda da cena com queue_free(). Pra isso funcionar, selecione o node Player, va na aba Node > Groups e adicione ele ao grupo player.
Pra contar pontos, declare uma variavel na cena principal e incremente antes do queue_free(). Mas o nucleo do coletavel e isso: detectar a sobreposicao e sumir. Espalhe varias instancias da moeda.tscn pela fase e ja tem objetivo.
Um inimigo ou perigo basico
Um plataforma sem risco e um passeio. O perigo mais simples de fazer e um espinho fixo: encostou, voltou pro inicio. A estrutura e a mesma do coletavel, um Area2D, so muda o que acontece na colisao.
Monte uma cena espinho.tscn com Area2D, CollisionShape2D e Sprite2D do espinho. O script:
extends Area2D
@export var ponto_de_renascimento: Vector2 = Vector2(100.0, 100.0)
func _ready() -> void:
body_entered.connect(_ao_entrar_corpo)
func _ao_entrar_corpo(corpo: Node2D) -> void:
if corpo.is_in_group("player"):
corpo.global_position = ponto_de_renascimento
corpo.velocity = Vector2.ZERO
Quando o player encosta no espinho, eu teleporto ele de volta pro ponto_de_renascimento e zero a velocidade pra ele nao chegar caindo. O @export deixa esse ponto ajustavel no Inspector, entao cada espinho pode mandar o jogador pro checkpoint que fizer sentido.
Se voce quiser um inimigo que anda em vez de um espinho parado, a base e um CharacterBody2D que inverte a direcao ao bater na parede. Mas pra um primeiro jogo, o espinho fixo ensina o conceito de perigo sem complicar.
O fim de fase
Falta fechar o ciclo: chegar no final e vencer. De novo, Area2D. Uma bandeira no fim do nivel que, ao ser tocada, encerra a fase.
extends Area2D
@export var proxima_fase: String = ""
func _ready() -> void:
body_entered.connect(_ao_entrar_corpo)
func _ao_entrar_corpo(corpo: Node2D) -> void:
if corpo.is_in_group("player"):
if proxima_fase != "":
get_tree().change_scene_to_file(proxima_fase)
else:
get_tree().reload_current_scene()
Aqui, quando o player toca a bandeira, eu uso change_scene_to_file() pra carregar a proxima fase, se voce tiver definido o caminho dela no Inspector. Se nao tiver, o reload_current_scene() reinicia o nivel atual, util pra testar. Voce tem agora um loop de jogo completo: nasce, corre, pula, coleta, evita o perigo e termina.
Deixando o pulo mais gostoso
O jogo ja funciona ponta a ponta, mas o pulo ainda e o basico. A diferenca entre "pula" e "controla bem" mora em detalhes pequenos. Tres que valem muito o esforco:
Altura de pulo variavel. Soltar o botao cedo encurta o salto. Assim um botao so produz desde um pulinho ate o pulo cheio.
const CORTE_PULO: float = 0.45
func _physics_process(delta: float) -> void:
if Input.is_action_just_released("pular") and velocity.y < 0.0:
velocity.y *= CORTE_PULO
# ... resto do _physics_process
Queda mais rapida que a subida. Um pulo simetrico parece flutuante. Multiplicar a gravidade quando velocity.y > 0.0 (descendo) da peso ao salto.
Coyote time. Uma fracao de segundo de tolerancia pra pular depois de sair da plataforma, que perdoa o erro de timing do jogador.
E depois disso, da pra somar movimento aereo de verdade. O caminho natural e o double jump e o wall jump, que deixam o jogador se reposicionar no ar e subir corredores verticais. Eu mostro os dois passo a passo no tutorial de double jump e wall jump no Godot, construindo em cima exatamente dessa mesma base de movimento.
Fechando
Voce acabou de ver como fazer um jogo de plataforma no Godot do comeco ao fim: configurou o personagem com CharacterBody2D, andou com Input.get_axis(), aplicou gravidade, pulou checando is_on_floor(), montou o cenario com TileMapLayer e colisao no TileSet, criou um coletavel e um perigo com Area2D, e fechou a fase com a bandeira. Nenhum truque, so as pecas certas no lugar certo.
O melhor proximo passo e abrir o Godot e refazer isso com a sua arte, mudando os valores e quebrando coisas pra entender. Cada mecanica que voce somar (inimigos que andam, plataformas moveis, vida e dano) encaixa nessa base. E se voce quer um caminho guiado pra ir alem do primeiro projeto sem se perder, vale dar uma olhada no melhor curso de Godot pra acelerar o aprendizado. O importante e nao parar no tutorial: o que fixa o conhecimento e montar o seu proprio jogo.
Perguntas frequentes
Qual node usar para o personagem de plataforma no Godot?
Use o CharacterBody2D. Ele foi feito pra personagens controlados pelo jogador: voce define a velocity a cada quadro e chama move_and_slide() pra resolver a colisao com o cenario. Diferente do RigidBody2D, ele nao reage a forcas fisicas sozinho, entao voce tem controle total sobre andar, pular e cair.
Como faco o personagem pular so quando esta no chao?
Cheque is_on_floor() antes de aplicar o pulo. Esse metodo do CharacterBody2D retorna true quando a ultima chamada de move_and_slide() detectou chao embaixo. Junte com Input.is_action_just_pressed("pular") num mesmo if e o personagem so sobe quando esta apoiado, nunca no meio do ar.
Como adiciono o cenario do jogo de plataforma?
Use um node TileMapLayer com um TileSet montado a partir da sua imagem de blocos. Marque as celulas com colisao na aba Physics do TileSet e o CharacterBody2D ja vai bater nelas via move_and_slide(), sem voce escrever codigo de colisao. O chao, as paredes e as plataformas saem todos do mesmo TileMapLayer.
Preciso saber programar pra fazer um jogo de plataforma?
Um pouco de logica ajuda, mas da pra comecar copiando os scripts e mexendo nos valores pra entender o que cada linha faz. O GDScript do Godot e proximo do Python e perdoa o iniciante. Quanto mais voce monta, mais natural fica ler e escrever o codigo, entao comece simples e va somando uma mecanica de cada vez.
Como deixo o pulo mais gostoso de jogar?
Comece ajustando a forca do pulo e a gravidade, e faca o personagem cair um pouco mais rapido do que sobe. Depois adicione altura de pulo variavel (soltar o botao cedo corta o salto) e tecnicas como coyote time, double jump e wall jump. Esses detalhes pequenos mudam muito a sensacao de controle.


