Voltar para o Blog
Quest Log

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

Cena de um jogo de plataforma 2D feito no Godot com personagem pulando entre blocos

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.

Próximo nível
Quer aprender isso na prática?

No CursoGame.Dev você sai dos tutoriais soltos e constrói jogos publicáveis, com trilha progressiva, quests práticas e feedback real.

Conhecer a plataforma
+500 alunos4.9/5Garantia 7 dias

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.