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.

Progettando la scena del mob

In questa parte, programmerai i mostri, che chiameremo mob. Nella prossima lezione, li genereremo casualmente attorno l'area di gioco.

Progettiamo i mostri stessi in una nuova scena. La struttura dei nodi sarà simile a quella della scena player.tscn.

Crea una scena con, ancora una volta, un nodo CharacterBody3D come radice. Diamogli il nome Mob. Aggiungi un nodo figlio Node3D, con il nome Pivot. Trascina e rilascia il file mob.glb dal pannello FileSystem sul nodo Pivot per aggiungere il modello 3D del mostro alla scena.

../../_images/drag_drop_mob.webp

Puoi rinominare il nodo mob appena creato in Character.

image0

Abbiamo bisogno di una forma di collisione affinché il nostro corpo funzioni. Fai clic destro sul nodo Mob, la radice della scena, e fai clic su Aggiungi nodo figlio.

image1

Aggiungi un CollisionShape3D.

image2

Nell'Ispettore, assegna una BoxShape3D alla proprietà Shape.

../../_images/08.create_box_shape3D.webp

Dovremmo cambiarne le dimensioni per adattarle meglio al modello 3D. Puoi farlo interattivamente cliccando e trascinando i punti arancioni.

Il riquadro dovrebbe toccare il pavimento ed essere leggermente più sottile del modello. I motori di fisica funzionano in modo tale che se la sfera del giocatore tocca anche solo l'angolo del riquadro, si verificherà una collisione. Se il riquadro è leggermente troppo grande rispetto al modello 3D, si potrebbe morire lontano dal mostro e il gioco sembrerà ingiusto nei confronti dei giocatori.

image4

Nota che il mio riquadro è più alto del mostro. In questo gioco va bene perché stiamo guardando la scena dall'alto e usando una prospettiva fissa. Le forme di collisione non devono coincidere esattamente con il modello. È l'impressione che si ha quando si prova il gioco a determinarne forma e dimensioni.

Rimozione dei mostri fuori dallo schermo

Faremo comparire mostri a intervalli regolari nel livello di gioco. Se non stiamo attenti, il loro numero potrebbe aumentare all'infinito, e non vogliamo che ciò accada. Ogni istanza di mob ha un costo sia di memoria sia di elaborazione, e non vogliamo pagarlo quando il mob è fuori dallo schermo.

Una volta che un mostro lascia lo schermo, non ne abbiamo più bisogno, quindi dovremmo eliminarlo. Godot ha un nodo che rileva quando gli oggetti lasciano lo schermo, VisibleOnScreenNotifier3D, e lo utilizzeremo per distruggere i nostri mob.

Nota

Quando si istanzia continuamente un oggetto, esiste una tecnica che permette di evitare il costo di creare e distruggere istanze continuamente, chiamata "pooling". Consiste nel creare in anticipo un array di oggetti e riutilizzarli più e più volte.

When working with GDScript, this usually isn't needed. The main reason to use pools is to avoid freezes with garbage-collected languages like C# or Lua. GDScript uses a different technique to manage memory, reference counting, which doesn't have that caveat. You can learn more about that here: Gestione della memoria.

Seleziona il nodo Mob e aggiungi un nodo figlio VisibleOnScreenNotifier3D. Apparirà un altro riquadro, stavolta rosa. Quando questo riquadro scomparirà completamente dallo schermo, il nodo emetterà un segnale.

image5

Ridimensionalo tramite i punti arancioni fino a coprire tutto il modello 3D.

image6

Programmare il movimento del mob

Implementiamo il movimento del mostro. Lo faremo in due passaggi. Per prima cosa, scriveremo uno script sulla scena Mob che definisce una funzione per inizializzare il mostro. Poi scriveremo il codice per il meccanismo di generazione casuale nella scena main.tscn e chiameremo la funzione da lì.

Aggiungi uno script al Mob.

image7

Ecco il codice di movimento da cui cominciare. Definiamo due proprietà, min_speed e max_speed, per definire un intervallo di velocità casuale, che useremo in seguito per definire CharacterBody3D.velocity.

extends CharacterBody3D

# Minimum speed of the mob in meters per second.
@export var min_speed = 10
# Maximum speed of the mob in meters per second.
@export var max_speed = 18


