Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Muovere il giocatore tramite codice

È ora di programmare! Useremo le azioni di input che abbiamo creato nella parte precedente per muovere il personaggio.

Nota

Per questo progetto, seguiremo le convenzioni di denominazione di Godot.

  • GDScript: Le classi (nodi) usano PascalCase, le variabili e le funzioni snake_case e le costanti usano ALL_CAPS (Vedi Guida di stile di GDScript).

  • C#: Le classi, le variabili di esportazione e i metodi usano PascalCase, i campi privati usano _camelCase, le variabili locali e i parametri usano camelCase (Consultare Guida di stile C#). Bisogna fare attenzione a scrivere il nome dei metodi correttamente quando si connettono i segnali.

Fai clic destro sul nodo Player e seleziona Allega script per aggiungere un nuovo script ad esso. Nel popup, imposta Modello su Empty prima di premere il pulsante Crea. Lo impostiamo su Empty (vuoto) perché vogliamo scrivere il nostro codice per il movimento del giocatore.

image0

Cominciamo con le proprietà della classe. Definiremo una velocità di movimento, un'accelerazione di caduta che rappresenta la gravità e una velocità che useremo per muovere il personaggio.

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

Queste sono proprietà comuni per un corpo in movimento. La target_velocity è un vettore 3D che combina una velocità con una direzione. Qui, la definiamo come una proprietà perché vogliamo aggiornarne e riutilizzarne il valore tra un frame all'altro.

Nota

I valori sono piuttosto diversi rispetto al codice 2D perché le distanze sono espresse in metri. In 2D, mille unità (pixel) potrebbero corrispondere a metà della larghezza dello schermo, ma in 3D, sono un chilometro.

Programmiamo il movimento. Cominciamo calcolando il vettore di direzione di input tramite l'oggetto Input globale, in _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

Qui, invece di _process(), effettueremo tutti i calcoli attraverso la funzione virtuale _physics_process(). È progettata specificamente per codice relativo alla fisica, come il movimento di un corpo cinematico o rigido. Essa aggiorna il nodo a intervalli di tempo fissi.

Vedi anche

Per saperne di più sulla differenza tra _process() e _physics_process(), vedere Processo di inattività e di fisica.

Cominciamo inizializzando una variabile direction a Vector3.ZERO. Poi controlliamo se il giocatore sta premendo uno o più azioni di input move_* e aggiorniamo di conseguenza le componenti x e z del vettore. Queste corrispondono agli assi del piano di appoggio.

Queste quattro condizioni ci offrono otto possibilità e otto possibili direzioni.

Nel caso in cui il giocatore prema, ad esempio, i tasti W e D allo stesso tempo, il vettore avrà una lunghezza di circa 1.4. Ma se preme un solo tasto, avrà una lunghezza di 1. Vogliamo che la lunghezza del vettore sia costante e non si muova più velocemente in diagonale. Per fare ciò, possiamo chiamare il suo metodo 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)

Qui, normalizziamo il vettore solo se la direzione ha una lunghezza maggiore di zero, il che significa che il giocatore sta premendo un tasto direzionale.

Calcoliamo la direzione in cui mira $Pivot creando un Basis che guarda nella direzione direction.

Quindi, aggiorniamo la velocità. Dobbiamo calcolare la velocità al suolo e la velocità di caduta separatamente. Assicurati di tornare indietro di una tabulazione in modo che le righe siano dentro la funzione _physics_process() ma fuori alla condizione appena scritta sopra.

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 funzione CharacterBody3D.is_on_floor() restituisce true se il corpo è entrato in collisione con il pavimento in questo frame. Ecco perché applichiamo la gravità al Player solo mentre è in aria.

Per la velocità verticale, sottraiamo l'accelerazione di caduta moltiplicata per il tempo delta a ogni frame. Questa riga di codice assicurerà che il nostro personaggio cada in ogni frame, a patto che non si trovi sul pavimento o non vi si scontri.

Il motore di fisica può rilevare interazioni con le pareti, il pavimento o altri corpi durante un determinato frame solo se si verificano movimenti e collisioni. Utilizzeremo questa proprietà in seguito per programmare il salto.

