Voltar para o Blog
Quest Log

Escadas (Ladders) em Plataforma 2D no Godot 4

Personagem pixel art subindo uma escada de madeira entre duas plataformas em um jogo 2D

Aprenda a implementar escada godot 2d com CharacterBody2D, Area2D e máquina de estados. Subir, descer, sair no topo e ignorar a gravidade na prática.

Escada é um daqueles sistemas que parecem triviais até você sentar para programar. O personagem precisa parar de cair, grudar na escada, subir e descer com input vertical, e sair de forma limpa no topo. Neste guia você vai montar uma escada godot 2d completa em um jogo de plataforma usando CharacterBody2D, uma Area2D para a zona da escada e uma máquina de estados bem simples com enum. Sem mágica, só lógica clara que você consegue ler e adaptar.

Escadas (Ladders) em Plataforma 2D no Godot 4

A ideia central é separar dois modos de movimento. No modo normal, a gravidade puxa o personagem para baixo e ele anda na horizontal. No modo escada, a gravidade é ignorada, o personagem se move na vertical seguindo o input e o movimento horizontal é travado ou bem reduzido. A transição entre esses dois modos é o que faz a escada parecer boa ou quebrada.

Se você ainda não tem uma base de andar e pular, vale revisar primeiro o movimento de personagem em plataforma 2D, porque a escada entra como uma camada em cima desse movimento.

Montando a cena da escada

A escada física no mundo é uma Area2D. Ela não colide com nada, só detecta quando o player entra na região. Estruture assim:

  • Ladder (Area2D)
    • CollisionShape2D com um RectangleShape2D cobrindo a área da escada

Coloque a Area2D no grupo ladder. No editor, selecione o nó, vá em Node > Groups e adicione ladder. Assim o player não precisa saber o nome de cada escada da fase, ele só pergunta se a área pertence a esse grupo.

Sobre as collision layers da Area2D: a escada deve viver em uma layer própria, separada do chão e dos inimigos. O player precisa monitorar essa layer para detectar a escada, mas a escada não deve bloquear o movimento. Se você ainda está confuso com layers e masks, dá uma olhada em collision layers e masks no Godot antes de seguir, porque configurar isso errado é a causa número um de escada que não detecta nada.

Uma boa convenção: deixe a escada na layer 4, com mask zerada (ela não precisa detectar nada, só ser detectada). O player, por sua vez, terá uma Area2D detectora ou usará a sinalização da própria escada.

Detectando o player na escada

O jeito mais limpo é a escada avisar o player quando ele entra e sai. Conecte os sinais body_entered e body_exited da Area2D para um script na própria escada:

extends Area2D

func _on_body_entered(body: Node2D) -> void:
    if body.has_method("set_ladder"):
        body.set_ladder(self, true)

func _on_body_exited(body: Node2D) -> void:
    if body.has_method("set_ladder"):
        body.set_ladder(self, false)

E no player, métodos para receber esse aviso:

var current_ladder: Area2D = null
var can_use_ladder: bool = false

func set_ladder(ladder: Area2D, entered: bool) -> void:
    if entered:
        current_ladder = ladder
        can_use_ladder = true
    else:
        can_use_ladder = false
        current_ladder = null
        if state == State.LADDER:
            exit_ladder()

Repare que sair da área força o player para fora do estado escada. Isso evita o bug clássico de continuar grudado no ar depois que a escada acabou.

A máquina de estados

Vamos usar um enum com poucos estados. Para escada bastam dois: o estado normal e o estado escada. Você pode ter outros (pulo, dash), mas aqui o foco é a transição para a escada.

extends CharacterBody2D

enum State { NORMAL, LADDER }

@export var speed: float = 130.0
@export var jump_velocity: float = -320.0
@export var climb_speed: float = 90.0

var gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")
var state: State = State.NORMAL

A variável state decide qual bloco de física roda a cada frame. Manter isso explícito facilita debugar: a qualquer momento você sabe em que modo o personagem está.

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

Entrando e saindo do modo escada

O player entra na escada quando está dentro da área e aperta cima ou baixo. Centralizamos a lógica de entrada e saída em métodos próprios para não espalhar a regra pelo código:

func enter_ladder() -> void:
    state = State.LADDER
    velocity.x = 0.0
    velocity.y = 0.0
    # alinha o player ao centro horizontal da escada
    if current_ladder:
        global_position.x = current_ladder.global_position.x

func exit_ladder() -> void:
    state = State.NORMAL

Alinhar o global_position.x ao centro da escada é um detalhe que muda muito a sensação. Sem isso, o player sobe torto e pode escorregar para fora da área. Se você não quiser o snap rígido, interpole com lerp para um efeito mais suave.

O loop de física

Agora o coração do sistema dentro de _physics_process. Cada estado tem sua própria função:

func _physics_process(delta: float) -> void:
    match state:
        State.NORMAL:
            process_normal(delta)
        State.LADDER:
            process_ladder(delta)
    move_and_slide()

O estado normal é o movimento de plataforma de sempre, com a checagem extra para entrar na escada:

