Agachar e deslizar (crouch) em plataforma 2D no Godot 4

Tutorial de agachar godot 2d: reduzir a colisao, checar o teto com RayCast antes de levantar e adicionar slide. GDScript real para CharacterBody2D no Godot 4.
Agachar parece um detalhe pequeno, mas muda a sensacao do seu jogo. Com agachar godot 2d voce abre passagens baixas, esquiva de projeteis e cria aquele slide rapido que deixa o movimento gostoso de controlar. O problema e que quase todo mundo erra na mesma coisa: reduz a colisao para agachar, mas esquece de checar se ha teto antes de levantar. O resultado e o personagem atravessando o chao de cima e ficando preso. Neste tutorial montamos um crouch completo num CharacterBody2D, com reducao de colisao, perda de velocidade, checagem de teto e um slide opcional.
Agachar e deslizar (crouch) em plataforma 2D no Godot 4
A ideia central de agachar godot 2d e simples: quando o jogador segura o botao de agachar, voce encolhe a forma de colisao e troca a animacao. Quando solta, voce devolve a forma original. O cuidado fica todo na hora de devolver, porque pode nao ter espaco.
Antes de escrever codigo, monte a cena. Um CharacterBody2D com:
CollisionShape2D(a colisao principal, em pe).AnimatedSprite2DouSprite2Dpara a animacao.ShapeCast2Dapontando para cima, usado para detectar teto.
Vou usar uma CapsuleShape2D na colisao porque ela e facil de redimensionar pela altura. Se voce usar RectangleShape2D, o raciocinio e o mesmo, voce ajusta a propriedade size.
Estrutura basica do movimento
Comecamos com um movimento de plataforma padrao. Se voce ainda nao tem essa base, vale conferir o guia de movimento de personagem em plataforma 2D antes de seguir, porque aqui parto do principio que pular e andar ja funcionam.
extends CharacterBody2D
const SPEED := 200.0
const CROUCH_SPEED := 90.0
const JUMP_VELOCITY := -350.0
@onready var collision: CollisionShape2D = $CollisionShape2D
@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
@onready var ceiling_cast: ShapeCast2D = $CeilingCast
var is_crouching := false
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity += get_gravity() * delta
_handle_crouch()
_handle_jump()
_handle_horizontal()
_update_animation()
move_and_slide()
Cada responsabilidade fica numa funcao separada. Isso deixa o agachar facil de ler e de ajustar depois.
Reduzindo a colisao ao agachar
A parte fisica do crouch e mudar a altura da CapsuleShape2D. Guardamos a altura original em constantes para nunca perder o valor de referencia.
const STAND_HEIGHT := 32.0
const CROUCH_HEIGHT := 16.0
func _set_collision_height(height: float) -> void:
var shape := collision.shape as CapsuleShape2D
shape.height = height
# Reposiciona a colisao para os pes ficarem no mesmo lugar.
collision.position.y = -height * 0.5
Reparou no ajuste de position.y? Sem ele, a capsula encolhe a partir do centro e o personagem parece flutuar ou afundar no chao. Ao mover a colisao para baixo na mesma proporcao, os pes continuam tocando o solo e so a parte de cima desce.
Um detalhe importante: collision.shape e compartilhado entre instancias por padrao no Godot. Se voce tiver varios personagens usando a mesma cena, ative Local to Scene no recurso da forma, ou duplique com collision.shape = collision.shape.duplicate() no _ready. Sem isso, agachar um inimigo agacha todos.
A logica do agachar e o teto
Aqui mora o erro mais comum. Quando o jogador solta o botao, voce so pode levantar se houver espaco. Para checar isso usamos o ShapeCast2D apontado para cima.
Configure o CeilingCast no editor: defina o shape como uma forma parecida com a colisao em pe (uma capsula ou retangulo da altura do personagem), aponte target_position para cima (por exemplo Vector2(0, -18)) e marque a layer das paredes e do chao em collision_mask. Tambem ative Enabled para ele atualizar todo frame.
func _handle_crouch() -> void:
var wants_crouch := Input.is_action_pressed("crouch") and is_on_floor()
if wants_crouch and not is_crouching:
_enter_crouch()
elif not wants_crouch and is_crouching:
# So levanta se nao houver teto bloqueando.
if not _has_ceiling():
_exit_crouch()
func _enter_crouch() -> void:
is_crouching = true
_set_collision_height(CROUCH_HEIGHT)
func _exit_crouch() -> void:
is_crouching = false
_set_collision_height(STAND_HEIGHT)
func _has_ceiling() -> bool:
ceiling_cast.force_shapecast_update()
return ceiling_cast.is_colliding()
O force_shapecast_update() garante uma leitura fresca naquele instante, sem depender do timing do frame. Se houver teto, simplesmente ignoramos o pedido de levantar e o personagem continua agachado ate o jogador sair da passagem baixa. Esse e o detalhe que evita o bug de atravessar o teto: nunca devolvemos a altura cheia se o espaco acima estiver ocupado.
Um alerta sobre o erro classico: muita gente troca a altura da colisao direto no _input ou compara o botao sem checar o teto. Quando isso acontece embaixo de uma plataforma, o Godot expande a capsula para dentro do colisor de cima e o motor de fisica empurra o corpo para fora pelo caminho mais curto, que costuma ser para cima. O personagem teleporta atravessando o chao superior. A checagem com ShapeCast2D resolve porque trava o levantar enquanto nao ha folga.
Velocidade reduzida e movimento horizontal
Agachado, o personagem deve andar mais devagar. Basta escolher a velocidade conforme o estado.
func _handle_horizontal() -> void:
var direction := Input.get_axis("move_left", "move_right")
var current_speed := CROUCH_SPEED if is_crouching else SPEED
if direction != 0.0:
velocity.x = direction * current_speed
else:
velocity.x = move_toward(velocity.x, 0.0, current_speed)
Note que tambem impedimos o pulo enquanto agachado dentro de um espaco apertado. Se o jogador estiver agachado mas com espaco livre, ele levanta primeiro e o pulo acontece no frame seguinte. Para deixar isso explicito:
func _handle_jump() -> void:
if Input.is_action_just_pressed("jump") and is_on_floor():
if is_crouching:
# Tenta levantar antes de pular; se ha teto, ignora o pulo.
if _has_ceiling():
return
_exit_crouch()
velocity.y = JUMP_VELOCITY
Assim o pulo nunca atravessa o teto, porque ele depende da mesma checagem que o levantar.
Slide com impulso
O slide e a recompensa de agachar enquanto corre. Quando o jogador esta em velocidade alta e aperta agachar, damos um empurrao horizontal que decai com o tempo. Ele e opcional, mas deixa o movimento muito mais expressivo.
const SLIDE_SPEED := 320.0
const SLIDE_FRICTION := 600.0
var is_sliding := false
func _enter_crouch() -> void:
is_crouching = true
_set_collision_height(CROUCH_HEIGHT)
# So desliza se estava correndo rapido o bastante.
if absf(velocity.x) > SPEED * 0.8:
is_sliding = true
velocity.x = sign(velocity.x) * SLIDE_SPEED
func _exit_crouch() -> void:
is_crouching = false
is_sliding = false
_set_collision_height(STAND_HEIGHT)
Durante o slide, ignoramos o controle horizontal normal e deixamos o atrito frear o personagem. Ajustamos o _handle_horizontal para respeitar esse estado.
func _handle_horizontal() -> void:
if is_sliding:
velocity.x = move_toward(velocity.x, 0.0, SLIDE_FRICTION * get_physics_process_delta_time())
if absf(velocity.x) < CROUCH_SPEED:
is_sliding = false
return
var direction := Input.get_axis("move_left", "move_right")
var current_speed := CROUCH_SPEED if is_crouching else SPEED
if direction != 0.0:
velocity.x = direction * current_speed
else:
velocity.x = move_toward(velocity.x, 0.0, current_speed)
Quando a velocidade do slide cai abaixo da velocidade de agachado, voltamos ao crouch normal e o jogador retoma o controle. Se ele soltar o botao e nao houver teto, levanta direto. Se houver teto, continua agachado, sem slide.
Trocando a animacao e o sprite
A parte visual fecha a experiencia. Com um AnimatedSprite2D, basta escolher a animacao certa conforme o estado.
func _update_animation() -> void:
if is_sliding:
sprite.play("slide")
elif is_crouching:
if absf(velocity.x) > 5.0:
sprite.play("crouch_walk")
else:
sprite.play("crouch_idle")
elif not is_on_floor():
sprite.play("jump")
elif absf(velocity.x) > 5.0:
sprite.play("run")
else:
sprite.play("idle")
# Vira o sprite na direcao do movimento.
if velocity.x != 0.0:
sprite.flip_h = velocity.x < 0.0
Garanta que as animacoes crouch_idle, crouch_walk e slide existam no seu SpriteFrames. Se ainda nao tiver arte de agachado, use o sprite normal com scale.y menor como placeholder, mas troque por arte de verdade depois, porque escalar deforma a leitura do personagem.
Checklist para nao quebrar
Antes de considerar pronto, valide estes pontos no seu projeto:
- A colisao realmente encolhe e os pes ficam no chao (ajuste de
position.y). - O
ShapeCast2Dcobre a altura cheia do personagem e mira para cima. - Levantar e pular sempre passam pela checagem de teto.
- O slide so dispara em velocidade alta e decai sozinho.
- As animacoes de crouch e slide estao no
SpriteFrames.
Com isso voce tem um crouch solido, sem o bug de atravessar o teto, e ainda ganha um slide que da personalidade ao movimento. Da para evoluir o sistema com som de deslizar, particulas de poeira e ate dano por deslizar contra inimigos. Para continuar deixando seu personagem mais completo, o proximo passo natural e adicionar interacao com escadas em plataforma 2D, que combina bem com o agachar para criar niveis verticais mais ricos.


