Concepto: CharacterBody2D, velocidad y física
CharacterBody2D es un nodo pensado para personajes controlados por código. Su idea central es simple: tú calculas una velocidad (un Vector2) en cada frame de física y luego le pides al motor que mueva el cuerpo respetando colisiones mediante move_and_slide(). En 2D, normalmente separas el movimiento en dos ejes: X (horizontal: izquierda/derecha) y Y (vertical: gravedad/salto). La gravedad acelera hacia abajo (aumenta velocity.y), y el salto aplica un impulso hacia arriba (un valor negativo en velocity.y).
En Godot 4, el patrón típico es implementar el movimiento dentro de _physics_process(delta), porque ahí el motor ejecuta la simulación física a un ritmo estable.
Paso a paso: preparar el Input Map (izquierda, derecha, saltar)
1) Crear acciones en Project Settings
Ve a Project > Project Settings > Input Map y crea estas acciones (nombres sugeridos):
move_leftmove_rightjump
2) Asignar teclas (y opcionalmente mando)
Asigna al menos:
move_left:Ay/o flecha izquierdamove_right:Dy/o flecha derechajump:Space
Opcional (recomendado para pruebas): agrega entradas de gamepad como Left Stick Left/Right para movimiento y un botón (por ejemplo South) para salto. Mantener el Input Map limpio y consistente te permite cambiar controles sin tocar el código.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
3) Verificación rápida desde código
Para comprobar que las acciones existen y responden, puedes imprimir temporalmente:
func _process(_delta):
if Input.is_action_just_pressed("jump"):
print("Jump presionado")Si no imprime, revisa el nombre exacto de la acción (mayúsculas/minúsculas) y que tenga al menos una tecla asignada.
Paso a paso: escena del jugador con CharacterBody2D
1) Estructura mínima de nodos
Crea una escena Player.tscn con:
CharacterBody2D(raíz) llamadoPlayerCollisionShape2Dcomo hijo (con unRectangleShape2DoCapsuleShape2D)- (Opcional)
Sprite2DoAnimatedSprite2Dpara visualizar
Asegúrate de que el CollisionShape2D tenga tamaño razonable y que el pivote del jugador no quede “enterrado” en el suelo al iniciar.
2) Script base de movimiento
Adjunta un script a Player. Este ejemplo implementa movimiento horizontal, gravedad y salto usando velocity y move_and_slide():
extends CharacterBody2D
@export var speed: float = 220.0
@export var jump_velocity: float = -420.0
@export var gravity: float = 1200.0
func _physics_process(delta: float) -> void:
# 1) Leer input horizontal (-1 izquierda, +1 derecha)
var dir := Input.get_axis("move_left", "move_right")
# 2) Aplicar velocidad horizontal
velocity.x = dir * speed
# 3) Gravedad (solo si no estás en el suelo, o siempre si prefieres)
if not is_on_floor():
velocity.y += gravity * delta
else:
# Opcional: estabiliza al tocar suelo para evitar acumulaciones pequeñas
if velocity.y > 0:
velocity.y = 0
# 4) Salto (solo cuando estás en el suelo)
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jump_velocity
# 5) Mover con colisiones
move_and_slide()Claves del ejemplo: Input.get_axis() devuelve un valor continuo entre -1 y 1 combinando dos acciones; is_on_floor() te permite restringir el salto; la gravedad se integra con delta para que sea estable; y move_and_slide() usa la velocity actual para resolver el movimiento y las colisiones.
Depuración del movimiento: valores, límites y “sensación”
1) Mostrar valores en tiempo real
Para entender qué está pasando, imprime valores clave. Hazlo de forma controlada para no saturar la consola. Por ejemplo, imprime cada cierto tiempo:
var _t := 0.0
func _physics_process(delta):
_t += delta
# ... tu lógica de movimiento ...
if _t >= 0.25:
_t = 0.0
print("vel=", velocity, " on_floor=", is_on_floor())
move_and_slide()Si velocity.y crece sin límite al caer, es normal (aceleración por gravedad), pero puede sentirse “pesado” o “rápido” según el valor de gravity. Ajusta gravity y jump_velocity en conjunto.
2) Ajuste práctico de parámetros (regla de trabajo)
- Si el salto es muy bajo: aumenta la magnitud de
jump_velocity(más negativo) o reducegravity. - Si el personaje cae demasiado lento: aumenta
gravity. - Si el movimiento horizontal se siente lento: sube
speed. - Si “patina” al aterrizar: revisa que estés poniendo
velocity.xsegún input y que tu colisión no tenga formas extrañas; luego considera aceleración/desaceleración (ver mejoras).
3) Límites para evitar valores extremos
En juegos de plataformas, suele ser útil limitar la velocidad vertical máxima para que las caídas no se vuelvan incontrolables:
@export var max_fall_speed: float = 900.0
# Dentro de _physics_process, tras aplicar gravedad:
velocity.y = min(velocity.y, max_fall_speed)Esto no “rompe” la física del personaje; solo evita que la velocidad crezca indefinidamente en caídas largas.
4) Problemas comunes y cómo detectarlos
| Síntoma | Causa probable | Qué revisar |
|---|---|---|
| No se mueve | Acciones mal nombradas o sin teclas | Input Map, nombres exactos en get_axis |
| No salta | is_on_floor() nunca es true | Colisión del suelo, capas/máscaras, forma del collider |
| Se queda pegado en paredes | Colisión demasiado grande o geometría irregular | Tamaño del CollisionShape2D, tiles con bordes |
| Vibra al aterrizar | Pequeñas correcciones de colisión + velocidad residual | Forzar velocity.y = 0 al estar en suelo (como en el ejemplo) |
Mejoras para un control más pulido
1) Aceleración y desaceleración (en lugar de velocidad instantánea)
Asignar velocity.x = dir * speed es directo, pero puede sentirse “robótico”. Una mejora clásica es acelerar hacia la velocidad objetivo y frenar gradualmente cuando no hay input.
@export var speed: float = 240.0
@export var accel: float = 1400.0
@export var decel: float = 1800.0
func _physics_process(delta):
var dir := Input.get_axis("move_left", "move_right")
var target_x := dir * speed
if dir != 0:
velocity.x = move_toward(velocity.x, target_x, accel * delta)
else:
velocity.x = move_toward(velocity.x, 0.0, decel * delta)
# gravedad/salto como antes...
move_and_slide()move_toward() te da un cambio suave y controlable. Sube accel para respuesta más inmediata; sube decel para frenar más rápido (menos “hielo”).
2) Salto variable (altura según cuánto mantienes el botón)
Un salto variable hace que un toque corto produzca un salto bajo y mantener el botón produzca un salto alto. Una forma común: si el jugador suelta el botón mientras aún sube, recortas la velocidad vertical.
@export var jump_velocity: float = -420.0
@export var jump_cut_multiplier: float = 0.45
func _physics_process(delta):
# ... aplicar gravedad ...
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jump_velocity
# Si suelta el salto mientras sube, recorta
if Input.is_action_just_released("jump") and velocity.y < 0:
velocity.y *= jump_cut_multiplier
move_and_slide()Ajusta jump_cut_multiplier: valores más bajos recortan más (saltos cortos más marcados). Esto mejora mucho la “sensación” sin complicar el código.
3) Límites de velocidad horizontal y control en el aire
Si añades aceleración, puede ser útil limitar la velocidad horizontal y diferenciar control en el aire (air control):
@export var max_speed_x: float = 260.0
@export var air_accel_multiplier: float = 0.6
func _physics_process(delta):
var dir := Input.get_axis("move_left", "move_right")
var target_x := dir * max_speed_x
var a := accel
if not is_on_floor():
a *= air_accel_multiplier
if dir != 0:
velocity.x = move_toward(velocity.x, target_x, a * delta)
else:
velocity.x = move_toward(velocity.x, 0.0, decel * delta)
velocity.x = clamp(velocity.x, -max_speed_x, max_speed_x)
move_and_slide()Reducir la aceleración en el aire evita que el personaje “gire en seco” mientras cae, algo que suele sentirse más natural en plataformas.