1. Introdução
O Godot 4 introduziu grandes mudanças no movimento de personagens. Os antigos nodes KinematicBody2D e KinematicBody3D foram renomeados para CharacterBody2D e CharacterBody3D, e a API do move_and_slide() foi completamente redesenhada. Se você está migrando do Godot 3 ou começando do zero, este guia cobre tudo o que você precisa saber.
CharacterBody2D e CharacterBody3D são corpos físicos projetados para personagens controlados por código. Diferentemente do RigidBody, eles não respondem a forças automaticamente — você controla o movimento deles inteiramente por script. Isso os torna ideais para personagens jogáveis, NPCs e qualquer coisa que precise de movimento preciso e determinístico.
2. O que mudou desde o Godot 3
Estas são as mudanças mais importantes no movimento de personagens entre o Godot 3 e o Godot 4:
-
KinematicBody2Dagora éCharacterBody2D -
KinematicBody3Dagora éCharacterBody3D -
velocityagora é uma propriedade embutida — você não a passa mais paramove_and_slide() -
move_and_slide()não recebe argumentos — ele lê diretamente da propriedadevelocity -
move_and_slide_with_snap()foi removido — use a propriedadefloor_snap_lengthno lugar -
is_on_floor(),is_on_wall(),is_on_ceiling()ainda funcionam da mesma forma -
get_gravity()foi adicionado no Godot 4.4 para ler a gravidade padrão do projeto como um Vector — muito conveniente
No Godot 3, move_and_slide() retornava a velocidade resultante. No Godot 4, ele retorna um bool (se ocorreu uma colisão), e a velocidade é atualizada diretamente na propriedade velocity.
3. Controlador básico de plataforma 2D
Este é o controlador padrão de plataforma com rolagem lateral. O personagem pode se mover para a esquerda/direita e pular quando está no chão. Este é o template que o Godot gera quando você cria um novo script de CharacterBody2D.
extends CharacterBody2D
const SPEED = 300.0
const JUMP_VELOCITY = -400.0
func _physics_process(delta: float) -> void:
# Apply gravity when not on floor
if not is_on_floor():
velocity += get_gravity() * delta
# Jump when on floor and jump pressed
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Horizontal movement
var direction := Input.get_axis("ui_left", "ui_right")
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
move_and_slide()
Explicação linha por linha
-
extends CharacterBody2D— O script herda de CharacterBody2D, dando acesso avelocity,move_and_slide(),is_on_floor(), etc. -
SPEED = 300.0— Velocidade de movimento horizontal em pixels por segundo. -
JUMP_VELOCITY = -400.0— Negativo porque o eixo Y aponta para baixo em 2D. Um valor negativo move o personagem para cima. -
get_gravity() * delta— Aplica a gravidade do projeto a cada frame.get_gravity()retorna umVector2apontando para baixo (ex.:Vector2(0, 980)). Multiplicar pordeltatorna o resultado independente da taxa de frames. -
Input.get_axis("ui_left", "ui_right")— Retorna um float de -1.0 a 1.0 conforme a tecla direcional pressionada. Suporta entrada analógica. -
move_toward(velocity.x, 0, SPEED)— Desacelera suavemente até zero quando nenhuma entrada é pressionada. O terceiro argumento é a mudança máxima por chamada. -
move_and_slide()— Move o corpo usando avelocityatual, trata colisões, desliza ao longo das superfícies e atualizavelocityautomaticamente.
Seu CharacterBody2D precisa de pelo menos um node filho CollisionShape2D com uma forma atribuída (ex.: RectangleShape2D ou CapsuleShape2D). Sem isso, move_and_slide() não detectará nenhuma colisão.
4. Controlador básico top-down 2D
Para jogos top-down (RPGs, twin-stick shooters, etc.), você se move em todas as quatro direções sem gravidade. O código é mais simples que o de uma plataforma.
extends CharacterBody2D
const SPEED = 200.0
func _physics_process(delta: float) -> void:
var input_direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
velocity = input_direction * SPEED
move_and_slide()
Como funciona
-
Input.get_vector()retorna umVector2normalizado a partir de quatro ações de entrada. Isso trata o movimento diagonal corretamente — o comprimento do vetor é sempre 1.0 ou 0.0, então o movimento diagonal não é mais rápido que o movimento cardinal. -
Nenhuma gravidade é aplicada, já que esta é uma visão top-down. O personagem para imediatamente quando não há entrada porque definimos
velocitydiretamente (sem inércia).
Adicionando aceleração e atrito
Para uma sensação mais suave com aceleração e desaceleração:
extends CharacterBody2D
const SPEED = 200.0
const ACCELERATION = 1200.0
const FRICTION = 1000.0
func _physics_process(delta: float) -> void:
var input_direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
if input_direction != Vector2.ZERO:
velocity = velocity.move_toward(input_direction * SPEED, ACCELERATION * delta)
else:
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
move_and_slide()
5. Controlador 3D em terceira pessoa
A versão 3D segue o mesmo padrão. A principal diferença é trabalhar com Vector3 e usar transform.basis para converter a entrada 2D em movimento no espaço-mundo 3D.
extends CharacterBody3D
const SPEED = 5.0
const JUMP_VELOCITY = 4.5
func _physics_process(delta: float) -> void:
# Apply gravity
if not is_on_floor():
velocity += get_gravity() * delta
# Jump
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Get input and convert to 3D direction
var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED)
move_and_slide()
Principais diferenças em relação ao 2D
-
Unidades: Em 3D, 1 unidade = 1 metro (convenção). Então
SPEED = 5.0significa 5 metros por segundo — números muito menores do que os valores em pixels do 2D. -
A velocidade de pulo é positiva: Em 3D, o eixo Y aponta para cima (ao contrário do 2D), então
JUMP_VELOCITY = 4.5é positivo. -
Transformação de direção:
transform.basis * Vector3(input_dir.x, 0, input_dir.y)converte a entrada 2D em uma direção 3D relativa à orientação do personagem. O Y da entrada é mapeado para o eixo Z (frente/trás em 3D). -
Apenas X e Z são controlados: Definimos
velocity.xevelocity.zseparadamente, deixandovelocity.ypara a gravidade e o pulo.
O método get_gravity() foi adicionado no Godot 4.4. Para versões mais antigas, use Vector2(0, ProjectSettings.get_setting("physics/2d/default_gravity")) para 2D, ou Vector3(0, -ProjectSettings.get_setting("physics/3d/default_gravity"), 0) para 3D.
6. Referência das principais propriedades
Tanto CharacterBody2D quanto CharacterBody3D compartilham estas propriedades importantes. Ajuste-as no Inspetor ou via código.
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
velocity |
Vector2 / Vector3 | Vector2.ZERO |
A velocidade do personagem. Defina isso antes de chamar move_and_slide(). Atualizada após a chamada com a velocidade resultante.
|
floor_snap_length |
float | 1.0 |
Substitui move_and_slide_with_snap(). Distância para encaixar o personagem no chão. Defina como 0 para desativar. Útil para rampas e escadas.
|
up_direction |
Vector2 / Vector3 | Vector2.UP |
Define qual direção é "para cima". Isso determina o que conta como chão, parede ou teto. O padrão é (0, -1) em 2D e (0, 1, 0) em 3D.
|
floor_stop_on_slope |
bool | true |
Quando true, o personagem não desliza pelas rampas ao ficar parado. Essencial para jogos de plataforma. |
floor_max_angle |
float | 0.785 (45°) |
Ângulo máximo de rampa caminhável em radianos. Superfícies mais íngremes que isso são tratadas como paredes. Use deg_to_rad(60) para definir 60 graus.
|
max_slides |
int | 6 |
Número máximo de iterações de colisão por chamada de move_and_slide(). Valores maiores são mais precisos, mas mais lentos.
|
wall_min_slide_angle |
float | 0.262 (15°) |
Ângulo mínimo para deslizar em paredes. Evita que o personagem fique preso em paredes quase paralelas. |
platform_on_leave |
PlatformOnLeave | ADD_VELOCITY |
Comportamento ao deixar uma plataforma móvel. ADD_VELOCITY preserva o momento, ADD_UPWARD_VELOCITY adiciona apenas a componente para cima, DO_NOTHING ignora a velocidade da plataforma.
|
slide_on_ceiling |
bool | true |
Quando true, permite deslizar nos tetos. Quando false, interrompe a velocidade horizontal ao tocar o teto. |
7. Detecção de colisão após move_and_slide()
Após chamar move_and_slide(), você pode inspecionar as colisões que ocorreram. Isso é útil para reagir a tipos específicos de colisores, tocar efeitos sonoros ao aterrissar ou implementar mecânicas de pulo em parede.
func _physics_process(delta: float) -> void:
# ... set velocity here ...
move_and_slide()
# Check all collisions from this frame
for i in get_slide_collision_count():
var collision := get_slide_collision(i)
var collider := collision.get_collider()
print("Collided with: ", collider.name)
print("Normal: ", collision.get_normal())
print("Position: ", collision.get_position())
# Practical example: bounce off enemies
for i in get_slide_collision_count():
var collision := get_slide_collision(i)
if collision.get_collider().is_in_group("enemies"):
velocity = collision.get_normal() * 300
break
Métodos úteis de KinematicCollision
get_collider()— O objeto que foi atingido.get_normal()— A normal da superfície de colisão. Aponta para longe da superfície.get_position()— O ponto no espaço-mundo onde a colisão ocorreu.get_travel()— Quanto o corpo se moveu antes de colidir.get_remainder()— O movimento restante que não foi aplicado.get_collider_velocity()— Velocidade do colisor (útil para plataformas móveis).
8. Guia rápido de migração
Referência rápida para converter código do Godot 3 para o Godot 4:
| Godot 3 | Godot 4 |
|---|---|
KinematicBody2D |
CharacterBody2D |
KinematicBody3D |
CharacterBody3D |
velocity = move_and_slide(velocity, Vector2.UP) |
velocity = ... |
move_and_slide_with_snap(vel, snap, up) |
floor_snap_length = 4.0 |
var vel = move_and_slide(...)retorna velocity |
move_and_slide()a propriedade velocity é atualizada diretamente |
var gravity = ProjectSettings.get("physics/2d/default_gravity") |
get_gravity() (4.4+) |
move_and_slide(..., up_direction, ...) |
up_direction = Vector2.UP (definido como propriedade) |
move_and_slide(..., stop_on_slope, ...) |
floor_stop_on_slope = true (definido como propriedade) |
Ao abrir um projeto do Godot 3 no Godot 4, a engine oferece converter o seu projeto. Ela renomeia os nodes e tenta atualizar os scripts, mas nem sempre acerta as chamadas de move_and_slide(). Você provavelmente precisará corrigir manualmente o seu código de movimento.
9. Erros comuns
Erro 1: Passar argumentos para move_and_slide()
# ERROR: move_and_slide() takes no arguments in Godot 4
velocity = move_and_slide(velocity, Vector2.UP)
# Set velocity, then call move_and_slide() with no arguments
velocity.x = direction * SPEED
move_and_slide()
Erro 2: Esquecer de definir velocity antes de move_and_slide()
func _physics_process(delta: float) -> void:
move_and_slide() # velocity is Vector2.ZERO — nothing happens
Erro 3: Usar _process() em vez de _physics_process()
move_and_slide() deve ser chamado em _physics_process(), que roda a uma taxa fixa (padrão de 60 Hz). Usar _process() vincula a física à taxa de frames de renderização, causando comportamento inconsistente.
Erro 4: Não configurar o CollisionShape
Um CharacterBody2D/CharacterBody3D sem um filho CollisionShape atravessará tudo. O Godot exibe um ícone de aviso no editor se a forma de colisão estiver ausente.
Erro 5: Sobrescrever velocity.y com gravidade a cada frame
# This overwrites any jump velocity!
velocity.y = gravity * delta # should be +=, not =
# Gravity accumulates over time
velocity += get_gravity() * delta
Erro 6: Aplicar gravidade enquanto está no chão
Sempre envolva a gravidade em if not is_on_floor(). Sem essa verificação, a gravidade continua acumulando enquanto o personagem está no chão. Quando ele sai de uma borda, despenca a uma velocidade extrema em vez de cair naturalmente.