Voltar para o Blog
Quest Log

Molas e trampolins (bounce pads) no Godot 4

Personagem de plataforma 2D sendo arremessado para cima por um trampolim com efeito de mola.

Aprenda a criar um trampolim godot 2d com impulso ajustavel, animacao de mola e versoes direcionais sem disparos duplicados. Codigo GDScript pronto.

Poucos elementos deixam um plataforma tao gostoso de jogar quanto uma boa mola. O trampolim godot 2d e aquele cogumelo, aquela placa elastica ou aquele cano que joga o personagem para o alto e abre rotas novas no mapa. O conceito e simples: detectar o player, aplicar um impulso vertical forte e mostrar um feedback visual. A execucao tem alguns detalhes que separam um trampolim que parece bug de um que parece intencional. Neste tutorial montamos um bounce pad do zero, ajustamos a forca, adicionamos animacao de mola, criamos versoes direcionais e resolvemos o problema classico de disparo multiplo.

Molas e trampolins (bounce pads) no Godot 4

A ideia central de qualquer trampolim e interceptar o movimento vertical do player e substitui-lo por uma velocidade negativa (para cima, ja que no Godot 2D o eixo Y cresce para baixo). Vamos partir de um player CharacterBody2D padrao, com uma propriedade velocity exposta, e construir o restante em cima disso.

Montando o trampolim com Area2D

A forma mais simples e leve de detectar o player e usar uma Area2D. Ela nao colide fisicamente, apenas dispara sinais quando algo entra. Estrutura de nos recomendada:

  • Trampolim (Area2D)
    • CollisionShape2D
    • Sprite2D

Conecte o sinal body_entered da Area2D ao script. O codigo basico fica assim:

extends Area2D

@export var forca_pulo: float = 800.0

func _on_body_entered(body: Node2D) -> void:
    if body is CharacterBody2D:
        body.velocity.y = -forca_pulo

A propriedade forca_pulo esta marcada com @export, entao voce ajusta o valor direto no Inspector sem mexer no codigo. Um valor de 800.0 lanca o player bem mais alto que um pulo normal, mas o numero ideal depende da gravidade que voce configurou. Quanto maior a gravidade, maior precisa ser a forca para alcancar a mesma altura.

Repare em um ponto importante: usamos body.velocity.y = -forca_pulo e nao colocamos o trampolim como StaticBody2D. Como a Area2D nao bloqueia o movimento, o player atravessa ela de cima para baixo, o sinal dispara e ele e arremessado. Se voce quiser que o trampolim tambem sirva de chao solido, a proxima secao cobre isso.

Trampolim solido com Area2D acoplada

Quando o trampolim e uma plataforma em que o player pisa, voce precisa de um corpo solido para o chao e uma Area2D para a deteccao. Use um StaticBody2D como raiz e adicione uma Area2D como filha, posicionada um pouco acima da superficie:

extends StaticBody2D

@export var forca_pulo: float = 900.0

@onready var area_deteccao: Area2D = $AreaDeteccao

func _ready() -> void:
    area_deteccao.body_entered.connect(_on_area_body_entered)

func _on_area_body_entered(body: Node2D) -> void:
    if body is CharacterBody2D:
        body.velocity.y = -forca_pulo

Aqui conectamos o sinal por codigo dentro de _ready usando connect, o que e pratico quando voce instancia varios trampolins via cena. O StaticBody2D segura o player como chao normal, e a Area2D logo acima detecta o contato e aplica o impulso antes que ele fique parado em cima.

Setar velocity direto vs somar

Esse e o detalhe que mais gera duvida. Existem duas formas de aplicar o impulso:

# Opcao A: substitui a velocidade vertical
body.velocity.y = -forca_pulo

# Opcao B: soma ao que ja existe
body.velocity.y -= forca_pulo

A diferenca importa muito. Na opcao A, o trampolim garante sempre a mesma altura de salto, nao importa se o player chegou caindo rapido ou parado. A velocidade anterior e descartada e substituida por um valor fixo. Isso da consistencia: o jogador aprende exatamente quao alto a mola arremessa.

Na opcao B, o impulso soma com a velocidade atual. Se o player chega caindo rapido (velocity.y positivo e grande), a soma com -forca_pulo resulta em um salto mais baixo, porque parte da forca cancela a queda. Se ele chega subindo, o salto fica ainda mais alto. Isso cria comportamento imprevisivel e geralmente nao e o que voce quer em um trampolim.

A recomendacao para a maioria dos jogos e a opcao A. Use a soma apenas se quiser um trampolim que recompensa quem chega com momentum, um detalhe de design especifico que precisa ser testado com cuidado.

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

Animacao de mola com Tween

Um trampolim sem feedback visual parece quebrado. O minimo e fazer a mola comprimir e voltar. O Tween do Godot 4 resolve isso sem precisar de um AnimationPlayer completo:

extends Area2D

@export var forca_pulo: float = 800.0

@onready var sprite: Sprite2D = $Sprite2D

func _on_body_entered(body: Node2D) -> void:
    if body is CharacterBody2D:
        body.velocity.y = -forca_pulo
        _animar_mola()

func _animar_mola() -> void:
    var tween := create_tween()
    tween.tween_property(sprite, "scale", Vector2(1.2, 0.6), 0.06)
    tween.tween_property(sprite, "scale", Vector2(1.0, 1.0), 0.12)

