4. Oktober 2012
Von den Physik-Libraries etwas enttäuscht, habe ich mich entschlossen, die "Physik", bzw. die Kollisionsabfragen für mein Spiel selber zu programmieren. Da ich viel Zeit verloren habe, musste ich mich etwas beeilen. Spielidee/Konzept Ich wollte das Spiel Pong verändern und die Spielfläche auf einen Kreis projezieren. Zwei Spieler müssen dafür sorgen, dass der Ball nie den Rand erreicht. In der Mitte wirkt eine Gravitationskraft dafür, dass der Ball abgelenkt wird und der Einschlagort schwer vorauszusehen ist. Die Gravitation wechselt zwischen anziehend und abstossend. Bedienungskonzept Die Idee wäre gewesen, dass das Spiel schlussendlich mit Kinect gespielt werden kann und die Paddles durch Kreisbewegung der Hand, bzw. Hände gesteuert werden kann. Zur Umsetzung bin ich leider nicht mehr gekommen. Entwicklung Den Grossteil der Zeit habe ich damit verbracht, den Ball physikalisch korrekt abprallen zu lassen und damit verbundene Fehler zu flicken. Ich hatte lange "Tunneling"-Effekte und der Ball verliess das Spielfeld. Erst durch das Zurückverschieben des Balles ins Spielfeld nach der Kollision (mit seeeehr viel Sinus-Kosinus-Kram!! 🙁 ) hats geklappt. Hier ein paar Beispiele der Prototypen: Endergebniss Für das Endspiel habe ich noch ein paar Hintergrundgrafiken erstellt, um das ganze grafisch noch etwas ansprechend zu gestalten. Aufgrund der Gravitation habe ich als Thema was Sternen-Starwars-Space-Mässiges gewählt. Leider bin ich nicht mehr dazu gekommen, das Spiel im Multiplayer-Modus umzusetzen und im Moment ist es einfach Singleplayer. (Survival-Mode) Hauptmenü Screen Game-Over Screen Hauptspiel mit anziehender Gravitation Projekt zum Download Download hier Codebeispiele Initialisierung von Spiel und Gravityprivate void initGame() { ball = new Ball(0,0, 25); ball.set(new PVector(width/2, height/2+200), new PVector(random(-3,3),-1)); paddleLeft = new Paddle(20, 0, 360, 270, 30); gravityDirection = 0; if(gravityController != null) { gravityController.quit(); } gravityController = new TimeController(2000,6000,THREAD_MODE_GRAVITY_CONTROL); gravityController.start(); gameStartTime = millis(); }Auszug aus der Kollisionsabfrage
private void checkMovementAndGravity() { PVector vDistFromCenter = PVector.sub(pos, centerPos); float r = vDistFromCenter.mag(); float K = 2500.0; if(gravityDirection < 0) { K = 1800; } float a = K / sq(r); PVector gravityVector = PVector.sub(centerPos,pos); gravityVector.setMag(a); gravityVector.mult(gravityDirection); if(r > minGravityRadius && r < maxGravityRadius) { dir.add(gravityVector); } if(r > 150 && dir.mag() > 20) { dir.setMag(8); } pos.add(dir); } private void checkEdgeCollision() { PVector vDistFromCenter = PVector.sub(pos, centerPos); if(vDistFromCenter.mag() > gameCircleRadius-radius) { if(checkCollision) { speed = START_SPEED; checkCollision = false; checkPaddleCollisionBlocker = true; paddleCollisionBlockTime = millis(); bounceOff(vDistFromCenter, gameCircleRadius); stopGame(); } } else { checkCollision = true; } } private void checkPaddleCollision() { PVector vDistFromCenter = PVector.sub(pos, centerPos); float ballAngleFromCenter = calculateBallAngleFromCenter(vDistFromCenter); if(millis() - paddleCollisionBlockTime > 200) { checkPaddleCollisionBlocker = false; } float paddleStart = paddleLeft.angle; if(ballAngleFromCenter < radians(30)) { paddleStart = paddleStart - radians(360); } float paddleEnd = paddleLeft.angle + radians(paddleLeft.lengthAngle); if(vDistFromCenter.mag() > paddleRadius-radius) { println("start " + degrees(paddleStart)); println("end " + degrees(paddleEnd)); println("degree " + degrees(ballAngleFromCenter) + "\n"); if(ballAngleFromCenter > paddleStart && ballAngleFromCenter < paddleEnd) { if(checkPaddleCollision && !checkPaddleCollisionBlocker) { bounceOff(vDistFromCenter, paddleRadius); checkPaddleCollision = false; speed += 0.2; } } } else { checkPaddleCollision = true; } } private void bounceOff(PVector vDistFromCenter, float borderRadius) { float entryAngle = PVector.angleBetween(dir,vDistFromCenter); float bounceAngle = entryAngle * 2; if(bounceAngle < radians(10)) { bounceAngle = radians(15); } else if(bounceAngle > radians(60)) { bounceAngle = radians(50); } float Rgoal = borderRadius - radius; float Rnow = vDistFromCenter.mag(); float part2 = (sq(Rnow) * sq(cos(entryAngle))) - sq(Rnow) + sq(Rgoal); float offset = (Rnow * cos(entryAngle)) - sqrt(part2); PVector correctionMove = dir.get(); correctionMove.setMag(offset); correctionMove.mult(-1); pos.add(correctionMove); float orientierung = dir.x * vDistFromCenter.y - dir.y * vDistFromCenter.x; rotate2D(dir,bounceAngle * (orientierung > 0 ? 1 : -1)); dir.x *=-1; dir.y *=-1; dir.setMag(speed); } private void rotate2D(PVector v, float theta) { float m = v.mag(); float a = v.heading2D(); a += theta; v.x = m * cos(a); v.y = m * sin(a); } private float calculateBallAngleFromCenter(PVector vDistFromCenter) { float ballAngleFromCenter = 0; float ballAngle = PVector.angleBetween(vDistFromCenter,new PVector(1,0)); if(pos.y < height/2) { ballAngleFromCenter = radians(360) - ballAngle; } else { ballAngleFromCenter = ballAngle; } return ballAngleFromCenter; } void draw() { calcPos(); PVector vDistFromCenter = PVector.sub(pos, centerPos); float drawAngle = PVector.angleBetween(dir,new PVector(0,-1)); pushMatrix(); translate(pos.x,pos.y); fill(255); image(backgroundImage,-50,-50); popMatrix(); //drawDebugLines(); }