func process_normal(delta: float) -> void:
    if not is_on_floor():
        velocity.y += gravity * delta

    var direction := Input.get_axis("move_left", "move_right")
    velocity.x = direction * speed

    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = jump_velocity

    # entra na escada apertando cima ou baixo dentro da area
    if can_use_ladder:
        var climb := Input.get_axis("move_up", "move_down")
        if climb != 0.0:
            enter_ladder()

E o estado escada, onde a gravidade some por completo:

func process_ladder(_delta: float) -> void:
    var climb := Input.get_axis("move_up", "move_down")
    velocity.y = climb * climb_speed

    # movimento horizontal travado na escada
    velocity.x = 0.0

    # pular sai da escada com impulso
    if Input.is_action_just_pressed("jump"):
        exit_ladder()
        velocity.y = jump_velocity
        return

    # chegou ao topo e pisou no chao, sai naturalmente
    if is_on_floor() and climb > 0.0:
        exit_ladder()

Note que dentro de process_ladder nunca somamos gravity. Esse é o ponto mais importante de toda a implementação: enquanto o estado for LADDER, a gravidade simplesmente não existe. Se você esquecer e deixar a gravidade rodando em paralelo, o personagem desce sozinho e a escada vira uma luta contra a física.

Se quiser permitir um leve ajuste horizontal na escada (alguns jogos deixam o personagem se inclinar um pouco), troque o velocity.x = 0.0 por algo como velocity.x = Input.get_axis("move_left", "move_right") * speed * 0.3. Reduzir para 30 por cento mantém a sensação de escada sem prender o jogador.

O cuidado com o topo da escada

O topo é onde a maioria das implementações quebra. Em geral a plataforma do topo da escada é uma plataforma one-way (StaticBody2D com one_way_collision ativo no CollisionShape2D, ou um TileMap com colisão de subida só por um lado). O comportamento esperado é: o player sobe pela escada atravessando essa plataforma por baixo e, ao chegar no topo, pisa firme nela.

Como a plataforma é one-way, o player consegue subir através dela sem ser bloqueado. Quando a velocity.y é negativa (subindo) e ele cruza a borda, o is_on_floor() passa a retornar verdadeiro assim que ele apoia em cima. Foi por isso que adicionamos a checagem if is_on_floor() and climb > 0.0 em process_ladder. Subiu o suficiente para pisar no chão, então sai da escada já em pé, sem cair de volta.

Um detalhe prático: estenda a Area2D da escada um pouco acima da plataforma do topo, uns poucos pixels. Isso dá margem para o player completar a subida antes de sair da zona de detecção. Se a área terminar exatamente na borda da plataforma, o body_exited dispara cedo demais e o personagem perde o controle da escada no pior momento.

Para descer a partir do topo, o player precisa primeiro sair da plataforma one-way. A solução comum é: estando parado em cima da escada e apertando para baixo, force a entrada na escada e desligue a colisão one-way por um instante, ou empurre o player alguns pixels para dentro da área antes de entrar no estado escada. Uma versão simples:

func try_enter_from_top() -> void:
    if can_use_ladder and is_on_floor():
        if Input.is_action_just_pressed("move_down"):
            # empurra um pouco para dentro da escada
            global_position.y += 4.0
            enter_ladder()

Chame try_enter_from_top dentro de process_normal se quiser esse comportamento. Ajuste os 4 pixels conforme a altura do seu personagem e da plataforma.

Saídas possíveis da escada

Vale ter o mapa mental claro de todas as formas de sair do estado escada, porque cada uma precisa funcionar:

  • Pulo: aperta jump, sai com impulso vertical. Bom para soltar da escada no meio.
  • Topo: chega em cima, pisa no chão, sai em pé automaticamente.
  • Saída pela área: o body_exited da escada chama exit_ladder e devolve o player ao modo normal.
  • Base da escada: ao descer e tocar o chão, a checagem is_on_floor() no estado normal já segura o personagem na próxima vez que a gravidade voltar.

Se a sua escada está bugando, quase sempre é uma dessas saídas que não está coberta. Teste cada caminho isoladamente.

Polindo a sensação

Algumas melhorias rápidas que valem o tempo. Pause a animação de subida quando velocity.y for zero, para o personagem ficar parado na escada em vez de fazer a animação de climbing no vazio. Toque um som de passo a cada intervalo enquanto sobe. E considere desligar a habilidade de dash ou de double jump enquanto estiver no estado escada, senão o jogador escapa por caminhos que você não planejou. Se o seu jogo já tem movimentação aérea avançada, dá para ver como isolar essas mecânicas no guia de double jump e wall jump no Godot.

Com a Area2D no grupo ladder, as collision layers certas, a gravidade ignorada no estado escada e o cuidado com a plataforma one-way do topo, você tem um sistema sólido e previsível. O segredo não é código complicado, é deixar cada transição de estado explícita e testar todas as saídas. A partir daqui é só ajustar climb_speed e os pixels de folga ao feel do seu jogo.