Nell'ultima riga, chiamiamo CharacterBody3D.move_and_slide(), un potente metodo della classe CharacterBody3D che permette di muovere un personaggio fluidamente. Se colpisce un muro durante il movimento, il motore cercherà di allisciarlo. Utilizza il valore velocity nativo del CharacterBody3D

E questo è tutto il codice necessario per muovere il personaggio sul pavimento.

Ecco il codice completo di player.gd come riferimento.

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()
        # Setting the basis property will affect the rotation of the node.
        $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()

Provare il movimento del nostro giocatore

Metteremo il nostro giocatore nella scena Main per testarlo. Per farlo, dobbiamo istanziare il giocatore e poi aggiungere una telecamera. A differenza del 2D, in 3D non vedrai nulla se la tua viewport non ha una telecamera puntata su qualcosa.

Salva la scena Player e apri la scena Main. Per farlo, clicca sulla scheda Main in alto all'editor.

image1

Se hai chiuso la scena in precedenza, vai al pannello FileSystem e fai doppio clic su main.tscn per riaprirla.

Per creare un'istanza di Player, fai clic destro sul nodo Main e seleziona Istanzia scena figlia.

image2

Nel popup, fai doppio clic su player.tscn. Il personaggio dovrebbe apparire al centro della viewport.

Aggiunta di una telecamera

Aggiungiamo ora la telecamera. Come abbiamo fatto con il Pivot del nostro Player, creeremo un rig di base. Fai di nuovo clic destro sul nodo Main e seleziona Aggiungi nodo figlio. Crea un nuovo Marker3D e dagli il nome CameraPivot. Seleziona CameraPivot e aggiungi un nodo figlio Camera3D. L'albero di scene dovrebbe apparire simile a questo.

image3

Nota la casella di spunta Anteprima che appare in alto a sinistra nella vista 3D quando hai la Camera selezionata. Puoi cliccarci sopra per visualizzare un'anteprima della proiezione della telecamera di gioco.

image4

Useremo il Pivot per ruotare la telecamera come se fosse montata su una gru. Dividiamo prima la vista 3D per poter navigare liberamente nella scena e vedere cosa vede la telecamera.

Nella barra degli strumenti giusto sopra la viewport, clicca su View, quindi su 2 Viewports. Puoi anche premere Ctrl + 2 (Cmd + 2 su macOS).

image11

image5

Nella vista in basso, seleziona la Camera3D e attiva l'anteprima della telecamera cliccando sulla casella di spunta.

image6

Nella vista dall'alto, assicurati che la tua Camera3D sia selezionata e sposta la telecamera di circa 19 unità sull'asse Z (trascina la freccia blu).

image7

È qui che avviene la magia. Seleziona CameraPivot e ruotalo di -45 gradi attorno all'asse X (utilizzando il cerchio rosso). Vedrai la telecamera muoversi come se fosse agganciata a una gru.

image8

Puoi eseguire la scena premendo F6 (Cmd + R su macOS) e premere i tasti freccia per muovere il personaggio.

image9

Possiamo vedere un po' di spazio vuoto attorno al personaggio a causa della proiezione prospettica. In questo gioco, utilizzeremo invece una proiezione ortografica per inquadrare meglio l'area di gioco e rendere le distanze più facili da giudicare per il giocatore.

Seleziona di nuovo la Telecamera e nell'Ispettore imposta Projection su Orthogonal e Size su 19. Il personaggio dovrebbe ora apparire più piatto e il terreno dovrebbe riempire lo sfondo.

Nota

Quando si utilizza una telecamera ortogonale in Godot 4, la qualità delle ombre direzionali dipende dal valore Far della telecamera. Più alto è il valore Far, più lontano sarà la telecamera in grado di vedere. Tuttavia, valori Far più alti riducono anche la qualità delle ombre, poiché il rendering delle ombre deve coprire una distanza maggiore.

Se le ombre direzionali appaiono troppo sfocate dopo essere passati a una telecamera ortogonale, riduci la proprietà Far della telecamera a un valore inferiore, ad esempio 100. Non ridurre troppo questa proprietà Far, altrimenti gli oggetti in lontananza inizieranno a scomparire.

image10

Prova la tua scena e dovresti riuscire a muoverti in tutte le 8 direzioni senza passare attraverso il pavimento!

Alla fine, abbiamo sistemato sia il movimento del giocatore sia la vista. Successivamente, lavoreremo sui mostri.