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.

Créez une scène avec, encore une fois, un nœud KinematicBody comme racine. Nommez-la Mob. Ajoutez-lui un nœud Spatial comme enfant, nommez-le Pivot. Puis faîtes 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. Vous pouvez renommer le nœud mob nouvellement créé en Character.

image0

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

image1

Ajouter une CollisionShape.

image2

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

image3

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, VisibleNotifier, et nous allons l'utiliser pour détruire nos mobs.

Note

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

Lorsque vous travaillez avec GDScript, vous n'avez pas à vous soucier de cela. La principale raison d'utiliser les pools est pour éviter les freezes avec les langages à ramasse-miettes comme le C# ou le Lua. GDScript utilise une technique différente pour gérer la mémoire, le comptage de références, qui ne présente pas cet inconvénient. Vous pouvez en apprendre plus à ce sujet ici Gestion de la mémoire.

Sélectionnez le nœud Mob et ajoutez-lui un VisibilityNotifier comme enfant. 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 et 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. Nous définissons et initialisons ensuite la velocity.

extends KinematicBody

# 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

var velocity = Vector3.ZERO


func _physics_process(_delta):
    move_and_slide(velocity)

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

Vous pourriez voir un avertissement dans GDScript indiquant que la valeur de move_and_slide()``est inutilisée. Cela est normal. Vous pouvez simplement ignorer cet avertissement ou bien le cacher en ajoutant le commentaire ``# warning-ignore:return_value_discarded au dessus de la ligne de move_and_slide(velocity). Pour en savoir plus a propos du système d'avertissements de GDScript, voir Système d’avertissement de GDScript.

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

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 le tournons vers le joueur en utilisant la méthode look_at() et rendons l'angle aléatoire en effectuant une rotation d'un montant aléatoire autour de l'axe Y. Ci-dessous, rand_range() produit une valeur aléatoire entre -PI / 4 radians et PI / 4 radians.

# We will call this function from the Main scene.
func initialize(start_position, player_position):
    # We position the mob and turn it so that it looks at the player.
    look_at_from_position(start_position, player_position, Vector3.UP)
    # And rotate it randomly so it doesn't move exactly toward the player.
    rotate_y(rand_range(-PI / 4, PI / 4))

Nous calculons ensuite une vitesse aléatoire en utilisant rand_range() une nouvelle fois, et l'utilisons pour calculer la vélocité.

Nous commençons par créer un vecteur 3D pointant vers l'avant, le multiplions par notre random_speed, et enfin nous le faisons pivoter à l'aide de la méthode rotated() de la classe Vector3.

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

    # We calculate a random speed.
    var random_speed = rand_range(min_speed, max_speed)
    # We calculate a forward velocity that represents the speed.
    velocity = Vector3.FORWARD * random_speed
    # We then rotate the vector based on the mob's Y rotation to move in the direction it's 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 VisibilityNotifier au Mob.

Revenez à la fenêtre d'affichage 3D en cliquant sur le bouton 3D en haut de l'éditeur. Vous pouvez également appuyer sur Ctrl + F2 (Alt + 2 sur macOS).

image8

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

image9

Connectez le signal au Mob.

image10

Cela vous ramènera à l'éditeur de script et ajoutera une nouvelle fonction pour vous, _on_VisibilityNotifier_screen_exited(). Depuis cette dernière, appelez la méthode queue_free(). Cela détruira l'instance du mob lorsque la boîte du VisibilityNotifier quittera l'écran.

func _on_VisibilityNotifier_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 KinematicBody

# 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

var velocity = Vector3.ZERO


func _physics_process(_delta):
    move_and_slide(velocity)

func initialize(start_position, player_position):
    look_at_from_position(start_position, player_position, Vector3.UP)
    rotate_y(rand_range(-PI / 4, PI / 4))

    var random_speed = rand_range(min_speed, max_speed)
    velocity = Vector3.FORWARD * random_speed
    velocity = velocity.rotated(Vector3.UP, rotation.y)


func _on_VisibilityNotifier_screen_exited():
    queue_free()