Willkommen auf unserem Seminar-Blog

Immer auf dem aktuellen Stand bleiben

Dieser Seminar-Blog befindet sich noch im Aufbau und wird in den kommenden Tagen entsprechend verfeinert.

Member Login

Lost your password?

Registration is closed

Sorry, you are not allowed to register by yourself on this site!

You must either be invited by one of our team member or request an invitation by email at viad.info {at} zhdk {dot} ch.

Finale: Breaking out of “Breakout”!

5. Oktober 2012

Seventh and Eighth Day: For the end of the two weeks of programming, we got to "program" the game "Breakout". The title of this blog entry practically means that we had to alter the game in a sense that it wouldn't be like the original version anymore (not only the graphics). Due to the previous 2-3 assignments, we were able to get a good head start in this task. Although I had a lot of ideas of how I wanted my game to develop into, it was difficult to convert what I had in mind into the reality because of the lack of knowledge in processing. But the problem didn't end there: There were times where altering a minor part could cause a whole chain reaction throughout the code and it would not work because of careless mistakes in the syntax. It took time to experiment as well as modify the game. For the game play: I took off the barricade from the upper side of the window and made another racket which was parallel to the first and had quite a distance from it too (a bit like a sandwich-principle). How to win: Destroy all the bars given. How to lose (not that it's hard to): When the ball flies out of the upper/bottom side of the window. If the ball hits the left/right side, it'll just bounce off, so no worries there. Surprises: If the racket sped up too fast to hit the ball, the ball might not bounce off the racket but pass through it instead (actually not intended but it's a similar problem with the previous assignment). As for the design, I came up with personifying the racket, ball and bars. The racket and the ball would be the protagonists (with knowledge of martial arts apparently) and the bars would be the minions of an evil Overlord (if there were be a boss after multiple levels). But as for now, it seems to be the other way around: Due to the missing Overlord, it seems that the protagonists are the evil ones, beating up the bars for no reason. The conclusion: I feel quite mischievous while playing the game the way it is now. The "Breakout" game:

import ddf.minim.*;

Minim minim;
AudioSample ballHitSnd;
AudioSample ballHitSchlaegerSnd;
AudioSample ballHitSchlaeger2Snd;
BouncingBall ball;
Racket schlaeger = null;
Racket schlaeger2 = null;
Brick[] brickList = null;
int pointCount = 0;
PImage[] imageList = null;
PFont font;

final static int GAME_MODE_MENU = 0; //final static = can't be changed anymore
final static int GAME_MODE_PLAY = 1;
final static int GAME_MODE_GAMEOVER = 2;
final static int GAME_MODE_WIN = 3;

int gameMode = GAME_MODE_MENU;
void setup()
{
 size(800, 600);

 imageList = new PImage[6];
 imageList[0] = loadImage("./data/RACKET.jpg");
 imageList[1] = loadImage("./data/BALL.png");
 imageList[2] = loadImage("./data/PLAYBACK.jpg");
 imageList[3] = loadImage("./data/START.jpg");
 imageList[4] = loadImage("./data/YOUWIN.jpg");
 imageList[5] = loadImage("./data/GAMEOVER.jpg");

ball = new BouncingBall(width/2, height/3, 3);

 schlaeger = new Racket(0, height - 40, 70, 10);
 schlaeger2 = new Racket(0, height, 70, 10);
 // start den sound
 minim = new Minim(this);
 ballHitSnd = minim.loadSample("hit.mp3");
 ballHitSchlaegerSnd = minim.loadSample("hitSchlaeger.mp3");
 ballHitSchlaeger2Snd = minim.loadSample("hitSchlaeger.mp3");
 smooth();
 noCursor();
}

void initGameScene()
{
 ball.set(new PVector(width/2, height/2), new PVector(random(-7, 7), -7), 1);
 // erzeuge die ziegel
 int Zeile = 2;
 int Spalte = 5;

 brickList = new Brick[Zeile*Spalte];

 int xZ = 300;
 int yZ = 300;

 for (int a = 0; a < Zeile; a++)
 {
 for (int i = 0; i < brickList.length/Zeile;i++)
 {
 brickList[i+(brickList.length/Zeile*a)] = new Brick(xZ , yZ, 50, 20);
 xZ += 55;
 }

 xZ = 80;
 yZ += 25;
 }
}
 // erzeuge ziegel
// brickList = new Brick[(int)random(3, 12)];
// for (int i=0;i < brickList.length;i++)
// {
// brickList[i] = new Brick(200 + i * 55, 200, 50, 20); //i is the distance between the bricks
// }
//}

void draw()
{
 // ghosting

 fill(0, 0, 0, 255);
 rect(0, 0, width, height);

// update schlaeger pos
 schlaeger.move(mouseX, mouseY-200);
 schlaeger2.move(mouseX, mouseY+200);

 

drawScene();
}

void drawScene()
{
 switch(gameMode)
 {
 case GAME_MODE_MENU:
 drawMenu();
 break;
 case GAME_MODE_PLAY:
 drawGame();
 break;
 case GAME_MODE_GAMEOVER:
 drawGameOver();
 break;
 case GAME_MODE_WIN:
 drawWin();
 break;
 }
}

void drawMenu()
{
 pushStyle();
 fill(255);
// text("BreakOut", 350, 200);
//
// text("\'s\' : start game", 350, 230);
// text("Space : stop game", 350, 250);
 image(imageList[3], 0, 0, width, height);
 popStyle();
}

void drawGameOver()
{
 pushStyle();
 fill(150);

 image(imageList[5], 0, 0, width, height);
 font = loadFont("AmericanTypewriter-48.vlw");
 textFont(font,40);
 text(pointCount, width-200, height-30);
 popStyle();
}
void drawGame()
{
 // berechne kollisionen
 updateScene();

 pushStyle();
 fill(255);
 image(imageList[2], 0, 0, width, height);
 font = loadFont("AmericanTypewriter-48.vlw");
 textFont(font,20);
 text("Score:" + pointCount,700,50);
 popStyle();

// zeichne ziegel
 for (int i=0;i < brickList.length;i++)
 {
 if (brickList[i] != null)
 brickList[i].draw();
 }

// zeichne den ball
 ball.draw();

// zeichne schlaeger
 schlaeger.draw();
 schlaeger2.draw();
}

void keyPressed()
{
 switch(key)
 {
 case 's':
 // start game
 gameMode = GAME_MODE_PLAY;
 initGameScene();
 pointCount = 0;
 break;
 case ' ':
 // end
 gameMode = GAME_MODE_MENU;
 break;
 }
}

void updateScene()
{
 // test of ball aus der scene
 if (ball._pos.y > height- ball._r || ball._pos.y < 0+ ball._r)
 {
 gameMode = GAME_MODE_GAMEOVER;
 return;

 }
 if (pointCount == brickList.length)
 {
 gameMode = GAME_MODE_WIN;
 return;
 }

 // update schlaeger pos
 schlaeger.move(mouseX, mouseY);

PVector hitPos = new PVector();
 float hitV;
 float speed;

 // teste ob ball den schlaeger trifft
 int retVal = schlaeger.checkCollision(ball._pos.x, ball._pos.y, ball._r, ball._dir,hitPos);
 switch(retVal)
 {
 case Brick.HIT_LEFT:
 // aendere flugrichtung ball
 ball._dir.x *= -1;

 // setzte ball buendig auf den schlaeger(das er nicht im schlaeger ist)
 ball._pos.x = schlaeger._pos.x - schlaeger._w/2 - ball._r;

 // spiele sound
 ballHitSchlaegerSnd.trigger();
 break;
 case Brick.HIT_RIGTH:
 // aendere flugrichtung ball
 ball._dir.x *= -1;

 // setzte ball buendig auf den schlaeger(das er nicht im schlaeger ist)
 ball._pos.x = schlaeger._pos.x + schlaeger._w/2 + ball._r;

// spiele sound
 ballHitSchlaegerSnd.trigger();
 break;
 case Brick.HIT_TOP:
 // aendere flugrichtung ball

 // anschnitt berechnen
 hitV = hitPos.x - (schlaeger._pos.x - schlaeger._w/2);
 hitV = 1.0 / schlaeger._w * hitV - 0.5;

 speed = ball._dir.mag();

ball._dir.x += hitV * 8;
 ball._dir.y *= -1;

 // ball soll die gleiche geschwindigkeit haben wie am anfang
 ball._dir.normalize();
 ball._dir.mult(speed);

 // setzte ball buendig auf den schlaeger(das er nicht im schlaeger ist)
 ball._pos.y = schlaeger._pos.y - schlaeger._h/2 - ball._r ;

 // spiele sound
 ballHitSchlaegerSnd.trigger();
 break;
 case Brick.HIT_BOTTOM:
 // aendere flugrichtung ball

 // anschnitt berechnen
 hitV = hitPos.x - (schlaeger._pos.x - schlaeger._w/2);
 hitV = 1.0 / schlaeger._w * hitV - 0.5;

 speed = ball._dir.mag();

ball._dir.x += hitV * 8;
 ball._dir.y *= -1;

 // ball soll die gleiche geschwindigkeit haben wie am anfang
 ball._dir.normalize();
 ball._dir.mult(speed);

 // setzte ball buendig auf den schlaeger(das er nicht im schlaeger ist)
 ball._pos.y = schlaeger._pos.y + schlaeger._h/2 + ball._r ;

// spiele sound
 ballHitSchlaegerSnd.trigger();
 break;
 }

 retVal = schlaeger2.checkCollision(ball._pos.x, ball._pos.y, ball._r, ball._dir,hitPos);
 switch(retVal)
 {
 case Brick.HIT_LEFT:
 // aendere flugrichtung ball
 ball._dir.x *= -1;

 // setzte ball buendig auf den schlaeger(das er nicht im schlaeger ist)
 ball._pos.x = schlaeger2._pos.x - schlaeger2._w/2 - ball._r;

 // spiele sound
 ballHitSchlaeger2Snd.trigger();
 break;
 case Brick.HIT_RIGTH:
 // aendere flugrichtung ball
 ball._dir.x *= -1;

 // setzte ball buendig auf den schlaeger(das er nicht im schlaeger ist)
 ball._pos.x = schlaeger2._pos.x + schlaeger2._w/2 + ball._r;

// spiele sound
 ballHitSchlaeger2Snd.trigger();
 break;
 case Brick.HIT_TOP:
 // aendere flugrichtung ball

 // anschnitt berechnen
 hitV = hitPos.x - (schlaeger2._pos.x - schlaeger2._w/2);
 hitV = 1.0 / schlaeger._w * hitV - 0.5;

 speed = ball._dir.mag();

ball._dir.x += hitV * 8;
 ball._dir.y *= -1;

 // ball soll die gleiche geschwindigkeit haben wie am anfang
 ball._dir.normalize();
 ball._dir.mult(speed);

 // setzte ball buendig auf den schlaeger(das er nicht im schlaeger ist)
 ball._pos.y = schlaeger2._pos.y - schlaeger2._h/2 - ball._r ;

 // spiele sound
 ballHitSchlaeger2Snd.trigger();
 break;
 case Brick.HIT_BOTTOM:
 // aendere flugrichtung ball

 // anschnitt berechnen
 hitV = hitPos.x - (schlaeger2._pos.x - schlaeger2._w/2);
 hitV = 1.0 / schlaeger2._w * hitV - 0.5;

 speed = ball._dir.mag();

ball._dir.x += hitV * 8;
 ball._dir.y *= -1;

 // ball soll die gleiche geschwindigkeit haben wie am anfang
 ball._dir.normalize();
 ball._dir.mult(speed);

 // setzte ball buendig auf den schlaeger(das er nicht im schlaeger ist)
 ball._pos.y = schlaeger2._pos.y + schlaeger2._h/2 + ball._r ;

// spiele sound
 ballHitSchlaeger2Snd.trigger();
 break;
 }

/*
 PVector hitPos = new PVector();

// teste of ball den schlaeger trifft
 int retVal = schlaeger.checkCollision(ball._pos.x, ball._pos.y, ball._r, ball._dir,hitPos);
 int retVal2 = schlaeger2.checkCollision(ball._pos.x, ball._pos.y, ball._r, ball._dir,hitPos);

 switch(retVal)
 {
 case Brick.HIT_LEFT:
 case Brick.HIT_RIGTH:
 // aendere flugrichtung ball
 ball._dir.x *= -1;

 // spiele sound
 ballHitSchlaegerSnd.trigger();
 break;
 case Brick.HIT_TOP:
 case Brick.HIT_BOTTOM:
 // aendere flugrichtung ball

 // anschnitt berechnen
 float hitV = hitPos.x - (schlaeger._pos.x - schlaeger._w/2);
 hitV = 1.0 / schlaeger._w * hitV - 0.5;

 float speed = ball._dir.mag();

 ball._dir.x += hitV * 8;
 ball._dir.y *= -1;

 ball._dir.normalize();
 ball._dir.mult(speed);

// spiele sound
 ballHitSchlaegerSnd.trigger();
 break;
 }

 // teste of ball den schlaeger trifft

 switch(retVal2)
 {
 case Brick.HIT_LEFT:
 case Brick.HIT_RIGTH:
 // aendere flugrichtung ball
 ball._dir.x *= -1;

 // spiele sound
 ballHitSchlaeger2Snd.trigger();
 break;
 case Brick.HIT_TOP:
 case Brick.HIT_BOTTOM:
 // aendere flugrichtung ball

 // anschnitt berechnen
 float hitV2 = hitPos.x - (schlaeger2._pos.x - schlaeger2._w/2);
 hitV2 = 1.0 / schlaeger2._w * hitV2 - 0.5;

 float speed = ball._dir.mag();

 ball._dir.x += hitV2 * 8;
 ball._dir.y *= -1;

 ball._dir.normalize();
 ball._dir.mult(speed);

// spiele sound
 ballHitSchlaeger2Snd.trigger();
 break;
 }

*/

 // test ob ball einen ziegel trifft
 for (int i=0;i < brickList.length;i++)
 {
 if (brickList[i] != null)
 {
 retVal = brickList[i].checkCollision(ball._pos.x, ball._pos.y, ball._r, ball._dir,hitPos);
 switch(retVal)
 {
 case Brick.HIT_LEFT:
 case Brick.HIT_RIGTH:
 // aendere flugrichtung ball
 ball._dir.x *= -1;
 pointCount +=1;

// entferne ziegel aus liste
 brickList[i] = null;

 // spiele sound
 ballHitSnd.trigger();
 break;
 case Brick.HIT_TOP:
 case Brick.HIT_BOTTOM:
 // aendere flugrichtung ball
 ball._dir.y *= -1;
 pointCount +=1;

// entferne ziegel aus liste
 brickList[i] = null;

// spiele sound
 ballHitSnd.trigger();
 break;
 }
 }
 }
}
void drawWin()
{
 pushStyle();
 fill(150);
 font = loadFont("AmericanTypewriter-48.vlw");
 textFont(font,40);
 image(imageList[4], 0, 0, width, height);
 text(pointCount, width-550, height-72);
 popStyle();

}

LOSING SITUATION (which happens quite often): WINNING SCENE: The bouncing ball and racket (the bricks stayed the way as they were from the beginning):

class BouncingBall
{
 PVector _pos;
 PVector _dir;
 float _dampV;
 int _d;
 int _r;

 // konstruktor
 BouncingBall(int x,int y,int radius)
 {
 _pos = new PVector(x, y);
 _dir = new PVector(0,0);
 _dampV = 1;

 _r = radius;
 _d = _r * 3;
 }

 // setzt die neue pos + richtung + daempfung
 void set(PVector pos,PVector dir,float dampV)
 {
 _pos = pos.get();
 _dir = dir.get();
 _dampV = dampV;
 }

 // erneuert die aktuelle position
 void calcPos()
 {
 // aktuelle position verschieben
 _pos.add(_dir);

 // bewegungs vektor veraendert
 _dir.mult(_dampV);

 // teste horizontal
 if(_pos.x + _r > width)
 {
 _dir.x *= -1;
 _pos.x = width - _r;
 }
 else if(_pos.x - _r < 0)
 {
 _dir.x *= -1;
 _pos.x = _r;
 }

// // teste vertikal
// if(_pos.y - _r < 0)
// {
// _dir.y *= -1;
// _pos.y = _r;
// }

 }

// zeichnet den ball
 void draw()
 {
 calcPos();
 pushStyle();
 fill(255);
 noStroke();
 ellipse(_pos.x,_pos.y,_d,_d);
 image(imageList[1],_pos.x-16.5, _pos.y-18, 30,30);
 popStyle();
 }
}

 

class Racket extends Brick
{

 Racket(int posX,int posY,int w,int h)
 {
 super(posX,posY,w,h);
 }

 void move(int x,int y)
 {
 _pos.x = x;
 _pos.y = y;
 }

 void draw()
 {
 pushStyle();
 rectMode(CENTER);
 fill(255);
 rect(_pos.x, _pos.y, _w, _h);
 image(imageList[0],_pos.x-35, _pos.y-4.5,70,10);

 popStyle();
 }

}