func _physics_process(_delta):
    move_and_slide()

In modo simile al giocatore, muoviamo il mob a ogni frame chiamando la funzione CharacterBody3D.move_and_slide(). Questa volta, non aggiorniamo velocity a ogni frame; vogliamo che il mostro si muova a velocità costante e lasci lo schermo, anche se dovesse urtare un ostacolo.

Dobbiamo definire un'altra funzione per calcolare CharacterBody3D.velocity. Questa funzione girerà il mostro verso il giocatore e ne randomizzerà sia il suo angolo di movimento sia la sua velocità.

La funzione accetterà come argomenti start_position, la posizione di apparizione del mob, e player_position.

Posizioniamo il mob in start_position e lo giriamo verso il giocatore tramite il metodo look_at_from_position(), e rendiamo casuale l'angolo ruotandolo di una quantità casuale attorno all'asse Y. Di seguito, randf_range() restituisce un valore casuale compreso tra -PI / 4 radianti e PI / 4 radianti.

# This function will be called from the Main scene.
func initialize(start_position, player_position):
    # We position the mob by placing it at start_position
    # and rotate it towards player_position, so it looks at the player.
    look_at_from_position(start_position, player_position, Vector3.UP)
    # Rotate this mob randomly within range of -45 and +45 degrees,
    # so that it doesn't move directly towards the player.
    rotate_y(randf_range(-PI / 4, PI / 4))

Abbiamo ottenuto una posizione casuale, ora ci serve random_speed. randi_range() sarà utile perché restituisce valori interi casuali, e useremo min_speed e max_speed. random_speed è solo un numero intero, e lo usiamo solo per moltiplicare CharacterBody3D.velocity. Dopo aver applicato random_speed, ruotiamo il Vector3 CharacterBody3D.velocity verso il giocatore.

func initialize(start_position, player_position):
    # ...

    # We calculate a random speed (integer)
    var random_speed = randi_range(min_speed, max_speed)
    # We calculate a forward velocity that represents the speed.
    velocity = Vector3.FORWARD * random_speed
    # We then rotate the velocity vector based on the mob's Y rotation
    # in order to move in the direction the mob is looking.
    velocity = velocity.rotated(Vector3.UP, rotation.y)

Lasciare lo schermo

Dobbiamo ancora distruggere i mob quando escono dallo schermo. Per farlo, connetteremo il segnale screen_exited del nostro nodo VisibleOnScreenNotifier3D al Mob.

Seleziona il nodo VisibleOnScreenNotifier3D e, sul lato destro dell'interfaccia, vai al pannello Segnali. Fai doppio clic sul segnale screen_exited().

image9

Connetti il segnale al Mob

image10

Questo aggiungerà una nuova funzione al tuo script mob, _on_visible_on_screen_notifier_3d_screen_exited(). Da questa, chiama il metodo queue_free(). Questa funzione distrugge l'istanza su cui viene chiamata.

func _on_visible_on_screen_notifier_3d_screen_exited():
    queue_free()

Il nostro mostro è pronto a entrare nel gioco! Nella prossima parte, genererai mostri nel livello di gioco.

Ecco lo script mob.gd completo come riferimento.

extends CharacterBody3D

# Minimum speed of the mob in meters per second.
@export var min_speed = 10
# Maximum speed of the mob in meters per second.
@export var max_speed = 18

func _physics_process(_delta):
    move_and_slide()

# This function will be called from the Main scene.
func initialize(start_position, player_position):
    # We position the mob by placing it at start_position
    # and rotate it towards player_position, so it looks at the player.
    look_at_from_position(start_position, player_position, Vector3.UP)
    # Rotate this mob randomly within range of -45 and +45 degrees,
    # so that it doesn't move directly towards the player.
    rotate_y(randf_range(-PI / 4, PI / 4))

    # We calculate a random speed (integer)
    var random_speed = randi_range(min_speed, max_speed)
    # We calculate a forward velocity that represents the speed.
    velocity = Vector3.FORWARD * random_speed
    # We then rotate the velocity vector based on the mob's Y rotation
    # in order to move in the direction the mob is looking.
    velocity = velocity.rotated(Vector3.UP, rotation.y)

func _on_visible_on_screen_notifier_3d_screen_exited():
    queue_free()