# Help to generate the good number of bullets (tower defense game)

**URL:** <https://discourse.processing.org/t/help-to-generate-the-good-number-of-bullets-tower-defense-game/42787>\
**Category:** Libraries\
**Tags:** homework\
**Created:** [September 19, 2023, 12:53pm UTC](https://discourse.processing.org/t/help-to-generate-the-good-number-of-bullets-tower-defense-game/42787 "2023-09-19T12:53:26Z")\
**Posts on this page:** 1\
**Showing post:** 1

<div class="post-metadata">

**Author:** ![Malcolm0609](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/malcolm0609/32/18792_2.png) [@Malcolm0609](https://discourse.processing.org/u/Malcolm0609)\
**Post date:** [September 19, 2023, 12:53pm UTC](https://discourse.processing.org/t/help-to-generate-the-good-number-of-bullets-tower-defense-game/42787/1 "2023-09-19T12:53:26Z")

</div>

Hello everyone,

as part of my studies, I need to create a small Processing game. I’ve decided to create a tower defense game with a fixed turret that automatically shoots at enemies approaching it. Unfortunately, I’m having an issue with the bullets. When the turret’s firing speed is high, it shoots many more bullets than necessary. I don’t know how to make it fire the correct number of bullets at the right turret firing speed. Can you please advise me on which part of the code to modify to achieve the desired result ?

I think the best way is to calculate the number of ball to kill one enemy. But this code need to see the healt of enemy in each time because the enemy can take domage by other way in the futur.

Sorry i can’t give the asset of the bullet and the tower and enemy.

Thanks in advance! 😀

```auto
import controlP5.*;
ControlP5 cp5;

// Variables pour la tourelle
float tourelleX, tourelleY;
PImage tourelleImage;
float argent = 100;
float vieTourelle = 100;
float vitesseTireTourelle = 100;
float visionTourelle = 500;
int coutAmeliorationVitesse = 10;

// Variables pour les balles de la tourelle
ArrayList<Balle> balles = new ArrayList<Balle>();
float balleRayon = 5;
float balleVitesse = 10;
float balleDegats = 1;
long dernierTir = 0;

// Boutons d'amélioration
Button boutonAmeliorationVitesse;
Button boutonAmeliorationVision;
boolean ameliorationVisionActive = false;
boolean ameliorationVitesseActive = false;

// Variables pour les ennemis
boolean vagueEnCours = false;
int NombreVague = 1;
int ennemisCrees = 0;
int delaiEntreEnnemis = 30;
int nombreTotalEnnemisDansVague = 10;
int etatJeu = 0;
long tempsDerniereVague = 0;
int delaiEntreVagues = 5000;
ArrayList<Ennemi> ennemis = new ArrayList<Ennemi>();

float angleTourelle = 0;

void setup() {
  size(800, 600);
  cp5 = new ControlP5(this);
  tourelleImage = loadImage("Image/weapon/medium/cannon/turret_01_mk1.png");
  initialiserBoutons();
  tourelleY = height - tourelleImage.height;
  tourelleX = width / 2 - tourelleImage.width / 2;
}

void initialiserBoutons() {
  boutonAmeliorationVitesse = cp5.addButton("AméliorerVitesse")
    .setPosition(50, 50)
    .setSize(100, 30)
    .setLabel("Améliorer Vitesse")
    .moveTo("ameliorations");

  boutonAmeliorationVision = cp5.addButton("AméliorerVision")
    .setPosition(50, 80)
    .setSize(100, 30)
    .setLabel("Améliorer Vision")
    .moveTo("ameliorations");
}

void draw() {
  background(0);
  noFill();
  stroke(150, 0, 255);
  ellipse(tourelleX + tourelleImage.width / 2, tourelleY + tourelleImage.height / 2, visionTourelle * 2, visionTourelle * 2);
  fill(255);
  textSize(16);
  textAlign(LEFT, TOP);
  text("Argent : " + argent, 20, 20);
  text("Points de vie de la tourelle : " + vieTourelle, 20, 40);
  text("Vague N°" + NombreVague, 20, 60);

  // Gère les ennemis
  for (int i = ennemis.size() - 1; i >= 0; i--) {
    Ennemi ennemi = ennemis.get(i);
    ennemi.deplacer(tourelleX + tourelleImage.width / 2, tourelleY + tourelleImage.height / 2);
    ennemi.afficher();
    float distance = dist(tourelleX + tourelleImage.width / 2, tourelleY + tourelleImage.height / 2, ennemi.x, ennemi.y);
    if (distance < 70) {
      vieTourelle -= ennemi.degat;
      ennemis.remove(i);
    }
  }

  // Logique lorsque le bouton d'amélioration de la vitesse est cliqué
  if (boutonAmeliorationVitesse.isPressed() && !ameliorationVitesseActive) {
    if (argent >= coutAmeliorationVitesse) {
      argent -= coutAmeliorationVitesse;
      vitesseTireTourelle += 1;
    }
  }

  // Logique lorsque le bouton d'amélioration de la vision est cliqué
  if (boutonAmeliorationVision.isPressed() && !ameliorationVisionActive) {
    if (argent >= coutAmeliorationVitesse) {
      argent -= coutAmeliorationVitesse;
      visionTourelle += 10;
    }
  }

  // Gère les balles de la tourelle
  for (int i = balles.size() - 1; i >= 0; i--) {
    Balle balle = balles.get(i);
    balle.deplacer();
    balle.afficher();

    for (int j = ennemis.size() - 1; j >= 0; j--) {
      Ennemi ennemi = ennemis.get(j);
      float distance = dist(balle.x, balle.y, ennemi.x, ennemi.y);
      if (distance < balleRayon + ennemi.rayon) {
        ennemi.vie -= balleDegats;
        balles.remove(i);
        if (ennemi.vie <= 0) {
          ennemis.remove(j);
          argent += ennemi.recompense;
        }
      }
    }

    if (balle.enDehorsDeLEcran()) {
      balles.remove(i);
    }
  }

  if (vagueEnCours) {
    if (ennemisCrees < nombreTotalEnnemisDansVague) {
      if (frameCount % delaiEntreEnnemis == 0) {
        Ennemi nouvelEnnemi = choisirTypeEnnemi();
        ennemis.add(nouvelEnnemi);
        ennemisCrees++;
      }
    } else {
      if (ennemis.isEmpty()) {
        vagueEnCours = false;
        NombreVague += 1;
        ennemisCrees = 0;
      }
    }
  }

  if (!ennemis.isEmpty()) {
    long maintenant = millis();
    if (maintenant - dernierTir >= vitesseTireTourelle) {
      Ennemi ennemiPlusProche = null;
      float distanceMin = visionTourelle;

      for (Ennemi ennemi : ennemis) {
        float distance = dist(tourelleX + tourelleImage.width / 2, tourelleY + tourelleImage.height / 2, ennemi.x, ennemi.y);
        if (distance < visionTourelle && distance < distanceMin) {
          ennemiPlusProche = ennemi;
          distanceMin = distance;
        }
      }

      if (ennemiPlusProche != null) {
        angleTourelle = atan2(ennemiPlusProche.y - (tourelleY + tourelleImage.height / 2), ennemiPlusProche.x - (tourelleX + tourelleImage.width / 2));
        angleTourelle *= -1;
        tirerTourelle(ennemiPlusProche);
        dernierTir = maintenant;
      }
    }
  }
  gestionTirsTourelle();

  pushMatrix();
  translate(tourelleX + tourelleImage.width / 2, tourelleY + tourelleImage.height / 2);
  rotate(angleTourelle);
  imageMode(CENTER);
  image(tourelleImage, 0, 0);
  popMatrix();

  if (millis() - tempsDerniereVague >= delaiEntreVagues) {
    lancerVague(nombreTotalEnnemisDansVague);
    tempsDerniereVague = millis();
  }

  cp5.draw();
}

// Fonction pour calculer le nombre de balles nécessaires pour tuer un ennemi
int ballesNecessairesPourTuer(float degatsBalle, float vieEnnemi) {
  return ceil(vieEnnemi / degatsBalle);
}

class Ennemi {
  float x, y;
  float vie;
  float degat;
  float recompense;
  float vitesse;
  float rayon;
  PImage design; // Variable pour stocker le design de l'ennemi

  Ennemi(float vie, float degat, float recompense, float vitesse, float rayon, PImage design) {
    this.vie = vie; 
    this.degat = degat;
    this.recompense = recompense;
    this.vitesse = vitesse;
    this.design = design;
    this.rayon = rayon;
    // Position initiale aléatoire en haut de l'écran
    this.x = random(width);
    this.y = random(-100, -50); // Génère les ennemis au-dessus de l'écran

  }

  void afficher() {
    
    // Nouveau calcul de l'angle en utilisant les coordonnées centrées de la tourelle
    float angle = atan2( tourelleY + tourelleImage.height / 2 - y, tourelleX + tourelleImage.width / 2 - x);
    // Utiliser pushMatrix() et translate() pour configurer la rotation
    pushMatrix();
    translate(x, y); // Déplacer l'origine au centre du vaisseau
  
    // Calculer la rotation nécessaire pour faire face au centre de la tourelle
    float rotation = angle + PI/2; // Ajoutez PI/2 pour faire face au centre
  
    rotate(rotation); // Tourner l'image en fonction de l'angle
    imageMode(CENTER);
    image(design, 0, 0, 64, 64); // Afficher l'image du vaisseau au centre (0,0)
    popMatrix(); // Restaurer la matrice de transformation 
  }

  void deplacer(float destinationX, float destinationY) {
    // Calculer l'angle vers la destination (tourelle)
    float angle = atan2(destinationY - y, destinationX - x);
  
    // Déplacez l'ennemi dans la direction de la destination
    x += cos(angle) * vitesse;
    y += sin(angle) * vitesse;
  
    // Réinitialisez la position si l'ennemi sort de l'écran
    if (y > height) {
      x = random(width);
      y = random(-100, -50);
    }
  }
  
}

class EnnemiFaible extends Ennemi {
  PImage[] designs; // Tableau pour stocker les designs
  int designIndex; // Index du design actuel
  EnnemiFaible() {
    super(5, 2, 1, 0.2, 20, null);//vie, degat, recompense, vitesse, rayon
  designs = new PImage[3]; // Créer un tableau pour stocker les designs
    for (int i = 0; i < 3; i++) {
      designs[i] = loadImage("Image/ennemi/Kla'ed/Base/PNGs/Kla'ed_0" + (i + 1) + ".png");
    }
    designIndex = int(random(designs.length)); // Choisir un design aléatoirement
    design = designs[designIndex]; // Utiliser le design choisi
  }

}

class EnnemiMoyen extends Ennemi {
  PImage[] designs; // Tableau pour stocker les designs
  int designIndex; // Index du design actuel
  EnnemiMoyen() {
    super(3, 4, 2, 0.5, 20, null);//vie, degat, recompense, vitesse, rayon
    designs = new PImage[3]; // Créer un tableau pour stocker les designs
    for (int i = 0; i < 3; i++) {
      designs[i] = loadImage("Image/ennemi/Kla'ed/Base/PNGs/Kla'ed_0" + (i + 1) + ".png");
    }
    designIndex = int(random(designs.length)); // Choisir un design aléatoirement
    design = designs[designIndex]; // Utiliser le design choisi
  }

}

Ennemi choisirTypeEnnemi() {
    float rand = random(100); // Génère un nombre aléatoire entre 0 et 100
    if (rand < 60) {
      // Génère un ennemi faible
      return new EnnemiFaible();
    } else if (rand < 101) {
      // Génère un ennemi moyen
      return new EnnemiMoyen();
    } else{
      // Si aucun des cas précédents n'est atteint, renvoyer un ennemi par défaut
      return new Ennemi(10, 2, 5, 1, 20, null); // Vous pouvez ajuster les valeurs par défaut
  }
}

// Modifiez la fonction de tir de la tourelle
void tirerTourelle(Ennemi cible) {
  // Calculez les coordonnées du centre de la tourelle
  float centreTourelleX = tourelleX + tourelleImage.width / 2;
  float centreTourelleY = tourelleY + tourelleImage.height / 2;
  float angle = atan2(cible.y - centreTourelleY, cible.x - centreTourelleX);

  // Ajoutez un décalage de 90 degrés (en radians) pour corriger l'orientation
  angle += radians(90);

  // Assurez-vous que l'angle est compris entre 0 et 2*PI (360 degrés)
  if (angle < 0) {
    angle += TWO_PI;
  }

  // Calcul du nombre de balles nécessaires pour tuer un ennemi
  int ballesNecessaires = calculerBallesNecessaires(cible);

  // Tirer le nombre de balles nécessaires
  for (int j = 0; j < ballesNecessaires; j++) {
    // Affectez l'angle corrigé à la variable d'angle de la tourelle
    angleTourelle = angle;
    // Créez une nouvelle balle à partir du centre de la tourelle
    Balle nouvelleBalle = new Balle(centreTourelleX, centreTourelleY, cible);
    // Ajoutez la nouvelle balle à la liste des balles
    balles.add(nouvelleBalle);
  }
}

int calculerBallesNecessaires(Ennemi cible) {
  int ballesNecessaires = ceil(cible.vie / balleDegats);
  return ballesNecessaires;
}

void lancerVague(int nombreEnnemis) {
  vagueEnCours = true;
  nombreTotalEnnemisDansVague = nombreEnnemis;
}

void gestionTirsTourelle() {
  long maintenant = millis(); // Temps actuel en millisecondes

  // Si la liste des ennemis est vide, réinitialisez le temps du dernier tir
  if (ennemis.isEmpty()) {
    dernierTir = maintenant;
    return;
  }

  // Si le délai entre les tirs est écoulé, tirer sur les ennemis dans la portée
  if (maintenant - dernierTir >= vitesseTireTourelle) {
    for (int i = ennemis.size() - 1; i >= 0; i--) {
      Ennemi ennemi = ennemis.get(i);
      float distance = dist(tourelleX + tourelleImage.width / 2, tourelleY + tourelleImage.height / 2, ennemi.x, ennemi.y);
      if (distance <= visionTourelle) {
        // Calculez le nombre de balles nécessaires pour tuer l'ennemi
        int ballesNecessaires = ballesNecessairesPourTuer(balleDegats, ennemi.vie);

        // Tirer le nombre de balles nécessaires
        for (int j = 0; j < ballesNecessaires; j++) {
          tirerTourelle(ennemi);
        }

        // Mettre à jour le temps du dernier tir
        dernierTir = maintenant;
        break; // Tirer sur le premier ennemi trouvé dans la portée
      }
    }
  }
}

// Classe pour représenter les balles de la tourelle
class Balle {
  float x, y;
  float rayon;
  float angle;
  Ennemi cible;
  PImage imageBalle; // Ajout de la variable PImage pour l'image de la balle

  Balle(float _x, float _y, Ennemi _cible) {
    x = _x;
    y = _y;
    rayon = balleRayon;
    cible = _cible;
    imageBalle = loadImage("Image/Laser_Sprites/15.png"); // Charge l'image de la balle

    angle = atan2(cible.y - y, cible.x - x);
  }

  void deplacer() {
    x += cos(angle) * balleVitesse;
    y += sin(angle) * balleVitesse;
  }

  void afficher() {
    pushMatrix(); // Sauvegarde la matrice de transformation courante
    translate(x, y); // Place la balle à la position (x, y)
    float angle = atan2(cible.y - y, cible.x - x); // Calcule l'angle vers la cible
    rotate(angle); // Applique une rotation en fonction de l'angle
    image(imageBalle, -rayon, -rayon, 24, 24); // Dessine l'image de la balle centrée en (0, 0)
    popMatrix(); // Restaure la matrice de transformation précédente
  }

  boolean enDehorsDeLEcran() {
    return (x < 0 || x > width || y < 0 || y > height);
  }
}

```

---

_[View the full topic](https://discourse.processing.org/t/help-to-generate-the-good-number-of-bullets-tower-defense-game/42787)._
