This first milestone includes all of the logistics and organization for getting
ID: 3816005 • Letter: T
Question
This first milestone includes all of the logistics and organization for getting your team setup and internal processes in place (team repository URLs will be provided for you). It also includes approximately 40% of the overall software implementation, which is why you have two weeks to complete it. You will essentially be implementing a functioning prototype of the base game play, which will be extended in milestones 2 and 3 to a code-complete final product. Create a graphical user interface using pygames for your modified breakout including the following features: Display of brick layer at the top of the screen where each brick is an individual object. Player paddle at the bottom of the screen. A ball placed randomly between the bricks and the paddle and given a random initial direction. Detection of when the ball hits a side wall and appropriate change in direction. Detection of when the ball hits the ceiling and indication of a level completed. Detection of when the ball hits the floor and indication of a level failed Player ability to control the paddle via keyboard or mouse interface Change in angle of reflection of the ball depending on where it hits the player paddle. Destruction of a brick (removal from the set of bricks and from the display) when hit by the ball.Explanation / Answer
In the game, there are rows of bricks in the top of the screen. The objective of the game is to destroy all the bricks with a small ball that travels across the screen. A brick is destroyed when hit by the ball. The ball bounces off when hits the screen borders, the bricks and the paddle. The player must avoid the ball to touch the bottom of the screen (or in common words, falling to the ground). To prevent this from happening, the player uses the paddle to bounce the ball upward, keeping it in play.
Your score increases when you destroy bricks. On the other hand, you lose one life each time the ball falls in the ground.
Game states
The game has 4 states:
This is the sequence of events. The game begins in the state “ball in paddle”. In this state, the ball is glued to the paddle. We can move the paddle, and when we do so, the ball follows the paddle, because it is glued to the paddle. Then, when we press SPACE the game goes into “Playing” state. In this state the ball travels across the screen bouncing off the screen borders, bricks and the paddle. Each time the ball hits a brick we increase the score. On the other hand, if we lose the ball, we decrease the life count and if it is greater than zero we enter the “ball in paddle state”. Otherwise, we enter the “game over” state.
While in the “playing” state, if we destroy all the bricks, we enter the “won” state. We transit from the “game over” and “won” states to “ball in paddle” state by pressing the ENTER key. Actually, this means playing again.
Paddle movement
We move the paddle using the LEFT and RIGHT arrow keys. When moving the paddle we make sure it stays inside the screen.
Ball movement
The ball moves in 4 directions: up-right, up-left, down-right, down-left. To simulate this we use a velocity vector. Wait, don’t be afraid of this expression. It just means we use separate values to update the position of the ball. For example, to simulate the up-right movement we use velX = 5, velY = -5 (the velY is negative to make the ball go up). Below I show more examples of velocity vector values.
1
2
3
4
5
[X,Y]
[5,-5] --> up-right direction
[-5,-5] --> up-left direction
[-5,5] --> down-right direction
[-5,-5] --> down-left direction
We make sure the ball is inside the screen. When it hits the left and right borders we invert the X velocity component. So, if it was going to the left it will end up going to the right and vice versa. On the other hand, if the ball hits the paddle, top or bottom borders or one brick, we invert the Y velocity component. So, if the ball was going up it ends up going down and vice versa.
Collision Detection
Collision detection is a technique used to know if two objects collide or not. Basically, two objects collide if they overlap. We employ collision detection to determine if the ball collides with the bricks and the paddle.
The Code
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
import pygame
SCREEN_SIZE = 640,480
# Object dimensions
BRICK_WIDTH = 60
BRICK_HEIGHT = 15
PADDLE_WIDTH = 60
PADDLE_HEIGHT = 12
BALL_DIAMETER = 16
BALL_RADIUS = BALL_DIAMETER / 2
MAX_PADDLE_X = SCREEN_SIZE[0] - PADDLE_WIDTH
MAX_BALL_X = SCREEN_SIZE[0] - BALL_DIAMETER
MAX_BALL_Y = SCREEN_SIZE[1] - BALL_DIAMETER
# Paddle Y coordinate
PADDLE_Y = SCREEN_SIZE[1] - PADDLE_HEIGHT - 10
# Color constants
BLACK = (0,0,0)
WHITE = (255,255,255)
BLUE = (0,0,255)
BRICK_COLOR = (200,200,0)
# State constants
STATE_BALL_IN_PADDLE = 0
STATE_PLAYING = 1
STATE_WON = 2
STATE_GAME_OVER = 3
We begin by importing the pygame module. Next we define some constants. The first constant defines the screen dimensions. Then, we have constants that define the dimensions of the paddle, ball and bricks. After that, we define constants that specify the maximum X coordinate for the ball and paddle. These constants will be used later to enforce the paddle and ball stay inside the screen. Finally, we define color and state constants.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Bricka:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("bricka (a breakout clone by codeNtronix.com)")
self.clock = pygame.time.Clock()
if pygame.font:
self.font = pygame.font.Font(None,30)
else:
self.font = None
self.init_game()
We have encapsulated the game code inside the Bricka class. In the constructor, firstly, we initialize the pygame modules. Next, we create the game window and we set a title for it. Next we create a clock object that will be used later to lock our frame rate to a constant value. Next, we create a font object, only if the font module is available. This object will be used later to draw text in the screen. Finally, we call the init_game() function. This function is described below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def init_game(self):
self.lives = 3
self.score = 0
self.state = STATE_BALL_IN_PADDLE
self.paddle = pygame.Rect(300,PADDLE_Y,PADDLE_WIDTH,PADDLE_HEIGHT)
self.ball = pygame.Rect(300,PADDLE_Y - BALL_DIAMETER,BALL_DIAMETER,BALL_DIAMETER)
self.ball_vel = [5,-5]
self.create_bricks()
def create_bricks(self):
y_ofs = 35
self.bricks = []
for i in range(7):
x_ofs = 35
for j in range(8):
self.bricks.append(pygame.Rect(x_ofs,y_ofs,BRICK_WIDTH,BRICK_HEIGHT))
x_ofs += BRICK_WIDTH + 10
y_ofs += BRICK_HEIGHT + 5
In the init_game() function we reset some variables. We start with 3 lives, score 0, and state set to STATE_BALL_IN_PADDLE. Next we define the rectangles for the paddle and ball. We will, later, use these rectangles for movement, drawing, and for collision detection. Then, we initialize the ball velocity, setting it to go up-right. Finally, we call the create_bricks() function that will create the bricks. The bricks are maintained in a list.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def check_input(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.paddle.left -= 5
if self.paddle.left < 0:
self.paddle.left = 0
if keys[pygame.K_RIGHT]:
self.paddle.left += 5
if self.paddle.left > MAX_PADDLE_X:
self.paddle.left = MAX_PADDLE_X
if keys[pygame.K_SPACE] and self.state == STATE_BALL_IN_PADDLE:
self.ball_vel = [5,-5]
self.state = STATE_PLAYING
elif keys[pygame.K_RETURN] and (self.state == STATE_GAME_OVER or self.state == STATE_WON):
self.init_game()
The check_input() function handles keyboard input. Firstly, we get a list with the states of all keys. After that, if the LEFT arrow key is pressed we move the paddle left. Likewise, if the RIGHT arrow key is pressed we move the paddle right. During the movement, we make sure the paddle stays inside the screen. Then, if the SPACE key is pressed while we are in state Ball in Paddle, we change the velocity to make it go up-right and change the state to Playing. This causes the ball to be launched. Finally, if the ENTER key is pressed while in the game over or won states, we call init_game() to restart the game.
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
def move_ball(self):
self.ball.left += self.ball_vel[0]
self.ball.top += self.ball_vel[1]
if self.ball.left <= 0:
self.ball.left = 0
self.ball_vel[0] = -self.ball_vel[0]
elif self.ball.left >= MAX_BALL_X:
self.ball.left = MAX_BALL_X
self.ball_vel[0] = -self.ball_vel[0]
if self.ball.top < 0:
self.ball.top = 0
self.ball_vel[1] = -self.ball_vel[1]
def handle_collisions(self):
for brick in self.bricks:
if self.ball.colliderect(brick):
self.score += 3
self.ball_vel[1] = -self.ball_vel[1]
self.bricks.remove(brick)
break
if len(self.bricks) == 0:
self.state = STATE_WON
if self.ball.colliderect(self.paddle):
self.ball.top = PADDLE_Y - BALL_DIAMETER
self.ball_vel[1] = -self.ball_vel[1]
elif self.ball.top > self.paddle.top:
self.lives -= 1
if self.lives > 0:
self.state = STATE_BALL_IN_PADDLE
else:
self.state = STATE_GAME_OVER
The move_ball() functions takes care of moving the ball. First, it updates the position coordinates adding the velocity components. After that, it checks if the ball hit the left or right screen border. If true, the X velocity component is inverted making it bounce. Finally, it checks if the ball hit the top border, inverting the Y velocity component, if true.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def handle_collisions(self):
for brick in self.bricks:
if self.ball.colliderect(brick):
self.score += 3
self.ball_vel[1] = -self.ball_vel[1]
self.bricks.remove(brick)
break
if len(self.bricks) == 0:
self.state = STATE_WON
if self.ball.colliderect(self.paddle):
self.ball.top = PADDLE_Y - BALL_DIAMETER
self.ball_vel[1] = -self.ball_vel[1]
elif self.ball.top > self.paddle.top:
self.lives -= 1
if self.lives > 0:
self.state = STATE_BALL_IN_PADDLE
else:
self.state = STATE_GAME_OVER
The handle_collisions() function determines if the ball collided with a brick, the paddle, or has fallen to the ground. First, it checks if the ball has collided with a brick. If true, it increments the score by 3 units and removes the brick from the brick list. After making the brick collision test, it checks if there are remaining bricks. If not, it changes the state to Won. Finally, it checks if the ball hit the paddle. If true, the ball is repositioned so that is right above the paddle and the Y velocity component is inverted. Otherwise, it checks if the ball is below the paddle (going to the ground), and if true, decreases the life count. If the count drops to zero, it changes the state to Ball in paddle. Otherwise, changes the state to Ball in paddle.
1
2
3
4
5
6
7
8
9
10
11
12
def show_stats(self):
if self.font:
font_surface = self.font.render("SCORE: " + str(self.score) + " LIVES: " + str(self.lives), False, WHITE)
self.screen.blit(font_surface, (205,5))
def show_message(self,message):
if self.font:
size = self.font.size(message)
font_surface = self.font.render(message,False, WHITE)
x = (SCREEN_SIZE[0] - size[0]) / 2
y = (SCREEN_SIZE[1] - size[1]) / 2
self.screen.blit(font_surface, (x,y))
These 2 functions draw texts in the screen. The show_stats() shows score and life info while show_message() is used to show game state related messages.
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
def run(self):
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit
self.clock.tick(50)
self.screen.fill(BLACK)
self.check_input()
if self.state == STATE_PLAYING:
self.move_ball()
self.handle_collisions()
elif self.state == STATE_BALL_IN_PADDLE:
self.ball.left = self.paddle.left + self.paddle.width / 2
self.ball.top = self.paddle.top - self.ball.height
self.show_message("PRESS SPACE TO LAUNCH THE BALL")
elif self.state == STATE_GAME_OVER:
self.show_message("GAME OVER. PRESS ENTER TO PLAY AGAIN")
elif self.state == STATE_WON:
self.show_message("YOU WON! PRESS ENTER TO PLAY AGAIN")
self.draw_bricks()
# Draw paddle
pygame.draw.rect(self.screen, BLUE, self.paddle)
# Draw ball
pygame.draw.circle(self.screen, WHITE, (self.ball.left + BALL_RADIUS, self.ball.top + BALL_RADIUS), BALL_RADIUS)
self.show_stats()
pygame.display.flip()
This is the game loop. First, we handle the window events. If a request to quit the application exists, we do quit the application. After handling the events, we use the clock object to lock the frame rate to 50 FPS. Then, we handle keyboard input. After that, the next action depends on the game state. If we are in Playing state, we move the ball calling move_ball(), and handle collisions calling handle_collisions(). Otherwise, we print a message with instructions. In state Ball in paddle we ensure the ball is glued to the paddle. After that, we draw the paddle, the ball, and display the score and lives text. Finally, we call pygame.display.flip() to display everything that has been drawn in the frame.
1
2
if __name__ == "__main__":
Bricka().run()
Finally, this piece of code creates an instance of the game class and runs it. It makes sure the file is run directly and not imported from a module.
1
2
3
4
5
[X,Y]
[5,-5] --> up-right direction
[-5,-5] --> up-left direction
[-5,5] --> down-right direction
[-5,-5] --> down-left direction
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.