import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.sql.Connection; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.Timer; public class Jeu implements KeyListener, ActionListener{ //------------------------------------------------------------------------- // PROPRIETES //------------------------------------------------------------------------- // Ecran : instance de ZoneDessin, qui contiendra tous les objets graphiques // On ne va pas créer directement l'écran car ça va créer un flash // Dès le lancement du jeu avec l'image de fond par défaut et l'imga du niveau // et les animera protected ZoneDessin ecran; // Le cercle : en fait 2 demis-cercles, un qui passera sous la ligne // un qui passera dessus protected Cercle demiCercleAvant = new Cercle(90,-180); // celui qui est sur la ligne protected Cercle demiCercleArriere = new Cercle(90,180); // celui qui est derrière la ligne // A FAIRE : ajouter les objets graphiques manquants, s'il y en a protected Ligne laLigne = new Ligne(); // Ligne infinie du jeu // Timer : un objet qui émet des événements à un intervalle choisi, // il sert à donner le pas de l'animation protected Timer horloge; // Une variable qui contiendra le score protected double score=0; // un label qui servira à afficher le score protected JLabel labScore; protected JLabel labTriche; protected JLabel labPause; protected boolean jeuCommence = false; protected boolean pause = false; protected boolean modeTriche = false; protected BonusMalus itemCourant = new BonusMalus(1500, 300, true); // On crée un seul item (carré ou triangle) qui va tourner en boucle pendant la partie protected int chronoBonus = 0;// Compteur qui va gérer la durée du bonus x5 (125 frames = 5 secondes) // Propriété de ma connexion à ma base de données Connection conn = null; private int utilisateurId; // Variables actuelles du début de jeu protected int idNiveauActuel = 1; protected Niveau niveauEnCours; protected int segmentsTermines = 0; // Variable compteur de segments protected Segment dernierSegmentValide = null; // On sauvegarde la fenêtre principale de la partie en cours dans une variable. // Cela nous permettra de la fermer proprement (avec la méthode .dispose()) // quand le joueur voudra lancer une nouvelle partie. protected JFrame fenetrePrincipale; protected javax.swing.JButton btnRejouer; //------------------------------------------------------------------------- // METHODES //------------------------------------------------------------------------- //------------------------------------------------------------------------- // Constructeur de la classe //------------------------------------------------------------------------- public Jeu(int utilisateurId){ // Créer les tables de la base de données si elles n'existent pas GestionBDD.creerTableUtilisateurSiAbsente(); GestionBDD.creerTableScoreSiAbsente(); JFrame fenetre = new JFrame(); this.utilisateurId = utilisateurId; Background premierFond = null; // On crée le jeu avec sa base de données try { conn = NiveauxDataConnect.getInstance().getConnection(); System.out.println("Connexion réussie !!!"); } catch (Exception ex) { ex.printStackTrace(); javax.swing.JOptionPane.showMessageDialog(fenetre,"Erreur de connexion à la base de données. Le jeu ne peut pas démarrer.", "Erreur Fatale", javax.swing.JOptionPane.ERROR_MESSAGE ); } // On récupère le niveau 1 Niveau n1 = NiveauxDataConnect.recupererNiveau(conn, 1); if( n1 != null ){ try { // On charge l'mage du niveau 1 java.net.URL url = Jeu.class.getResource("/" + n1.getImage()); if(url != null){ java.awt.Image img = javax.imageio.ImageIO.read(url); premierFond = new Background(img); } } catch (Exception e) { e.printStackTrace(); } } this.ecran = new ZoneDessin(premierFond); // Gestion du score : a réactiver en fin de TP, inutile au début ecran.setLayout(null); labScore = new JLabel(); labScore.setText("

score : 0

"); labScore.setBounds(20, 0, 200, 50); ecran.add(labScore); labTriche = new JLabel(); labTriche.setText("

Mode de triche activé

"); labTriche.setBounds(600, 0, 200, 50); // Placé en haut à droite (la fenêtre fait 800 de large) labTriche.setVisible(false); // Caché par défaut au lancement ecran.add(labTriche); labPause = new JLabel(); labPause.setText("

Pause

"); labPause.setBounds(365, 300, 200, 50);//Placé au millieu de l'écran labPause.setVisible(false);// Caché par défaut au lancement ecran.add(labPause); } //------------------------------------------------------------------------- // Méthodes qu'il faut implémenter pour être // conforme à un KeyListener //------------------------------------------------------------------------- //------------------------------------------------------------------------- // Appui sur une touche // -> l'événement est émis lorsqu'on appuie, puis selon le rythme de // répétition du clavier //------------------------------------------------------------------------- @Override public void keyPressed(KeyEvent e){ // keyCode 38 : up // keyCode 40 : down int keyCode = e.getKeyCode(); if (keyCode==38){ // touche "flèche vers le haut" // On demande aux deux demi-cercle de "monter" demiCercleAvant.Monter(); demiCercleArriere.Monter(); } if(keyCode==32){//touche espace modeTriche =!modeTriche; labTriche.setVisible(modeTriche); //afficher ou cacher le label demiCercleAvant.setModeTriche(modeTriche); demiCercleArriere.setModeTriche(modeTriche); } if(keyCode==27){//touche echap pause = !pause;//active ou desactive le mode pause à chaque fois que la touche espace est appuyé labPause.setVisible(pause); if(pause==false && this.ecran.partiePerdue==false){ this.horloge.start(); }else{ this.horloge.stop();// Le jeux se stoppe si le joueur appuie sur pause } } } //------------------------------------------------------------------------- // Relâchement de la touche //------------------------------------------------------------------------- @Override public void keyReleased(KeyEvent e){ // keyCode 38 : up // keyCode 40 : down int keyCode = e.getKeyCode(); if (keyCode==38){ // On demande aux deux demi-cercle "d'arrêter de monter" demiCercleAvant.ArreterMonter(); demiCercleArriere.ArreterMonter(); } } //------------------------------------------------------------------------- // Une méthode que nous n'utilisons pas //------------------------------------------------------------------------- @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } //------------------------------------------------------------------------- // Démarrage du jeu : // création de diverses instances et //------------------------------------------------------------------------- public void demarrer(){ // Création d'une fenêtre JFrame fenetre = new JFrame(); this.fenetrePrincipale = fenetre; // On sauvegarde la fenêtre principale de la partie en cours dans une variable. Cela nous permettra de la fermer proprement (avec la méthode .dispose()) quand le joueur voudra lancer une nouvelle partie. // A FAIRE : // placer dans l'instance de l'écran tous les objets graphiques nécessaires // par exemple : ecran.ajouterObjet(demiCerleArriere); this.ecran.ajouterObjet(demiCercleArriere); this.ecran.ajouterObjet(laLigne); this.ecran.ajouterObjet(demiCercleAvant); // on indique que c'est le jeu qui traitera les appuis sur une touche ecran.addKeyListener(this); ecran.setFocusable(true); fenetre.setContentPane(ecran); fenetre.pack(); fenetre.setLocation(100, 100); fenetre.setVisible(true); // s'assurer que l'écran reçoit le focus clavier ecran.requestFocusInWindow(); fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); javax.swing.JButton boutonscores = BoutonsManager.creerBoutonScores(fenetre, utilisateurId); this.ecran.add(boutonscores); // Création du timer horloge = new Timer(40, this); //propriétés dont on aura besoin pour utiliser NiveauxDataConnect //--------------------------------------------------------------------------------- // C'est ici qu'on appelle NiveauxDataConnect pour initialiser la connexion à la base de données des niveaux // On fera une synchronisation pour éviter que le jeu se fige // pendant l'initialisation de la base de données, qui peut prendre un peu de temps //-------------------------------------------------------------------------------- // démarrer le timer sur l'EDT même si l'initialisation BDD a échoué if (horloge != null && !horloge.isRunning()) { chargerNiveau(idNiveauActuel); horloge.start(); } // A FAIRE : // donner la couleur des 2 demi-cercles, par exemple : demiCerleArriere.setCouleur(new Color(0.8f,0.0f,0.0f)); //demiCercleArriere.setCouleur(new Color(26, 95, 161)); //demiCercleAvant.setCouleur(new Color(26, 95, 161)); ecran.ajouterObjet(itemCourant); javax.swing.JButton bscores = BoutonsManager.creerBoutonScores(fenetre, utilisateurId); this.ecran.add(bscores); // On initialise le bouton avec son texte btnRejouer = new javax.swing.JButton("Rejouer"); // Positionnement absolu sur l'écran : (x, y, largeur, hauteur) // On le place vers le centre (x=300), un peu vers le bas (y=400) // pour qu'il apparaisse proprement sous le grand texte "Game Over" btnRejouer.setBounds(300, 400, 200, 50); btnRejouer.setBackground(new java.awt.Color(46, 204, 113)); btnRejouer.setForeground(java.awt.Color.WHITE); btnRejouer.setFont(new java.awt.Font("Arial", java.awt.Font.BOLD, 20)); // TRÈS IMPORTANT : Le bouton est invisible tant que la partie est en cours ! // Il ne s'affichera qu'au moment précis où on perd (ou gagne). btnRejouer.setVisible(false); btnRejouer.addActionListener(e -> { // ÉTAPE 1 : On détruit la fenêtre de la partie actuelle. // Cela libère la mémoire et ferme l'écran du jeu fini. fenetrePrincipale.dispose(); // ÉTAPE 2 : On crée une toute nouvelle instance de Jeu, // On lui repasse l'ID de l'utilisateur pour qu'il reste connecté à son compte. Jeu nouveauJeu = new Jeu(this.utilisateurId); // ÉTAPE 3 : On lance cette nouvelle partie. // Cela va automatiquement créer une nouvelle fenêtre, remettre le score à 0, // et recharger le niveau 1. C'est magique ! nouveauJeu.demarrer(); }); // Enfin, on ajoute physiquement le bouton à notre ZoneDessin (l'écran) this.ecran.add(btnRejouer); } //------------------------------------------------------------------------------------------------ // Méthode permettant de charger le niveau et de l'appliquer //------------------------------------------------------------------------------------------------ public void chargerNiveau(int id){ System.out.println(conn); this.niveauEnCours = NiveauxDataConnect.recupererNiveau(conn, id); if(this.niveauEnCours != null){ // Remise à zéro de l'état propre au niveau this.segmentsTermines = 0; this.dernierSegmentValide = null; this.ecran.partiePerdue = false; // Formule : Gravité de base - (Niveau * incrément) // Niveau 1 : 10.0 + (1 * 1.50) = 8.5 (poids normal) // Niveau 4 : 10.0 + (4 * 1.50) = 4.0 (Très léger et plane) double graviteNiveau = 10.0 - (this.niveauEnCours.getId() * 1.5); // Sécurité: La gravité du niveau ne doit pas descendre en dessous de 2 // Sinon le cercle ne retomberait plus jamais if(graviteNiveau < 2.0){ graviteNiveau = 2.0; } this.demiCercleAvant.setGravite(graviteNiveau); this.demiCercleArriere.setGravite(graviteNiveau); // On corse le jeu avec son ID //On applique les paramètres aux options de jeu this.laLigne.generer(niveauEnCours.getNbSegments(), niveauEnCours.getId()); this.laLigne.setVitesseLigne(niveauEnCours.getVitesseLigne()); // On modifie la vitesse de déplacement this.demiCercleAvant.setCouleur(niveauEnCours.getCouleurCercle()); this.demiCercleArriere.setCouleur(niveauEnCours.getCouleurCercle()); this.ecran.changerImageFond(niveauEnCours.getImage()); this.itemCourant.vitesse = niveauEnCours.getVitesseLigne(); }else{ JFrame fenetre = new JFrame(); JOptionPane.showMessageDialog(fenetre, "CONGRATULATIONS !!! BRAVO VOUS AVEZ TERMINE LE JEU"); System.out.println("Fin du jeu !!! BRAVO VOUS AVEZ TERMINE LE JEU"); horloge.stop(); if (btnRejouer != null) { btnRejouer.setVisible(true); } } } //------------------------------------------------------------------------------------------------ // Méthode permettant de passer au niveau suivant à travers l'id //------------------------------------------------------------------------------------------------ private void passerAuNiveauSuivant(){ // Nettoyage de l'état de validation avant de charger le niveau suivant this.segmentsTermines = 0; this.dernierSegmentValide = null; // On incrémente l'ID du niveau actuel idNiveauActuel++; JFrame fenetre = new JFrame(); // Petite pause avec un message horloge.stop(); JOptionPane.showMessageDialog(fenetre, "Niveau terminé ! Préparez-vous pour le niveau suivant ;)"); chargerNiveau(idNiveauActuel); horloge.start(); } //------------------------------------------------------------------------------------------------ // Méthode appelée lorsqu'un événement timer se produit // -> C'est ici que la loqique et l'animation du jeu sont traitées // -> C'est ici que la vérification de la collision entre la ligne et le cercle doit être faite //------------------------------------------------------------------------------------------------ @Override public void actionPerformed(ActionEvent e) { // 1. Animer et recalculer le segment courant this.ecran.traiterBoucleAnimation(); this.laLigne.actualiserSegCourant(); if (itemCourant.actif){ itemCourant.y = this.laLigne.getYSurLigne(itemCourant.x) - 40; } // Mode triche : Le cercle suit la ligne // On utilisera le calcul pour déterminer si la ligne traverse le cercle if (modeTriche && this.laLigne.getSegCourant() != null) { // double yPoint = this.getSegCourant().y + (this.SegCourant.yLong / this.SegCourant.xLong) * (this.xCercle - this.SegCourant.x); double yPointTricheArrire = this.laLigne.getSegCourant().getY() + (this.laLigne.getSegCourant().getYLong() / this.laLigne.getSegCourant().getXLong()) * (this.demiCercleArriere.getX() - this.laLigne.getSegCourant().getX()); double yPointTricheAvant = this.laLigne.getSegCourant().getY() + (this.laLigne.getSegCourant().getYLong() / this.laLigne.getSegCourant().getXLong()) * (this.demiCercleAvant.getX() - this.laLigne.getSegCourant().getX()); this.demiCercleAvant.y = yPointTricheArrire ; this.demiCercleArriere.y = yPointTricheAvant; } if (itemCourant.estTouche(demiCercleAvant)) { if (itemCourant.estBonus) { chronoBonus = 125; } else { score -= 2000; if (score < 0){ score = 0; } } itemCourant.x = -1000; // On le place à droite de l'écran pour qu'il disparaisse itemCourant.actif = false; // On le désactive pour qu'on ne puisse pas le ramasser en boucle } // Si le bonus est inactif (ramassé) ou s'il est sorti de l'écran par la gauche if (!itemCourant.actif || itemCourant.x < -50) { if (Math.random() < 0.01) { //taux d apparition d'un bonus/malus : 1% à chaque frame // On cherche un segment qui est en train d'arriver par la droite (genre vers X = 800) for (Segment s : this.laLigne.listeSegments) { if (s.x > 750 && s.x < 850) { itemCourant.x = s.x; // On l'accroche au début du segment itemCourant.estBonus = Math.random() > 0.5;// 50% de chance d'être un bonus itemCourant.actif = true; // On le réactive ! break; } } } } // 2. On vérifie si la ligne traverse bien le cercle boolean verification = this.laLigne.estDansCercle(this.demiCercleAvant); // NB: Le nombre de segment s'incrémente qu'il y est collision ou pas if(verification){ // On compte seulement lorsqu'on change de segment Segment segmentCourant = this.laLigne.getSegCourant(); if(segmentCourant != null && segmentCourant != this.dernierSegmentValide){ this.dernierSegmentValide = segmentCourant; this.segmentsTermines++; // Ajouter les points continuellement tant que le segment est vert (cyan) if(chronoBonus>0){ this.score+=(this.niveauEnCours.getMultiplicateurScore()*5);//si le compteur est actif, on ajoute 5 fois le score du niveau }else{ this.score+=this.niveauEnCours.getMultiplicateurScore(); } this.jeuCommence = true; } // Vérification si on a atteint l'objectif du niveau : Nb de Segments du niveau atteint if(this.segmentsTermines >= this.niveauEnCours.getNbSegments()){ passerAuNiveauSuivant(); } }else{ // On compte seulement lorsqu'on change de segment Segment segmentCourant = this.laLigne.getSegCourant(); if(segmentCourant != null && segmentCourant != this.dernierSegmentValide){ this.dernierSegmentValide = segmentCourant; this.segmentsTermines++; } //------------------------------------------------------------------------------------------------------------------- // A FAIRE: Le jeu peut s'arrêter si la ligne n'est pas dans le cercle // NB: Le jeu commence quand la ligne est dans le cercle // Au début le ligne n'est pas dans le cercle car la ligne est à droite du cercle, et elle se déplace vers la gauche //------------------------------------------------------------------------------------------------------------------- if (this.laLigne.getSegCourant()!=null && !this.modeTriche) { // Le jeu s'arrête si le joueur n'a pas traversé la première ligne this.horloge.stop(); // 1. Arrêter le temps this.ecran.partiePerdue = true; // 2. Signaler à l'écran this.btnRejouer.setVisible(true); // 3. Afficher le bouton "Rejouer" this.ecran.repaint(); // 4. Forcer l'affichage du texte GestionBDD.ajouterScore((int)this.score, this.utilisateurId);// enregistrement du score dans la base de donne } } if (chronoBonus>0) { chronoBonus--; //On enlève 1 frame au compteur } labScore.setText("

score : " + this.score + "

"); } }