boolean gagnant= false;

 1ère partie : juste un Listener

Suivre le tutoriel « tuto GUI » pour créer une 1ère interface graphique avec :

  • un titre JLabel
  • un séparateur JSeparator
  • un champ de texte JTextField
  • un bouton « Valider » JButton

Ensuite ajouter un Listener sur le bouton « Valider » pour qu’en cliquant sur ce bouton :

  • s’affiche un message de bienvenue avec le nom de l’utilisateur.
  • change la couleur du contentPane.

Ci-dessous :  AVANT de valider ………….. APRES avoir validé !

ja_MiniNom1 ja_MiniNom2

2e partie : setVisible() et setEnabled()

Objectif : Compliquer l’interface, en ajoutant l’un de ces petits jeux : calcul mental OU opérateur vide OU 3 curseurs, dans le but :

  • multiplier les intéractions (les Listeners).
  • utiliser setVisible() pour n’afficher le jeu qu’une fois le nom rentré.
  • utiliser setEnabled() pour arrêter le jeu (en désactivant un bouton).

calcul mental

  • Proposer un calcul. Le client inscrit un nombre.
  • Lorsqu’il valide, un message apparaît : « bonne réponse » ou « mauvaise réponse, essaie encore ».
  • Dans le cas d’une bonne réponse, arrêter le jeu (désactiver le bouton, supprimer les JLabel JTextField …).

ja_MiniCalcul1 ja_MiniCalcul2

ja_MiniCalcul3 ja_MiniCalcul4

opérateur vide

  • Proposer un calcul.
  • Le client clique sur un opérateur => le texte de la JLabel est rafraîchie avec l’opérateur à la place de « ??? ».
  • Lorsqu’il valide l’opérateur, un message apparaît : « bonne réponse » ou « mauvaise réponse, essaie encore ».
  • Dans le cas d’une bonne réponse, arrêter le jeu (désactiver les boutons, supprimer les JLabel JButton …).

ja_MiniOpe1 ja_MiniOpe2

ja_MiniOpe3 ja_MiniOpe4

3 curseurs RGB

  • Dès le démarrage, ajouter 3 curseurs (Rouge, Vert, Bleu de 0 à 255) et le champ de saisie du nom.
  • Lorsque le client varie un curseur => la couleur de  fond change (utiliser des variables R,G,B).
  • Puis lorsque le client valide son nom, alors la couleur de fond passe en violet, donc … il faut penser à ré-actualiser les 3 curseurs pour qu’ils indiquent les valeurs RGB du « violet ».
       

3e partie : Styliser un JButton (une classe)

Objectif : Créer un nouveau Java Projet contenant une classe pour customiser plus facilement un composant (avec champs de classe et méthodes spécifiques) : dans le but de préparer un jeu de CARTES ou un MORPION.

Jeu de cartes

étape 1 : Créer un nouveau Java Projet, avec une interface graphique GUI (qui est le programme principal).
Utiliser le WindowBuilder pour ajouter un JPanel vert.
Mettre un « Layout absolute » sur ce JPanel puis insérer un JButton (de grande taille) sur ce JPanel.

étape 2 : Créer une autre classe :

étape 3 : Ajouter une classe ResourceLoader :

  • Suivre le tutoriel pour créer un répertoire de ressource (Source Folder) et une classe qui charge les images  ResourceLoader.
  • Cliquer sur cette image de 54 cartes jeu54cartes.png pour l’avoir en taille réelle=1300 x 780px. Puis la télécharger sur votre ordinateur  ; et glisser ce fichier dans le répertoire main.images des Ressources.
    jeu54cartes   

