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.
Checking the stable version of the documentation...
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.
Puoi rinominare il nodo mob appena creato in Character.

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.

Aggiungi un CollisionShape3D.

Nell'Ispettore, assegna una BoxShape3D alla proprietà Shape.
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.

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.

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

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.

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()
using Godot;
public partial class Mob : CharacterBody3D
{
// Don't forget to rebuild the project so the editor knows about the new export variable.
// Minimum speed of the mob in meters per second
[Export]
public int MinSpeed { get; set; } = 10;
// Maximum speed of the mob in meters per second
[Export]
public int MaxSpeed { get; set; } = 18;
public override void _PhysicsProcess(double delta)
{
MoveAndSlide();
}
}
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))
// This function will be called from the Main scene.
public void Initialize(Vector3 startPosition, Vector3 playerPosition)
{
// We position the mob by placing it at startPosition
// and rotate it towards playerPosition, so it looks at the player.
LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
// Rotate this mob randomly within range of -45 and +45 degrees,
// so that it doesn't move directly towards the player.
RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
}
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)
public void Initialize(Vector3 startPosition, Vector3 playerPosition)
{
// ...
// We calculate a random speed (integer).
int randomSpeed = GD.RandRange(MinSpeed, MaxSpeed);
// We calculate a forward velocity that represents the speed.
Velocity = Vector3.Forward * randomSpeed;
// 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().

Connetti il segnale al Mob

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()
// We also specified this function name in PascalCase in the editor's connection window.
private void OnVisibilityNotifierScreenExited()
{
QueueFree();
}
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()
using Godot;
public partial class Mob : CharacterBody3D
{
// Minimum speed of the mob in meters per second.
[Export]
public int MinSpeed { get; set; } = 10;
// Maximum speed of the mob in meters per second.
[Export]
public int MaxSpeed { get; set; } = 18;
public override void _PhysicsProcess(double delta)
{
MoveAndSlide();
}
// This function will be called from the Main scene.
public void Initialize(Vector3 startPosition, Vector3 playerPosition)
{
// We position the mob by placing it at startPosition
// and rotate it towards playerPosition, so it looks at the player.
LookAtFromPosition(startPosition, playerPosition, Vector3.Up);
// Rotate this mob randomly within range of -45 and +45 degrees,
// so that it doesn't move directly towards the player.
RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
// We calculate a random speed (integer).
int randomSpeed = GD.RandRange(MinSpeed, MaxSpeed);
// We calculate a forward velocity that represents the speed.
Velocity = Vector3.Forward * randomSpeed;
// 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);
}
// We also specified this function name in PascalCase in the editor's connection window.
private void OnVisibilityNotifierScreenExited()
{
QueueFree();
}
}