Générer des monstres¶
Dans cette partie, nous allons faire apparaître des monstres aléatoirement le long d'un chemin. D'ici la fin, vous aurez des monstres qui parcourront le plateau de jeu.
Doublez-cliquez sur Main.tscn
dans le dock Système de fichiers pour ouvrir la scène Main.
Avant de tracer le chemin, nous allons changer la définition du jeu. Notre jeu a une taille de fenêtre par défaut de 1024x600
. Nous allons la définir à 720x540
, une belle petite boîte.
Allez dans Projet -> Paramètres du projet.
Dans le menu de gauche, naviguez jusqu'à Display -> Window. À droite, mettez la Width à 720
et la Height à 540
.
Création du chemin d'apparition¶
Comme vous l'avez fait dans le tutoriel du jeu 2D, vous allez tracer un chemin et utiliser un nœud PathFollow pour échantillonner des emplacements aléatoires dessus.
Cependant, en 3D, c'est un peu plus compliqué de dessiner le chemin. Nous voulons qu'il soit autour de la vue du jeu pour que les monstres apparaissent à l'extérieur de l'écran. Mais si nous traçons un chemin, nous ne le verrons pas dans l'aperçu de la caméra.
Pour trouver les limites de la vue, nous pouvons utiliser des modèles temporaires. Votre fenêtre d'affichage devrait toujours être divisée en deux parties, avec l'aperçu de la caméra en bas. Si ce n'est pas le cas, appuyez sur Ctrl + 2 (Cmd + 2 sur MacOS) pour diviser la vue en deux. Sélectionnez le nœud Camera et cochez la case Aperçu dans la vue du bas.
Ajout de cylindres génériques¶
Ajoutons les modèles temporaires. Ajoutez un nouveau nœud Spatial comme enfant du nœud Main, et nommez-le Cylinders. Nous l'utiliserons pour regrouper les cylindres. Comme enfant de ce nœud, ajoutez un nœud MeshInstance.
Dans l'Inspecteur, assignez un CylinderMesh à la propriété Mesh.
Définissez la vue du haut sur la vue de dessus orthogonale en utilisant le menu dans le coin supérieur gauche de la fenêtre d'affichage. Vous pouvez également appuyer sur la touche 7 du pavé numérique.
La grille est un peu gênante pour moi. Vous pouvez la désactiver en allant dans le menu Affichage de la barre d'outils et en cliquant sur Afficher la grille.
Vous voulez maintenant déplacer le cylindre le long du plan du sol, en regardant l'aperçu dans la vue du bas. Je recommande d'utiliser l'accrochage à la grille pour ce faire. Vous pouvez l'activer en cliquant sur l'icône d'aimant dans la barre d'outils ou en appuyant sur Y.
Placez le cylindre de façon à ce qu'il soit juste en dehors du champ de vision de la caméra dans le coin supérieur gauche.
Nous allons créer des copies du maillage et les placer autour de la zone de jeu. Appuyez sur Ctrl + D (Cmd + D sur MacOS) pour dupliquer le nœud. Vous pouvez également faire un clic droit sur le nœud dans le dock Scène et sélectionner Dupliquer. Déplacez la copie vers le bas le long de l'axe Z bleu jusqu'à ce qu'elle soit juste en dehors de l'aperçu de la caméra.
Sélectionnez les deux cylindres en maintenant la touche Shift et en cliquant sur celui qui n'est pas sélectionné, et dupliquez-les.
Déplacez-les vers la droite en faisant glisser l'axe X rouge.
Ils sont un peu difficiles à voir en blanc, n'est-ce pas ? Faisons-les ressortir en leur donnant un nouveau matériau.
En 3D, les matériaux définissent les propriétés visuelles d'une surface, comme sa couleur, comment elle reflète la lumière, etc. Nous pouvons les utiliser pour modifier la couleur d'un modèle.
Nous pouvons mettre à jour les quatre cylindres en même temps. Sélectionnez toutes les instances de modèles dans le dock Scène. Pour ce faire, vous pouvez cliquer sur la première et faire un shift-clic sur la dernière.
Dans l'Inspecteur, développez la section Material et assignez un SpatialMaterial à l'emplacement 0.
Cliquez sur l'icône de sphère pour ouvrir la ressource du matériau. Vous obtenez un aperçu du matériau et une longue liste de sections remplies de propriétés. Vous pouvez les utiliser pour créer toutes sortes de surfaces, allant du métal jusqu'à la roche ou l'eau.
Développez la section Albedo et définissez une couleur qui contraste avec le fond, comme un orange vif.
Nous pouvons maintenant utiliser les cylindres comme des guides. Repliez-les dans le dock Scène en cliquant sur la flèche grise à côté d'eux. À l'avenir, vous pourrez également modifier leur visibilité en cliquant sur l'icône d'œil à côté de Cylinders.
Ajoutez un nœud Path comme enfant de Main. Dans la barre d'outils, quatre icônes apparaissent. Cliquez sur l'outil Ajouter un point, symbolisé par un "+" vert.
Note
Vous pouvez survoler n'importe quelle icône pour voir une info-bulle décrivant l'outil.
Cliquez au centre de chaque cylindre pour créer un point. Ensuite, cliquez sur l'icône Fermer la courbe dans la barre d'outils pour fermer le chemin. Si un point est un peu décalé, vous pouvez le glisser pour le repositionner.
Votre chemin devrait ressembler à ceci.
Pour échantillonner des positions aléatoires sur le chemin, nous avons besoin d'un nœud PathFollow. Ajoutez un PathFollow comme enfant du Path. Renommez les deux nœuds en SpawnPath et SpawnLocation, respectivement. C'est plus descriptif de ce pour quoi nous allons les utiliser.
Avec ça, nous sommes prêts à coder le mécanisme d'apparition.
Faire apparaître des monstres au hasard¶
Faites un clic droit sur le nœud Main et attachez-y un nouveau script.
Nous exportons d'abord une variable vers l'Inspecteur afin que nous puissions lui attribuer Mob.tscn
ou n'importe quel autre monstre.
Ensuite, comme nous allons faire apparaître les monstres de manière procédurale, nous voulons que les numéros soient aléatoires à chaque fois que nous jouons. Sinon, les monstres apparaîtront toujours selon la même séquence.
extends Node
export (PackedScene) var mob_scene
func _ready():
randomize()
public class Main : Node
{
// Don't forget to rebuild the project so the editor knows about the new export variable.
#pragma warning disable 649
// We assign this in the editor, so we don't need the warning about not being assigned.
[Export]
public PackedScene MobScene;
#pragma warning restore 649
public override void _Ready()
{
GD.Randomize();
}
}
Nous voulons faire apparaître les mobs à des intervalles de temps réguliers. Pour ça, nous devons revenir à la scène et ajouter un timer. Mais avant cela, nous devons affecter le fichier Mob.tscn
à la propriété mob_scene
.
Revenez à l'écran 3D et sélectionnez le nœud Main. Faîtes glisser Mob.tscn
depuis le dock Système de fichiers vers l'emplacement Mob Scene dans l'Inspecteur.
Ajoutez un nouveau nœud Timer comme enfant de Main. Nommez-le MobTimer.
Dans l'Inspecteur, réglez son Wait Time à 0.5
secondes et activez l'Autostart pour qu'il commence automatiquement au lancement du jeu.
Les timers émettent un signal timeout
à chaque fois qu'ils atteignent la fin de leur Wait Time. Par défaut, ils redémarrent automatiquement, en émettant le signal dans un cycle. Nous pouvons nous connecter à ce signal depuis le nœud Main pour faire apparaître des monstres toutes les 0.5
secondes.
En gardant le MobTimer sélectionné, allez dans le dock Nœud à droite, et double-cliquez sur le signal timeout
.
Connectez-le au noeud Main.
Cela vous ramènera au script, avec une nouvelle fonction vide _on_MobTimer_timeout()
.
Codons la logique d'apparition des mobs. Nous allons :
Instancier la scène du mob.
Échantillonner une position aléatoire sur le chemin d'apparition.
Récupérer la position du joueur.
Appeler la méthode
initialize()
du mob, en lui passant la position aléatoire et la position du joueur.Ajouter le mob comme enfant du nœud Main.
func _on_MobTimer_timeout():
# Create a new instance of the Mob scene.
var mob = mob_scene.instance()
# Choose a random location on the SpawnPath.
# We store the reference to the SpawnLocation node.
var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
# And give it a random offset.
mob_spawn_location.unit_offset = randf()
var player_position = $Player.transform.origin
mob.initialize(mob_spawn_location.translation, player_position)
add_child(mob)
// We also specified this function name in PascalCase in the editor's connection window
public void OnMobTimerTimeout()
{
// Create a new instance of the Mob scene.
Mob mob = (Mob)MobScene.Instance();
// Choose a random location on the SpawnPath.
// We store the reference to the SpawnLocation node.
var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
// And give it a random offset.
mobSpawnLocation.UnitOffset = GD.Randf();
Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
mob.Initialize(mobSpawnLocation.Translation, playerPosition);
AddChild(mob);
}
Ci-dessus, randf()
produit une valeur aléatoire entre 0
et 1
, ce qui est ce que le unit_offset
du nœud PathFollow attend.
Voici le script complet Main.gd
jusqu'à présent, pour référence.
extends Node
export (PackedScene) var mob_scene
func _ready():
randomize()
func _on_MobTimer_timeout():
var mob = mob_scene.instance()
var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
mob_spawn_location.unit_offset = randf()
var player_position = $Player.transform.origin
mob.initialize(mob_spawn_location.translation, player_position)
add_child(mob)
public class Main : Node
{
#pragma warning disable 649
[Export]
public PackedScene MobScene;
#pragma warning restore 649
public override void _Ready()
{
GD.Randomize();
}
public void OnMobTimerTimeout()
{
Mob mob = (Mob)MobScene.Instance();
var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
mobSpawnLocation.UnitOffset = GD.Randf();
Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
mob.Initialize(mobSpawnLocation.Translation, playerPosition);
AddChild(mob);
}
}
Vous pouvez tester la scène en appuyant sur F6. Vous devriez voir les monstres apparaître et se déplacer en ligne droite.
Pour l'instant, ils se heurtent et glissent les uns contre les autres lorsque leurs chemins se croisent. Nous aborderons ce problème dans la prochaine partie.