étape 4 : Charger l’image dans une variable.
Dans la classe BoutonCartes :

  • Ajouter  import main.ResourceLoader;   (si ce n’est pas déjà fait)
    Cet import doit être mis tout en haut de la classe BoutonCarte, il permet d’accéder au ResourceLoader.
  • Déclarer une variable générale BufferedImage image54;  (faire Ctrl+Shift+O pour importer la librairie BufferedImage).
  • Mettre l’instruction ci-dessous dans le constructeur BoutonCartes() pour charger l’image de la feuille de 54 cartes dans la variable image54  :
    image54 = ResourceLoader.loadImage("images/jeu54cartes.png");
  • Clic sur l’erreur pour ajouter un Try / Catch (ceci permet d’attraper l’erreur si le fichier ne peut pas être lu par Java).

    try {
      image54 = ResourceLoader.loadImage("images/jeu54cartes.png");
    } catch (IOException e) {
      e.printStackTrace();
    }

étape 5 : Découper cette grande image pour récupérer les images chaque carte et stocker ces sous-images dans un tableau.
On ne va traiter que la 1ère ligne (la ligne des Trèfles).
Dans la classe BoutonCartes :

  • Dans les variables générales : on crée un tableau d’images de 13 colonnes numérotées de 0 à 12 :
    ImageIcon[] spriteCarte = new ImageIcon[13];     (Faire Ctrl+Shift+O pour importer la librairie ImageIcon.)
  • Dans le constructeur BoutonCartes()  : on va remplir chaque case du tableau par une subdivision de la grande image grâce à la méthode  getSubimage(x, y, width, height)
    Comme la grande image a pour dimension 1300 x 780 alors chaque carte fait 100 x 156 px.
    Donc, à la fin du constructeur BoutonCartes() , on peut remplir les 13 cases du tableau spriteCarte, avec le découpage suivant :

    spriteCarte[0] = new ImageIcon(image54.getSubimage(0, 0, 100, 156));
    spriteCarte[1] = new ImageIcon(image54.getSubimage(100, 0, 100, 156));
    spriteCarte[2] = new ImageIcon(image54.getSubimage(200, 0, 100, 156));
    etc ...
    spriteCarte[12] = new ImageIcon(image54.getSubimage(1200, 0, 100, 156));

    => Ce code est répétitif … donc il faut mieux utiliser une boucle  for (int i = ... ; i < ... ; i++)  à vous de jouer !

  • FACULTATIF : si on veut être plus professionnel, on met un code qui s’adapte aux éventuels changements de dimensions :
    • La largeur de la sous-image devrait plutôt être calculée (au lieu d’être fixée à 100px)  :  spriteWidth = image54.getWidth()/13; et idem pour la hauteur de la sous-image.
    • Pour la boucle sur le n° de colonnes i, au lieu de mettre  i<13  il faut mieux mettre  i<spriteCarte.length

étape 6 : On va afficher l’image sur le bouton :

Dans la classe BoutonCartes :

  • Dans le constructeur BoutonCarte() : on enlève  setText(figure);  et on rajoute à la fin de ce constructeur   setIcon(spriteCarte[0]);  (car l’image n°0 est celle de l’As de Trèfle : la valeur initiale de la carte).
  • Dans la méthode changeFigure() : remplacer  setText(figure);  par  setIcon(spriteCarte[i]);  (car il s’agit de l’image de i-ème carte : i étant la valeur aléatoire choisi par random).
  • Si besoin, on pourra ajuster les dimensions du bouton dans la GUI aux dimensions des sous-images 100 x 156.

étape 7 : FACULTATIF : Ajouter les autres couleurs (Carreau, Coeur, Pique)  :
Dans la classe BoutonCartes :

  • Dans les variables générales : on ajoute une 2e dimension au tableau spriteCarte (4 lignes) :
    ImageIcon[][] spriteCarte = new ImageIcon[13][4];
  • Pour corriger les 3 erreurs : ajouter l’index 0 :
  • Dans le constructeur BoutonCartes() : on peut remplir les 4 lignes du tableau spriteCarte, avec les sous-images de hauteur 156px :

    => Ce code est répétitif … donc il faut mieux utiliser une boucle  for (int j = ... ; j < ... ; j++)  à vous de jouer !
    Donc, à la fin du constructeur BoutonCartes() , on peut remplir les 13 cases du tableau spriteCarte, avec le découpage suivant :
  • Dans la méthode changeFigure() : Ajouter un entier j aléatoire (de 0 à 3) et mettre cet entier comme 2e index du setIcon :
    public void changeFigure() { //met une figure aléatoire
        int i;
        do {
            i = random.nextInt(tabFigure.length);
        } while (figure.equals(tabFigure[i]));
        figure = tabFigure[i];
        
        int j = random.nextInt(4); //choisit une ligne aléatoire : 0 à 3
        setIcon(spriteCarte[i][j]);
    }

    On pourrait mémoriser la couleur de la carte dans une variable (générale) mais, en fait, la couleur de la carte ne sert pas lorsqu’on joue à la bataille (seule la figure donne la valeur de la carte).

