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

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! :grinning:

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

so basically when one enemy dies, his health is 0.

So when his health is 60 and one bullet causes 5 damage you need to fire
60/5 = 12 bullets.

Fire 13 just to be sure.

Timer

you can use a timer to fire one bullet (of the 13) every 0.9 seconds (for example)
similar to this timer:

    long maintenant = millis();
    if (maintenant - dernierTir >= vitesseTireTourelle) {

(sorry, I don’t know French, so I don’t know what this is)

Chrisir

1 Like

Hello Chrisir,

Indeed, it is more convenient to use English in programming, so I have changed the variables and comments for easier understanding.

I completely agree with your explanation regarding the number of bullets.

I tried to create a timer, the one you mentioned in your response. However, it doesn’t seem to work. I’m not sure what to modify to make it work. It feels like when I use this code, all the bullets are fired at once (like all 13 bullets overlapping, for example). I attempted to integrate a timer in the draw function when creating the bullets, but there was no change.

import controlP5.*;
ControlP5 cp5;

// Variables for the turret
float turretX, turretY;
PImage turretImage;
float money = 100;
float turretLife = 100;
float turretFireSpeed = 100;
float turretVision = 500;
int speedUpgradeCost = 10;

// Variables for turret bullets
ArrayList<Bullet> bullets = new ArrayList<Bullet>();
float bulletRadius = 5;
float bulletSpeed = 10;
float bulletDamage = 1;
long lastShot = 0;

// Upgrade buttons
Button speedUpgradeButton;
Button visionUpgradeButton;
boolean visionUpgradeActive = false;
boolean speedUpgradeActive = false;

// Variables for enemies
boolean waveInProgress = false;
int WaveNumber = 1;
int enemiesSpawned = 0;
int timeBetweenEnemies = 30;
int totalEnemiesInWave = 10;
int gameState = 0;
long lastWaveTime = 0;
int timeBetweenWaves = 5000;
ArrayList<Enemy> enemies = new ArrayList<Enemy>();

float turretAngle = 0;

void setup() {
  size(800, 600);
  cp5 = new ControlP5(this);
  turretImage = loadImage("Image/weapon/medium/cannon/turret_01_mk1.png");
  initializeButtons();
  turretY = height - turretImage.height;
  turretX = width / 2 - turretImage.width / 2;
}

void initializeButtons() {
  speedUpgradeButton = cp5.addButton("UpgradeSpeed")
    .setPosition(50, 50)
    .setSize(100, 30)
    .setLabel("Upgrade Speed")
    .moveTo("upgrades");

  visionUpgradeButton = cp5.addButton("UpgradeVision")
    .setPosition(50, 80)
    .setSize(100, 30)
    .setLabel("Upgrade Vision")
    .moveTo("upgrades");
}

void draw() {
  background(0);
  noFill();
  stroke(150, 0, 255);
  ellipse(turretX + turretImage.width / 2, turretY + turretImage.height / 2, turretVision * 2, turretVision * 2);
  fill(255);
  textSize(16);
  textAlign(LEFT, TOP);
  text("Money: " + money, 20, 20);
  text("Turret Life: " + turretLife, 20, 40);
  text("Wave #" + WaveNumber, 20, 60);

  // Handle enemies
  for (int i = enemies.size() - 1; i >= 0; i--) {
    Enemy enemy = enemies.get(i);
    enemy.move(turretX + turretImage.width / 2, turretY + turretImage.height / 2);
    enemy.display();
    float distance = dist(turretX + turretImage.width / 2, turretY + turretImage.height / 2, enemy.x, enemy.y);
    if (distance < 70) {
      turretLife -= enemy.damage;
      enemies.remove(i);
    }
  }

  // Logic when speed upgrade button is pressed
  if (speedUpgradeButton.isPressed() && !speedUpgradeActive) {
    if (money >= speedUpgradeCost) {
      money -= speedUpgradeCost;
      turretFireSpeed += 1;
    }
  }

  // Logic when vision upgrade button is pressed
  if (visionUpgradeButton.isPressed() && !visionUpgradeActive) {
    if (money >= speedUpgradeCost) {
      money -= speedUpgradeCost;
      turretVision += 10;
    }
  }

  // Handle turret bullets
  for (int i = bullets.size() - 1; i >= 0; i--) {
    Bullet bullet = bullets.get(i);
    bullet.move();
    bullet.display();

    for (int j = enemies.size() - 1; j >= 0; j--) {
      Enemy enemy = enemies.get(j);
      float distance = dist(bullet.x, bullet.y, enemy.x, enemy.y);
      if (distance < bulletRadius + enemy.radius) {
        enemy.life -= bulletDamage;
        bullets.remove(i);
        if (enemy.life <= 0) {
          enemies.remove(j);
          money += enemy.reward;
        }
      }
    }

    if (bullet.isOutOfScreen()) {
      bullets.remove(i);
    }
  }

  if (waveInProgress) {
    if (enemiesSpawned < totalEnemiesInWave) {
      if (frameCount % timeBetweenEnemies == 0) {
        Enemy newEnemy = chooseEnemyType();
        enemies.add(newEnemy);
        enemiesSpawned++;
      }
    } else {
      if (enemies.isEmpty()) {
        waveInProgress = false;
        WaveNumber++;
        enemiesSpawned = 0;
      }
    }
  }

  if (!enemies.isEmpty()) {
    long now = millis();
    if (now - lastShot >= turretFireSpeed) {
      Enemy closestEnemy = null;
      float minDistance = turretVision;

      for (Enemy enemy : enemies) {
        float distance = dist(turretX + turretImage.width / 2, turretY + turretImage.height / 2, enemy.x, enemy.y);
        if (distance < turretVision && distance < minDistance) {
          closestEnemy = enemy;
          minDistance = distance;
        }
      }

      if (closestEnemy != null) {
        turretAngle = atan2(closestEnemy.y - (turretY + turretImage.height / 2), closestEnemy.x - (turretX + turretImage.width / 2));
        turretAngle *= -1;
        fireTurret(closestEnemy);
        lastShot = now;
      }
    }
  }
  manageTurretShots();

  pushMatrix();
  translate(turretX + turretImage.width / 2, turretY + turretImage.height / 2);
  rotate(turretAngle);
  imageMode(CENTER);
  image(turretImage, 0, 0);
  popMatrix();

  if (millis() - lastWaveTime >= timeBetweenWaves) {
    launchWave(totalEnemiesInWave);
    lastWaveTime = millis();
  }

  cp5.draw();
}

// Function to calculate the number of bullets needed to kill an enemy
int bulletsNeededToKill(float bulletDamage, float enemyLife) {
  return ceil(enemyLife / bulletDamage);
}

class Enemy {
  float x, y;
  float life;
  float damage;
  float reward;
  float speed;
  float radius;
  PImage design;

  Enemy(float life, float damage, float reward, float speed, float radius, PImage design) {
    this.life = life;
    this.damage = damage;
    this.reward = reward;
    this.speed = speed;
    this.design = design;
    this.radius = radius;
    this.x = random(width);
    this.y = random(-100, -50);
  }

  void display() {
    float angle = atan2(turretY + turretImage.height / 2 - y, turretX + turretImage.width / 2 - x);
    pushMatrix();
    translate(x, y);
    float rotation = angle + PI/2;
    rotate(rotation);
    imageMode(CENTER);
    image(design, 0, 0, 64, 64);
    popMatrix();
  }

  void move(float destinationX, float destinationY) {
    float angle = atan2(destinationY - y, destinationX - x);
    x += cos(angle) * speed;
    y += sin(angle) * speed;
    if (y > height) {
      x = random(width);
      y = random(-100, -50);
    }
  }
}

class WeakEnemy extends Enemy {
  PImage[] designs;
  int designIndex;
  WeakEnemy() {
    super(5, 2, 1, 0.2, 20, null);
    designs = new PImage[3];
    for (int i = 0; i < 3; i++) {
      designs[i] = loadImage("Image/enemy/Kla'ed/Base/PNGs/Kla'ed_0" + (i + 1) + ".png");
    }
    designIndex = int(random(designs.length));
    design = designs[designIndex];
  }
}

class MediumEnemy extends Enemy {
  PImage[] designs;
  int designIndex;
  MediumEnemy() {
    super(3, 4, 2, 0.5, 20, null);
    designs = new PImage[3];
    for (int i = 0; i < 3; i++) {
      designs[i] = loadImage("Image/enemy/Kla'ed/Base/PNGs/Kla'ed_0" + (i + 1) + ".png");
    }
    designIndex = int(random(designs.length));
    design = designs[designIndex];
  }
}

Enemy chooseEnemyType() {
  float rand = random(100);
  if (rand < 60) {
    return new WeakEnemy();
  } else if (rand < 101) {
    return new MediumEnemy();
  } else {
    return new Enemy(10, 2, 5, 1, 20, null);
  }
}

void fireTurret(Enemy target) {
  float turretCenterX = turretX + turretImage.width / 2;
  float turretCenterY = turretY + turretImage.height / 2;
  float angle = atan2(target.y - turretCenterY, target.x - turretCenterX);
  angle += radians(90);
  if (angle < 0) {
    angle += TWO_PI;
  }
  int bulletsRequired = calculateBulletsRequired(target);
  for (int j = 0; j < bulletsRequired; j++) {
    turretAngle = angle;
    Bullet newBullet = new Bullet(turretCenterX, turretCenterY, target);
    bullets.add(newBullet);
  }
}

int calculateBulletsRequired(Enemy target) {
  int bulletsRequired = ceil(target.life / bulletDamage);
  return bulletsRequired;
}

void launchWave(int numberOfEnemies) {
  waveInProgress = true;
  totalEnemiesInWave = numberOfEnemies;
}

void manageTurretShots() {
  long now = millis();
  if (enemies.isEmpty()) {
    lastShot = now;
    return;
  }
  if (now - lastShot >= turretFireSpeed) {
    for (int i = enemies.size() - 1; i >= 0; i--) {
      Enemy enemy = enemies.get(i);
      float distance = dist(turretX + turretImage.width / 2, turretY + turretImage.height / 2, enemy.x, enemy.y);
      if (distance <= turretVision) {
        int bulletsRequired = bulletsNeededToKill(bulletDamage, enemy.life);
        for (int j = 0; j < bulletsRequired; j++) {
          fireTurret(enemy);
        }
        lastShot = now;
        break;
      }
    }
  }
}

class Bullet {
  float x, y;
  float radius;
  float angle;
  Enemy target;
  PImage bulletImage;

  Bullet(float _x, float _y, Enemy _target) {
    x = _x;
    y = _y;
    radius = bulletRadius;
    target = _target;
    bulletImage = loadImage("Image/Laser_Sprites/15.png");
    angle = atan2(target.y - y, target.x - x);
  }

  void move() {
    x += cos(angle) * bulletSpeed;
    y += sin(angle) * bulletSpeed;
  }

  void display() {
    pushMatrix();
    translate(x, y);
    float angle = atan2(target.y - y, target.x - x);
    rotate(angle);
    imageMode(CENTER);
    image(bulletImage, -radius, -radius, 24, 24);
    popMatrix();
  }

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

Thank you for your response.

Malcolm

here is a timer example - see fire() in class Player

press and hold the mouse down to shoot within the game


// ********************************************************************************
//         joined pde-file of folder  ShootTest3
// ********************************************************************************


/* OpenProcessing Tweak of *@*http://www.openprocessing.org/sketch/77863*@* */
/* !do not delete the line above, required for linking your tweak if you upload again */

// overall structure: The tab ShootTest1 is the main program.
// A player (only a white rectangle)
// shoots bullets (in class Bullet).
// The player can only shoot upwards, follows mouse, click & hold mouse.
//
// idea from
// http://forum.processing.org/topic/need-help-adding-shooting
// with help from
// http://forum.processing.org/topic/have-eyes-follow-mouse
//

// Player
Player thePlayer = new Player();

boolean mouseDown=false;

// ------------------------------------------------------------------

void setup() {
  size(600, 480);
  cursor(CROSS);
}

void draw() {
  background(111);

  thePlayer.draw();
  if (mouseDown)
    thePlayer.fire();
}

void mousePressed() {
  // thePlayer.fire();
  mouseDown=true;
}

void mouseReleased() {
  // thePlayer.fire();
  mouseDown=false;
}

void keyPressed() {
  thePlayer.fire();
}

// ================================================================
// Bullet - Simple class for bullets
// http://www.processing.org/learning/topics/arraylistclass.html

class Bullet {

  float x;
  float y;
  float speedX;
  float speedY;
  float widthBullet;
  float life = 255;

  Bullet(float tempX, float tempY,
    float tempSpeedX, float tempSpeedY,
    float tempWidthBullet) {
    x = tempX;
    y = tempY;
    widthBullet = tempWidthBullet;
    speedX = tempSpeedX;
    speedY = tempSpeedY;
  }

  void move() {
    // Add speed to location
    x = x + speedX;
    y = y + speedY;
    //
    // kill bullet when outside screen
    if (x<4)
      life=-1;
    if (y<4)
      life=-1;
    if (x>width-4)
      life=-1;
    if (y>width-4)
      life=-1;
    //
  } // method

  boolean finished() {
    // bullet dead?
    if (life < 0) {
      return true;
    } else {
      return false;
    }
  }

  void display() {
    // Display the circle
    fill(244, 2, 2);
    noStroke();
    ellipse(x, y,
      widthBullet, widthBullet);
  }
} // class

// =====================================================================
// the Player class

class Player {

  ArrayList<Bullet> bullets;
  PVector position, velocity; // contains x and y...

  // bullets
  int lastFired=millis();  // timer to determine when next bullet starts
  final float bulletFlySpeed = 4.2;  // how fast bullet flies
  final int fireSpeed=200; // how often you fire / distance between bullets

  Player() {
    bullets  = new ArrayList();
    position = new PVector(333, 333);
    velocity = new PVector();
    velocity.x = 0;
    velocity.y = 0;
  }

  void fire() {     // new2
    if (fireSpeed<=millis()-lastFired) {

      // ammoCount--;
      lastFired=millis();

      float xSpeed ;
      float ySpeed ;
      float angle = update(mouseX, mouseY);
      xSpeed = cos(angle);
      ySpeed = sin(angle);
      xSpeed*= bulletFlySpeed;
      ySpeed*= bulletFlySpeed;
      if (ySpeed>0)
        ySpeed=0;
      bullets.add ( new Bullet(
        position.x+12, position.y-14,
        xSpeed, ySpeed,
        5 ));
    } // if
  } // method

  float update(int mx, int my) {
    //determines angle to mouse
    float angle = atan2(float(my)-(position.y-14), float(mx)-(position.x+12));
    return angle;
  }

  void draw() {
    // draw player & bullets
    //
    // draw player
    pushMatrix();
    translate(position.x, position.y);
    noStroke();
    fill(255);
    rect (0, 0, 20, 40);
    popMatrix(); // undoes all

    // draw bullets
    for (Bullet currentBullet : bullets ) {
      currentBullet.move();
      currentBullet.display();
    }

    // With an array, we say length, with an ArrayList, we say size()
    // The length of an ArrayList is dynamic
    // Notice how we are looping through the ArrayList backwards
    // This is because we are deleting elements from the list
    for (int i = bullets.size()-1; i >= 0; i--) {
      // An ArrayList
      Bullet currentBullet = bullets.get(i);
      if (currentBullet.finished()) {
        // Items can be deleted with remove()
        bullets.remove(i);
      }//if
    }//for
  }//method
}//class
// End of joined file. ****************************************************************************

1 Like

Hello,

I understand. I will try to start from the same base to understand the general functioning.

Because I can’t integrate this into my code.

Thank you so much.

1 Like