CSC 3101 – Algorithmique et langage de programmation

Portail informatique

CI9 : Alien vs ananas

L'objectif de cet exercice est de vous faire manipuler des classes anonymes et de vous introduire la bibliothèque JavaFX qui permet de créer facilement des applications graphiques.

Durée des exercices obligatoires : 2h30mn en présentiel
  • Exercice 1 : obligatoire (facile, ∼10mn)
  • Exercice 2 : obligatoire (facile, ∼45mn)
  • Exercice 3 : obligatoire (facile, ∼30mn)
  • Exercice 4 : obligatoire (moyen, ∼30mn)
  • Exercice 5 : obligatoire (moyen, ∼45mn)

Dans cet exercice, nous concevons un jeu vidéo simple nommé Alien vs Pinapples : une armée d'ananas mutants attaque votre galaxie et vous jouez un alien en charge de sauver l'univers, yipa !

Préparation (∼10mn – facile – obligatoire)

Avant de pouvoir programmer votre jeu, vous devez préparer votre environnement de travail afin d'utiliser la bibliothèque graphique JavaFX. Cette activité devrait vous prendre environ 10 minutes si tout se passe bien. Dans la suite de l'exercice, on considère que vous utilisez eclipse. Si vous utilisez un autre environnement de développement, il faut vous référer à la documentation de votre environnement.

Pour commencer, sachez que cette procédure est susceptible de ne pas fonctionner sur votre machine si vous avez une configuration originale. À cette question, on considère que vous pouvez être sous Linux ou MacOS, mais pas sous Windows.

  • Pour commencer, créez un projet Java alien dans eclipse. Dans votre projet alien, créez une classe Alien dans le package tsp.alien. Pour le moment, laissez le contenu de la classe Alien vide. Attention, le fait que la classe soit dans le package tsp.alien est important.
  • Maintenant, il faut trouver la version Java que vous utilisez. Si vous êtes en salle TP, vous devez suivre la procédure pour la version Java 17/Linux/Intel. Si vous n'êtes pas en salle TP, cliquez sur Properties dans le menu Project. Cliquez Libraries. Dans Java build path, sélectionnez Libraries. Vous devriez voir la version de Java que vous utilisez sur la ligne JRE System Library. Il est aussi possible que vous utilisiez la version de Java renvoyée par la commande java -version (à lancer dans un terminal). En fonction de votre version de Java, de votre système et de votre processeur :
    • Vous avez un Java 17, un Linux et un Intel, téléchargez la version de JavaFX qui se trouve ici
    • Vous avez une version de Java inférieure à 11, vous devez passer à l'ancienne procédure qui se trouve à la fin de l'exercice.
    • Sinon, télécharger le SDK (et non le jmods) de JavaFX qui convient à Java 19 et à votre système d'exploitation à partir de ce lien.
      • Si vous avez un PC ou un Mac Intel, il faut utiliser la version x64.
      • Si vous avez un Mac M1, il est conseillé de commencer par la version x64 (probablement pour les versions de MacOS < 13).
      • Pour les versions les plus à jour de MacOS M1, il faut utiliser la version aarch64 (probablement pour les versions de MacOS >= 13).
  • Extrayez l'archive dans un répertoire ou double cliquant sur l'archive ou en utilisant la commande unzip dans un terminal, par exemple avec :
    $ unzip ~/Téléchargement/openjfx-17.0.1_linux-x64_bin-sdk.zip
  • À partir de cette étape, ne déplacez plus les fichiers extraits ailleurs dans votre système de fichier.
  • Dans Eclipse, allez dans le menu Project puis Properties. Dans Java build path, sélectionnez Libraries et cliquez sur Modulepath, ce qui devrait dégriser le bouton Add External Jars. Cliquez sur ce bouton et ajoutez tous les fichiers jar contenus dans le sous-répertoire lib de JavaFX (le sous-répertoire lib du répertoire dans lequel vous avez extrait l'archive).
  • Vérifiez qu'il existe bien un fichier nommé module-info.java à la racine des sources de votre projet (c'est-à-dire dans le répertoire src de votre projet). Si ce n'est pas le cas, c'est que vous utilisez une vieille version de eclipse. Vous devez donc créer manuellement ce fichier.
  • Copiez-collez le contenu du fichier suivant dans votre module-info.java :
  • Enfin, copiez-collez le code suivant dans vitre fichier Alien.java :
  • Si vous arrivez à exécuter ce programme et que vous voyez une fenêtre s'afficher, félicitation, vous avez réussi à configurer JavaFX ! Pensez à fermer la fenêtre JavaFX avant de passer à la suite.

