init: finish implementation niveau dans le jeu

This commit is contained in:
2026-03-24 22:32:59 +01:00
parent f8cf66f731
commit ca614a198b
19 changed files with 329 additions and 82 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,20 @@
import java.awt.Image;
//-----------------------------------------------------------------------------
// Classe permettant de définir pour moi ce qu'est un fond d'écran pour mon jeu
//-----------------------------------------------------------------------------
public class Background {
private Image image;
public Background(Image image){
this.image = image;
}
public Image getImage(){
return image;
}
}

View File

@@ -3,9 +3,10 @@ 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.SwingUtilities;
import javax.swing.JOptionPane;
import javax.swing.Timer;
public class Jeu implements KeyListener, ActionListener{
@@ -14,8 +15,10 @@ public class Jeu implements KeyListener, ActionListener{
//-------------------------------------------------------------------------
// Ecran : instance de ZoneDessin, qui contiendra tous les objets graphiques
// On ne vapas 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 = new ZoneDessin();
protected ZoneDessin ecran;
// Le cercle : en fait 2 demis-cercles, un qui passera sous la ligne
// un qui passera dessus
@@ -41,6 +44,15 @@ public class Jeu implements KeyListener, ActionListener{
protected boolean modeTriche = false;
// Propriété de ma connexion à ma base de données
Connection conn = null;
// 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;
//-------------------------------------------------------------------------
// METHODES
//-------------------------------------------------------------------------
@@ -49,6 +61,39 @@ public class Jeu implements KeyListener, ActionListener{
// Constructeur de la classe
//-------------------------------------------------------------------------
public Jeu(){
JFrame fenetre = new JFrame();
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) {
}
}
this.ecran = new ZoneDessin(premierFond);
// Gestion du score : a réactiver en fin de TP, inutile au début
ecran.setLayout(null);
@@ -123,6 +168,7 @@ public class Jeu implements KeyListener, ActionListener{
// création de diverses instances et
//-------------------------------------------------------------------------
public void demarrer(){
// Création d'une fenêtre
JFrame fenetre = new JFrame();
@@ -191,31 +237,73 @@ public class Jeu implements KeyListener, ActionListener{
// 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
// On utilise un thread synchrone pour ne pas bloquer le thread principal du jeu
//--------------------------------------------------------------------------------
// Initialisation de la BDD dans un thread séparé ;
// démarrer le timer quoiqu'il arrive pour ne pas figer l'UI
new Thread(() -> {
try {
NiveauxDataConnect niveau = new NiveauxDataConnect();
System.out.println("Connexion réussie !!!");
// démarrer le timer sur l'EDT même si l'initialisation BDD a échoué
SwingUtilities.invokeLater(() -> {
if (horloge != null && !horloge.isRunning()) {
horloge.start();
}
});
} catch (Exception ex) {
ex.printStackTrace();
System.err.println("Erreur lors de l'initialisation de la connexion à la base de données : " + ex.getMessage());
}
}).start();
// 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));
//demiCercleArriere.setCouleur(new Color(26, 95, 161));
//demiCercleAvant.setCouleur(new Color(26, 95, 161));
}
//------------------------------------------------------------------------------------------------
// 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;
// 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());
}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();
}
}
//------------------------------------------------------------------------------------------------
// 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();
}
@@ -226,14 +314,39 @@ public class Jeu implements KeyListener, ActionListener{
//------------------------------------------------------------------------------------------------
@Override
public void actionPerformed(ActionEvent e) {
// 1. Animer et recalculer le segment courant
this.ecran.traiterBoucleAnimation();
this.laLigne.actualiserSegCourant();
// 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){
// Ajouter les points continuellement tant que le segment est vert (cyan)
this.score += 5;
this.jeuCommence = true;
// 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)
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

View File

@@ -24,6 +24,12 @@ public class Ligne extends ObjetGraphique{// Hérite de la classe ObjetGraphique
// Stockage des segments afin de pouvoir les afficher et les animer plus tard
protected ArrayList<Segment> listeSegments = new ArrayList<>();
// propriété de la vitesse de base de déplace de la ligne
protected int vitesseLigne = 5;
// On définit les limites (plafond et sol)
protected int plafond = 50; // Empêche de monter trop haut (Y=0 est le bord haut)
protected int sol = 550; // Empêche de descendre trop bas (Y=600, est le bas)
//-------------------------------------------------------------------------
// METHODES
@@ -33,27 +39,75 @@ public class Ligne extends ObjetGraphique{// Hérite de la classe ObjetGraphique
// Constructeur de la classe
//-------------------------------------------------------------------------
public Ligne(){
}
// Méthode pour (RE)générer la ligne
public void generer(int nbSegments, int difficulte){
// On vide la ligne précédente
this.listeSegments.clear();
// Valeurs initiales pour la position de la Ligne
double x = 800;
double y = 200;
double dx,dy;
// Plus le niveau est grand plus dy peut-être grand (pics plus abrupts)
double amplitude = 30 * difficulte;
// On réduit la largeur des segments pour "serrer" les pics
// Plus dx est petit, plus les pics sont proches les uns des autres
double largeurMin = 100 - (difficulte * 15);
// Création de la liste de segments qui composent la ligne
Segment s;
for (int i=0; i<nbSegments; i++){
// définition d'un nouveau segment
dx = Math.random()*20+80;
dy = Math.random()*40-20;
dx = Math.random()*20 + largeurMin;
dy = Math.random()*(amplitude*2) - amplitude;
// Vérification plafond de l'écran
if(y + dy < plafond){
// On force le segment à s'arréter à 50
dy = plafond - y;
}
// Vérification sol de l'écran
if(y + dy > sol){
// Si ça dépasse en bas, on force le segment à s'arrêter à 550
dy = sol - y;
}
s = new Segment(x,y,dx,dy);
s.setCouleur(new Color(0.2f,0.2f,0.2f));
//Ajouter un segment dans la ligne
this.listeSegments.add(s);
x+=dx;
y+=dy;
x += dx;
y += dy;
}
// On remet le segment courant à zéro afin de ne pas avoir un retard sur un cycle
// Et repartir proprement
this.SegCourant = null;
}
//Méthode set pour modifier la vitesse de déplacement la ligne
public void setVitesseLigne(int new_vitesse){
this.vitesseLigne = new_vitesse;
}
// ------------------------------------------------------------------------
// Méthode pour réactualiser le segment courant
// ------------------------------------------------------------------------
public void actualiserSegCourant(){
this.SegCourant = null;
for(Segment s: this.listeSegments){
if( (xCercle >= s.x) && ( xCercle <= (s.x + s.xLong) ) ){
this.SegCourant = s;
break;
}
}
}
//-------------------------------------------------------------------------
// Redéfinition de la méthode Afficher, spécifiquement pour la classe Ligne
@@ -64,19 +118,10 @@ public class Ligne extends ObjetGraphique{// Hérite de la classe ObjetGraphique
// On définit l'épaisseur du trait
Graphics2D g2D = (Graphics2D) g;
g2D.setStroke(new BasicStroke(3.0f));
this.actualiserSegCourant();
for(Segment s: this.listeSegments){
s.Afficher(g);
if(SegCourant == null){
if( (xCercle >= s.x) && ( xCercle <= (s.x + s.xLong) ) ){
this.SegCourant = s;
}
}else{
if( (this.SegCourant.x + this.SegCourant.xLong) < xCercle ){
if( (s.x <= xCercle) && ((s.x + s.xLong) >= xCercle) ){
this.SegCourant = s;
}
}
}
}
}
@@ -92,7 +137,7 @@ public class Ligne extends ObjetGraphique{// Hérite de la classe ObjetGraphique
// vers la gauche
// On modifiera cette méthode en fonction du niveau de jeu
for(Segment s: this.listeSegments){
s.x -= 5;
s.x -= this.vitesseLigne;
}
}

View File

@@ -1,6 +1,6 @@
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
@@ -11,6 +11,8 @@ public class NiveauxDataConnect {
//Création d'une variable de connexion à la base de données
private static final String url = "jdbc:sqlite:Niveaux.db";
private static boolean tablesCreees = false; // Variable pour vérifier si les tables ont été créées
private static NiveauxDataConnect instance; // Instance unique de la classe NiveauDataConnect
private static Connection maConnexion; // Objet connexion à partager
@@ -23,45 +25,68 @@ public class NiveauxDataConnect {
// Il est nécessaire de l'initialiser afin de pouvoir le lire et écrire dessus
// Car dans la .jar il est en lecture seule
// Il faut donc le copier dans un endroit où il peut être modifié, par exemple dans le dossier du projet
// Mon constructeur est en privé car personne ne doit pouvoir faire new NiveauDataConnect
// ----------------------------------------------------------------
public NiveauxDataConnect() throws ClassNotFoundException, SQLException {
private NiveauxDataConnect() throws ClassNotFoundException, SQLException {
// Initialisation de la connexion à la base de données
Connection conn = getConnection();
if (conn != null) {
System.out.println("Connexion à la base de données réussie !");
maConnexion = getConnection();
if (maConnexion != null) {
System.out.println("Instance du singleton créée et connexion prête.");
} else {
System.out.println("Connexion échouée !");
}
}
//--------------------------------------------------------------------------
// Point d'accès global pour obtenir l'instance du singleton
// On utilise synchronized pour éviter les problèmes si deux threads appellent
// la méthode au démarrage en même temps
//--------------------------------------------------------------------------
public static synchronized NiveauxDataConnect getInstance(){
try{
if(instance == null){
instance = new NiveauxDataConnect();
}
return instance;
}catch(Exception e){
e.printStackTrace();
}
return instance;
}
// -------------------------------------------------------------------------
// Méthode de connexion à la base de données Niveaux.db
// -------------------------------------------------------------------------
public static Connection getConnection(){
public static Connection getConnection() throws SQLException{
// On vérifie si ma connexion, singleton existe déjà et à déjà une valeur
// Ou si elle a été fermé
if(maConnexion == null || maConnexion.isClosed()){
// On vérifiera si la BDD est déjà créé et possède déjà des données à l'intérieur
// On devra ainsi l'extraire depuis le .jar
boolean baseExiste = bddInit.init();
try{
Connection conn = DriverManager.getConnection(url);
System.out.println("Connexion à la base de données réussie !");
if(!baseExiste){
// Vérifier si les tables ont déjà été créées pour éviter de les recréer à chaque connexion
if (!tablesCreees) {
// Créer la table niveau si elle n'existe pas déjà
CreerTableNiveaux(conn);
tablesCreees = true; // Marquer les tables comme créées
// Insérer les niveaux dans la table niveau
insererNiveau(conn);
boolean baseExiste = bddInit.init();
try{
maConnexion = DriverManager.getConnection(url);
System.out.println("Connexion à la base de données réussie !");
if(!baseExiste){
// Vérifier si les tables ont déjà été créées pour éviter de les recréer à chaque connexion
if (!tablesCreees) {
// Créer la table niveau si elle n'existe pas déjà
CreerTableNiveaux(maConnexion);
tablesCreees = true; // Marquer les tables comme créées
// Insérer les niveaux dans la table niveau
insererNiveau(maConnexion);
}
}
}
return conn;
}catch(Exception e){
e.printStackTrace();
return maConnexion;
}catch(Exception e){
e.printStackTrace();
}
}
return null;
return maConnexion;
}
@@ -97,6 +122,7 @@ public class NiveauxDataConnect {
// Les niveaux sont connus à l'avance et sont insérés directement
// dans la base de données pour éviter de les insérer à chaque fois que
// le jeu est lancé
// Pour corser le jeu, on utilisera l'id du niveau qui nous servira de difficulté
// --------------------------------Niveau 1 --------------------------------
// nom = "Facile", vitesse_ligne = 5, nb_segments = 50, image = "images\ciel.jpeg", multiplicateur_score = 1, couleur_cercle = "bleu"
// --------------------------------Niveau 2 --------------------------------
@@ -137,17 +163,20 @@ public class NiveauxDataConnect {
Niveau niveauRecup = null;
String sql = "SELECT * FROM niveau WHERE id = ?;";
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
int id1 = rs.getInt("id");
String nom = rs.getString("nom");
int vitesse_ligne = rs.getInt("vitesse_ligne");
int nb_segments = rs.getInt("nb_segments");
String image = rs.getString("image");
int multiplicateur_score = rs.getInt("multiplicateur_score");
String couleur_cercle = rs.getString("couleur_cercle");
niveauRecup = new Niveau(id1, nom, vitesse_ligne, nb_segments, image, multiplicateur_score, couleur_cercle);
try (PreparedStatement pstmt = conn.prepareStatement(sql)){
pstmt.setInt(1, id); // On remplace le 1er ? par l'id
try(ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
int id1 = rs.getInt("id");
String nom = rs.getString("nom");
int vitesse_ligne = rs.getInt("vitesse_ligne");
int nb_segments = rs.getInt("nb_segments");
String image = rs.getString("image");
int multiplicateur_score = rs.getInt("multiplicateur_score");
String couleur_cercle = rs.getString("couleur_cercle");
niveauRecup = new Niveau(id1, nom, vitesse_ligne, nb_segments, image, multiplicateur_score, couleur_cercle);
}
}
} catch (SQLException e) {

View File

@@ -51,4 +51,8 @@ abstract class ObjetGraphique {
public void setCouleur(Color c){
couleur = c;
}
public void setCouleur(String colString){
couleur = Color.decode(colString);
}
}

View File

@@ -3,8 +3,11 @@ import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ZoneDessin extends JPanel { // hérite d'une classe du frameWork standard
@@ -22,6 +25,12 @@ public class ZoneDessin extends JPanel { // hérite d'une classe du frameWork st
// Pour permettre à traiterBoucleAnimation() de parcourir les objets sans distinction pour les animer
protected ArrayList<ObjetGraphique> listeObjets = new ArrayList<>();
// Variable pour l'image
protected Image imageDeFond;
// Variable pour stocker l'objet Background au lieu d'une simple image
protected Background fondActuel;
//-------------------------------------------------------------------------
// METHODES
//-------------------------------------------------------------------------
@@ -29,16 +38,40 @@ public class ZoneDessin extends JPanel { // hérite d'une classe du frameWork st
//-------------------------------------------------------------------------
// Constructeur de la classe
//-------------------------------------------------------------------------
public ZoneDessin(){
public ZoneDessin(Background fondInitial){
// on prépare la zone d'affichage
setLayout(new BorderLayout());
setPreferredSize(new Dimension(800, 600));
setBackground(new Color(220,170,0));
this.fondActuel = fondInitial;
}
//Redefinition de la méthode setBcakground grâce à ma classe Background
public void setBackground(Background nouveauFond){
this.fondActuel = nouveauFond;
// On redessine le fond
this.repaint();
}
// Méthode pour modifier l'image de fond depuis la classe Jeu
public void changerImageFond(String cheminDansSrc){
try {
java.net.URL url = getClass().getResource("/" + cheminDansSrc); // On cherche l'image depuis src
if(url != null){
java.awt.Image img = ImageIO.read(url);
this.fondActuel = new Background(img);
this.repaint(); // On redessine le fond
}else{
System.err.println("URL introuvable pour : " + cheminDansSrc);
}
} catch (IOException e) {
e.printStackTrace();
}
}
//-------------------------------------------------------------------------
// Ajout d'un objet graphique à la zonde de dessin
// Ajout d'un objet graphique à la zone de dessin
//-------------------------------------------------------------------------
public void ajouterObjet(ObjetGraphique unObjet) {
this.listeObjets.add(unObjet);
@@ -93,6 +126,9 @@ public class ZoneDessin extends JPanel { // hérite d'une classe du frameWork st
super.paintComponent(g);
// Puis on ajoute ce qui est spécifique à la classe
if(fondActuel != null && fondActuel.getImage() != null){
g.drawImage(fondActuel.getImage(), 0, 0, getWidth(), getHeight(), this);
}
// on indique qu'il faut de l'antialiasing
Graphics2D g2D = (Graphics2D) g;

Binary file not shown.