Godot do Zero: Movimento 2D com CharacterBody2D e física

Capítulo 5

Tempo estimado de leitura: 7 minutos

+ Exercício

CharacterBody2D: por que usar e como a física entra no movimento

Para personagens controlados pelo jogador em jogos 2D, o CharacterBody2D é o nó mais indicado porque ele já foi pensado para movimentação baseada em colisão e “deslizamento” em superfícies. A ideia central é simples: você mantém um vetor velocity (velocidade atual) e, a cada passo de física, atualiza esse vetor com aceleração, gravidade e pulo; em seguida chama move_and_slide() para aplicar o movimento respeitando colisões.

No Godot 4, CharacterBody2D já possui a propriedade velocity. Você não move o personagem alterando diretamente position; em vez disso, você calcula velocity e deixa o motor resolver o deslocamento e as colisões.

O papel de move_and_slide()

  • Move o corpo de acordo com velocity e o tempo de física.
  • Ao colidir, ajusta o movimento para “deslizar” ao longo das superfícies.
  • Atualiza informações úteis como is_on_floor(), is_on_wall() e is_on_ceiling(), que você usa para pulo, atrito, etc.

_process vs _physics_process: quando usar cada um

Na Godot, existem dois loops principais para atualizar lógica:

MétodoRitmoUse paraEvite para
_process(delta)Varia conforme FPSAnimações não físicas, UI, efeitos visuais, timers simples, ler input para interfaceMovimento com colisão e física (pode ficar inconsistente)
_physics_process(delta)Fixo (ex.: 60x/s)Movimento com colisão, gravidade, pulo, qualquer lógica dependente de físicaAtualizações visuais que não precisam de passo fixo

Regra prática: se você chama move_and_slide(), faça isso em _physics_process.

Passo a passo prático: movimento e física com CharacterBody2D

Pré-requisitos da cena (sem repetir conceitos básicos)

Você precisa de um nó CharacterBody2D com um CollisionShape2D configurado e um chão com colisão (por exemplo, um StaticBody2D com CollisionShape2D). O script abaixo deve estar no CharacterBody2D.

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

Etapa 1 — Movimento horizontal básico (sem suavização)

Objetivo: andar para esquerda/direita com velocidade constante. Isso ajuda a validar colisões e o fluxo velocity + move_and_slide().

extends CharacterBody2D

const SPEED := 220.0

func _physics_process(delta: float) -> void:
	var dir := Input.get_axis("move_left", "move_right")
	velocity.x = dir * SPEED
	move_and_slide()

Como testar: rode a cena e verifique se o personagem para ao soltar as teclas e não atravessa o chão/parede. Se atravessar, o problema geralmente está na colisão (shapes) ou no tipo de corpo do chão.

Etapa 2 — Suavização com aceleração e desaceleração

Agora vamos evitar mudanças instantâneas de velocidade. Em vez de setar velocity.x diretamente, vamos aproximar a velocidade atual de um alvo usando aceleração (quando há input) e desaceleração (quando não há input).

Uma forma comum é usar move_toward (função do Godot para aproximar um valor de outro por um passo máximo).

extends CharacterBody2D

@export var max_speed: float = 260.0
@export var acceleration: float = 1200.0
@export var deceleration: float = 1600.0

func _physics_process(delta: float) -> void:
	var dir := Input.get_axis("move_left", "move_right")
	var target_speed := dir * max_speed

	if dir != 0.0:
		velocity.x = move_toward(velocity.x, target_speed, acceleration * delta)
	else:
		velocity.x = move_toward(velocity.x, 0.0, deceleration * delta)

	move_and_slide()

Valores iniciais sugeridos:

  • max_speed: 240 a 320
  • acceleration: 900 a 2000
  • deceleration: 1200 a 2600 (geralmente maior que a aceleração para “frear” mais rápido)

Como sentir a diferença: aumente acceleration para deixar a resposta mais “arcade” (arranca rápido). Aumente deceleration para parar mais seco ao soltar o botão.

Etapa 3 — Limites de velocidade e parâmetros ajustáveis em tempo real

Mesmo com alvo, é útil garantir limites (por exemplo, se você adicionar empurrões, vento, knockback). Vamos aplicar clamp em velocity.x para assegurar que não passe do máximo.

extends CharacterBody2D

@export var max_speed: float = 280.0
@export var acceleration: float = 1400.0
@export var deceleration: float = 1800.0

func _physics_process(delta: float) -> void:
	var dir := Input.get_axis("move_left", "move_right")
	var target_speed := dir * max_speed

	if dir != 0.0:
		velocity.x = move_toward(velocity.x, target_speed, acceleration * delta)
	else:
		velocity.x = move_toward(velocity.x, 0.0, deceleration * delta)

	velocity.x = clamp(velocity.x, -max_speed, max_speed)
	move_and_slide()