Si à cette étape, vous avez encore des erreurs ou ne voyez pas de fenêtre s'afficher, vous pouvez essayer les solutions proposées ci-après.

Sur un MacOS, tout à l'air de fonctionner mais aucune fenêtre ne s'affiche

Pour les utilisateurs de MacOS, vous allez peut-être avoir le problème suivant. L'application se lance correctement, mais aucune fenêtre n'est affichée. Pour corriger le problème, il faut aller dans Run As->Run configurations, puis dans l'onglet (x)= Arguments, et désélectionner Use the -FstartOnFirstThread argument when launching with SWT.

Sur un Linux, votre IDE se demande où sont les bibliothèques natives (installation avec apt-get)

Si vous avez installé JavaFX avec l'outil apt (ou apt-get) de ubuntu ou debian, il faut parfois explicitement indiquer à la machine virtuelle Java le chemin où se trouvent les bibliothèques natives JavaFX. Pour cela, il faut d'abord identifier ce chemin avec :

dpkg -L libopenjfx-jni
-->

Cette commande devrait vous donner un chemin comme /usr/lib/x86_64-linux-gnu/jni/. Dans Eclipse, cliquez dans le menu Run->Run configurations puis selectionner l'onglet (x)= Arguments. Dans VM Arguments, ajoutez le paramètre -Djava.library.path=/usr/lib/x86_64-linux-gnu/jni/ dans lequel vous remplacerez le /usr/lib/x86_64-linux-gnu/jni/ par le chemin identifié avec la commande dpkg -L libopenjfx-jni

Tout à l'air correct, mais Eclipse se plaint toujours de ne pas trouver le package javafx

Sur de vielles versions d'Eclipse, Eclipse peut considérer qu'il vaut mieux ne pas utiliser JavaFX car cette bibliothèque n'a été que récemment intégrée à la bibliothèque standard Java. Pour corriger ce problème, il faut cliquer dans le menu Project puis dans le sous-menu Properties.

Dans la fenêtre ouverte par Eclipse, il faut ensuite sélectionner Java Build Path dans la partie gauche de la fenêtre. Dans la fenêtre qui apparaît, il faut ensuite sélectionner l'onglet Libraries (partie droite de la fenêtre). Vous devriez pouvoir dérouler un menu JRE System Library ce qui devrait vous donner une fenêtre similaire à la fenêtre de gauche présentée dans la figure ci-dessous.

