Work in progress

The content of this page was not yet updated for Godot 4.2 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

Programando el movimiento del jugador

¡Es hora de codificar! Vamos a utilizar las acciones de entrada que creamos en la última parte para mover el personaje.

Haz clic derecho en el nodo Player y selecciona Añadir Script para agregar un nuevo script. En la ventana emergente, establece la Plantilla a Empty antes de hacer clic en el botón Crear.

image0

Vamos a empezar con las propiedades de la clase. Vamos a definir una rapidez de movimento, una aceleración que representa la gravedad, y una velocidad que usaremos para mover el jugador.

extends CharacterBody3D

# How fast the player moves in meters per second.
@export var speed = 14
# The downward acceleration when in the air, in meters per second squared.
@export var fall_acceleration = 75

var target_velocity = Vector3.ZERO

Estas son propiedades comunes para un cuerpo en movimiento. La target_velocity es un 3D vector que combina una velocidad con una dirección. Aquí, la definimos como una propiedad debido a que queremos actualizar y reusar su valor a través de los fotogramas.

Nota

Los valores son bastante diferentes del código 2D porque las distancias están en metros. En 2D mil unidades (píxeles) pueden corresponder a sólo mitad del ancho de tu pantalla, pero en 3D, es un kilómetro.

Ahora programemos el movimiento. Comenzaremos por calculando el vector de dirección de entrada utilizando el objeto global Input, en _physics_process().

func _physics_process(delta):
    # We create a local variable to store the input direction.
    var direction = Vector3.ZERO

    # We check for each move input and update the direction accordingly.
    if Input.is_action_pressed("move_right"):
        direction.x += 1
    if Input.is_action_pressed("move_left"):
        direction.x -= 1
    if Input.is_action_pressed("move_back"):
        # Notice how we are working with the vector's x and z axes.
        # In 3D, the XZ plane is the ground plane.
        direction.z += 1
    if Input.is_action_pressed("move_forward"):
        direction.z -= 1

Aquí, haremos todos los cálculos usando la función virtual _physics_process(). Como _process(), permite que actualices el nodo cada cuadro, pero está específicamente diseñado para código relacionado a la física, como mover un cuerpo cinemático o rígido.

Ver también

Para aprender más sobre la diferencia entre _process() y _physics_process(), véase Procesamiento en Reposo y Físico.

Comenzamos por inicializar una variable direction a Vector3.ZERO. Luego, verificamos si el jugador está presionando una o más de las entradas move_* y actualizamos las componentes x y z como corresponda. Éstas atañan a los ejes del avión en el suelo.

Estas cuatro condiciones nos dan ocho posibilidades y ocho posibles direcciones.

En el caso que el jugador presione, por ejemplo W y D a la vez, el vector tendrá un largo de aproximadamente 1.4. Pero si presiona una sola tecla, tendrá el largo de 1. Queremos que el largo del vector sea consistente y que no se mueva más rápido en diagonal. Para ello, podemos llamar el método normalized().

#func _physics_process(delta):
    #...

    if direction != Vector3.ZERO:
        direction = direction.normalized()
        # Setting the basis property will affect the rotation of the node.
        $Pivot.basis = Basis.looking_at(direction)

Aquí, sólo normalizamos el vector si la dirección es mayor a cero, lo que significa que el jugador presiona una tecla de dirección.

We compute the direction the $Pivot is looking by creating a Basis that looks in the direction direction.

Entonces, actualizamos la velocidad. Tenemos que calcular velocidad en suelo y la velocidad de caída por separado. Asegúrate de regresar una sangría de modo que las lineas queden dentro de la función ``_physics_process()``pero fuera de la condición que acabamos de escribir.

func _physics_process(delta):
    #...
    if direction != Vector3.ZERO:
        #...

    # Ground Velocity
    target_velocity.x = direction.x * speed
    target_velocity.z = direction.z * speed

    # Vertical Velocity
    if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
        target_velocity.y = target_velocity.y - (fall_acceleration * delta)

    # Moving the Character
    velocity = target_velocity
    move_and_slide()

La función CharacterBody3D.is_on_floor() retorna true si el cuerpo colisionó con el suelo en este fotograma. Por eso aplicamos gravedad al Player sólo cuando está en el aire.

Para la velocidad vertical, restamos la aceleración de caída multiplicada por el delta tiempo en cada fotograma. Esta línea de código causará que nuestro personaje caiga en cada frame mientras no esté en el suelo o esté colisionando con él.

El motor de física sólo puede detectar interacciones con las paredes, el suelo u otros cuerpos durante un fotograma dado si se producen movimientos y colisiones. Utilizaremos esta propiedad más adelante para codificar el salto.

