Created
January 27, 2015 13:16
-
-
Save CSchoel/db5fcd307862f6482697 to your computer and use it in GitHub Desktop.
A cannon game with realistic ballistics including wind and a moving target
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Autor: Christopher Schölzel | |
//Klasse für die Kanonenkugel | |
class CannonBall { | |
float x,y; //Position der Kugel | |
float vx,vy; //Geschwindigkeit auf der x- bzw. y-Achse | |
float d; //Durchmesser der Kugel | |
float dragCoefficient = 0.001; //Luftwiderstand | |
Environment env; | |
CannonBall(float x, float y, float vx, float vy, float d, Environment env) { | |
this.x = x; | |
this.y = y; | |
this.vx = vx; | |
this.vy = vy; | |
this.d = d; | |
this.env = env; | |
} | |
void display() { | |
fill(0); | |
ellipse(x,y,d,d); //Kugel zeichnen | |
} | |
//Luftwiderstand berechnen | |
void applyDrag() { | |
float relVx = vx-env.getWindVx(); //relative Geschwindigkeit der Kugel zum Wind | |
float dragX = dragCoefficient*pow(relVx,2); | |
float dragY = dragCoefficient*pow(vy,2); | |
//Richtung des Widerstands verläuft entgegengesetzt zur Bewegung | |
if(relVx > 0) dragX *= -1; | |
if(vy > 0) dragY *= -1; | |
vx += dragX; | |
vy += dragY; | |
} | |
void move() { | |
if(env.hasDrag()) { | |
applyDrag(); | |
} | |
vy += env.getGravity(); //Gravitation als Beschleunigung in y-Richtung | |
x += vx; //gleichförmige Bewegung in x-Richtung | |
y += vy; //aktualisierte Geschwindigkeit in y-Richtung anwenden | |
} | |
float getX() { | |
return x; | |
} | |
float getY() { | |
return y; | |
} | |
} | |
//Zielscheibe | |
class Target { | |
float x,y; //Position des Ziels | |
float d; //Durchmesser des Ziels | |
float md; //Minimale Distanz, den die Kugel zum Ziel erreicht hat | |
int currentPoints; | |
int totalPoints; | |
float vy = 1; //Geschwindigkeit in y-Richtung | |
Target (float x, float y, float d) { | |
this.x = x; | |
this.y = y; | |
this.d = d; | |
currentPoints = 0; | |
totalPoints = 0; | |
md = Float.POSITIVE_INFINITY; | |
} | |
void move() { | |
y += vy; | |
if (y <= d*2 || y > height-d*2) vy *= -1; | |
} | |
void display() { | |
//vier konzentrische Kreise zeichnen mit einer for-Schleife | |
for(int i =0; i < 4; i++) { | |
float diam = d*(4-i)/4; //Kreisdurchmesser | |
stroke(0); | |
if (md < diam/2.0) { | |
fill(255,0,0); //Rot zeichnen, falls der Kreis erreicht wurde | |
} else { | |
fill(255); | |
} | |
ellipse(x,y,diam,diam); | |
} | |
} | |
//prüft ob der übergebene Ball die Zielscheibe getroffen hat und berechnet Punkte | |
void registerHit(CannonBall ball) { | |
float dist = sqrt(pow(ball.getX()-x,2)+pow(ball.getY()-y,2)); | |
if (dist < md) md = dist; | |
float ringDiam = d/2.0/4.0; | |
currentPoints = max(0,4-floor(md/ringDiam)); | |
} | |
//bereitet diese Zielscheibe für den nächsten Schuss vor (entfernt Treffermarkierung) | |
void reset() { | |
totalPoints += currentPoints; | |
currentPoints = 0; | |
md = Float.POSITIVE_INFINITY; | |
} | |
int getPoints() { | |
return totalPoints + currentPoints; | |
} | |
} | |
//Kanone | |
class Cannon { | |
float d = 15; //Durchmesser des Kanonenrohrs | |
float rot = -45; //Winkel der Kanone | |
float x,y; //Position der Kanone | |
float vx,vy; //Geschwindigkeit, die eine abgefeuerte Kugel bekommen würde | |
boolean loaded = true; //gibt an, ob die Kanone feuern kann | |
int shotsFired = 0; | |
Environment env; | |
Cannon(float x, float y, float d, Environment e) { | |
this.x = x; | |
this.y = y; | |
this.d = d; | |
this.env = e; | |
} | |
// Zeichnet eine Ziellinie von der Kanone zum Cursor | |
void drawAim() { | |
stroke(255,0,0); | |
line(x,y,x+vx*25,y+vy*25); | |
} | |
void display() { | |
drawAim(); | |
stroke(0); | |
fill(0); | |
ellipse(x,y+10,20,20);//Rad der Kanone | |
pushMatrix(); //alte Transformationswerte speichern | |
translate(x,y); //Ursprung des Koordinatensystems verschieben | |
rotate(radians(rot)); //Rotation um den (neuen) Ursprung | |
rect(-25,-d/2,50,d); //Kanonenrohr zeichnen | |
popMatrix(); //alte Transformationswerte wiederherstellen | |
} | |
//richtet die Kanone auf den Zielpunkt aus und bestimmt Schussstärke | |
void aimFor(float x, float y) { | |
float dx = max(0,x-this.x); | |
float dy = min(0,y-this.y); | |
float ang = degrees(atan(1.0*dy/dx)); | |
vx = dx/50.0; | |
vy = dy/50.0; | |
rot = ang; | |
} | |
//feuert die Kanone ab und gibt eine Kugel zurück | |
CannonBall fire() { | |
if(!loaded) return null; | |
loaded = false; | |
shotsFired++; | |
return new CannonBall(x,y,vx,vy,d-5,env); | |
} | |
int getShotsFired() { | |
return shotsFired; | |
} | |
boolean canFire() { | |
return loaded; | |
} | |
void reload() { | |
loaded = true; | |
} | |
} | |
//Klasse für Umgebungsvariablen (Gravitation, Windgeschwindigkeit) | |
class Environment { | |
float g = 0.05; //Gravitation | |
float windVx; | |
boolean hasDrag; //soll Luftwiderstand berechnet werden? | |
Environment(float g) { | |
this.g = g; | |
hasDrag = false; | |
} | |
Environment(float g, float windVx) { | |
this.g = g; | |
this.windVx = windVx; | |
hasDrag = true; | |
} | |
float getGravity() { | |
return g; | |
} | |
float getWindVx() { | |
return windVx; | |
} | |
void setWindVx(float vx) { | |
windVx = vx; | |
} | |
//true wenn Luftwiderstand berechnet werden muss/soll | |
boolean hasDrag() { | |
return hasDrag; | |
} | |
} | |
Target target; | |
Cannon cannon; | |
Environment env; | |
//Liste mit abgefeuerten Bällen. | |
//Im moment ist die Liste leer oder enthält einen Ball. | |
//Erweiterung zu Kanonen mit mehr Schuss pro Runde ist aber möglich. | |
ArrayList<CannonBall> balls = new ArrayList<CannonBall>(); | |
void setup() { | |
size(600,400); //Größe des Anzeigefensters | |
//Startpunkte festlegen für Kugel, Ziel und Kanone | |
env = new Environment(0.05,random(-3,3)); | |
target = new Target(width-50,100,50); | |
cannon = new Cannon(50,350,15,env); | |
} | |
void draw() { | |
background(255); //Zeichenfläche zurücksetzen | |
target.move(); | |
target.display(); | |
cannon.display(); | |
for(CannonBall ball : balls) { | |
ball.move(); | |
ball.display(); | |
println(ball.vx,ball.vy); | |
target.registerHit(ball); | |
} | |
int shots = cannon.getShotsFired(); | |
int points = target.getPoints(); | |
float pps = 1.0*points/shots; | |
fill(0); | |
textAlign(RIGHT); | |
textSize(20); | |
text("Schüsse: "+shots+" Punkte: "+points+" Durchschnitt: "+nf(pps,1,2),width-10,20); | |
text(cannon.canFire() ? "geladen" : "ungeladen (nachladen: Leertaste)",width-10,height-20); | |
textAlign(LEFT); | |
text("Wind: "+nf(env.getWindVx(),1,2),10,20); | |
} | |
void keyPressed() { | |
if(key == ' ') { | |
//Leertaste für neuen Schuss | |
cannon.reload(); | |
target.reset(); | |
balls.clear(); | |
env.setWindVx(random(-3,3)); | |
} | |
} | |
void mousePressed() { | |
//Kanone wird mit Mausklick abgefeuert, wenn sie bereit ist | |
if(cannon.canFire()) { | |
balls.add(cannon.fire()); | |
} | |
} | |
void mouseMoved() { | |
//Zielen immer dann, wenn die Maus sich bewegt | |
cannon.aimFor(mouseX,mouseY); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment