Skip to content

Instantly share code, notes, and snippets.

@CSchoel
Created January 27, 2015 13:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CSchoel/db5fcd307862f6482697 to your computer and use it in GitHub Desktop.
Save CSchoel/db5fcd307862f6482697 to your computer and use it in GitHub Desktop.
A cannon game with realistic ballistics including wind and a moving target
//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