-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbreakout.pde
451 lines (376 loc) · 12 KB
/
breakout.pde
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
/*
Program title: Breakout Game
Program description: Arkanoid mobile game
Created by: John Samuel as SUACODE AFRICA 2019 course project
Date of creation: June 3, 2019
Date of completion: December 13, 2019
*/
Game game;
Paddle paddle;
Ball ball;
int dummyVariable = 0; //this variable ensures that game is RESTARTED at the detection of a screen touch after game has been WON
int x, y;//variables to hold the bricks' coordinates during initialization of array in setup()
int brickWidth = 100;
int brickHeight = 50;
int brickPadding = 1;
int firstBrickXCoordinate = 1;
int firstBrickYCoordinate = 1;
int bricksColumnCount;
int bricksRowCount;
int totalBricks;
Brick[] bricks;
int numOfBricksHit; //this variable stores the number of bricks that have been hit by ball
int count; //this variable's iteration much later makes display to be updated before animation is paused
int avoidClash = 0; //this dummy variable prevents a clash in the restart game conditional statement under the mousePressed callback
void setup ()
{
fullScreen();
orientation(LANDSCAPE);
int dpi = round(displayDensity * 100); //this variable helps deal with the visual difference in ball speed across various processors
game = new Game();
//constructor arguments: paddleX, paddleY, paddleWidth, paddleHeight
paddle = new Paddle(width/2, height-10, 130, 20);
//constructor arguments: ballX, ballY, ballWidth, ballHeight, radius, xSpeed, ySpeed
ball = new Ball((width/2)-65, height-30, 10, 10, 5, dpi/30, dpi/30);
int k = 0;
bricksColumnCount = width/brickWidth;
bricksRowCount = 1+((height/3)/brickHeight);
totalBricks = bricksRowCount*bricksColumnCount;
bricks = new Brick[totalBricks];
y = firstBrickYCoordinate;
for (int i = 0; i < bricksRowCount; i++)
{
x = firstBrickXCoordinate; // x coordinate for the first column of Bricks
for (int j = 0; j < bricksColumnCount; j++)
{
bricks[k] = new Brick(x, y);
k++;
x += brickWidth + brickPadding;
}
y += brickHeight + brickPadding;
}
}
void draw ()
{
background(40, 110, 251);
game.startGame(paddle, ball, bricks);
}
class Game
{
int fontSize;
int score;
int playerScore;
int scoreTextX;
int scoreTextY;
int scoreNumberX;
int scoreNumberY;
int scoreIncrement;
int lifesOfPlayer;
int playerLifes;
int lifesTextX;
int lifesTextY;
int lifesNumberX;
int lifesNumberY;
boolean gameOn;
Game()
{
fontSize = 40;
score = 0;
playerScore = score;
scoreTextX = 1;
scoreTextY = 52;
scoreNumberX = 115;
scoreNumberY = 52;
scoreIncrement = 10;
lifesOfPlayer = 1+((height/3)/brickHeight); //playerLifes is equal to row number of bricks
playerLifes = lifesOfPlayer;
lifesTextX = 303;
lifesTextY = 52;
lifesNumberX = 308;
lifesNumberY = 52;
gameOn = false;//animation is PAUSED at this state
}
void createRectangle(float xPaddle, float yPaddle, float widthPaddle, float heightPaddle)
{
rect(xPaddle, yPaddle, widthPaddle, heightPaddle);
}
void startGame(Paddle paddle, Ball ball, Brick[] bricks)
{
//this conditional statement ensures that game engine is turned ON for a RESTART of the game after game has been previously WON
if(dummyVariable != 0)
{
game.gameOn = true;
}
numOfBricksHit = 0;
paddle.display(game);
ball.display(game);
for (int i = 0; i < totalBricks; i++)
{
bricks[i].displayBricks(game);
bricks[i].checkBallCollisionWithBrick(game, ball);
}
for (int j = 0; j < bricksColumnCount; j++)
{
displayPlayerScoreAndLifes();//this is located HERE (in a loop), to avoid this function's output being hid under the bricks at the display output
}
if (gameOn)
{
ball.move(game);
movePaddle(paddle);
paddle.checkBallCollisionWithPaddle(ball);
ball.checkWall(game);
gameOver();
gameWon();
}
}
//function that display player's scores
void displayPlayerScoreAndLifes()
{
fill(255);
textSize(fontSize);
textAlign(LEFT);
text("Score:"+playerScore+" ", scoreTextX, scoreTextY);
text("Lifes:"+playerLifes+" ", lifesTextX, lifesTextY);
}
//function that moves paddles vertically only
void movePaddle(Paddle paddle)
{
if (mouseY > (height - 200))// this conditional statement prevents a clash between the use of mouseX by the keywords constraint & mousePressed at the same time
{
paddle.paddleX = constrain(mouseX, 0, (width - paddle.paddleHeight));
}
}
void incrementPlayerScore()
{
playerScore = playerScore + scoreIncrement; // increases player's score
}
void gameOver()
{
//conditional statement that checks if all player lifes has been exhausted
if (playerLifes == 0)
{
count++;
playerGameStatus("GAME LOST!!!");
if (count == 2) // this ensures that playerLifes becomes 0 on display before animation is PAUSED by noLoop()
{
//the below lines ensures that the paddle & ball starts off at their initial position when animation is RESUMED
ball.ballX = (width/2)-65;
ball.ballY = height-40;
paddle.paddleX = width/2;
paddle.paddleY = height-20;
noLoop();
}
}
}
void gameWon()
{
//conditional statement that checks if player has attained maximum score
if (numOfBricksHit == totalBricks)
{
count++;
avoidClash++;
playerGameStatus("GAME WON!!!");
if (count == 2) // this ensures that playerScore becomes maximum before animation is PAUSED by noLoop()
{
numOfBricksHit = 0;
//the below lines ensures that the paddle & ball starts off at their initial position when animation is RESUMED
ball.ballX = (width/2)-65;
ball.ballY = height-20;
paddle.paddleX = width/2;
paddle.paddleY = height-10;
noLoop();
}
}
}
void playerGameStatus(String status)
{
fill(255);
textSize(100);
textAlign(CENTER);
text(status, width/2, height/2);
}
void restartGame()
{
playerScore = score;
playerLifes = lifesOfPlayer;
setup();//this permits the redraw of ALL bricks by initializing the brick array
startGame(paddle, ball, bricks);
}
}
class Brick
{
int brickX;
int brickY;
boolean brickHit;
boolean hasOverlappedBrick;
Brick(int tempBrickX, int tempBrickY)
{
brickX = tempBrickX;
brickY = tempBrickY;
brickHit = false;
hasOverlappedBrick = false;
}
void displayBricks(Game game)
{
if (!brickHit)
{
fill(204, 102, 0);
rectMode(CORNER);
game.createRectangle(brickX, brickY, brickWidth, brickHeight);
}
}
//function that checks for a collision between ball and a brick
void checkBallCollisionWithBrick(Game game, Ball ball)
{
if (brickHit)
{
numOfBricksHit++;
return;
}
//function call that determines if the ball overlaps brick
hasOverlappedBrick = doesOverlap(brickX, brickY, brickWidth, brickHeight, ball.ballX, ball.ballY, ball.radius);
//conditional statement that is executed when the ball overlaps brick
if (hasOverlappedBrick)
{
ball.ySpeed = (-1) * (ball.ySpeed);
game.incrementPlayerScore();
brickHit = true;
}
}
}
class Paddle
{
float paddleX; //maxX/2
float paddleY; // maxY - paddleHeight
float paddleWidth;
float paddleHeight;
boolean hasOverlappedPaddle;
Paddle(float tempPaddleX, float tempPaddleY, float tempPaddleWidth, float tempPaddleHeight)
{
paddleX = tempPaddleX; //maxX/2
paddleY = tempPaddleY; // maxY - paddleHeight
paddleWidth = tempPaddleWidth;
paddleHeight = tempPaddleHeight;
hasOverlappedPaddle = false;
}
//function that display paddle
void display(Game game)
{
fill(255);//colors ball & paddle white
rectMode(CENTER);
game.createRectangle(paddleX, paddleY, paddleWidth, paddleHeight);//creates paddle
}
//function that checks for a collision between ball and paddle
void checkBallCollisionWithPaddle(Ball ball)
{
//function call that determines if the ball overlaps paddle
hasOverlappedPaddle = doesOverlap(paddleX, paddleY, paddleWidth, paddleHeight, ball.ballX, ball.ballY, ball.radius);
//conditional statement that is executed when the ball overlaps paddle
if (hasOverlappedPaddle)
{
ball.ySpeed = (-1) * (ball.ySpeed);
}
}
}
class Ball
{
float ballX; //paddleX - (paddleWidth / 2)
float ballY; //paddleY - paddleHeight
float ballWidth;
float ballHeight;
float radius;
float xSpeed;
float ySpeed;
Ball(float tempBallX, float tempBallY, float tempBallWidth, float tempBallHeight, float tempRadius, float tempXSpeed, float tempYSpeed)
{
ballX = tempBallX; //paddleX - (paddleWidth / 2)
ballY = tempBallY; //paddleY - paddleHeight
ballWidth = tempBallWidth;
ballHeight = tempBallHeight;
radius = tempRadius;
xSpeed = tempXSpeed;
ySpeed = tempYSpeed;
}
//function that displays ball
void display(Game game)
{
fill(255);//colors ball white
rectMode(CORNER);
game.createRectangle(ballX, ballY, ballWidth, ballHeight);//creates a square-shaped ball
}
void move(Game game)
{
if (game.gameOn) //starts game
{
//moveball statements
ballX = ballX + xSpeed;
ballY = ballY - ySpeed;//Ball expected to move upwards ONLY, hence the need for the minus sign
}
}
void checkWall(Game game)
{
//detects if ball collides with the top wall
if (ballY - radius < 0)
{
ySpeed = (-1) * (ySpeed); // reverses ball's direction of travel
}
//detects if ball collides with either the left or right wall
if (ballX - radius < 0 || ballX + radius > width)
{
xSpeed = (-1) * (xSpeed);
}
//checks if ball collides with the screen's bottom
if (ballY + radius > height)
{
if (game.playerLifes > 0)
{
ySpeed = (-1) * (ySpeed);
game.playerLifes--;
}
}
}
}
//function that turns ON the game when screen is touched
void mousePressed()
{
dummyVariable++;
count = 0;
game.gameOn = true;//animation is PLAYED at this state
//this conditional statement prevents the reversal of ball mid-way
if (ball.ballY == (height - paddle.paddleHeight))//this value is where ySpeed negative value should become positive before the detection of another touch
{
ball.ySpeed = (-1) * (ball.ySpeed);//makes the value of ySpeed POSITIVE everytime screen is touched. This prevents the downward movement of ball after a screen touch
}
if (mouseY < (height - 200))// this conditional statement prevents a clash between the use of mouseX by the keywords constraint & mousePressed at the same time
{
if (looping == false)
{
loop();
}
}
if (game.playerLifes == 0 || (numOfBricksHit == 0 && avoidClash != 0))//this conditional statement prevents the restart of game on the detection of another mousePressed while game is ON
{
game.restartGame();
}
}
//function that checks if ball collides with an object
boolean doesOverlap(float xPaddle, float yPaddle, float widthPaddle, float heightPaddle, float xBall, float yBall, float radius)
{
float ballLeftEdge = xBall-radius; //left edge of ball
float ballBottomEdge = yBall+radius; //bottom edge of ball
float ballRightEdge = xBall+radius; //right edge of ball
float ballTopEdge = yBall-radius; //top edge of ball
float paddleLeftEdge = xPaddle; //left edge of paddle
float paddleRightEdge = xPaddle+widthPaddle; //right edge of paddle
float paddleTopEdge = yPaddle; //top edge of paddle
float paddleBottomEdge = yPaddle+heightPaddle; //bottom edge of paddle
if (ballBottomEdge >= paddleTopEdge //Check if bottom edge of ball and top edge of paddle overlap
&& ballTopEdge <= paddleBottomEdge //Check if top edge of ball and bottom edge of paddle overlap
&& ballLeftEdge <= paddleRightEdge //Check if left edge of ball and right edge of paddle overlap
&& ballRightEdge >= paddleLeftEdge) //Check if right edge of ball and left edge of paddle overlap
{
return true;
} else {
return false;
}
}