Molas e trampolins (bounce pads) no Godot 4

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)CollisionShape2DSprite2D
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.
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.


