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.

Conception de la scène du mob

Dans cette partie, vous allez coder les monstres, que nous appellerons mobs. Dans la prochaine leçon, nous les ferons apparaître aléatoirement autour de la zone de jeu.

Concevons les monstres eux-mêmes dans une nouvelle scène. La structure du nœud sera similaire à celle de la scène player.tscn.

Créez une scène avec, encore une fois, un nœud CharacterBody3D comme racine. Nommez-le Mob. Ajoutez-lui un nœud Node3D comme enfant, nommez-le Pivot. Faîtes ensuite glisser le fichier mob.glb depuis le dock Système de fichiers sur le Pivot pour ajouter le modèle 3D du monstre à la scène.

../../_images/drag_drop_mob.webp

Vous pouvez renomme le nœud mob nouvellement créé en Character.

image0

Nous avons besoin d'une forme de collision pour que le corps fonctionne. Faites un clic droit sur le nœud Mob, la racine de la scène, et cliquez sur Ajoutez un nœud enfant.

image1

Ajoutez un CollisionShape3D.

image2

Dans l'Inspecteur, assignez une BoxShape3D à la propriété Shape.

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

Nous devons changer sa taille pour mieux s'adapter au modèle 3D. Vous pouvez le faire interactivement en faisant glisser les points orange.

La boîte devrait toucher le sol et être un peu plus fine que le modèle. Les moteurs physiques fonctionnent de telle sorte que si la sphère du joueur touche ne serait-ce que le coin de la boîte, une collision se produira. Si la boîte est un petit peu trop large par rapport au modèle 3D, vous risquez de mourir à une certaine distance du monstre, et le jeu semblera injuste pour les joueurs.

image4

Remarquez que ma boîte est plus grande que le monstre. Ce n'est pas grave dans ce jeu car nous regardons la scène d'en haut et utilisons une perspective fixe. Les formes de collision n'ont pas besoin de correspondre exactement au modèle. C'est votre ressenti quand vous testez le jeu qui doit dicter leur forme et leur taille.

Suppression des monstres hors écran

Nous allons faire apparaître des monstres à des intervalles réguliers dans le niveau de jeu. Si nous ne faisons pas attention, leur nombre pourrait augmenter à l'infini, et nous ne voulons pas cela. Chaque instance de mob a un coût en mémoire et un coût de traitement, et nous ne voulons pas le payer lorsque le mob est en dehors de l'écran.

Une fois qu'un monstre quitte l'écran, nous n'en avons plus besoin, donc nous pouvons le supprimer. Godot a un nœud qui détecte lorsque des objets quittent l'écran, VisibleOnScreenNotifier3D, et nous allons l'utiliser pour détruire nos mobs.

Note

Lorsque vous instanciez souvent un objet, il y a une technique qui vous permet d'éviter le coût de la création et de la destruction constante d'instances : le pooling. Elle consiste à pré-créer un tableau d'objets et de les réutiliser encore et encore.

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: Gestion de la mémoire.

Sélectionnez le nœud Mob et ajoutez-lui un nœud enfant VisibleOnScreenNotifier3D. Une autre boîte, rose cette fois, apparaît. Quand cette boîte quitte complètement l'écran, le nœud émettra un signal.

image5

Redimensionnez-la en utilisant les points orange jusqu'à ce qu'elle couvre l'ensemble du modèle 3D.

image6

Coder le mouvement du mob

Implémentons le déplacement du monstre. Nous allons faire ça en deux étapes. D'abord, nous allons écrire un script sur le Mob qui définit une fonction pour initialiser le monstre. Nous allons ensuite coder le mécanisme d'apparition aléatoire dans la scène main.tscn et nous appellerons la fonction à partir de là.

Attachez un script au Mob.

image7

Voici le code du mouvement pour commencer. Nous définissons deux propriétés, min_speed et max_speed, pour définir une plage de vitesse aléatoire, que nous utiliserons ensuite pour définir la vitesse 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()

Comme pour le joueur, nous déplaçons le mob à chaque image en appelant la méthode CharacterBody3D.move_and_slide(). Cette fois, nous ne voulons pas mettre à jour la velocity à chaque image ; nous voulons que le monstre se déplace à une vitesse constante jusqu'à quitter l'écran, même s'il venait à rencontrer un obstacle.

Nous devons définir une autre fonction pour calculer la vélocité CharacterBody3D.velocity. Cette fonction va orienter le monstre vers le joueur et rendra aléatoire son angle de déplacement et sa vélocité.

La fonction prendra une start_position, la position d'apparition du mob, et la player_position comme arguments.

Nous positionnons le monstre à start_position puis nous l'orientons vers le joueur en utilisant la méthode look_at_from_position() et rendons l'angle aléatoire en effectuant une rotation aléatoire autour de l'axe Y. Ci-dessous, randf_range() produit une valeur aléatoire entre -PI / 4 radians et PI / 4 radians.

# 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))

Nous avons obtenu une position aléatoire, maintenant nous avons besoin d'une random_speed. randi_range() sera utile car il donne des valeurs entières aléatoires, et nous utiliserons min_speed et max_speed. random_speed est juste un entier, et nous l'utilisons simplement pour multiplier notre CharacterBody3D.velocity. Une fois random_speed appliquée, nous faisons pivoter le Vector3 CharacterBody3D.velocity vers le joueur.

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)

Quitter l'écran

Il nous reste à détruire les mobs lorsqu'ils quittent l'écran. Pour ce faire, nous connectons le signal screen_exited de notre nœud VisibleOnScreenNotifier3D au Mob.

Sélectionnez le nœud VisibleOnScreenNotifier3D et, sur le côté droit de l'interface, naviguez vers le dock Signaux. Double-cliquez sur le signal screen_exited().

image9

Connectez le signal au Mob

image10

Cela ajoutera pour vous une nouvelle fonction à votre script mob, _on_visible_on_screen_notifier_3d_screen_exited(). Depuis cette fonction, appelez la méthode queue_free(). Cela détruira l'instance sur laquelle la fonction a été appelée.

func _on_visible_on_screen_notifier_3d_screen_exited():
    queue_free()

Notre monstre est prêt à entrer dans le jeu ! Dans la partie suivante, nous ferons apparaître des monstres dans le niveau de jeu.

Voici le script complet mob.gd pour référence.

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()