Voltar para o Blog
Quest Log

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

Personagem 2D agachado embaixo de uma plataforma estreita em um jogo de plataforma no Godot.

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.

Ferramenta:Godot

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.

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).
  • AnimatedSprite2D ou Sprite2D para a animacao.
  • ShapeCast2D apontando 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.

Curso de Godot
Quer fazer seu primeiro jogo na Godot?

No curso de Godot da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Godot
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

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.

Descer de plataformas one-way agachando

Um pedido que aparece logo depois do crouch pronto: deixar o jogador descer por uma plataforma de sentido unico (aquelas que voce atravessa de baixo pra cima, mas pisa por cima) apertando baixo. O gesto natural e o mesmo do agachar, entao vale resolver os dois juntos.

No Godot 4, plataformas one-way sao um StaticBody2D com o CollisionShape2D marcado como One Way Collision. Para o personagem furar essa plataforma por um instante, a forma mais limpa e desligar a layer dela na collision_mask do corpo por uma fracao de segundo e religar logo em seguida. Coloque as plataformas numa layer dedicada (aqui, a layer 2) e marque um grupo one_way nelas.

const DROP_LAYER := 2  # layer das plataformas one-way
const DROP_TIME := 0.2

func _handle_drop_through() -> void:
    if Input.is_action_just_pressed("crouch") and is_on_floor():
        if _is_on_one_way_platform():
            _drop_through()

func _is_on_one_way_platform() -> bool:
    for i in get_slide_collision_count():
        var collision := get_slide_collision(i)
        var collider := collision.get_collider()
        if collider is StaticBody2D and collider.is_in_group("one_way"):
            return true
    return false

func _drop_through() -> void:
    set_collision_mask_value(DROP_LAYER, false)
    await get_tree().create_timer(DROP_TIME).timeout
    set_collision_mask_value(DROP_LAYER, true)

A checagem em _is_on_one_way_platform evita o bug de o personagem afundar no chao solido so por apertar baixo: a queda so acontece quando ele esta de fato em cima de uma plataforma atravessavel. Chame _handle_drop_through() dentro do _physics_process, antes do _handle_crouch(), para o baixo em plataforma one-way cair, e o baixo em chao normal agachar. Esse pareamento combina direto com a janela de tolerancia do pulo: se voce ainda nao configurou isso, o coyote time e jump buffer no Godot deixam o controle perdoar os frames em que o jogador aperta pulo logo depois de descer.

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 ShapeCast2D cobre 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. Se a ideia e deixar o movimento ainda mais expressivo, o slide combina demais com um dash de personagem 2D, que usa a mesma logica de impulso e decaimento. 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.

Perguntas frequentes

Como fazer o personagem agachar no Godot 4?

Reduza a altura da forma de colisao (CapsuleShape2D ou RectangleShape2D) quando o jogador segurar o botao de agachar, troque a animacao e diminua a velocidade. Guarde a altura original em uma constante para restaurar ao levantar.

Por que meu personagem atravessa o chao ao levantar do crouch?

Porque a colisao voltou ao tamanho cheio dentro de um espaco baixo e a fisica empurrou o corpo para fora, geralmente para cima. Antes de levantar, cheque se ha teto com um ShapeCast2D apontado para cima e so restaure a altura se nao houver colisao.

Qual a diferenca entre RayCast2D e ShapeCast2D para checar o teto?

O RayCast2D testa uma linha unica e pode passar entre quinas, dando falso negativo. O ShapeCast2D projeta uma forma com a largura do personagem, entao detecta qualquer obstaculo real acima. Para crouch, ShapeCast2D e mais confiavel.

Como fazer o slide (deslizar) no Godot 2D?

Ao entrar no agachar com velocidade horizontal alta, aplique um impulso maior (por exemplo 320) e deixe um atrito constante reduzir a velocidade a cada frame com move_toward. Quando cair abaixo da velocidade de agachado, encerre o slide.

Por que agachar um inimigo agacha todos os outros?

Porque o recurso de forma (shape) e compartilhado entre instancias por padrao no Godot. Ative Local to Scene no recurso ou duplique a forma no _ready com collision.shape.duplicate() para cada instancia ter a sua.