En Cliquant sur la ligne Access rule, vous devrez voir une fenêtre similaire à celle du milieu sur la figure ci-dessous, mais sans la règle javafx. Cliquez sur add, vous devriez voir une fenêtre similaire à celle de droite. Ajoutez une règle Accessible (par défaut la règle est à Forbidden). Autorisez ensuite tous les packages de JavaFX en mettant javafx/** dans Rule Pattern. Vous devriez obtenir l'affichage représenté sur la figure de droite. Après avoir cliqué sur OK, vous devriez voir votre règle autorisant JavaFX, comme dans la fenêtre représentée sur la figure du milieu.

Image not found Image not found Image not found

Procédure pour des versions de Java strictement inférieur à 11 (attention, cette procédure n'a plus été testée depuis des années)

Avec de nombreuses distributions Linux (typiquement Debian ou Ubuntu), les versions Java inférieures strictement à 11 ne fournissent pas la bibliothèque JavaFX. Pour cette raison, vous allez devoir installer une version complète de Java 8 qui contient cette bibliothèque. Pour cela, il vous suffit de lancer la commande suivante dans un terminal après avoir remplacé [login] par votre nom de connexion sur les machines de Telecom SudParis :

scp -r [login]@ssh.imtbs-tsp.eu:~thomas_g/jfx/jdk1.8.0_131 ~
(vous pouvez aussi trouver le code ici http://www-public.imtbs-tsp.eu/~sellam_m/jdk1.8.0_131.zip).

Suite à cette étape, vous devriez avoir un jdk 8 dans ce répertoire que nous noterons JVM_PATH dans la suite : ~/jdk1.8.0_13.

Dans eclipse, dans le menu File->New, sélectionnez Java Project. Avant de saisir le nom du projet, il faut cliquer sur le lien Configure JREs qui se trouve vers le milieu de la fenêtre.

Création du projet.

Dans la fenêtre qui vient de s'ouvrir, cliquez sur le bouton Add. Sélectionnez Standard JVM, puis cliquez sur Next. Dans la zone de saisie JRE Home, copiez-collez le chemin JVM_PATH correspondant au répertoire dans lequel se trouve la version Java fournissant JavaFX (voir le début de la question). En salle de TP, il vous suffit de copier-coller le texte suivant /mci/inf/thomas_g/jfx/jdk1.8.0_131. Vous devriez voir tout un tas de lignes s'afficher dans la zone JRE System libraries. Cliquez alors sur Finish.

Dans la fenêtre dans laquelle vous venez de revenir indiquant les Installed JREs, sélectionnez jdk1.8.0_131 et cliquez sur ok.
De nombreux étudiants ratent cette étape !
Sélection de l'environnement Java.

Dans la fenêtre de création de projets dans laquelle vous devriez être revenu, sélectionner Use default JRE (currently jdk1.8.0_131).

Vérifiez bien que le numéro de version que vous voyez à l'écran est correct. Si ce n'est pas le cas, c'est que vous avez raté une étape et vous devez donc recommencer la question au début.

Enfin, donnez le nom alien à votre projet (zone de saisie Project Name) avant de cliquer sur Finish.

L'univers (∼45mn – facile – obligatoire)

Dans ce premier exercice, nous construisons une fenêtre avec JavaFX dans laquelle nous affichons une image. Cet exercice a pour but de vous présenter les concepts de base de la bibliothèque JavaFX.

La bibliothèque JavaFX gère des fenêtres représentées par des instances de la classe javafx.stage.Stage. Un Stage peut ensuite afficher une scène, c'est-à-dire un ensemble d'éléments. Une scène est représentée par la classe javafx.scene.Scene. Le développeur peut définir plusieurs scènes pour la même fenêtre, ce qui lui permet de passer d'une scène à l'autre facilement. Dans notre cas, une unique scène sera largement suffisant.

Une scène est constituée d'un ensemble d'éléments. Techniquement, une scène affiche un arbre d'éléments, chaque élément héritant de javafx.scene.Node. Les feuilles de l'arbre sont directement des objets affichés : des formes comme des cercles ou des polygones, des boutons, ou encore des objets plus complexes comme le canevas que l'ont va étudier dans cet exercice, Les nœuds intermédiaires sont des instances de la classe javafx.scene.Group. Une instance de la classe Group est un conteneur à éléments gérant la disposition interne des éléments qu'il contient.

La figure ci-dessous illustre l'architecture d'une application JavaFX. Une fenêtre est représentée par une instance de la classe Stage. Celle-ci affiche une instance de la classe Scene. La scène est associée à un nœud, c'est-à-dire une instance de la classe Node. Dans la figure, ce nœud est un Group et il contient un cercle, un bouton et un sous-groupe.
--------- --------- --------- | Stage | ----> | Scene | ----> | Group | --------- --------- --------- / | \ / | \ / | \ / | \ ---------- ---------- ---------- | Cercle | | Button | | Group | ---------- ---------- ---------- / | \ .............

Cette question a pour but de vous expliquer le code que vous copié/collé dans la classe Alien lors de la procédure d'installation. Elle ne nécessite pas de répondre à une question spécifique. Il vous suffit de lire le texte qui suit.

Pour initialiser JavaFX, il faut créer une classe qui hérite de javafx.application.Application. Dans notre exercice, nous créons donc la classe nommée tsp.alien.Alien héritant de javafx.application.Application.

La classe Application offre une méthode publique statique nommée void launch(String... args). Cette méthode permet de démarrer une application avec une unique fenêtre. Elle alloue une instance de la classe qui a généré l'appel (dans notre cas, une instance de tsp.alien.Alien), prépare le moteur graphique, crée une fenêtre (une instance de Stage) et finalement délègue le remplissage de la fenêtre à la méthode d'instance void start(Stage primaryStage). C'est en redefinissant cette méthode dans tsp.alien.Alien que nous pouvons associer une scène à la fenêtre et des éléments à la scène avant d'afficher la fenêtre avec la méthode void Stage.show().

Dans le programme que vous avez copié/collé, la méthode statique main de Alien appelle la méthode statique launch héritée de Application. Cette dernière crée donc une fenêtre, puis une instance de Alien afin de déléguer le remplissage de la fenêtre à la méthode d'instance start de la classe Alien. Comme cette méthode start ne fait rien, la fenêtre est encore vide.

Nous pouvons maintenant ajouter un titre à la fenêtre : Alien vs Pinapples. Pour cela, utilisez la méthode javafx.stage.Stage.setTitle.
Le stage.show() doit, pendant tout l'exercice, rester la dernière opération exécutée dans la méthode start(), sinon, vous risquez de ne pas voir les décorations que vous ajoutez à votre fenêtre.

Avant d'afficher des éléments intéressants dans notre fenêtre, il faut créer un groupe, une scène et les associer à la fenêtre. Pour cela, dans la méthode Alien.start, vous devez :

À cette étape, votre fenêtre ne devrait toujours rien contenir puisqu'on n'a pas encore ajouter d'élément au groupe.

Maintenant que nous avons un groupe, nous pouvons définir la zone dans laquelle nous affichons le jeu. Pour cela, nous utilisons la classe javafx.scene.canvas.Canvas. Un canevas permet d'afficher un dessin que vous fabriquerez dans les questions suivantes.

À cette question, nous nous occupons de fixer la taille du canevas à sa création : 694 pixels de largeur et 520 pixels de hauteur. De façon à pouvoir modifier facilement ces valeurs, définissez, dans la méthode Alien.start, deux variables stockant ces nombres et utilisez les dans la suite de l'exercice.

Ensuite, créez un canevas avec le constructeur Canvas(double width, double height). De façon à ajouter le canevas aux enfants du groupe associé à la scène, vous devez :

Enfin, comme nous ne nous soucions pas du redimensionnement de la fenêtre dans cet exercice, utilisez Stage.setResizable pour empêcher l'utilisateur de redimensionner la fenêtre. Vous pouvez vérifier que votre code est correct en vérifiant que (i) vous ne pouvez pas redimensionner votre fenêtre, et (ii) que la taille de la fenêtre a changé depuis la dernière question.

Notre canevas est maintenant prêt et nous pouvons commencer à dessiner son contenu. Pour dessiner le contenu d'un canevas, il faut en extraire un javafx.scene.canvas.GraphicsContext à l'aide la méthode javafx.scene.canvas.Canvas.getGraphicsContext2D.

À cette question, nous affichons le score du joueur. Comme pour le moment, le joueur n'a pas de score, nous affichons donc juste Score: 42. Après avoir extrait le javafx.scene.canvas.GraphicsContext du canevas, utilisez la méthode javafx.scene.canvas.GraphicsContext.fillText pour afficher le score à la position (540, 36).

Cet affichage est un peu neutre et ne correspond pas exactement à la philosophie d'un jeu comme Alien vs Pinapples. Nous décorons donc ce texte. Pour commencer, nous entourons le texte en utilisant javafx.scene.canvas.GraphicsContext.strokeText à la position (540, 36). Le rendu ne correspondant pas encore à un jeu d'aliens, nous changeons la police et les couleurs. Copiez coller le code suivant dans votre programme :
gc.setFont(Font.font("Helvetica", FontWeight.BOLD, 24)); gc.setFill(Color.BISQUE); gc.setStroke(Color.RED); gc.setLineWidth(1);
puis remplacez gc par le nom de variable qui référence le GraphicsContext.

Pensez à changer la police et les couleurs avant d'appeler fillText, sinon, fillText utilisera les polices et couleurs originale.

Nous pouvons maintenant mettre un fond pour créer l'ambiance du jeu. Pour cela, nous devons charger une image que nous devons ajouter à notre projet. Dans Eclipse, cliquez sur le Menu File, puis sur le sous-menu New et enfin sur le sous-menu Source Folder. Créez ensuite un répertoire que vous pouvez nommer resources. Le contenu de ce répertoire est directement ajouté aux classes Java de votre application, ce qui permet d'y accéder pendant l'exécution.

Ne vous trompez pas, utilisez bien le sous-menu Source Folder et non Folder

Ensuite, téléchargez l'image suivante (fournie par la Nasa) : space.jpg. Enfin, placez cette image dans le dossier resources. Pour cela, il faut cliquer à droite sur le dossier resources, sélectionner le sous-menu Import, double-cliquer sur File System et enfin retrouver où vous avez téléchargé l'image.

Maintenant que l'image est incluse dans votre projet, vous pouvez l'afficher dans votre canevas. Pensez à l'afficher avant d'afficher le score, car mettre le fond du canevas écrase l'ancien contenu du canevas. Pour afficher le fond, vous devez :
  • Utilisez le constructeur javafx.scene.image.Image(String, double, double, boolean, boolean) pour charger l'image. Le premier argument est le chemin vers l'image, c'est-à-dire space.jpg dans notre cas. Les deux arguments suivants sont la taille que nous souhaitons donner à notre image, c'est à dire la taille de la fenêtre dans notre cas. Les deux argument suivants doivent être positionnés à false : il ne faut pas préserver les ratios de l'image d'origine et il n'est pas nécessaire d'utiliser un algorithme de qualité pour mettre l'image à l'échelle.
  • Utilisez javafx.scene.canvas.GraphicsContext.drawImage pour afficher l'image aux coordonnées (0, 0) dans le canevas.

Les lutins (∼30mn – facile – obligatoire)

L'univers étant maintenant prêt, nous pouvons ajouter des lutins (sprites en anglais). Un lutin est un terme utilisé pour parler d'un objet ou d'un personnage pouvant se déplacer dans la fenêtre du jeu. Nous aurons besoin de deux types de lutins : un lutin représentant l'alien et des lutins représentant les ananas. La seule différence entre les deux types de lutins n'étant que leur image, une unique classe suffit pour les représenter.

Commencez par ajouter les images associées aux lutins dans le répertoire resources de votre projet : alien.png et pinapple.png.

Pour l'instant, un lutin doit posséder une image de type javafx.scene.image.Image, une largeur, une hauteur et des coordonnées (de type double) x et y. Ajoutez une classe tsp.alien.Sprite à votre projet et ajoutez les champs appropriés. Ajoutez aussi à votre lutin un constructeur prenant en paramètre :

Vérifiez que vous chargez correctement l'alien (largeur 62 et hauteur 36) et l'ananas (largeur 19 et hauteur 36). Comme vous n'affichez pas encore les lutins, il est normal qu'il ne se passe rien de nouveau d'un point de vue graphisme.

Nous pouvons maintenant afficher un lutin. Pour cela, comme les coordonnées d'un lutin sont privées, commencez par ajouter une méthode void setPosition(double x, double y) permettant de modifier les coordonnées (x, y) d'un lutin à la classe Sprite. Ensuite, ajoutez une méthode render à la classe Sprite prenant un GraphicsContext en paramètre et permettant d'afficher le lutin aux coordonnées données par les champs x et y de la classe Sprite. Vérifiez que vous pouvez afficher vos lutins.

Les lutins volants (– 30mn – moyen – obligatoire)

Maintenant que nous pouvons afficher des lutins, nous nous occupons de les déplacer. Pour cela, il faut être capable de mettre à jour le contenu du canevas régulièrement. JavaFX offre différentes possibilités. Nous utilisons la classe javafx.animation.AnimationTimer qui permet d'invoquer régulièrement la méthode abstraite javafx.animation.AnimationTimer.handle redéfinie par héritage. Techniquement, JavaFX invoque cette méthode 60 fois par seconde, mais il peut y avoir des décalages lorsque la méthode mets plus de 1/60ième de seconde à s'exécuter.

Dans la méthode tsp.alien.Alien.start, créez une instance d'une classe anonyme héritant de javafx.animation.AnimationTimer. Ensuite, appelez la méthode javafx.animation.AnimationTimer.start sur cette instance pour démarrer le minuteur. Dans votre classe anonyme, définissez la méthode handle et utilisez System.out.println pour afficher un message dans le terminal/ Enfin, vérifiez que votre programme affiche bien régulièrement le message.

Au lieu d'afficher un message à chaque invocation de handle, nous redessinons notre canevas. Supprimez le code affichant l'alien et un ananas de start. Ensuite, déplacer les codes affichant le fond d'écran et le score dans la méthode handle de la classe anonyme, ce qui permet de les redessiner 60 fois par secondes. Enfin, après avoir affiché le fond d'écran, mais avant d'afficher le score, affichez un ananas à une position aléatoire se trouvant dans les bornes de la fenêtre en utilisant la méthode java.lang.Math.random. Vérifiez que votre ananas sautille continuellement dans l'écran.
Pensez à utiliser la méthode setPosition pour mettre à jour les coordonnées du lutin à une position aléatoire.

Nous nous occupons maintenant de déplacer correctement les ananas. Ajoutez des champs xSpeed et ySpeed à la classe tsp.alien.Sprite. Ces champs représentent le déplacement, en nombre de pixels, que doit faire le lutin à chaque appel à handle. Ajoutez aussi une méthode setSpeed(double xSpeed, double ySpeed) permettant de modifier la vitesse de déplacement d'une lutin. Finalement, ajoutez une méthode update() à un lutin, permettant de déplacer les positions du lutin de xSpeed suivant l'axe des x et de ySpeed suivant l'axe des y.

Après avoir supprimé le code permettant d'afficher un ananas à une position aléatoire, positionnez initialement un ananas à la position (100,100), donnez lui une vitesse de (1, 1), et mettez à jour sa position à chaque appel à handle avant de l'afficher. Vous devriez voir un ananas qui se déplace tranquillement jusqu'aux bords de la fenêtre avant de disparaître.

Courir après un ananas en dehors de la fenêtre n'est pas chose aisée pour un joueur. Lorsqu'un ananas arrive sur un bord, nous vous proposons de le faire rebondir. Si l'ananas atteint les limites de la fenêtre suivant l'axe des x, il faut inverser sa vitesse suivant cet axe. De façon similaire, si l'ananas atteint les limites de la fenêtre suivant l'axe des y, il faut inverser sa vitesse suivant cet axe. Ajoutez une méthode validatePosition() que vous appellerez à la fin de update et qui :
  • s'assure que l'ananas est toujours entièrement visible dans la fenêtre (attention, pensez que votre ananas à une largeur et une hauteur),
  • s'occupe de faire rebondir l'ananas lorsqu'il atteint un des 4 bords de la fenêtre.

Pour mettre en œuvre validatePosition, vous avez besoin d'accéder à la taille de la fenêtre à partir d'une instance de Sprite. Le plus simple est d'ajouter deux champs pour stocker cette taille dans la classe Sprite et de les initialiser via le constructeur.
Si votre programme est correct, vous devriez voir un ananas qui rebondit gentiment sur les bords de l'écran.

Nous pouvons maintenant créer une armée d'ananas, un peu comme nous avions créé une armée de monstre (voir le CI3). Nous vous proposons d'utiliser le tableau extensible fourni par la bibliothèque Java pour stocker vos lutins ( java.util.ArrayList).

Au lieu de créer un unique ananas dans Alien.start(), créez 15 ananas que vous stockerez dans votre tableau extensible. Initialisez la position initiale de chaque ananas à une valeur aléatoire se trouvant dans les limites de la fenêtre, et le vecteur de vitesse de chaque ananas à une valeur comprise entre -5 et 5.

Dans AnimationTimer.handle, mettez à jour la position des ananas avant de les afficher. Comme nous aurons besoin de supprimer des lutins dans la suite de l'exercice, plutôt que d'utiliser une boucle sur une collection pour parcourir votre tableau extensible de lutins, nous vous demandons d'utiliser explicitement un itérateur (voir java.util.Collection.iterator et java.util.Iterator). Vous devriez maintenant voir un univers peuplé d'ananas rebondissants sur les bords de la fenêtre.

L'alien volant (∼45mn – moyen – obligatoire)

Maintenant que des ananas se promènent dans l'univers, il faut créer un alien pour les arrêter avant qu'ils ne fassent trop de dégâts.

L'alien est créé à une position initiale (par exemple 100x100). Le joueur contrôle l'alien avec le clavier. Lorsqu'il presse une des flèches, la vitesse de l'alien est incrémentée de 1 suivant l'axe de la flèche. Il faut donc intercepter les événements claviers. Pour cela, il faut appeler la méthode javafx.scene.Scene.setOnKeyPressed en lui fournissant en argument une instance d'une classe anonyme héritant de javafx.event.EventHandler. Ce javafx.event.EventHandler doit être paramétré par le type javafx.scene.input.KeyEvent. Dans la classe anonyme, redéfinissez la méthode javafx.event.EventHandler.handle(KeyEvent e) de façon à ajuster la vitesse de déplacement de l'alien. Le code de la touche pressé est donné par la méthode javafx.scene.input.KeyEvent.getCode. Les flèches sont respectivement associées aux valeurs KeyCode.LEFT, KeyCode.RIGHT, KeyCode.UP et KeyCode.DOWN. Pensez à mettre à jour la position de l'alien avec la méthode update() et à l'afficher dans handle. Vérifiez que vous arrivez bien à déplacer l'alien quand vous pressez les flèches.

La classe KeyCode est ce qu'on appelle une énumération, ce qui signifie que les valeurs KeyCode.LEFT, KeyCode.RIGHT, KeyCode.UP et KeyCode.DOWN sont des constantes. Pour savoir quelle touche a été pressée, vous pouvez simplement utiliser un switch :
switch(code) { case LEFT: ...; break; case RIGHT: ...; break; case UP: ...; break; case DOWN: ..; break; default: }

Lorsqu'un alien touche un ananas, le joueur gagne 100 points et l'ananas disparaît. Ajoutez un champ score initialisé à 0 dans la classe tsp.alien.Alien. Ensuite, modifiez le code de handle de façon à identifier les ananas touchés par l'alien. Pour cela, ajoutez une méthode d'instance boolean tsp.alien.Sprite.intersects(Sprite s) renvoyant vrai si les rectangles dans lesquels se trouvent les deux lutins se touchent. Dans handle, lorsque vous parcourez la liste des ananas, après avoir mis à jour la position d'un ananas, supprimez l'ananas du tableau d'ananas si il est touché par l'alien en utilisant votre iterateur, puis incrémentez de 100 le score du joueur. Pensez aussi à afficher le vrai score du joueur dans handle().

bonus, nécessite de lire le sous-cours sur les classes anonymes

Pour quelle raison le programme ne compile plus si vous déplacez la variable score dans la méthode start, alors qu'il compile si vous la déplacez dans la classe anonyme héritant de javafx.animation.AnimationTimer ?
Une classe anonyme est une classe interne de méthode sans nom :
  • Si score est un champ de classe anonyme, on peut bien sûr y accéder en lecture/écriture à partir de la méthode de la classe anonyme.
  • Si score est une variable de la méthode Alien.start, comme le classe anonyme est une classe interne de méthode, Java effectue une copie de score dans l'instance de la classe anonyme lorsque l'instance est créée. De façon à éviter que les scores de la méthode et de l'instance de la classe anonyme divergent, Java interdit tout accès en écriture au champ score à partir de la création de l'instance de la classe anonyme. Comme score est modifié dans la méthode handle de la classe anonyme (c'est-à-dire qu'on y accède en écriture dans la méthode handle), on ne peut pas définir score comme variable de la méthode start().
  • Si score est un champ de la classe Alien, la classe anonyme y accède via son champ Alien.this, et ce champ peut bien être accédé en lecture/écriture.

bonus

En tant que développeur, vous trouvez que de supprimer les ananas en déplaçant l'alien avec les flèches devient vite fastidieux pour faire des tests. On vous propose donc de créer un mode triche qui utilise la souris : lorsque l'utilisateur clique quelque part sur le canevas, le vaisseau doit se déplacer à la position cliquée et prendre une vitesse nulle. Pour cela, il faut créez une instance d'un classe anonyme héritant de javafx.event.EventHandler paramétrée par le type javafx.scene.input.MouseEvent. Les méthodes javafx.scene.input.MouseEvent.getX et javafx.scene.input.MouseEvent.getY permettent de connaître la position de la souris. Ensuite, il faut enregistrer ce gestionnaire d'événement via javafx.scene.Scene.setOnMousePressed qui est invoqué dès que l'utilisateur commence à cliquer sur la souris, et via javafx.scene.Scene.setOnMouseDragged qui indique le déplacement dans la souris pendant que l'utilisateur clique dessus.
Félicitations, depuis le début du module, vous avez développé entre 1500 et 2000 lignes de code !

Vous savez maintenant programmer en Java !

Pour les étudiants curieux et qui veulent apprendre à utiliser de nouvelles bibliothèques, n'hésitez pas à aller consulter les incroyables tutoriels de Jean-Michel Doudoux qui regroupent à ce jour 117 chapitres répartis en 17 parties. Pensez aussi que la documentation officielle Java constitue une inestimable source d'information.