O primeiro passo achata o sprite (mais largo, mais baixo) para simular a compressao, e o segundo volta ao tamanho original. Os tempos curtos, 0.06 e 0.12 segundos, dao a sensacao de algo elastico e rapido. Voce pode trocar por set_trans e set_ease para curvas mais suaves:

func _animar_mola() -> void:
    var tween := create_tween()
    tween.set_trans(Tween.TRANS_BACK)
    tween.set_ease(Tween.EASE_OUT)
    tween.tween_property(sprite, "scale", Vector2(1.2, 0.6), 0.06)
    tween.tween_property(sprite, "scale", Vector2(1.0, 1.0), 0.15)

Se o seu trampolim tem quadros desenhados, o caminho e um AnimationPlayer com uma animacao chamada comprimir. Basta chamar $AnimationPlayer.play("comprimir") dentro do _on_body_entered. As duas abordagens funcionam; o Tween e mais leve para um efeito procedural, o AnimationPlayer e melhor quando o artista entrega frames prontos.

Trampolins direcionais

Nem todo trampolim aponta para cima. Uma parede com mola arremessa para o lado, e um trampolim inclinado lanca em diagonal. Para isso, em vez de setar so o eixo Y, definimos um vetor de direcao completo.

A forma mais flexivel e exportar um Vector2 de direcao e normaliza-lo, garantindo que a forca seja sempre consistente independente do valor digitado:

extends Area2D

@export var forca_pulo: float = 800.0
@export var direcao: Vector2 = Vector2.UP

func _on_body_entered(body: Node2D) -> void:
    if body is CharacterBody2D:
        body.velocity = direcao.normalized() * forca_pulo

Com direcao = Vector2.UP voce tem o trampolim vertical de sempre. Com Vector2(1, -1) ele lanca para a direita e para cima em diagonal. O normalized() garante que Vector2(1, -1) e Vector2(2, -2) produzam a mesma forca, mudando so o sentido.

Outra abordagem util e seguir a rotacao do proprio no, usando global_transform. Assim voce gira o trampolim no editor e a direcao do impulso acompanha:

extends Area2D

@export var forca_pulo: float = 800.0

func _on_body_entered(body: Node2D) -> void:
    if body is CharacterBody2D:
        # global_transform.y aponta para "baixo" local; invertemos para lancar na direcao "para cima" do no
        var direcao := -global_transform.y.normalized()
        body.velocity = direcao * forca_pulo

Aqui global_transform.y representa o eixo Y local do trampolim ja convertido para coordenadas globais. Invertendo o sinal, obtemos a direcao para onde o topo do trampolim aponta. Gire o no 45 graus no editor e o impulso sai em diagonal automaticamente, sem editar nenhum numero.

Evitando disparos multiplos

Esse e o bug mais comum em bounce pads. Dependendo de como o player se mexe, o sinal body_entered pode disparar mais de uma vez em frames seguidos, ou o player fica colado na area e leva impulsos repetidos. O resultado e um salto absurdamente alto ou um tremor estranho.

A solucao mais limpa e um cooldown com timer interno:

extends Area2D

@export var forca_pulo: float = 800.0
@export var cooldown: float = 0.2

var pode_disparar: bool = true

func _on_body_entered(body: Node2D) -> void:
    if not pode_disparar:
        return
    if body is CharacterBody2D:
        body.velocity.y = -forca_pulo
        pode_disparar = false
        _iniciar_cooldown()

func _iniciar_cooldown() -> void:
    await get_tree().create_timer(cooldown).timeout
    pode_disparar = true

A flag pode_disparar bloqueia novos impulsos por 0.2 segundos. Isso e tempo suficiente para o player sair da area antes que um novo disparo seja permitido. O await com um timer da arvore e a forma idiomatica de esperar no Godot 4, sem precisar de um no Timer separado.

Se preferir nao usar cooldown por tempo, da para checar a posicao: so dispara se o player estiver de fato vindo de cima ou se a velocidade vertical for descendente. Mas para a maioria dos casos o cooldown simples resolve e evita os disparos encadeados.

Juntando tudo

Um trampolim completo e robusto combina as partes: deteccao por Area2D, forca exportada, opcao A para velocity consistente, animacao de mola via Tween, direcao por Vector2 ou global_transform e um cooldown para evitar disparos duplos. Com essas pecas voce cobre molas verticais, paredes elasticas e trampolins diagonais usando praticamente o mesmo script.

A partir daqui vale experimentar variacoes. Um trampolim que so funciona se o player cair em cima, outro que carrega forca conforme quanto tempo voce fica parado, ou um que muda de cor quando esta em cooldown. Se voce quer entender melhor como o impulso interage com gravidade e atrito, vale revisar os fundamentos de fisica de jogos no Godot. E se a sua mola vai alimentar manobras mais avancadas, combine ela com as tecnicas de pulo duplo e wall jump para criar trajetorias que dependem de timing e leitura de mapa.

Teste valores extremos, sinta o peso do salto e ajuste a forca_pulo ate o movimento ficar gostoso. No fim, um bom trampolim e menos sobre o codigo e mais sobre a sensacao de ser arremessado.