ntro.ca

        • Contrats de classe
        • Liens utiles
        • Calendrier
        • Calendrier groupe 3
        • Calendrier groupes 1, 2
        • Structure du cours
        • Évaluations
        • Matériel à se procurer
        • Les profs
          • Marc-Olivier Tremblay
          • Mathieu Bergeron
        • Module 1.1: installation + trier des cartes
        • Module 1.2: rappels POO
          • URL dépôt Git à remettre
        • Module 1.3: tableau d'objets
        • Examen 1
        • Validation des ateliers
        • Module 2.1: données JSON
        • Module 2.2: données en Java
        • Module 2.3: récursivité
        • Examen 2
        • Validation des ateliers
        • Module 3.1: structure générique
        • Module 3.2: efficacité (1)
        • Module 3.3: efficacité (2)
        • Examen 3
        • Validation des ateliers
        • Module 4.1: liste naïve
        • Module 4.2: liste par tableau
        • Module 4.3: liste chaînée
        • Examen 4
        • Validation des ateliers
        • Module 5.1: mappage naïf
        • Module 5.2: mappage par hachage
        • Module 5.3: mappage par arbre
        • Examen 5
        • Validation des ateliers
        • Travail de rattrapage
        • Projets vedettes
          • Contrat gr1
          • Contrat gr2
          • Contrat gr3
        • Survol
        • Structure
        • Calendrier semaines
          • Calendrier gr1
          • Calendrier gr2
          • Calendrier gr3
        • Utilisation IA
        • Évaluations
        • Exemples de pages
        • Exemples de jeu
        • Jeux choisis
        • Réponses à vos questions
        • Module 1: créer le projet
        • Module 2: concevoir l'application
        • Module 3: vues NtroFx
        • Module 4: modèle et navigation
        • Module 5: ajouter le dorsal, modifier le modèle
        • Module 6: améliorer l'affichage
        • Module 7: jeu en 2d
        • Guide de style 4F5
        • TP1
        • Examen 1
        • Examen 2
        • Calendrier
        • Évaluations
        • Structure du cours
        • Contrat de classe
        • Le prof
        • 01: Windows et Word
          • Astuces et raccourcis
        • 02: Word
          • Exercice Word: insertion d'éléments spéciaux
          • Exercice Word: tableaux
        • 03: Word
          • Exercice Word: références
          • TP01: Word (15%)
        • 04: Word
        • 05: PowerPoint
          • TP02: PowerPoint (10%)
        • 06: Examen Word (20%)
        • 07: Excel
        • 08: Excel
        • 09: Excel
          • TP03: Excel (15%)
        • 10: Excel
        • 11: Examen Excel (20%)
        • 12: Access
        • 13: Access
        • 14: Access
        • 15: Examen Access
      • Sondage H2023 (dept. info)
      • Vision H2023 (dept. info)
      • P1) exercices interactifs de lecture
      • P2) transition Excel vers Python
        • Atelier 2: un exemple
      • Index
      • Point de vue sur l'IA
    Tutoriel 6: projet pong minimal
    • Tutoriel 6: projet pong minimal
      • Étape 1) créer le projet pong06 à partir de dep06
      • Étape 2) implanter le bon modèle
        • ModeleFileAttente
        • PartieEnFile
      • Vérifier l’état courant du projet
      • Étape 3) adapter les vues et le fragment
        • Modifier les fichiers .properties
        • Adapter le fichier vue_a.fxml
        • Adapter la classe VueA
        • Adapter le fichier vue_b.fxml
        • Adapter le fichier fragment_a.fxml
        • Adapter la classe FragmentA
      • Vérifier le comportement et l’affichage
      • Étape 5) Ajouter un fichier prod.css et le déclarer dans FrontalPong.java
      • Vérifier le comportement et l’affichage
      • Étape 5) implanter le bon message
      • Étape 6) adapter MaquetteDepart
      • Vérifier l’état courant du projet

    Tutoriel 6: projet pong minimal #

    Étape 1) créer le projet pong06 à partir de dep06 #

    1. Dans un GitBash à la racine du dépôt Git

      $ cp -rf dep06 pong06
      
    2. Modifier settings.gradle pour ajouter l’include

      include 'pong06'
      
    3. Vérifier que pong06 s’exécute

      $ sh gradlew pong06
      
    4. Dans VSCode, faire un clean workspace pour afficher le projet pong06

    Étape 2) implanter le bon modèle #

    ModeleFileAttente #

    1. Avec F2, renommer la classe

      • ModeleA => ModeleFileAttente
    2. Avec F2, renommer la classe

      • ValeurA => PartieEnFile
    3. Modifier ModeleFileAttente

      • avec F2, renommer l’attribut

        • valeurs => parties
      • s’assurer aussi d’avoir l’attribut prochainId

      public class ModeleFileAttente implements Model, WatchJson, WriteObjectGraph {
      
          private int  prochainId = 0;
      
          private List<PartieEnFile> parties = new ArrayList<>();
      
          // ...
      
    4. Modifier ModeleFileAttente.methodeA avec des valeurs temporaires

      public void methodeA(String paramA, int paramB) {
      
          parties.add(new PartieEnFile(String.valueOf(prochainId++), 
                                       null, 
                                       paramA, 
                                       paramB, 
                                       null, 
                                       null, 
                                       0));
      }
      
    5. Modifier ModeleFileAttente.methodeC (affichage sur la Vue)

      public void methodeC(VueA vueA) {
      
          vueA.methodeE();
          for(PartieEnFile valeur : valeurs) {
              vueA.methodeF(valeur);
          }
      }
      

    PartieEnFile #

    1. Modifier PartieEnFile selon le document de conception:

      public class PartieEnFile implements ModelValue {
      
          private String idPartie;
      
          private String idJoueurA;
          private String nomJoueurA;
          private int    scoreJoueurA;
      
          private String idJoueurB;
          private String nomJoueurB;
          private int    scoreJoueurB;
      
          // ...
      
    2. Modifier le constructeur:

      public PartieEnFile(String idPartie, 
                          String idJoueurA, 
                          String nomJoueurA, 
                          int scoreJoueurA, 
                          String idJoueurB, 
                          String nomJoueurB, 
                          int scoreJoueurB) {
      
          this.idPartie = idPartie;
          this.idJoueurA = idJoueurA;
          this.nomJoueurA = nomJoueurA;
          this.scoreJoueurA = scoreJoueurA;
          this.idJoueurB = idJoueurB;
          this.nomJoueurB = nomJoueurB;
          this.scoreJoueurB = scoreJoueurB;
      }
      
    3. Modifier PartieEnFile.methodeA (affichage sur le fragment)

      public void methodeA(FragmentA paramA) {
          paramA.methodeA(nomJoueurA);
      
          if(nomJoueurB == null){
              paramA.methodeB("???");
          }else{
              paramA.methodeB(nomJoueurB);
          }
      
          paramA.methodeC(String.format("%d-%d", scoreJoueurA, scoreJoueurB));
      }
      
      • Avec Ctrl+., changer la signature de methodeB

      • Avec Ctrl+., créer la methodeC

    4. S’assurer que la méthode FragmentA.methodeC ne lance pas d’exception

      public class FragmentA extends ViewFragmentFx {
      
          // ...
      
          public void methodeC(String format) {
              // Retirer ce que VSCode a généré
      
              // TODO Auto-generated method stub
              // throw new UnsupportedOperationException("Unimplemented method 'methodeC'");
          }
      }
      

    Vérifier l’état courant du projet #

    1. Dans un GitBash à la racine du dépôt Git

      $ sh gradlew pong06
      

    Étape 3) adapter les vues et le fragment #

    Modifier les fichiers .properties #

    • fr.properties

      ajouter=Ajouter
      jouer=Jouer
      quitter=Quitter partie
      
    • en.properties

      ajouter=Add
      jouer=Play
      quitter=Exit Game
      

    Adapter le fichier vue_a.fxml #

    1. Modifier l’alignement pour le VBox racine

      <VBox xmlns:fx="http://javafx.com/fxml"
            fx:controller="depart.frontal.vues.VueA" 
            styleClass="conteneur,v,top-center">        <!-- changer à top-center -->
      
    2. Retirer le boutonA et les labelA, labelB

      <!-- ... -->
      
      <Button 
          fx:id="boutonA"
          text="%texteA">
      </Button>                                                   <!-- retirer -->
      
      <Pane styleClass="espacement,moyen" VBox.vgrow="ALWAYS"/>   <!-- retirer -->
      
      <Label 
          fx:id="labelA"/>                                        <!-- retirer -->
      
      <Pane styleClass="espacement,petit"/>                       <!-- retirer -->
      
      <Label 
          fx:id="labelB"/>                                        <!-- retirer -->
      
    3. Modifier le %var du boutonB

      <Button                                    
          fx:id="boutonB"
          text="%ajouter" />                                      <!-- modifier -->
      
    4. Déplacer le boutonB en haut du conteneurA

      <Button 
          fx:id="boutonB"
          text="%texteB">
      </Button>                                                 <!-- déplacer en haut du conteneur -->
      
      <Pane styleClass="espacement,petit"/>
      
      <HBox
          styleClass="conteneur,h,center-left"
          fx:id="conteneurA"/>
      
    5. Modifier le conteneurA en conteneur vertical (et ajouter un HBox.hgrow)

      <VBox                                           <!-- transformer en VBox -->
          styleClass="conteneur,v,top-center"         <!-- v -->
          fx:id="conteneurA"
          HBox.hgrow="ALWAYS"/>                       <!-- ajouter HBox.hgrow -->
      
    6. Enrober le conteneurA dans un HBox avec des espacements

      <HBox>                                                   <!-- HBox autour -->
      
          <Pane styleClass="espacement,moyen"/>
      
          <VBox
          styleClass="conteneur,v,top-center"
          fx:id="conteneurA"
          HBox.hgrow="ALWAYS"/>                               
      
          <Pane styleClass="espacement,moyen"/>
      
      </HBox>                                                 <!-- du contenurA -->
      

    Adapter la classe VueA #

    1. Retirer labelA, labelB et boutonA

      • retirer aussi le code où labelA, labelB et boutonA étaient utlisé
    2. Modifier methodeF pour ajouter aussi un espacement

      public void methodeF(PartieEnFile paramC) {
          FragmentA varA = viewLoader.createView();
          paramC.methodeA(varA);
      
          if(conteneurA.getChildren().size() > 0){
              Pane espacement = new Pane();
              espacement.getStyleClass().add("espacement");
              espacement.getStyleClass().add("petit");
      
              conteneurA.getChildren().add(espacement);
          }
      
          conteneurA.getChildren().add(varA.rootNode());
      }
      

    Adapter le fichier vue_b.fxml #

    1. Retirer le Label et modifier le %var du bouton

      <!-- ... -->
      
      <Label
          styleClass="label"
          text="%texteA">
      </Label>                                      <!-- retirer -->
      
      <Pane styleClass="espacement,moyen"/>         <!-- retirer -->
      
      <Button 
          fx:id="boutonA" 
          text="%quitter">
      </Button>                                     <!-- modifier -->
      

    Adapter le fichier fragment_a.fxml #

    1. Transformer le VBox racine en HBox + ajouter la classe partie-en-file

      <HBox xmlns:fx="http://javafx.com/fxml"
            fx:controller="depart.frontal.fragments.FragmentA" 
            styleClass="conteneur,h,center,partie-en-file">        <!-- changer de v à h --> 
                                                                   <!-- ajouter partie-en-file -->
      
            <!-- ... -->
      
      </HBox>
      
    2. Ajouter un boutonA après le labelB

      • (et ajouter HBox.hgrow à l’espacement pour pousser le bouton à droite)
      <Label 
      fx:id="labelB"/>
      
      <Pane styleClass="espacement,petit"/>      <!-- ajouter le HBox.hgrow -->
      
      <Button 
          fx:id="boutonA"
          text="%jouer"/>                        <!-- ajouter -->
      
    3. Envelopper les labels et le bouton dans un HBox, puis un VBox (avec des espacements)

      <VBox styleClass="center" HBox.hgrow="ALWAYS">
      
          <Pane styleClass="espacement,petit"/>
      
          <HBox styleClass="center" HBox.hgrow="ALWAYS">
      
              <Pane styleClass="espacement,petit"/>
      
                  <!-- labels et bouton ici -->
      
              <Pane styleClass="espacement,petit"/>
      
          </HBox>
      
      </VBox>
      

    Adapter la classe FragmentA #

    1. Ajouter le boutonA

      public class FragmentA extends ViewFragmentFx {
      
          //...
      
          @FXML
          private Button boutonA;
      
    2. Déclencher l’événement EvtB avec le boutonA

      public class FragmentA extends ViewFragmentFx {
      
          // ...
      
          @Override
          public void initialize() {
              // ...
      
              Ntro.assertNotNull(boutonA);
      
              boutonA.setOnAction(evtFx -> {
      
                  Ntro.newEvent(EvtB.class).trigger();
      
              });
          }
      
    3. Ajouter des attributs texteA, texteB pour sauvegarder le nom des joueurs

      public class FragmentA extends ViewFragmentFx {
      
          // ...
      
      
          private String texteA = "";
          private String texteB = "";
      
    4. Modifier les methodeA, methodeB pour afficher le nom des joueurs

      public class FragmentA extends ViewFragmentFx {
      
          // ...
      
          public void methodeA(String paramA) {
              texteA = paramA;
              labelA.setText(texteA + " VS " + texteB);
          }
      
          public void methodeB(String paramB) {
              texteB = paramB;
              labelA.setText(texteA + " VS " + texteB);
          }
      
    5. Considérer la ligne de code labelA.setText(texteA + " VS " + texteB); qui se répète ci-haut

      • sélectionner cette ligne dans la methodeA

      • avec Ctrl+. => Extract method, extraire la méthode afficherNoms

      • vérifier que la ligne a été remplacée par un appel à afficherNoms dans methodeA et methodeB

    6. Utiliser la methodeC pour afficher le score

      public class FragmentA extends ViewFragmentFx {
      
          // ...
      
          public void methodeC(String format) {
              labelB.setText(format);
          }
      

    Vérifier le comportement et l’affichage #

    1. Effacer le modèle actuel

      $ rm -rf pong06/_storage/models
      
    2. Démarrer avec un modèle vide

      $ sh gradlew pong06
      

    Étape 5) Ajouter un fichier prod.css et le déclarer dans FrontalPong.java #

    1. Télécharger le fichier prod.css et le copier dans pong06/src/main/resources/style

    2. Dans FrontalPong.java, modifier la déclaration du CSS:

      //registrar.registerStylesheet("/style/dev.css");
      registrar.registerStylesheet("/style/prod.css");
      

    Vérifier le comportement et l’affichage #

    1. Effacer le modèle actuel

      $ rm -rf pong06/_storage/models
      
    2. Démarrer avec un modèle vide

      $ sh gradlew pong06
      

    Étape 5) implanter le bon message #

    1. Avec F2, renommer la classe

      • MsgA => MsgAjouterPartie
    2. Modifier les attributs du message

      public class MsgAjouterPartie extends Message<MsgAjouterPartie> {
      
          private String idJoueurA;
          private String nomJoueurA;
          private int    scoreJoueurA;
      
          private String idJoueurB;
          private String nomJoueurB;
          private int    scoreJoueurB;
      
    3. Avec Ctrl+., générer les setters

      public void setIdJoueurA(String idJoueurA) {
          this.idJoueurA = idJoueurA;
      }
      
      public void setNomJoueurA(String nomJoueurA) {
          this.nomJoueurA = nomJoueurA;
      }
      
      public void setScoreJoueurA(int scoreJoueurA) {
          this.scoreJoueurA = scoreJoueurA;
      }
      
      public void setIdJoueurB(String idJoueurB) {
          this.idJoueurB = idJoueurB;
      }
      
      public void setNomJoueurB(String nomJoueurB) {
          this.nomJoueurB = nomJoueurB;
      }
      
      public void setScoreJoueurB(int scoreJoueurB) {
          this.scoreJoueurB = scoreJoueurB;
      }
      
    4. Adapter les setters pour retourner this

      public MsgAjouterPartie setIdJoueurA(String idJoueurA) {
          this.idJoueurA = idJoueurA;
          return this;
      }
      
      public MsgAjouterPartie setNomJoueurA(String nomJoueurA) {
          this.nomJoueurA = nomJoueurA;
          return this;
      }
      
      public MsgAjouterPartie setScoreJoueurA(int scoreJoueurA) {
          this.scoreJoueurA = scoreJoueurA;
          return this;
      }
      
      public MsgAjouterPartie setIdJoueurB(String idJoueurB) {
          this.idJoueurB = idJoueurB;
          return this;
      }
      
      public MsgAjouterPartie setNomJoueurB(String nomJoueurB) {
          this.nomJoueurB = nomJoueurB;
          return this;
      }
      
      public MsgAjouterPartie setScoreJoueurB(int scoreJoueurB) {
          this.scoreJoueurB = scoreJoueurB;
          return this;
      }
      
    5. Modifier l’appel à la methodeA

    6. Utiliser Ctrl+. pour modifier la signature de la methodeA:

    7. Finaliser le code de ModeleFileAttente.methodeA

      public void methodeA(String idJoueurA, 
                           String nomJoueurA, 
                           int scoreJoueurA, 
                           String idJoueurB, 
                           String nomJoueurB, 
                           int scoreJoueurB) {
      
          parties.add(new PartieEnFile(String.valueOf(prochainId++), 
                                      idJoueurA, 
                                      nomJoueurA, 
                                      scoreJoueurA, 
                                      idJoueurB, 
                                      nomJoueurB, 
                                      scoreJoueurB));
      }
      
    8. Modifier la création du message dans la VueA

      boutonB.setOnAction(evtFx -> {
      
          Ntro.newMessage(MsgAjouterPartie.class)
              .setNomJoueurA(MaquetteDepart.chaineAuHasard())
              .setScoreJoueurA(MaquetteDepart.entierAuHasard())
              .setNomJoueurB(MaquetteDepart.chaineAuHasard())
              .setScoreJoueurB(MaquetteDepart.entierAuHasard())
              .send();
      });
      

    Étape 6) adapter MaquetteDepart #

    1. Avec F2, renommer la classe

      • MaquetteDepart => MaquettePartie
    2. Modifier le code de MaquetteDepart

      public class MaquettePartie {
      
          private static String idJoueur = "alice";
      
          public static String idJoueur() {
      
              List<String> choixDeId = List.of("alice", 
                                               "bob", 
                                               "chaaya", 
                                               "dominic", 
                                               "élisabeth", 
                                               "firas", 
                                               "gregson",
                                               "mehdi",
                                               "louis",
                                               "marcel",
                                               "ashwin",
                                               "ichiro",
                                               "jun");
      
              idJoueur = Ntro.random().choice(choixDeId);
      
              return idJoueur;
      
          }
      
          public static String nomJoueur() {
              return idJoueur.substring(0,1).toUpperCase() + idJoueur.substring(1);
          }
      
          public static int scoreAuHasard() {
              return Ntro.random().nextInt(10);
          }
      }
      
    3. Modifier l’envoi du message dans VueA

      boutonB.setOnAction(evtFx -> {
      
          Ntro.newMessage(MsgAjouterPartie.class)
              .setIdJoueurA(MaquettePartie.idJoueur())
              .setNomJoueurA(MaquettePartie.nomJoueur())
              .setScoreJoueurA(MaquettePartie.scoreAuHasard())
              .setIdJoueurB(MaquettePartie.idJoueur())
              .setNomJoueurB(MaquettePartie.nomJoueur())
              .setScoreJoueurB(MaquettePartie.scoreAuHasard())
              .send();
      });
      

    Vérifier l’état courant du projet #

    1. Effacer le modèle actuel

      $ rm -rf pong06/_storage/models
      
    2. Dans un GitBash à la racine du dépôt Git

      $ sh gradlew pong06
      
    Creative Commons License Creative Commons Attribution Creative Commons ShareAlike
    • Tutoriel 6: projet pong minimal
      • Étape 1) créer le projet pong06 à partir de dep06
      • Étape 2) implanter le bon modèle
        • ModeleFileAttente
        • PartieEnFile
      • Vérifier l’état courant du projet
      • Étape 3) adapter les vues et le fragment
        • Modifier les fichiers .properties
        • Adapter le fichier vue_a.fxml
        • Adapter la classe VueA
        • Adapter le fichier vue_b.fxml
        • Adapter le fichier fragment_a.fxml
        • Adapter la classe FragmentA
      • Vérifier le comportement et l’affichage
      • Étape 5) Ajouter un fichier prod.css et le déclarer dans FrontalPong.java
      • Vérifier le comportement et l’affichage
      • Étape 5) implanter le bon message
      • Étape 6) adapter MaquetteDepart
      • Vérifier l’état courant du projet