En la ultima linea, llamamos a CharacterBody3D.move_and_slide(), el cual es un método poderoso de la clase CharacterBody3D, clase que permite que tu personaje se mueva suavemente. Si este golpea una pared a la mitad de su movimiento, el motor lo tratara de deslizarlo por ti. Usa el valor de velocity nativo del CharacterBody3D

Y esto es todo el código necesario para mover el personaje en el piso.

Aquí está el código completo de Player.gd como referencia.

extends CharacterBody3D

# How fast the player moves in meters per second.
@export var speed = 14
# The downward acceleration when in the air, in meters per second squared.
@export var fall_acceleration = 75

var target_velocity = Vector3.ZERO


func _physics_process(delta):
    var direction = Vector3.ZERO

    if Input.is_action_pressed("move_right"):
        direction.x += 1
    if Input.is_action_pressed("move_left"):
        direction.x -= 1
    if Input.is_action_pressed("move_back"):
        direction.z += 1
    if Input.is_action_pressed("move_forward"):
        direction.z -= 1

    if direction != Vector3.ZERO:
        direction = direction.normalized()
        $Pivot.basis = Basis.looking_at(direction)

    # Ground Velocity
    target_velocity.x = direction.x * speed
    target_velocity.z = direction.z * speed

    # Vertical Velocity
    if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
        target_velocity.y = target_velocity.y - (fall_acceleration * delta)

    # Moving the Character
    velocity = target_velocity
    move_and_slide()

Probando el movimiento de nuestro personaje

Vamos a poner al jugador en la escena Main para probarlo. Para hacer eso, necesitamos instanciar al jugador y luego agregar la cámara. A diferencia de 2D, en 3D no veras nada si tu viewport no tiene una cámara apuntando a algo.

Guarda tu escena Player y abre la escena Main. Puedes hacer click en el tab Main en la parte superior del editor, para ello.

|image1|

Si cerraste la escena anterior, ve al dock Sistema de Archivos y haz doble click en main.tscn para volver a abrirla.

Para instanciar al Player haz click derecho en el nodo Main y selecciona Instanciar Escena Hija.

image2

En la ventana emergente, haz doble click en player.tscn. El personaje deberia aparecer en el centro del puerto de vista.

Agregando una cámara

Agreguemos una cámara. Como lo hicimos con nuestro Player's Pivot, vamos a crear un rig básico. Haz click derecho en el nodo Main otra vez y selecciona Add Child Node.Crea un nuevo Marker3D, nómbralo CameraPivot. Selecciona CameraPivot y añade un nodo Camera3D como hijo de esta. El árbol de la escena debería verse así.

|image3|

Observa la casilla de selección preview que aparece en la parte superior izquierda donde tienes la Camera seleccionada. Puedes hacer click en ella para previsualizar la proyección de la cámara dentro del juego.

image4

Vamos a usar el Pivot para rotar la cámara como si estuviera en la grúa. Primero dividamos la vista 3D para poder navegar libremente por la escena y ver lo que la cámara ve.

En la barra de herramientas derecha, encima del puerto de vista, haz click en View, entonces 2 Viewports. Puedes también presionar Ctrl + 2`(:kbd:`Cmd + 2 en macOS).

image11

image5

En la vista inferior, selecciona la Camera3D y cambia a la previsualización de la cámara haciendo click en la casilla de selección.

image6

En la vista superior, mueve la cámara aproximadamente ``19``unidades en el eje Z (el azul).

image7

Aquí es donde la magia sucede. Selecciona la CameraPivot y rotála -45 grados alrededor del eje de X (usando el circulo rojo). Veras la cámara moverse como si estuviera adherida a la grúa.

image8

Puedes correr la escena presionando F6 y presionando las teclas de flecha para mover al personaje.

image9

Podemos ver algo de espacio vacío alrededor del personaje debido a la proyección en perspectiva. En este juego, vamos a utilizar en su lugar una proyección ortográfica para enmarcar mejor el área de juego y facilitar al jugador la lectura de las distancias.

Selecciona nuevamente la Cámara y en el Inspector, establece la Proyección en Ortogonal y el Tamaño en 19. El personaje ahora debería lucir más plano y el suelo debería llenar el fondo.

Nota

Cuando se utiliza una cámara ortogonal en Godot 4, la calidad de la sombra direccional es dependiente del valor Far de la cámara. Mientras más grande es el valor Far, más lejos verá la cámara. Sin embargo, valores grandes de Far también decrementan la calidad de la sombra ya que la sombra deberá cubrir una gran distancia.

Si la sombra direccional se ve muy borrosa al cambiar a una cámara ortogonal, decrementa la propiedad Far a valores más pequeños como 100. No achiques mucho la propiedad Far porque los objetos a la distancia comenzarán a desaparecer.

image10

Prueba tu escena y deberías poder moverte en las 8 direccines y no atravezar el piso!

Finalmente tenemos tanto el movimiento del jugador como la vista en su lugar. A continuación, trabajaremos en los monstruos.