étape 8 : Ajouter un 2e bouton :
Dans le programme principal (la GUI)  :

  • Ajouter un 2e bouton de type BoutonCarte (ne pas mettre de Listener sur ce 2e bouton) .
  • Ajouter dans le Listener du 1er bouton une instruction pour que la 2e carte change aussi de figure lors d’un clic.

étape 9 : Compter les points (score) :
Dans le programme principal (la GUI)  :

  • Dans les variables générales : ajouter 2 variables entières (int) : score1 et score2, initialisées à 0.
  • Ajouter 2 JLabel avec le score de chaque bouton.
  • A la fin du Listener du 1er bouton, on voudrait ajouter quelques instructions qui incrémentent le score du bouton qui a la plus grande figure. Pour y parvenir, une idée parmi d’autres, est :
    Dans la classe BoutonCarte :

    • Dans les variables générales : créer un tableau des valeurs de cartes : un AS = 14 pts ; un 2 = 2 pts ; … ; une DAME = 12 pts ; un ROI = 13 pts. Il ressemble beaucoup au tabFigure, sauf que c’est un tableau d’entiers :
      int[] tabValeurs = new int[] {14,2,3,4,5,6,7,8,9,10,11,12,13};
    • Créer une variable générale entière valeur initialisée à 14 (car la carte initiale est l’AS de Trèfle, donc une valeur 14 selon le barème ci-dessus).
    • Créer un ‘getter’ sur cette variable valeur. [Sélectionner la variable valeur > clic droit > Quick Fix > Create ‘getter’ and ‘setter’ ]
    • Puis dans la méthode changeFigure() : comme le bouton change de carte alors le bouton doit changer de valeur (c’est à cet endroit qu’on utilise le tableau des valeurs de cartes, c’est exactement comme pour la variable figure)

Dans le Listener du 1er bouton :

  • Il suffit de comparer les getValeur() des 2 boutons pour incrémenter le score du gagnant.
  • Puis, ne pas oublier d’actualiser l’affichage du nouveau score dans les JLabel.

Morpion

étape 1 : Créer une GUI :

  • Créer 2 JButton de taille carré, côte à côte, avec WindowBuilder (dans le but de comprendre, que seule l’abscisse change).
  • Créer un tableau de JButton 3×3 dans les variables générales de cette GUI :  JButton tabBtn [][] = new JButton[3][3];
  • Ecrire une 1ère boucle sur les colonnes : afin de faire une ligne n°0 de 3 boutons côte à côte  : tabBtn[0][0], tabBtn[1][0] et tabBtn[2][0] :
    for (int i = 0 ; i < tabBtn.length ; i++) {
      tabBtn[i][0] = new JButton();
      tabBtn[i][0].setBounds(30+50*i, 30, 50, 50);
      getContentPane().add(tabBtn[i][0]);				
    }
  • Insérer dans cette boucle sur les colonnes, une 2e boucle sur les lignes : afin de faire 3 lignes de 3 boutons côte à côte :
    for (int i = 0 ; i < tabBtn.length ; i++) {
      for (int j = 0 ; j < tabBtn[i].length ; j++) {
        tabBtn[i][j] = new JButton();
        tabBtn[i][j].setBounds(30+50*i, 30+50*j, 50, 50);
        getContentPane().add(tabBtn[i][j]);	
      }  
    }

