diff --git a/linea/BouleBonus.class b/linea/BouleBonus.class index 6c89d14..acf5e60 100644 Binary files a/linea/BouleBonus.class and b/linea/BouleBonus.class differ diff --git a/linea/BouleBonus.java b/linea/BouleBonus.java index c5014ab..52ae04e 100644 --- a/linea/BouleBonus.java +++ b/linea/BouleBonus.java @@ -15,6 +15,7 @@ public class BouleBonus extends ObjetGraphique { private double amplitudeOndulation = 0.2; private double vitesseHorizontaleLisse = 0.0; private double phaseOndulation = Math.random() * Math.PI * 2; // phase unique par boule + private double sensEvitementLigne = 0.0; private static double clamp(double value, double min, double max) { return Math.max(min, Math.min(max, value)); @@ -35,6 +36,7 @@ public class BouleBonus extends ObjetGraphique { this.rayon = 14; this.amplitudeOndulation = 0.14; this.vitesseHorizontaleLisse = 4.3; + this.sensEvitementLigne = Math.random() < 0.5 ? -1.0 : 1.0; } } @@ -45,15 +47,62 @@ public class BouleBonus extends ObjetGraphique { public boolean isVerte() { return estVerte; } + + public void garantirDistanceLigne(double yLigne, double margePx) { + if (estVerte) { + return; + } + + double distanceMiniCentre = rayon + Math.max(0.0, margePx); + double ecart = y - yLigne; + double distance = Math.abs(ecart); + if (distance >= distanceMiniCentre) { + return; + } + + double sens = (ecart == 0.0) ? sensEvitementLigne : Math.signum(ecart); + y = yLigne + sens * distanceMiniCentre; + vitesseVerticale = 0.0; + + if (y < 20) y = 20; + if (y > 580) y = 580; + } + + public void ajusterVitessePourDistanceLigne(double yLigne, double margePx) { + if (estVerte) { + return; + } + + double distanceCibleCentre = rayon + Math.max(0.0, margePx); + double ecart = y - yLigne; + double distance = Math.abs(ecart); + + + double progressionZone = clamp((560.0 - x) / 240.0, 0.0, 1.0); + if (progressionZone <= 0.0) { + return; + } + + double sens = (ecart == 0.0) ? sensEvitementLigne : Math.signum(ecart); + + // vitesse variable + if (distance > distanceCibleCentre) { + double excedent = distance - distanceCibleCentre; + double pas = clamp(excedent * (0.08 + progressionZone * 0.20), 0.0, 10.0 + progressionZone * 8.0); + y -= sens * pas; + } + + if (y < 20) y = 20; + if (y > 580) y = 580; + } @Override void Afficher(Graphics g) { Graphics2D g2D = (Graphics2D) g; - // Effet de pulsation (le rayon augmente/diminue) - double rayonAffiche = rayon + Math.sin(frameCounter * 0.12) * 1.5; + double rayonAffiche = rayon; - // Dessiner le cercle rempli (centré sur x,y) + // Dessiner le cercle rempli g2D.setColor(couleur); g2D.fillOval((int)(x - rayonAffiche), (int)(y - rayonAffiche), (int)(2 * rayonAffiche), (int)(2 * rayonAffiche)); @@ -67,7 +116,7 @@ public class BouleBonus extends ObjetGraphique { @Override void Animer() { - // Déplacement vers la gauche (même vitesse que la ligne) + // Déplacement vers la gauche x -= vitesse; } @@ -82,19 +131,26 @@ public class BouleBonus extends ObjetGraphique { double vitessePoursuite = estVerte ? 0.075 : 0.050; // meilleure réactivité double limiteVerticale = estVerte ? 1.85 : 1.05; // accélération verticale double amortissement = estVerte ? 0.87 : 0.81; // moins de friction - double zoneMorte = estVerte ? 18.0 : 16.0; // zone mort réduite + double zoneMorte = estVerte ? 18.0 : 16.0; // zone morte double vitesseVerticaleMax = estVerte ? 4.2 : 2.6; // vitesses verticales plus hautes + double proximiteJoueur = clamp((540.0 - x) / 280.0, 0.0, 1.0); + + if (!estVerte) { + // Plus rouge proche du joueur, plus on ouvre la fenêtre d'esquive. + vitessePoursuite *= (1.0 - proximiteJoueur * 0.66); + limiteVerticale *= (1.0 - proximiteJoueur * 0.52); + vitesseVerticaleMax *= (1.0 - proximiteJoueur * 0.40); + zoneMorte += proximiteJoueur * 22.0; + } double vitesseLigneSecurisee = Math.max(0.0, vitesseLigne); double vitesseCible = Math.min(vitesseMax, vitesseLigneSecurisee * facteurHorizontal); if (!estVerte) { - // Les rouges gardent une vitesse plus stable d'une boule à l'autre. vitesseCible = vitesseCible * 0.68 + 4.5 * 0.32; } double deltaVitesse = clamp(vitesseCible - vitesseHorizontaleLisse, -0.25, 0.25); double vitesseMaxLocale = vitesseMax; if (!estVerte && x < 380) { - // En fin d'écran, on plafonne un peu la vitesse pour laisser une fenêtre d'esquive stable. double facteurApproche = clamp((380.0 - x) / 180.0, 0.0, 1.0); vitesseMaxLocale = vitesseMax - (0.55 * facteurApproche); } @@ -103,11 +159,14 @@ public class BouleBonus extends ObjetGraphique { x -= vitesse; double vitesseJoueurLimitee = clamp(cibleVitesseY, -6.0, 6.0); - // Verte: anticipation légère + oscillation. Rouge: AUCUNE anticipation → trajectoire lisible, esquivable. double anticipation = estVerte ? vitesseJoueurLimitee * 3.2 : 0.0; double cibleLisse = cibleY + anticipation + (estVerte ? Math.sin(frameCounter * 0.055 + phaseOndulation) * 45.0 : 0.0); double deltaY = cibleLisse - y; + double ratioEcartY = clamp(Math.abs(deltaY) / 120.0, 0.50, 1.60); + vitessePoursuite *= ratioEcartY; + limiteVerticale *= Math.sqrt(ratioEcartY); + if (Math.abs(deltaY) < zoneMorte) { deltaY = 0; } @@ -136,7 +195,7 @@ public class BouleBonus extends ObjetGraphique { double cRayon = c.getRayon(); double dist = Math.hypot(cx - x, cy - y); - double seuil = estVerte ? (rayon + cRayon - 6.0) : (rayon + cRayon - 9.0); + double seuil = estVerte ? (rayon + cRayon - 6.0) : (rayon + cRayon - 10.5); return dist <= seuil; } diff --git a/linea/Jeu.java b/linea/Jeu.java index 779021f..f6b1202 100644 --- a/linea/Jeu.java +++ b/linea/Jeu.java @@ -355,26 +355,22 @@ public class Jeu implements KeyListener, ActionListener { * Évite de répéter ce code dans demarrer() et resetLevel(). */ private void initialiserPartie() { - // 1. Nettoyage de l'écran ecran.reinitialiser(); - // 2. Création des instances + // Création des instances demiCercleAvant = new Cercle(90, -180); demiCercleArriere = new Cercle(90, 180); ligne = new Ligne(_niv); ecran.setNiveau(_niv); - // 3. En mode immortel, positionner les cercles loin (la force les attirera vers la ligne) - // En mode normal, ils restent au centre (y = 200 par défaut du constructeur) + if (immortel) { demiCercleAvant.y = -300; demiCercleArriere.y = 300; } - // 4. Configuration visuelle appliquerThemeNiveau(); - // 5. Ajout à l'écran (l'ordre définit la superposition) ecran.ajouterObjet(demiCercleArriere); ecran.ajouterObjet(ligne); ecran.ajouterObjet(demiCercleAvant); diff --git a/linea/Ligne.class b/linea/Ligne.class index 96ab1b2..1c5b729 100644 Binary files a/linea/Ligne.class and b/linea/Ligne.class differ diff --git a/linea/Ligne.java b/linea/Ligne.java index 04fb297..f93c263 100644 --- a/linea/Ligne.java +++ b/linea/Ligne.java @@ -44,10 +44,21 @@ public class Ligne extends ObjetGraphique{ double baseAmp = 20; double levelFactor = 10; // coeff pour monter la difficulté double amplitude = baseAmp + niveau * levelFactor; + double centreY = SCREEN_HEIGHT / 2.0; // pente maximale par segment pour rester jouable double maxStep = 40; double rawDy = (Math.random()*2 - 1) * amplitude; + + // Biais de recentrage: plus on s'éloigne du milieu, plus on est ramené vers le centre. + double ecartCentre = centreY - y; + rawDy += ecartCentre * 0.14; + + // Si on est très proche des bords, renforcer le retour vers le milieu. + if (y < VERTICAL_MARGIN + 45 || y > SCREEN_HEIGHT - VERTICAL_MARGIN - 45) { + rawDy += Math.signum(ecartCentre) * 14.0; + } + // limiter la variation brute if (rawDy > maxStep) rawDy = maxStep; if (rawDy < -maxStep) rawDy = -maxStep; diff --git a/linea/ZoneDessin.class b/linea/ZoneDessin.class index dccabbb..60affbc 100644 Binary files a/linea/ZoneDessin.class and b/linea/ZoneDessin.class differ diff --git a/linea/ZoneDessin.java b/linea/ZoneDessin.java index 9c6599b..8fe7c4c 100644 --- a/linea/ZoneDessin.java +++ b/linea/ZoneDessin.java @@ -14,23 +14,23 @@ public class ZoneDessin extends JPanel { private static final long serialVersionUID = 1L; - // un booleen qui permet d'arreter l'animation (suspendre) + // arreter annimation protected boolean estArrete = false; // liste des objets graphiques private ArrayList listeObjets = new ArrayList(); - // indicateur qu'une défaite (perte de contact) est survenue + // indicateur d'une defaite private boolean collisionOccur = false; - // indique si le cercle a déjà été sur la ligne (début de la phase de maintien) - private boolean hadBeenOnLine = false; - // mode immortel - ignore les collisions - private boolean immortel = false; + // indique si le cercle a déjà été sur la ligne + private boolean AEteSurLigne = false; + // mode immortel + private boolean immortel = false; // liste des boules bonus private ArrayList boolesBonus = new ArrayList(); // compteur pour générer les boules à intervalle régulier private int compteurBoule = 0; private int intervalleBouleBase = 165; // moins de boules sur la durée - private int delaiInitialBoule = 97; // apparition initiale avancée de 3% + private int delaiInitialBoule = 85; // apparition initiale légèrement plus tôt private int niveauActuel = 1; private Double dernierJoueurY = null; // type de bonus récupéré (-1 = rouge, 1 = vert, 0 = aucun) @@ -100,7 +100,7 @@ public class ZoneDessin extends JPanel { } dernierJoueurY = joueurY; - // --- 0. Récupérer la ligne pour synchroniser les boules --- + // Récupérer la ligne pour synchroniser les boules Ligne ligneObjet = null; for (ObjetGraphique obj : listeObjets) { if (obj instanceof Ligne) { @@ -109,12 +109,12 @@ public class ZoneDessin extends JPanel { } } - // --- 1. Générer une boule au bord droit sur la ligne --- + // Générer une boule double progression = Math.min(1.0, (niveauActuel - 1) / 25.0); int intervalleBoule = (int) Math.round(intervalleBouleBase - progression * 20.0); // ~165 -> 145 - double probaVerteBase = 0.35 - progression * 0.06; // ~35% -> 29% + double probaVerteBase = 0.23 - progression * 0.04; // ~23% -> 19% double variationAleatoire = (Math.random() - 0.5) * 0.10; // +/- 5% - double probaVerte = Math.max(0.24, Math.min(0.47, probaVerteBase + variationAleatoire + 0.02)); + double probaVerte = Math.max(0.10, Math.min(0.31, probaVerteBase + variationAleatoire)); compteurBoule++; if (compteurBoule >= intervalleBoule && ligneObjet != null) { @@ -124,11 +124,11 @@ public class ZoneDessin extends JPanel { double spawnX = 800; double spawnY; if (estVerte) { - // Vertes: proches de la trajectoire du joueur pour être récupérables. + // boules Vertes double margeVerte = 95.0 + progression * 45.0; spawnY = joueurY + (Math.random() * (2.0 * margeVerte)) - margeVerte; } else { - // Rouges: visent davantage le joueur, mais démarrent avec une marge d'esquive. + // boules Rouges double offset = 220 + progression * 20.0 + Math.random() * 150.0; spawnY = joueurY + (Math.random() < 0.5 ? -offset : offset); } @@ -137,7 +137,7 @@ public class ZoneDessin extends JPanel { if (spawnY > 580) spawnY = 580; if (!estVerte) { - // Marge suffisante pour esquiver, sans rendre les rouges inoffensives. + // Marge suffisante pour esquiver double distanceMin = 125.0; if (Math.abs(spawnY - joueurY) < distanceMin) { if (spawnY >= joueurY) { @@ -148,22 +148,25 @@ public class ZoneDessin extends JPanel { } } - // On crée la boule au bord droit (800) + // On crée la boule au bord droit BouleBonus boule = new BouleBonus(spawnX, spawnY, estVerte); boolesBonus.add(boule); } - // --- 2. Animation des objets --- + // Animation des objets for (ObjetGraphique obj : listeObjets) obj.Animer(); - // Mettre à jour et animer les boules (une seule fois par frame) + // Mettre à jour et animer les boules if (ligneObjet != null) { for (BouleBonus boule : boolesBonus) { boule.animerAvecCible(ligneObjet.getVitesse(), joueurY, vitesseJoueurY); + double yLigneLocal = ligneObjet.getYAuX(boule.getX()); + boule.ajusterVitessePourDistanceLigne(yLigneLocal, 15.0); + boule.garantirDistanceLigne(yLigneLocal, 15.0); } } - // 2. vérifier collision entre la Ligne et le Cercle (une seule passe de segments) + // vérifier collision entre la Ligne et le Cercle if (ligneObjet != null && cercleReference != null) { double[] info = ligneObjet.contactInfo( cercleReference.getX(), cercleReference.getY(), cercleReference.getRayon()); @@ -171,27 +174,27 @@ public class ZoneDessin extends JPanel { boolean enContact = (info != null) && (info[1] <= cercleReference.getRayon()); double ligneY = (info != null) ? info[0] : ligneObjet.getYAuX(cercleReference.getX()); - // Phase initiale en mode immortel : force d'attraction depuis le début - if (!hadBeenOnLine && immortel) { + // force d'attraction depuis le début + if (!AEteSurLigne && immortel) { double delta = ligneY - cercleReference.y; cercleReference.vitesse += delta * 0.12; // Marquer le premier contact if (enContact) { - hadBeenOnLine = true; + AEteSurLigne = true; } - } else if (hadBeenOnLine && immortel) { - // Phase maintenance : après le premier contact, maintenir le cercle sur la ligne + } else if (AEteSurLigne && immortel) { + // après le premier contact, maintenir le cercle sur la ligne double rayon = cercleReference.getRayon(); double limite = ligneY - rayon; double limiteBas = ligneY + rayon; - // Clamping : empêcher le cercle de sortir au-dessus + // empêcher le cercle de sortir au-dessus if (cercleReference.y < limite) { cercleReference.y = limite; if (cercleReference.vitesse < 0) cercleReference.vitesse *= -0.3; } - // Clamping : empêcher le cercle de sortir en-dessous + // empêcher le cercle de sortir en-dessous else if (cercleReference.y > limiteBas) { cercleReference.y = limiteBas; if (cercleReference.vitesse > 0) cercleReference.vitesse *= -0.3; @@ -214,7 +217,7 @@ public class ZoneDessin extends JPanel { } } - // 3. vérifier collisions entre les boules bonus et le cercle + // vérifier collisions entre les boules bonus et le cercle for (int i = boolesBonus.size() - 1; i >= 0; i--) { BouleBonus boule = boolesBonus.get(i); @@ -224,7 +227,7 @@ public class ZoneDessin extends JPanel { if (obj instanceof Cercle) { Cercle c = (Cercle) obj; if (boule.collisionAvec(c)) { - // Si touché : on définit le bonus et on détruit la boule + // Si touché définit le bonus et on détruit la boule bonusRecupere = boule.isVerte() ? 1 : -1; boolesBonus.remove(i); collision = true; @@ -233,15 +236,14 @@ public class ZoneDessin extends JPanel { } } - //4. Détruire la boule si elle dépasse le joueur sans être touchée --- - // Le cercle est à X = 400. Si la boule est à X < 350, elle est "passée". + //Détruire la boule si elle dépasse le joueur sans être touchée + if (!collision && boule.getX() < 320) { boolesBonus.remove(i); - // On ne change pas bonusRecupere ici (donc rien ne se passe) + } } - // 5. on demande à redessiner repaint(); } @@ -274,7 +276,7 @@ public class ZoneDessin extends JPanel { boolesBonus.clear(); estArrete = false; collisionOccur = false; - hadBeenOnLine = false; + AEteSurLigne = false; bonusRecupere = 0; compteurBoule = -delaiInitialBoule; dernierJoueurY = null;