Trajectory

back

Notes

Click to fire an artillery shell. Hold to fire multiple shots.

The trajectory is calculated and plotted based on the firing angle, gravity, and initial bullet velocity.

Source

/* @pjs preload="bullet.png, ground.png, explosion.png"; */

PImage bullet;
PImage ground;
PImage explosion;

int bullet_x;
int bullet_y;
float bullet_rotation;

// Define constants
int SHOT_DELAY = 100;
int BULLET_SPEED = 25;
int NUMBER_OF_BULLETS = 20;
int GRAVITY = 1;
float MARCH_SPEED = 40;

float timeOffset = 0;
float correctionFactor = 0.99;

int lastBulletShotAt = 0;

Bullet[] bullets;
ArrayList explosions = new ArrayList();

// Setup the example
void setup(){
 size(640, 480); 

 //Draw images from center
 imageMode(CENTER);

 noStroke();
 
 // Load images
 bullet = loadImage("bullet.png");
 ground = loadImage("ground.png");
 explosion = loadImage("explosion.png");
 
 bullet_x = 50;
 bullet_y = height - 64;
 
 // Create an object pool of bullets
 bullets = new Bullet[NUMBER_OF_BULLETS];
 for(int i = 0; i < NUMBER_OF_BULLETS; i++){
   // Create each bullet and add it to the group.
   bullets[i] = new Bullet(bullet);
 }
 
}

// The draw() method is called every frame
void draw(){
  // Set stage background color
  background(#4488cc);
  
  // Create some ground, adjust because of imageMode(CENTER)
  for(int i = 0; i < width; i += ground.width){
     image(ground, i + 16, height - ground.height/2); 
  }
  
  // Detects if mousebutton is down
  if(mousePressed){
    // Shoot a bullet
    shootBullet(); 
  }
  
  // Aim the gun at the pointer.
  bullet_rotation = atan2(mouseY-bullet_y, mouseX-bullet_x);
  
  for(Bullet b : bullets){
    b.update();
    b.display();
    
    //Check collision with ground
    if(b.alive && rectangleCollision(b.x, b.y, b.img.width, b.img.height, 0, height - ground.height/2, width, ground.height) ){
      explosions.add(new Spritesheet(explosion, 4, b.x, b.y));
      b.kill();
    }
  }
  
  ArrayList deadExplosions = new ArrayList();
  for(Spritesheet s : explosions){
    s.display();
    if(s.isDead()){
      deadExplosions.add(s);
    }
  }
  for(Spritesheet s : deadExplosions){
    explosions.remove(s);
  }
  
  // Create an object representing our gun
  pushMatrix();
  translate(bullet_x, bullet_y);
  rotate(bullet_rotation);
  image(bullet, 0, 0);
  popMatrix();
  
  drawTrajectory();
}

void drawTrajectory(){
   timeOffset = (timeOffset + 1) % MARCH_SPEED;
   
   float theta = -bullet_rotation;
   int t_x = 0;
   int t_y = 0;
   for(float t = timeOffset / MARCH_SPEED; t < 100; t += 1){
     t_x = (int)(BULLET_SPEED * t * cos(theta) * correctionFactor);
     t_y = (int)(BULLET_SPEED * t * sin(theta) * correctionFactor - 0.5 * GRAVITY * t * t);
     ellipse(t_x + bullet_x, bullet_y - t_y, 3, 3);
     if(t_y < -15) break;
   }
}

void shootBullet(){
  // Enforce a short delay between shots by recording
  // the time that each bullet is shot and testing if
  // the amount of time since the last shot is more than
  // the required delay.
  if(millis() - lastBulletShotAt < SHOT_DELAY) return;
  lastBulletShotAt = millis();
  
  for(Bullet b : bullets){
    // Get a dead bullet from the pool
    if(!b.alive){
      
      // Revive the bullet
      // This makes the bullet "alive"
      b.revive();
      
      // Set the bullet position to the gun position.
      b.reset(bullet_x, bullet_y);
      
      b.rotation = bullet_rotation;
      b.x_velocity = cos(b.rotation) * BULLET_SPEED;
      b.y_velocity = sin(b.rotation) * BULLET_SPEED;
      break;
    }
  }
}

// Detects a single mouseclick
void mousePressed(){
  // Shoot a bullet
  //shootBullet(); 
}

//Detects collision between two rectangular objects
boolean rectangleCollision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2){
  return !(x1 > x2 + w2 || x1 + w1 < x2 || y1 > y2 + h2 || y1 + h1 < y2);
}

class Bullet{
  int x;
  int y;
  float x_velocity;
  float y_velocity;
  boolean alive;
  PImage img;
  float rotation;
  
  Bullet(PImage img){
   this.img = img; 
  }
  
  void display(){
    if(alive){
      pushMatrix();
      translate(x,y);
      rotate(rotation);
      image(img, 0, 0);
      popMatrix();
    } 
  }
  
  void update(){
    if(alive){
     x += x_velocity;
     y += y_velocity;
     
     y_velocity += GRAVITY;
     
     rotation = atan2(y_velocity, x_velocity);
     
     // Bullets should kill themselves when they leave the world.
     if(x > width || x < 0 || y > height || y < 0){
      kill(); 
     }
    }
  }
  
  void revive(){
   alive = true; 
  }
  
  void reset(int x, int y){
     this.x = x;
     this.y = y;
  }
  
  void kill(){
    alive = false;
  }
}

class Spritesheet{
 PImage[] frames;
 float currentFrame;
 int x;
 int y;
 
 Spritesheet(PImage img, int noFrames, int x, int y){
  int frameWidth = img.width / noFrames;
  frames = new PImage[noFrames];
  for(int i = 0; i < frames.length; i++){
    frames[i] = img.get(frameWidth * i, 0, frameWidth, img.height);  
  }
  
  this.x = x;
  this.y = y;
 }
 
 void display(){
   currentFrame = (currentFrame + 0.5);
   if(currentFrame < frames.length){
     image(frames[(int)currentFrame], x, y);
   }
 }
 
 boolean isDead(){
   return currentFrame >= frames.length;
 }
 
}

Warning: Cannot load module "http" because required module "raphf" is not loaded in Unknown on line 0