Partage
  • Partager sur Facebook
  • Partager sur Twitter

Tour par tour

Projet : jeu

Sujet résolu
    23 mai 2018 à 18:46:35

    Bonjour à tous ! Depuis maintenant quelques mois, moi et 2 de mes amis essayons de programmer un "jeu" en java dans le cadre d'un projet. Étant 3 débutants en programmation et en java, nous avons rencontré plusieurs problèmes, tous résolus aujourd'hui. Cependant, un problème persiste et nous ne savons pas commet le résoudre.

    Actuellement, nous nous sommes focalisés sur la création d'un tour par tour pour le bon fonctionnement de notre jeu. L'idée est donc la suivante : en appuyant sur un JButton, l'ennemi perd des points de vie ; 2 secondes pendant lesquelles rien ne se passe puis on reçoit, à notre tour, une attaque venant de l'ennemi qui nous fait perdre des points de vie. Pour ce faire, nous avons utilisé ce code :

    public void actionPerformed(ActionEvent ae){
        Object source = ae.getSource();
            
        if(source == attaque){
            Entite.HP[i] = Entite.HP[i] - Stanley.AA;
            this.repaint();
            try{
                Thread.sleep(2000);
            }
            catch(Exception e){}
            Stanley.hp = Stanley.hp - Entite.ATT[i];    
            this.repaint();
        }//Fin attaque
    }
    Malheureusement, en lançant l'application, le Thread.sleep s'effectue avant tout et ensuite notre héros et l'ennemi perdent leurs points de vie en même temps (Stanley étant le nom de notre héros et ayant ses propres caractéristiques dans une autre classe par le biais de tableaux, Entite étant le nom de l'ennemi). Si quelqu'un connaît la solution, qu'il n'hésite pas à en faire part ^^. Merci d'avance.

    -
    Edité par MathieuPeeters 23 mai 2018 à 18:47:42

    • Partager sur Facebook
    • Partager sur Twitter
      23 mai 2018 à 19:21:01

      Salut,

      Peut être ne devrais tu pas mettre le sleep dans l'action elle-même au niveau de... ce qui s'occupe de gérer les actions; l'idée étant d'avoir une logique proche de

      1. j'attends que le joueur ait choisi l'attaque
      2. j'enregistre le choix du joueur
      3. je bloque tout le temps requis (sleep)
      4. j'exécute l'attaque "maintenant"
      5. je passe au joueur suivant
      6. je recommence en (1)

      Ca ne te semble pas plus correcte que de te dire : quand l'ordre est passé, j'attends le temps requis avant de l'exécuter"?

      • Partager sur Facebook
      • Partager sur Twitter
      Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
        23 mai 2018 à 19:49:37

        Le but du jeu est de combattre des monstres, pas d'affronter un autre joueur humain. J'ai dû mal expliquer ^^ 

        D'où le fait que l'on ait créé différentes entités dans une autre classe avec, à chaque fois, des caractéristiques différentes.

        • Partager sur Facebook
        • Partager sur Twitter
          23 mai 2018 à 20:00:51

          Ah ben tu retire l'étape cinq (ou tu considère que le monstre est "l'autre joueur"), mais la logique reste la même:

          Pour respecter le SRP, tu as

          • le personnage (joueur, monstre ou seulement un seul des deux :D) qui sélectionne l'attaque
          • le "régulateur" qui "bloque" l'attaque "pendant le temps imparti" avant d'en permettre l'exécution et
          • l'attaque qui occasionne des dégâts à la cible

          Ce n'est pas de la responsabilité de l'attaque de se dire "ah mince, je dois attendre avant d'aller frapper ma cible";  ce n'est pas d'avantage la responsabilité du personnage de se dire "a mince, je dois attendre avant de lancer mon attaque".

          Donc, il faut "quelque chose" entre les deux dont la responsabilité sera ... d'assurer la temporisation ;)

          • Partager sur Facebook
          • Partager sur Twitter
          Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
            23 mai 2018 à 20:12:19

            D'accord ! Je pense comprendre désormais mais reste à savoir comment procéder. J'ai essayé ceci en dehors de l'actionPerformed mais cela ne fonctionne pas (HPC contient les mêmes valeurs que HP). 
            if(Entite.HP[i] != Entite.HPC[i]){
                    try{
                        Thread.sleep(2000);
                    }
                    catch(Exception e){}
                    Stanley.hp = Stanley.hp - Entite.ATT[i];    
                    this.repaint();
                    Entite.HPC[i] = Entite.HP[i];
            }
            • Partager sur Facebook
            • Partager sur Twitter
              23 mai 2018 à 21:04:28

              Attend, parce que ca devient un peu confus, là... quelles sont les classes dont tu disposes, et quelles sont les relations qui existent entre elles?

              A priori (mais je peux me tromper, hein?), j'aurais tendance à dire que tu devrais avoir:

              • une classe Character (n'importe quel caractère du jeu)
              • une class Human (le joueur "humain") qui hérite de Character
              • une class Monster(les "joueur non humains") qui hérite de Character
              • une class Attack (potentiellement dérivée en différent types
              • et maintenant une classe Referee qui assure la communication entre le personnage (quel qu'il soit) et l'attaque.

              Les noms sont peut-être différents, bien sur... Mais, est-ce que l'on part bien sur une structure de ce genre?

              • Partager sur Facebook
              • Partager sur Twitter
              Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                23 mai 2018 à 21:23:56

                Je vais essayer d'expliquer plus en détails.

                Nous avons au centre de notre jeu une classe "Panneau" qui fait office de JPanel. Dans celle-ci, on y a placé une méthode paintComponent pour dessiner des éléments du jeu, 4 boutons (1 pour l'attaque de base, 1 pour les sorts, 1 pour la défense et 1 pour la fuite) mais aussi une méthode actionPerformed dont j'ai montré un bout de code plus haut.

                Il y a également une classe "Fenetre" qui contient la JFrame à laquelle nous avons assigné le JPanel de "Panneau".

                Au-delà de cela, nous avons aussi une classe pour le menu principal du jeu, une autre pour gérer les 4 sorts ou encore une autre contenant la carte du jeu.

                Celles qui nous intéressent sont les classes "Entite" et "Stanley" (respectivement les monstres qu'on affronte et notre héros). "Entite" contient les statistiques de tous les monstres du jeu (Points de vie, dégâts d'attaque, etc...). Pareil avec Stanley sauf qu'ici, ce sont les statistiques du héros.

                Ce nous voulons : lorsque l'on clique sur un JButton présent dans "Panneau", les points de vie du monstre que l'on affronte diminue en fonction des dégâts d'attaque de notre héros, un certain labs de temps s'effectue puis ensuite le monstre nous attaque et l'on perd des points de vie.

                Si ça peut t'éclaircir ^^

                • Partager sur Facebook
                • Partager sur Twitter
                  24 mai 2018 à 6:39:13

                  Bonjour,

                  A mon sens, ton problème vient de la gestion des Threads. Quand tu cliques sur le bouton, l'affichage "perd la main" au profit de tes actions(attaques/attendre/contre attaque. Puis quand toutes tes actions sont terminées, l'affichage est rafraichit et tu vois toutes les conséquences de tes actions en même temps. Pour résoudre le problème, tu peux exécuter les actions dans un Thread séparé.

                  -
                  Edité par @rtur 24 mai 2018 à 7:02:25

                  • Partager sur Facebook
                  • Partager sur Twitter
                    25 mai 2018 à 9:37:51

                    Merci pour la réponse cependant je ne comprends pas vraiment comment séparer le tout en 2 threads.
                    • Partager sur Facebook
                    • Partager sur Twitter
                      25 mai 2018 à 11:46:03

                      Il y a un cours sur ce site java et le multithreading qui te donnera plein d'explication sur ce phénomène. Sinon, regarde l'exemple ci-dessous:

                      package test3;
                      
                      import java.awt.GridLayout;
                      
                      import javax.swing.JButton;
                      import javax.swing.JFrame;
                      import javax.swing.JLabel;
                      
                      /**
                       * Comparaison des comportements avec ou sans thread à l'aide d'une
                       * fenêtre comportant deux boutons pour afficher un compte à rebours:
                       * - le premier bouton sans Thread
                       * - le deuxième bouton avec Thread
                       */
                      public class Fenetre extends JFrame {
                      	private static final long serialVersionUID = 1L;
                      	private JLabel affichage;
                      
                      	public Fenetre() {
                      		//JFrame
                      		setLocationRelativeTo(null);
                      		setSize(200,150);
                      		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                      		setLayout(new GridLayout(3, 1, 5, 5));
                      		
                      		//Etiquette pour l'affichage
                      		affichage = new JLabel();
                      		getContentPane().add(affichage);
                      		
                      		//Bouton sans Tread
                      		JButton bouton1 = new JButton("sans Thread");
                      		bouton1.addActionListener(e->compteARebours());
                      		getContentPane().add(bouton1);
                      		
                      		//Bouton avec Thread
                      		JButton bouton2 = new JButton("avec Thread");
                      		bouton2.addActionListener(e-> new Thread() {
                      			public void run() {			//On redéfinit la méthode run() du Thread
                      				compteARebours();
                      			}
                      		}.start());						//On lance le Thread avec la méthode start()
                      		getContentPane().add(bouton2);
                      	}
                      	
                      	
                      	/**
                      	 * Méthode pour afficher un compte à rebours dans une JLabel
                      	 */
                      	public void compteARebours() {
                      		for(int i = 10 ; i >= 0; i--) {
                      			affichage.setText(Integer.toString(i));
                      			try {
                      				Thread.sleep(500);		//On temportise
                      			} catch (InterruptedException ex) {
                      				ex.printStackTrace();
                      			}
                      		}
                      	}
                      }
                      



                      • Partager sur Facebook
                      • Partager sur Twitter
                        25 mai 2018 à 12:15:12

                        Suivant ton modèle, j'ai essayé ceci, malheureusement les points de vie de Stanley ne se repaint pas convenablement
                        public void actionPerformed(ActionEvent ae){
                                Object source = ae.getSource();
                                
                                
                                if(source == attaque){
                                    Entite.HP[i] = Entite.HP[i] - Stanley.AA;
                                    this.repaint();
                                    jsp = true;
                                    if(jsp == true){
                                        Thread tache = new Thread(){
                                            Panneau p = new Panneau();
                                            public void run(){
                                                try{
                                                    sleep(2000);
                                                }catch(Exception e){}
                                                Stanley.hp = Stanley.hp - Entite.ATT[i];
                                            }
                                        };
                                        tache.start();
                                        this.repaint();
                                    }
                                    // bouleDeFeu = new gt(this);
                                    // bouleDeFeu.goSort();
                                }//Fin attaque
                        }

                        -
                        Edité par MathieuPeeters 25 mai 2018 à 12:15:34

                        • Partager sur Facebook
                        • Partager sur Twitter
                          25 mai 2018 à 14:34:27

                          et en mettant le deuxième this.repaint() dans le thread?

                          -
                          Edité par @rtur 25 mai 2018 à 15:11:34

                          • Partager sur Facebook
                          • Partager sur Twitter
                            25 mai 2018 à 16:40:56

                            Ca fonctionne ! Merci beaucoup pour ton aide qui m'a été d'une grande utilité :D
                            • Partager sur Facebook
                            • Partager sur Twitter

                            Tour par tour

                            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                            • Editeur
                            • Markdown