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!
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);
}
}