étape 2 : Créer une classe de JButton stylé Morpion :

  • Suivre ce tutoriel pour créer une classe BoutonMorpion (un JButton personnalisé) en incluant la méthode changeFigure().
  • Dans la GUI, changer le type de variable du tableau de JButton en BoutonMorpion : le code doit fonctionner.

étape 3 : Ajouter un ActionListener sur les boutons :

Vous avez le choix entre :

  • Soit mettre l’ActionListener dans le programme principal (en suivant ce tutoriel-là) : conseillé pour gérer plus facilement la fin de partie !
  • Soit définir l’ActionListener dans la classe BoutonMorpion (suivant ce tutoriel-ci).

étape 4 : Charger une image avec une classe ResourceLoader :

  • Suivre le tutoriel pour créer un répertoire de ressource (Source Folder) et une classe qui charge les images  ResourceLoader.
  • Télécharger le fichier de morpions.jpg  (taille 420 x 280 px) sur votre ordinateur.
  • Glisser cette image dans le répertoire main.images des Ressource du Java Project.
  • Dans la classe BoutonMorpion :
    • Créer une variable générale imageSprites (de type BufferedImage) :  BufferedImage imageSprites;
    • Faire Ctrl+Shift+O pour importer la librairie BufferedImage.
    • Ajouter  import main.ResourceLoader;   (si ce n’est pas déjà fait) (à placer tout en haut, comme sur le screenshot ci-dessous).
    • Puis dans le constructeur BoutonMorpion() on met l’instruction qui charge en mémoire cette image.
      imageSprites = ResourceLoader.loadImage("images/morpions.jpg");
  • Clic sur l’erreur pour ajouter un Try / Catch (ceci permet d’attraper l’erreur si le fichier ne peut pas être lu par Java).

    try {
      imageSprites = ResourceLoader.loadImage("images/morpions.jpg");
    } catch (IOException e) {
      e.printStackTrace();
    }

étape 5 : Créer un tableau d’images.
Concrètement, le morpion n’utilise qu’une couleur car chaque joueur a un symbole :  X ou O.
Donc vous choisirez une des 2 couleurs pour votre morpion : bleu ou rouge.

  • Dans les variables générales de la classe BoutonMorpion : on déclare un tableau :
    ImageIcon[] spriteMorpion = new ImageIcon[3];    (Faire Ctrl+Shift+O pour importer la librairie ImageIcon.)
  • Dans le constructeur de la classe BoutonMorpion : on va remplir chaque case du tableau par une subdivision de l’image grâce à la méthode  getSubimage(x, y, width, height)
    Comme la grande image a pour dimension  420 x 280 alors chaque petite image fait 140 x 140.
    Donc, à la fin du constructeur BoutonMorpion() ,on peut remplir chaque case du tableau, avec le découpage suivant :

    spriteMorpion[0] = new ImageIcon(imageSprites.getSubimage( 0 , 0, 140, 140));
    spriteMorpion[1] = new ImageIcon(imageSprites.getSubimage(140, 0, 140, 140));
    spriteMorpion[2] = new ImageIcon(imageSprites.getSubimage(280, 0, 140, 140));

    => Ce code est répétitif … donc il faut mieux utiliser une boucle for (int i = ... ; i < ... ; i++) !! A vous de jouer !

étape 6 : On va afficher l’image sur le bouton :

  • Dans le constructeur BoutonMorpion() : on enlève  setText(figure);  et on rajoute à la fin de ce constructeur   setIcon(spriteMorpion[0]);  (car l’image n°0 est celle de la case vide : la valeur initiale du bouton au démarrage du jeu).
  • Dans la méthode changeFigure() : remplacer  setText(figure);  par  setIcon(spriteMorpion[i]);  (car il s’agit de l’image de i-ème case : i étant la valeur aléatoire choisi par random).
  • Si besoin, on pourra ajuster les dimensions du bouton dans la GUI aux dimensions des sous-images 140 x 140.