Ajuste em tempo real: como as variáveis são @export, você pode selecioná-las no Inspector e alterar valores enquanto o jogo está rodando para “sentir” o controle. Um fluxo prático:

  • Comece com max_speed = 280, acceleration = 1400, deceleration = 1800.
  • Se estiver “escorregando” demais ao parar, aumente deceleration.
  • Se estiver “pesado” para arrancar, aumente acceleration.
  • Se estiver rápido demais para o tamanho da fase, reduza max_speed.

Gravidade e pulo com is_on_floor()

Para um platformer, você normalmente controla o eixo X (horizontal) e deixa o eixo Y (vertical) ser influenciado por gravidade e pulo. A gravidade é uma aceleração constante para baixo; o pulo é um impulso inicial para cima (velocidade negativa no eixo Y, pois em 2D o eixo Y cresce para baixo).

Implementando gravidade

A cada passo de física, se o personagem não estiver no chão, somamos gravidade em velocity.y. Quando está no chão, é comum manter velocity.y “colada” (por exemplo, 0), para evitar acumular valores residuais.

Implementando pulo com checagem de chão

O pulo deve acontecer apenas quando is_on_floor() for verdadeiro (ou seja, o personagem está apoiado). Isso impede pulo infinito no ar.

extends CharacterBody2D

@export var max_speed: float = 280.0
@export var acceleration: float = 1400.0
@export var deceleration: float = 1800.0

@export var gravity: float = 1400.0
@export var jump_velocity: float = -480.0

func _physics_process(delta: float) -> void:
	# Horizontal
	var dir := Input.get_axis("move_left", "move_right")
	var target_speed := dir * max_speed
	if dir != 0.0:
		velocity.x = move_toward(velocity.x, target_speed, acceleration * delta)
	else:
		velocity.x = move_toward(velocity.x, 0.0, deceleration * delta)
	velocity.x = clamp(velocity.x, -max_speed, max_speed)

	# Vertical (gravidade + pulo)
	if not is_on_floor():
		velocity.y += gravity * delta
	else:
		# Ajuda a estabilizar no chão (opcional)
		if velocity.y > 0.0:
			velocity.y = 0.0

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

	move_and_slide()

Valores iniciais sugeridos:

  • gravity: 1200 a 2200 (quanto maior, mais “pesado” e rápido cai)
  • jump_velocity: -380 a -620 (mais negativo = pula mais alto)

Como testar e ajustar rapidamente:

  • Se o pulo estiver baixo demais, torne jump_velocity mais negativo (ex.: de -480 para -540).
  • Se o personagem cair muito devagar, aumente gravity.
  • Se o personagem “flutuar” no topo do pulo, aumente um pouco a gravidade ou reduza a força do pulo.

Exercício incremental (3 etapas) para fixar o controle do personagem

Exercício 1 — Movimento básico

  • Implemente apenas a Etapa 1 (velocidade constante).
  • Teste em uma fase com uma plataforma longa e uma parede.
  • Checklist: encosta na parede e para? não atravessa o chão? a velocidade é a esperada?

Exercício 2 — Suavização

  • Substitua o movimento básico pela Etapa 2 (aceleração/desaceleração).
  • Durante o jogo rodando, ajuste acceleration e deceleration no Inspector.
  • Meta: encontrar um par de valores em que o personagem responda rápido, mas sem “teleportar” para a velocidade máxima.

Exercício 3 — Limites + gravidade + pulo com parâmetros exportados

  • Implemente a Etapa 3 completa (clamp + gravidade + pulo).
  • Defina valores iniciais: max_speed=280, acceleration=1400, deceleration=1800, gravity=1400, jump_velocity=-480.
  • Teste em tempo real: aumente gravity em passos de 200 e observe a queda; depois ajuste jump_velocity em passos de 40 até o pulo ficar confortável.
  • Checklist: o pulo só funciona no chão? ao cair, o personagem não atravessa o piso? ao soltar o direcional, ele desacelera de forma agradável?

Agora responda o exercício sobre o conteúdo:

Ao implementar movimento com colisão usando CharacterBody2D, qual prática garante comportamento consistente de física e evita inconsistências causadas por variação de FPS?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

O movimento com colisão deve rodar em _physics_process, pois o passo é fixo. A lógica calcula velocity (aceleração, gravidade e pulo) e move_and_slide() aplica o deslocamento respeitando colisões e atualiza estados como is_on_floor().

Próximo capitúlo

Godot do Zero: Colisões, shapes e camadas/máscaras

Arrow Right Icon
Capa do Ebook gratuito Godot do Zero: Criando seu Primeiro Jogo 2D com GDScript
29%

Godot do Zero: Criando seu Primeiro Jogo 2D com GDScript

Novo curso

17 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.