La scène principale du jeu

Il est maintenant temps de transformer tout ce que nous avons fait ensemble en une scène de jeu jouable.

Créez une nouvelle scène et ajoutez un noeud Node nommé Main. (Nous utilisons Node au lieu Node2D est que nous l'utiliserons comme contenant pour gérer la logique du jeu qui ne nécessite pas de gestion 2D en soit.)

Cliquez sur le bouton Instance (représenté par une icône en chaine) et sélectionne votre scène Player.tscn.

../../_images/instance_scene.png

Ajoutez maintenant les nœuds suivants en tant qu'enfants de Main, et nommez-les comme indiqué (les valeurs sont en secondes) :

  • Timer (nommé MobTimer) - pour contrôler à quelle fréquence les ennemis apparaissent

  • Timer (nommé ScoreTimer) - pour incrémenter le score à chaque seconde

  • Timer (nommé StartTimer) - pour ajouter un délai avant le début

  • Position2D (named StartPosition) - pour indiquer la position de départ du joueur

Réglez la propriété Wait Time de chacun des nœuds Timer comme suit :

  • MobTimer : 0.5

  • ScoreTimer : 1

  • StartTimer : 2

En outre, mettez la propriété One Shot de StartTimer sur "On" et réglez la Position du nœud StartPosition sur (240, 450).

Générer des monstres

Le nœud principal va générer de nouveaux monstres, et nous voulons qu'ils apparaissent à un endroit aléatoire sur le bord de l'écran. Ajouter un nœud Path2D nommé MobPath comme un enfant de Main. Lorsque vous sélectionnez Path2D, vous verrez de nouveaux boutons en haut de l'éditeur :

../../_images/path2d_buttons.png

Sélectionnez celui du milieu ("Ajouter un point") et tracez le chemin en cliquant pour ajouter les points aux coins montrés. Pour que les points s'accrochent à la grille, assurez-vous que "Utiliser l’aimantation à la grille" et "Utiliser l’aimantation intelligente" sont sélectionnés. Cette option se trouve à gauche du bouton "Verrouiller", apparaissant comme un aimant à côté de lignes qui se croisent.

../../_images/grid_snap_button.png

Important

Tracez le chemin dans le sens des aiguilles d'une montre, ou vos monstres pointeront vers l'extérieur au lieu de vers l'intérieur !

../../_images/draw_path2d.gif

Après avoir placé le point 4 dans l'image, cliquez sur le bouton "Fermer la courbe" et votre courbe sera terminée.

Maintenant que le chemin est défini, ajoutez un nœud PathFollow2D en tant qu'enfant de MobPath et nommez-le MobSpawnLocation. Ce nœud tournera automatiquement et suivra le chemin au fur et à mesure qu'il se déplace, de sorte que nous pouvons l'utiliser pour sélectionner une position et une direction aléatoires le long du chemin.

Votre scène devrait ressembler à ceci :

../../_images/main_scene_nodes.png

Script principal

Ajoutez un script à Main. Au début du script nous utilisons export (PackedScene) pour nous permettre de choisir la scène du monstre que nous voulons instancier.

extends Node

export(PackedScene) var mob_scene
var score

Nous ajoutons également un appel à randomize() ici afin que le générateur de nombres aléatoires génère des nombres aléatoires différents à chaque exécution du jeu :

func _ready():
    randomize()

Cliquez sur le nœud Main et vous verrez la propriété Mob Scene dans l'inspecteur sous "Script Variables".

Vous pouvez affecter la valeur de cette propriété de deux façons :

  • Faites glisser Mob.tscn depuis le dock "Système de fichiers" et déposez-le sur la propriété Mob.

  • Cliquez sur la flèche vers le bas à côté de "[empty]" et choisissez "Load". Sélectionnez Mob.tscn.

Ensuite, sélectionnez le nœud Player dans le dock Scène, et accédez au dock Nœud dans la barre latérale. Assurez-vous que l'onglet Signaux est sélectionné dans le dock Nœud.

Vous devriez voir une liste des signaux pour le nœud Player. Trouvez et double-cliquez sur le signal hit dans la liste (ou faites un clic droit et sélectionnez "Connect..."). Cela ouvrira le dialogue de connexion des signaux. Nous voulons créer une nouvelle fonction appelée game_over, qui gérera ce qui doit se passer quand une partie se termine. Tapez "game_over" dans la case "Receiver Method" en bas du dialogue de connexion des signaux et cliquez sur "Connect". Ajoutez le code suivant à la nouvelle fonction, ainsi qu'une fonction new_game qui configurera tout pour une nouvelle partie :

func game_over():
    $ScoreTimer.stop()
    $MobTimer.stop()

func new_game():
    score = 0
    $Player.start($StartPosition.position)
    $StartTimer.start()

Maintenant, connectez le signal timeout() de chacun des nœuds Timer (StartTimer, ScoreTimer et MobTimer) au script principal. StartTimer démarrera les deux autres timers. ScoreTimer incrémentera le score de 1.

func _on_ScoreTimer_timeout():
    score += 1

func _on_StartTimer_timeout():
    $MobTimer.start()
    $ScoreTimer.start()

Dans _on_MobTimer_timeout(), nous allons créer une instance de monstre, choisir un emplacement de départ aléatoire le long du Path2D, et mettre le monstre en mouvement. Le nœud PathFollow2D tournera automatiquement puisqu'il suit le chemin, donc nous l'utiliserons pour sélectionner la direction du monstre ainsi que sa position. Lorsque nous créons un mob, nous choisirons une valeur aléatoire entre 150.0 et 250.0 pour la vitesse de déplacement de chaque mob (ce serait ennuyeux s'ils se déplaçaient tous à la même vitesse).

Notez qu'une nouvelle instance doit être ajoutée à la scène en utilisant add_child().

func _on_MobTimer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instance()

    # Choose a random location on Path2D.
    var mob_spawn_location = get_node("MobPath/MobSpawnLocation")
    mob_spawn_location.offset = randi()

    # Set the mob's direction perpendicular to the path direction.
    var direction = mob_spawn_location.rotation + PI / 2

    # Set the mob's position to a random location.
    mob.position = mob_spawn_location.position

    # Add some randomness to the direction.
    direction += rand_range(-PI / 4, PI / 4)
    mob.rotation = direction

    # Choose the velocity for the mob.
    var velocity = Vector2(rand_range(150.0, 250.0), 0.0)
    mob.linear_velocity = velocity.rotated(direction)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

Important

Pourquoi PI ? Dans les fonctions nécessitant des angles, Godot utilise des radians et non des degrés. Pi représente un demi-tour en radians, environ 3.1415 (il existe aussi TAU qui est égal à 2 * PI). Si vous êtes plus à l'aise avec les degrés, vous devrez utiliser les fonctions deg2rad() et rad2deg() pour convertir les angles entre les deux.

Tester la scène

Testons la scène pour nous assurer que tout fonctionne. Ajoutez cet appel new_game à _ready() :

func _ready():
    randomize()
    new_game()

Assignons également Main comme "Main Scene" - celle qui s'exécute automatiquement au lancement du jeu. Appuyez sur le bouton "Play" et sélectionnez Main.tscn lorsque vous y êtes invité.

Astuce

Si vous avez déjà défini une autre scène comme "Scène Principale", vous pouvez faire du clic-droit sur Main.tscn dans le dock du système de fichier et sélectionner "Définir comme Scène Principale".

Vous devriez être capable de bouger le joueur, voir les monstres apparaître, et voir le joueur disparaître quand il est touché par un monstre.

Quand vous êtes sûr que tout fonctionne, supprimez l'appel à new_game() depuis _ready().

Que manque-t-il à notre jeu ? Une interface utilisateur. Dans la prochaine leçon, nous ajouterons un écran titre et afficherons le score du joueur.