étape 7 : Le symbole ne doit plus être aléatoire, mais suivre la parité du nombre de tours (variable statique).
Pour cela, dans le programme principal, on crée une variable entière nbTours et afin que la classe BoutonMorpion ait accès à cette variable alors nbTours doit être statique : static int nbTours=0;
Ensuite dans changeFigure() de la classe BoutonMorpion :

  • Enlever les instructions qui définissaient i de manière aléatoire (avec random). On va tout remplacer par ce qui suit.
  • On va incrémenter la variable nbTours. Mais comme cette variable est définie dans l’autre classe (la GUI) alors on doit la faire précéder du nom de sa classe :
    GUI.nbTours++;    (car ma classe principale porte le nom ‘GUI’)
  • Puis on doit calculer le symbole X ou O, en fonction de la parité de l’entier nbTours.
    Rappel : GUI.nbTours % 2 donne 0 si nbTours est pair et il donne 1 s’il est impair.
    Or les 2 symboles O et X ont pour index 1 et 2, donc on ajoute 1 au « modulo 2 » :
    int i = 1 + GUI.nbTours % 2;
  • Puis on actualise la variable ‘figure’ et l’image du bouton (la sous-image X ou O) :
    figure = tabFigure[i];
    setIcon(spriteMorpion[i]);
  • Enfin, le bouton doit devenir non-cliquable (donc on le désactive) et afin qu’il ne devienne pas gris (pas très joli), on définit la même image comme icone (en mode désactivé) :
    setDisabledIcon(spriteMorpion[i]);
    setEnabled(false);

étape 8 : Vérifier s’il y a un gagnant en testant : les 3 lignes + les 3 colonnes + les 2 diagonales.

Si vous avez mis l’ActionListener dans la BoutonMorpion  :

  • soit vous reprenez le tutoriel précédent pour transvaser l’ActionListener dans la GUI.
  • soit, comme pour la variable nbTours :
    1. vous rajoutez static devant  BoutonMorpion tabBtn [][] (le tableau de boutons de la GUI)
    2. vous coderez ce qui suit dans changeFigure() en rajoutant l’appel à la classe GUI devant tabBtn,
      exemple : GUI.tabBtn[i][j].getFigure()

Dans l’ActionListener des boutons dans la GUI :

  • créer une variable locale booléenne : boolean gagnant=false;
    Cette variable est surnommée un flag, elle consiste à changer de valeur uniquement si on trouve une combinaison gagnante lors de la série de tests.
    Concrètement, si à la fin des tests ‘gagnant = true’ alors on arrête le jeu en désactivant tous les boutons, sinon il n’y a pas de gagnant, on ne fait rien, le jeu continue.
  • test sur la 1ère ligne : si ( tabBtn[0][0]==tabBtn[1][0] et tabBtn[1][0]==tabBtn[2][0] ) gagnant = true;
    ce qui donne en java en employant equals() car il s’agit d’une égalité d’objets (figure est un String) :
    if (tabBtn[0][0].getFigure().equals(tabBtn[1][0].getFigure())
       && tabBtn[1][0].getFigure().equals(tabBtn[2][0].getFigure()))
       gagnant = true;
  • Bien entendu, il faut aussi tester les autres lignes, donc au final :
    si (tabBtn[0][0]==tabBtn[1][0] et tabBtn[1][0]==tabBtn[2][0]) gagnant = true;
    si (tabBtn[0][1]==tabBtn[1][1] et tabBtn[1][1]==tabBtn[2][1]) gagnant = true;
    si (tabBtn[0][2]==tabBtn[1][2] et tabBtn[1][2]==tabBtn[2][2]) gagnant = true;

    Evidemment, on peut résumer ces 3 lignes en une boucle … et en java.
  • Faire de même pour tester les colonnes.
  • Idem pour chaque diagonale.
  • Enfin, s’il y a un gagnant (gagnant=true) alors on désactive tous les boutons du tableau tabBtn[i][j] avec setEnabled(false) : le jeu est fini !