xxxxxxxxxx
140
// Shooter class to make it easy to add new shooters without having to enter everything manually
class shooter{
constructor(cs,x,y,z,h,v,a){
this.cs=cs // Call-sign
this.x=x // Map W-E-coordinate
this.y=y // Map N-S-coordinate
this.z=z // Shooter height
this.h=h // Target height
this.v=v // Speed
this.a=a // Angle
this.tf=0
}
stat(){
strokeWeight(5)
stroke('black')
point((5500-vej.dist(this.x,this.y))/10,884-(this.z-110)*5)
point((this.x-O[0])*s,(O[1]-this.y)*s)
strokeWeight(1)
text(this.cs,(this.x-O[0])*s-textWidth(this.cs),(O[1]-this.y)*s+15)
text(this.cs,(5500-vej.dist(this.x,this.y))/10-textWidth(this.cs),884-(this.z-110)*5+15)
stroke('red')
//text(this.cs,550-textWidth(this.cs),884-(this.h-110)*5+15)
strokeWeight(2)
point(550,884-(this.h-110)*3)
}
// Ballistic animation, shows the projectile path from the side
ball(t){
strokeWeight(1)
stroke('black')
noFill()
// Here I draw the parametric function r(t)=(x0+v*t,g/2*t^2-v*t+y0), where the x-coordinate is linear and I only take the horizontal speed component, and the y-coordinate is a quadratic with the vertical speed component. 5500 and 850 are just to place the dots below the map, and each pixel represents 10 meters horizontally and 1 meter vertically, where the whole gray area is 300 meters.
point((5500-vej.dist(this.x,this.y)+this.v*cos(this.a)*t)/10,(5*sq(t)-this.v*sin(this.a)*t-this.z+110)*5+884)
}
// Bird's-eye-view animation, shows the projectile from above
bird(t){
stroke("black")
strokeWeight(1)
// If the projectile hasn't hit the target yet, i.e. if the time elapsed is lower than the distance to the target divided by the horizontal speed, I find the current position of the projectile.
if(-5*sq(t)+this.v*sin(this.a)*t+this.z<this.h && t>0.5){
// If the projectile has hit the target I just make a yellow circle there.
fill('yellow')
circle((this.x+vej.n1*this.v*cos(this.a)*this.tf/vej.length-O[0])*s,(O[1]-(this.y+vej.n2*this.v*cos(this.a)*this.tf/vej.length))*s,6)
noFill()
}else{
noFill()
// I take the starting position and add a normal unit vector, (n1,n2)/|(n1,n2)|, multiplied by the horizontal speed and the time to find the current position. At the end I have to multiply by a scaling factor to go from "map-coordinates" to the canvas.
circle((this.x+vej.n1*this.v*cos(this.a)*t/vej.length-O[0])*s,(O[1]-(this.y+vej.n2*this.v*cos(this.a)*t/vej.length))*s,6)
this.tf=t
}
}
}
// I make a line class that has a method that allows me to calculate the distance from a point to it.
class dLine {
constructor(x1, y1, x2, y2, c, w) {
if (w === undefined) {
w = 1;
}
if (c === undefined) {
c = "black";
}
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.width = w;
this.colour = c;
this.n1 = y1 - y2; // Normal x-coordinate
this.n2 = x2 - x1; // Normal y-coordinate
this.c = -this.n1 * x1 - this.n2 * y1; // Line equation constant
this.length = sqrt(sq(this.n1) + sq(this.n2)); // Length of the line/normal vector.
}
draw() {
stroke(this.colour);
strokeWeight(this.width);
line((this.x1-O[0])*s, (O[1]-this.y1)*s, (this.x2-O[0])*s, (O[1]-this.y2)*s);
stroke("black");
strokeWeight(1);
}
// This method measures the distance from a point with coordinates (x,y) to the line
dist(x, y) {
// If the shortest path to the line is to one of the end points, I take the shortest of these distances
if (
max(dist(x, y, this.x1, this.y1), dist(x, y, this.x2, this.y2)) >
this.length
) {
return min(dist(x, y, this.x1, this.y1), dist(x, y, this.x2, this.y2));
} else {
// else I use the distance formula
return abs(this.n1 * x + this.n2 * y + this.c) / this.length;
}
}
}
let A
let T
let B
let G
let img
let t=[0]
let shooters
let O=[59906,60576] // May coordinates of the top-left corner
let w=73723-O[0] // Absolute width of the map segment
let s=584/w // Scale factor from map coordinates to the canvas
let shot=0 // Keeps track of whether I've fired yet
function setup() {
createCanvas(584, 884);
// These are shooters that students from my class came up with
BL=new shooter('Bobby Lee',68408,58037,125,122,308,6.55*PI/180)
BC=new shooter('Big Chungus', 66479,59803,122,124,310,13.9*PI/180)
BS=new shooter('BS',69008,58016,124,122,853,0.4*PI/180)
BG=new shooter('B.I.G',67652,47329,118,119,255,3.1*PI/180)
shooters=[BL,BC,BS,BG]
img = loadImage('Baghold.png')
vej = new dLine(70845,58756,67898,44925) // I define the target road
}
// Clicking the mouse resets the time and starts the firing sequence
function mouseClicked(){
t=[0]
shot=1
}
function draw() {
background(220);
image(img,0,0)
strokeWeight(5)
vej.draw()
if(shot==1){
t.push(t[t.length-1]+1/60)
}
for(i=0;i<shooters.length;i++){
shooters[i].stat()
for(j=0;j<t.length;j++){
shooters[i].ball(t[j])
}
shooters[i].bird(t[t.length-1])
}
//print(-5*sq(t[t.length-1])+BS.v*sin(BS.a)*t[t.length-1]+BS.z)
//print(BS.h)
//print(t[t.length-1])
}