class BreakoutGraphics: def __init__(self, ball_radius=BALL_RADIUS, paddle_width=PADDLE_WIDTH, paddle_height=PADDLE_HEIGHT, paddle_offset=PADDLE_OFFSET, brick_rows=BRICK_ROWS, brick_cols=BRICK_COLS, brick_width=BRICK_WIDTH, brick_height=BRICK_HEIGHT, brick_offset=BRICK_OFFSET, brick_spacing=BRICK_SPACING, title='Breakout', __dy=INITIAL_Y_SPEED): # Create a graphical window, with some extra space. self.window_width = brick_cols * (brick_width + brick_spacing) - brick_spacing self.window_height = brick_offset + 3 * (brick_rows * (brick_height + brick_spacing) - brick_spacing) self.window = GWindow(width=self.window_width, height=self.window_height, title=title) # Create a paddle. self.paddle = GRect(width=paddle_width, height=paddle_height, x=(self.window_width-paddle_width)/2, y=(self.window_height-paddle_offset-paddle_height)) self.paddle.filled = True self.paddle.color = '#513743' self.paddle.fill_color = '#513743' self.window.add(self.paddle) self.life_label = GLabel('● ● ●') self.life_label.font = '-15' self.life_label.color = '#c53d43' self.score_label = GLabel('score: 0') self.score_label.font = '-15' self.score_label.color = '#455765' self.brick_rows = BRICK_ROWS self.brick_cols = BRICK_COLS # Initialize our mouse listeners. onmousemoved(self.reset_paddle_location) # Draw bricks. self.create_bricks() def create_bricks(self, brick_offset=BRICK_OFFSET, brick_width=BRICK_WIDTH, brick_height=BRICK_HEIGHT, brick_spacing=BRICK_SPACING): d_y = brick_offset color = ['#96514d', '#d3381c', '#2c4f54', '#00a381', '#008899', '#3e62ad', '#316745', '#028760', '#fabf14', '#274a78'] for i in range(self.brick_rows): c = color[random.randint(0, 9)] d_x = 0 for j in range(self.brick_cols - 1): brick = GRect(brick_width, brick_height, x=d_x, y=d_y) brick.filled = True brick.color = c brick.fill_color = brick.color self.window.add(brick) d_x = d_x + brick_width + brick_spacing brick = GRect(brick_width, brick_height, x=d_x, y=d_y) brick.filled = True brick.color = c brick.fill_color = brick.color self.window.add(brick) d_y = d_y + brick_height + brick_spacing def reset_paddle_location(self, mouse): if mouse.x >= self.window.width: self.paddle.x = self.window.width - self.paddle.width/2 elif mouse.x <= 0: self.paddle.x = -self.paddle.width/2 else: self.paddle.x = mouse.x - self.paddle.width/2 # Default initial velocity for the ball. def get_dx(self): __dx = random.randint(1, MAX_X_SPEED) if random.random() > 0.5: __dx = -__dx return __dx def get_dy(self): __dy = INITIAL_Y_SPEED return __dy def ball_collide_paddle(self): if self.window.get_object_at(self.ball.x, self.ball.y+self.ball.height) is self.paddle: return True if self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y+self.ball.height) is self.paddle: return True if self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y) is self.paddle: return True if self.window.get_object_at(self.ball.x, self.ball.y) is self.paddle: return True def ball_collide_brick(self): if self.window.get_object_at(self.ball.x, self.ball.y+self.ball.height) is not None and self.window.get_object_at(self.ball.x, self.ball.y+self.ball.height) is not self.paddle and self.window.get_object_at(self.ball.x, self.ball.y+self.ball.height) is not self.life_label and self.window.get_object_at(self.ball.x, self.ball.y+self.ball.height) is not self.score_label: self.window.remove(self.window.get_object_at(self.ball.x, self.ball.y+self.ball.height)) return True if self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y+self.ball.height) is not self.paddle and self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y+self.ball.height) is not None and self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y+self.ball.height) is not self.life_label and self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y+self.ball.height) is not self.score_label: self.window.remove(self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y+self.ball.height)) return True if self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y) is not self.paddle and self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y) is not None and self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y) is not self.life_label and self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y) is not self.score_label: self.window.remove(self.window.get_object_at(self.ball.x+self.ball.width, self.ball.y)) return True if self.window.get_object_at(self.ball.x, self.ball.y) is not self.paddle and self.window.get_object_at(self.ball.x, self.ball.y) is not None and self.window.get_object_at(self.ball.x, self.ball.y) is not self.life_label and self.window.get_object_at(self.ball.x, self.ball.y) is not self.score_label: self.window.remove(self.window.get_object_at(self.ball.x, self.ball.y)) return True # Center a filled ball in the graphical window. def reset_ball_location(self, ball_radius=BALL_RADIUS): self.ball = GOval(ball_radius*2, ball_radius*2, x=self.window_width/2-ball_radius, y=self.window_height/2-ball_radius) self.ball.filled = True self.ball.color = '#705b67' self.ball.fill_color = self.ball.color self.window.add(self.ball) def lose(self): self.window.clear() lose_background = GRect(self.window.width, self.window.height) lose_background.filled = True lose_background.color = '#a6a5c4' lose_background.fill_color = '#a6a5c4' lose_label = GLabel('GAME OVER') lose_label.font = '-50' lose_label.color = '#e7e7eb' self.window.add(lose_background) self.window.add(lose_label, self.window.width/2-lose_label.width/2, self.window.height/2.5) self.window.add(self.score_label, (self.window_width-self.score_label.width)/2, lose_label.y+50) def win(self): self.window.clear() win_background = GRect(self.window.width, self.window.height) win_background.filled = True win_background.color = '#e5abbe' win_background.fill_color = '#e5abbe' win_label = GLabel('WIN') win_label.font = '-50' win_label.color = '#fdeff2' self.window.add(win_background) self.window.add(win_label, self.window.width/2-win_label.width/2, self.window.height/2.5) self.window.add(self.score_label, (self.window_width-self.score_label.width)/2, win_label.y+50)
class BreakoutGraphics: def __init__(self, ball_radius=BALL_RADIUS, paddle_width=PADDLE_WIDTH, paddle_height=PADDLE_HEIGHT, paddle_offset=PADDLE_OFFSET, brick_rows=BRICK_ROWS, brick_cols=BRICK_COLS, brick_width=BRICK_WIDTH, brick_height=BRICK_HEIGHT, brick_offset=BRICK_OFFSET, brick_spacing=BRICK_SPACING, title='Breakout'): """ Parameter settings. :param ball_radius: Controls the radius of the ball. :param paddle_width: Controls the width of the paddle. :param paddle_height: Controls the height of the paddle. :param paddle_offset: Controls the y-position of the paddle. :param brick_rows: Controls the number of rows of bricks. :param brick_cols: Controls the number of columns of bricks. :param brick_width: Controls the width of a single brick. :param brick_height: Controls the height of a single brick. :param brick_offset: Controls the y-position of the highest row of bricks. :param brick_spacing: Controls the interval between any two bricks. :param title: Set the title of the canvas. """ self.ball_radius = ball_radius self.paddle_width = paddle_width self.paddle_height = paddle_height self.paddle_offset = paddle_offset self.brick_cols = brick_cols self.brick_rows = brick_rows self.brick_width = brick_width self.brick_height = brick_height self.brick_offset = brick_offset self.brick_spacing = brick_spacing # Create a label for usernames before create a window. self.name = GLabel('Player: ' + input('Nickname: ')) self.name.font = '-15-bold' # Create a graphical window, with some extra space. self.window_width = self.brick_cols * (brick_width+brick_spacing) - brick_spacing self.window_height = brick_offset + 3 * (brick_rows * (brick_height+brick_spacing) - brick_spacing) self.window = GWindow(width=self.window_width, height=self.window_height, title=title) # Initial settings before the game starts. self.score = 0 self.score_label = GLabel('Score: ' + str(self.score)) self.score_label.font = '-15-bold' self.lives = 3 self.lives_label = GLabel('Lives: ' + str(self.lives)) self.lives_label.font = '-15-bold' self.click2start = GLabel('Click to Start !') self.click2start.font = '-20-bold' self.window.add(self.name, 5, self.brick_offset - 25) self.window.add(self.click2start, 130, self.window.height-150) self.window.add(self.score_label, 5, self.window.height) self.window.add(self.lives_label, self.window_width-100, self.window_height-5) # Create a paddle. self.count = 1 # For paddle width resetting after losing one live. self.paddle = GRect(self.paddle_width, self.paddle_height) self.paddle.filled = True self.window.add(self.paddle, x=(self.window_width-self.paddle_width)/2, y=self.window_height-paddle_offset) # Center a filled ball in the graphical window. self.ball = GOval(ball_radius*2, ball_radius*2) self.ball.filled = True self.window.add(self.ball, (self.window_width-self.ball.width)/2, (self.window_height-brick_height)/2) # Default initial velocity for the ball. self.__dx = 0 self.__dy = INITIAL_Y_SPEED self.set_x_velocity() # Initialize our mouse listeners. self.is_game_started = False onmouseclicked(self.handle_click) # Draw bricks. self.brick_left = self.brick_cols * self.brick_rows for i in range(self.brick_rows): for j in range(self.brick_cols): # Different colors for every two rows. self.brick = GRect(self.brick_width, self.brick_height) self.brick.filled = True if i < 2: self.brick.fill_color = 'lightcoral' elif 2 <= i < 4: self.brick.fill_color = 'peachpuff' elif 4 <= i < 6: self.brick.fill_color = 'lightyellow' elif 6 <= i < 8: self.brick.fill_color = 'palegreen' elif 8 <= i < 10: self.brick.fill_color = 'aqua' self.window.add(self.brick, x=j*(self.brick_width+self.brick_spacing), y=self.brick_offset+i*(self.brick_height+self.brick_spacing)) def paddle_move(self, event): """ This function will execute if the the game is started. If the game is started, it will detect the position of the mouse so that players can control the paddle with their mouse. :param event: Detects position of the mouse. """ if self.is_game_started: # If the mouse is out of the range of the width of the window, the paddle will still remain in the window. if event.x <= self.paddle.width/2: self.paddle.x = 0 elif event.x >= self.window_width - self.paddle_width/2: self.paddle.x = self.window_width - self.paddle_width else: self.paddle.x = event.x - self.paddle_width/2 def get_x_velocity(self): """ Since self.__dx is private instance variable, we need to create a getter for the user-end. :return: The value of self.__dx (the x-velocity) """ return self.__dx def get_y_velocity(self): """ Since self.__dy is private instance variable, we need to create a getter for the user-end. :return: The value of self.__dy (the y-velocity) """ return self.__dy def set_x_velocity(self): """ Setting a random x-velocity and y-velocity for the ball. The y-velocity is a constant, and the x-velocity is set between 1 and MAX_X_SPEED, There is a 50% chance that the ball will have opposite direction in x dimension. """ self.__dx = random.randint(2, MAX_X_SPEED) if random.random() < 0.5: self.__dx = -self.__dx def handle_click(self, _): """ This function will execute if a click on the mouse. After a single click, the game will start, and any clicks can no longer affect the game. :param _: Detects a click on the mouse. :return: self.count """ self.window.remove(self.click2start) self.is_game_started = True onmousemoved(self.paddle_move) def probe_obj(self): """ This function will detect any collision between the ball with other objects. :return: True, if the ball bump into the paddle or the bricks. """ # The variable 'obj' will change to the next corner if the checked None. obj1 = self.window.get_object_at(self.ball.x, self.ball.y) obj2 = self.window.get_object_at(self.ball.x + self.ball.width, self.ball.y) obj3 = self.window.get_object_at(self.ball.x, self.ball.y + self.ball.height) obj4 = self.window.get_object_at(self.ball.x + self.ball.width, self.ball.y + self.ball.height) point = obj1 if point is None or point is self.name and point is self.score_label and point is self.lives_label: point = obj2 if point is None or point is self.name and point is self.score_label and point is self.lives_label: point = obj3 if point is None or point is self.name and point is self.score_label and point is self.lives_label: point = obj4 if point is None or point is self.name and point is self.score_label and point is self.lives_label: pass if point is self.paddle: # To avoid the ball from sticking on the paddle, I move the ball above the paddle. self.ball.move(0, -self.paddle_height - self.ball.height) # When the ball touches the paddle, the paddle will change its color to the ball's color. self.paddle.color = self.ball.color self.paddle.fill_color = self.ball.fill_color return True if point is not self.paddle and point is not None and point is not self.name\ and point is not self.score_label and point is not self.lives_label: # Every time when the ball bump into a brick, the score will reset. self.window.remove(point) self.brick_left -= 1 # The formula of score counting. self.score = int((self.brick_rows * self.brick_cols - self.brick_left) ** 1.5) if self.brick_left == (self.brick_rows * self.brick_cols) * 1/2: self.lives += 1 self.window.remove(self.lives_label) self.lives_label = GLabel('Lives: ' + str(self.lives)) self.lives_label.font = '-15-bold' self.window.add(self.lives_label, self.window_width-100, self.window_height-5) self.window.remove(self.score_label) self.score_label = GLabel('Score: ' + str(self.score)) self.score_label.font = '-15-bold' self.window.add(self.score_label, 5, self.window.height) # The color of the ball depends on how many bricks are left. if self.brick_rows * self.brick_cols * 3/5 < self.brick_left <= self.brick_rows * self.brick_cols * 4/5: self.ball.color = 'palegreen' self.ball.fill_color = 'palegreen' elif self.brick_rows * self.brick_cols * 2/5 < self.brick_left <= self.brick_rows * self.brick_cols * 3/5: self.ball.color = 'lightyellow' self.ball.fill_color = 'lightyellow' elif self.brick_rows * self.brick_cols * 1/5 < self.brick_left <= self.brick_rows * self.brick_cols * 2/5: self.ball.color = 'peachpuff' self.ball.fill_color = 'peachpuff' # More difficult to see the ball. elif self.brick_left <= self.brick_rows * self.brick_cols * 1/5: self.ball.color = 'black' self.ball.fill_color = 'black' return True # Lives will reset when the ball fall out of the lower margin of the window. def fall_out(self): self.is_game_started = False # Every chance loses, the variable 'count', which affects the length of the paddle, will add 0.15. self.count += 0.15 self.lives -= 1 self.window.remove(self.lives_label) self.lives_label = GLabel('Lives: ' + str(self.lives)) self.lives_label.font = '-15-bold' self.window.add(self.lives_label, self.window_width - 100, self.window_height - 5) self.set_x_velocity() return self.count, self.lives, self.__dx def build_ball(self): """ This function will first remove the original ball and create a new ball. The color of the ball depends on the number of remaining bricks. """ self.window.remove(self.ball) self.ball = GOval(self.ball_radius*2, self.ball_radius*2) self.ball.filled = True # The color of the ball depends on how many bricks are left. if self.brick_rows * self.brick_cols * 3 / 5 < self.brick_left <= self.brick_rows * self.brick_cols * 4 / 5: self.ball.color = 'palegreen' self.ball.fill_color = 'palegreen' elif self.brick_rows * self.brick_cols * 2 / 5 < self.brick_left <= self.brick_rows * self.brick_cols * 3 / 5: self.ball.color = 'lightyellow' self.ball.fill_color = 'lightyellow' elif self.brick_rows * self.brick_cols * 1 / 5 < self.brick_left <= self.brick_rows * self.brick_cols * 2 / 5: self.ball.color = 'peachpuff' self.ball.fill_color = 'peachpuff' # More difficult to see the ball. elif self.brick_left <= self.brick_rows * self.brick_cols * 1 / 5: self.ball.color = 'black' self.ball.fill_color = 'black' self.window.add(self.ball, (self.window_width-self.ball.width)/2, (self.window_height-self.brick_height)/2) def build_paddle(self): """ This function will first remove the original paddle and create a new one. The width of the new paddle depends on the variable of count. """ self.window.remove(self.paddle) self.paddle = GRect(self.paddle_width/self.count, self.paddle_height, x=(self.window_width-self.paddle_width)/2, y=self.window_height-self.paddle_offset) self.paddle.filled = True self.paddle.color = self.ball.color self.paddle.fill_color = self.ball.fill_color self.window.add(self.paddle) def game_over(self): """ This function execute when no lives are remaining. The window will show the game is over and the final score. The score depends on the number of remaining bricks and the variable count. """ self.is_game_started = False self.window.clear() game_over = GLabel('Game Over') game_over.font = '-60-bold' lose = GLabel('You Lose...') lose.font = '-30-bold' score_label = GLabel('Your Score:' + str(self.score)) score_label.font = 'courier-25-bold' background = GRect(self.window.width, self.window.height) background.filled = True background.color = 'red' background.fill_color = 'red' self.window.add(background) self.window.add(game_over, 2, (self.window.height/2)+30) self.window.add(lose, 120, (self.window.height/2+120)) self.window.add(score_label, 100, self.window.height) def winner(self): """ This function will execute if and only if a player successfully eliminates all the bricks. After all bricks are eliminated, the window will show 'You win!!!!' and the final score. The score depends on the number of remaining bricks and the variable count. """ self.window.clear() you_win = GLabel('Love Cindy!!!!') you_win.font = '-60-bold' # One live remaining worth 100 points. self.score += self.lives * 100 score_label = GLabel('Your Score is:' + str(self.score), 0, 20) score_label.font = 'courier-25-bold' background = GRect(self.window.width, self.window.height) background.filled = True background.color = 'pink' background.fill_color = 'pink' self.window.add(background) self.window.add(you_win, 5, (self.window.height/2)+30) self.window.add(score_label, 50, self.window.height)
class BreakoutGraphics: def __init__(self, ball_radius=BALL_RADIUS, paddle_width=PADDLE_WIDTH, paddle_height=PADDLE_HEIGHT, paddle_offset=PADDLE_OFFSET, brick_rows=BRICK_ROWS, brick_cols=BRICK_COLS, brick_width=BRICK_WIDTH, brick_height=BRICK_HEIGHT, brick_offset=BRICK_OFFSET, brick_spacing=BRICK_SPACING, title='Breakout'): num_bricks = 100 # Create a graphical window, with some extra space. window_width = brick_cols * (brick_width + brick_spacing) - brick_spacing window_height = brick_offset + 3 * (brick_rows * (brick_height + brick_spacing) - brick_spacing) self.window = GWindow(width=window_width, height=window_height, title=title) self.paddle = GRect(paddle_width, paddle_height, x=(window_width - paddle_width)/2, y = window_height - \ paddle_offset) # initializing the paddle self.paddle.filled = True self.paddle.fill_color = 'black' self.window.add(self.paddle) # initializing the ball self.ball = GOval(width=ball_radius * 2, height=ball_radius * 2, x=window_width / 2 - BALL_RADIUS, y=window_height / 2 - BALL_RADIUS) self.ball.filled = True self.ball.fill_color = 'black' self.window.add(self.ball) # initial velocity self.vx = 0 self.vy = INITIAL_Y_SPEED # draw bricks self.draw_bricks() # number of lives left self.num_lives = 3 # running? big question mark self.running = False # brick count self.brick_count = 100 # mouse listeners onmouseclicked(self.start) onmousemoved(self.move_paddle) def start(self, event): """ Starts game on mouse click. """ self.running = True def draw_bricks(self): """ Draws bricks by row and with color changing for each turn. """ x_init = 0 curr_y = BRICK_OFFSET color_index = 0 for row in range(1, BRICK_ROWS + 1): curr_x = x_init for col in range(BRICK_COLS): self.brick = GRect(BRICK_WIDTH, BRICK_HEIGHT, x=curr_x, y=curr_y) self.brick.filled = True self.brick.fill_color = COLORS[color_index] self.window.add(self.brick) curr_x += BRICK_WIDTH + BRICK_SPACING curr_y += BRICK_HEIGHT + BRICK_SPACING if row % 2 == 0: color_index += 1 def move_paddle(self, event): """ Moves paddle with mouse event. """ self.paddle.x = event.x - PADDLE_WIDTH / 2 def move_ball(self): """ Moves ball with proper x and y velocities. """ self.ball.move(self.vx, self.vy) def handle_collisions(self): """ Handles collisions as necessary per animation loop. """ # changing for wall collisions if self.side_wall_hit( ): # checks for side wall hits and changes x-component print('oop hit a side wall') self.vx = -self.vx elif self.top_wall_paddle_hit( ): # checks for top/paddle hits and changes y-component print('oop hit the top or a paddle') self.vy = -self.vy # changing for block collisions elif self.brick_paddle_hit( ) != -1 and self.ball.y != self.window.height - 2 * BALL_RADIUS - PADDLE_OFFSET - PADDLE_HEIGHT: loc_x, loc_y = self.brick_paddle_hit() print(loc_x, loc_y) self.remove_brick(loc_x, loc_y) self.vy = -self.vy self.vx = random.uniform(-MAX_X_SPEED, MAX_X_SPEED) # changing for bottom collision elif self.bottom_hit(): self.num_lives -= 1 print('the number of lives is', self.num_lives) self.reset() def bottom_hit(self): """ Checks if the bottom was hit. """ if self.ball.y + 2 * BALL_RADIUS >= self.window.height: return True else: return False def side_wall_hit(self): """ Checks if the left or right walls were hit. """ if self.ball.x <= 0 or self.ball.x >= self.window.width - 2 * BALL_RADIUS: return True else: return False def top_wall_paddle_hit(self): """ Checks if the top wall or paddle was hit. """ if self.ball.y <= 0: return True elif self.brick_paddle_hit( ) != -1 and self.ball.y >= self.window.height - 2 * BALL_RADIUS - PADDLE_OFFSET: return True else: return False def brick_paddle_hit(self): """ Checks, according to the guidelines in the handout, if a collision has occurred with the ball and a brick or the paddle. """ if self.window.get_object_at(self.ball.x, self.ball.y) != None: return self.ball.x, self.ball.y elif self.window.get_object_at(self.ball.x + 2 * BALL_RADIUS, self.ball.y) != None: return self.ball.x + 2, self.ball.y elif self.window.get_object_at(self.ball.x, self.ball.y + 2 * BALL_RADIUS) != None: return self.ball.x, self.ball.y + 2 * BALL_RADIUS elif self.window.get_object_at(self.ball.x + 2 * BALL_RADIUS, self.ball.y + 2 * BALL_RADIUS) != None: return self.ball.x + 2 * BALL_RADIUS, self.ball.y + 2 * BALL_RADIUS else: return -1 def remove_brick(self, loc_x, loc_y): """ Remove a brick when hit. """ if self.window.get_object_at(loc_x, loc_y) != self.paddle: self.window.remove(self.window.get_object_at(loc_x, loc_y)) self.brick_count -= 1 def reset(self): """ Resets the game when the bottom is hit. """ self.window.clear() self.window.remove(self.ball) self.ball = GOval(width=BALL_RADIUS * 2, height=BALL_RADIUS * 2, x=self.window.width / 2 - BALL_RADIUS, y=self.window.height / 2 - BALL_RADIUS) self.ball.filled = True self.ball.fill_color = 'black' self.window.add(self.ball) self.draw_bricks() self.brick_count = 100 self.vx = 0 self.vy = INITIAL_Y_SPEED self.paddle.filled = True self.paddle.fill_color = 'black' self.window.add(self.paddle) self.running = False def game_over(self): game_over_label = GLabel('GAME OVER', x=self.window.width / 2 - 40, y=self.window.height / 2) self.window.add(game_over_label)
class BreakoutGraphics: def __init__(self, ball_radius=BALL_RADIUS, paddle_width=PADDLE_WIDTH, paddle_height=PADDLE_HEIGHT, title='Breakout'): # window self._window = GWindow( (BRICK_WIDTH + BRICK_SPACING) * BRICK_COLS + BRICK_SPACING, BRICK_OFFSET + 3 * (BRICK_ROWS * (BRICK_HEIGHT + BRICK_SPACING) - BRICK_SPACING), title=title) self._page = 0 # control which page to show # start page self._start_label = GLabel('') self._start_label.font = 'Chalkduster-40' self._start_label.color = 'black' self._highscore = 0 # game page self._ball = GOval(ball_radius, ball_radius) self._ball.filled = True self._paddle = GRect(paddle_width, paddle_height) self._paddle.filled = True self._score_board = GLabel('Score: 0') self._score_board.font = 'Chalkduster-20' self._score_board.color = 'black' self._num_lives = 3 self._live_board = GLabel(f'Lives: {self._num_lives}') self._live_board.font = 'Chalkduster-20' self._live_board.color = 'black' self._wind_vane = GLabel(f'Wind direction: ◎ ') self._wind_vane.font = 'Chalkduster-15' self._wind_vane.color = 'black' self._wind_sign = GLabel(f'Next wind: 10s') self._wind_sign.font = 'Chalkduster-15' self._wind_sign.color = 'black' self._last_wind_time = 0 self._next_wind_time = 0 self._wind_time_pass = 0 self._next_wind = 0 self._wind_change = False self._vx = 0 self._vy = 0 self._remove_ct = 0 self._ing = False self._protect_object = [ self._paddle, self._score_board, self._live_board, self._wind_vane, self._wind_sign ] self._invisible_object = [ self._score_board, self._live_board, self._wind_vane, self._wind_sign ] # mouse event onmouseclicked(self.click_event) onmousemoved(self.move_event) def draw_bricks(self): """ Draw bricks on the window. """ color_counter = -1 for row in range(BRICK_ROWS): if row % 2 == 0: color_counter += 1 for col in range(BRICK_COLS): brick = GRect(BRICK_WIDTH, BRICK_HEIGHT) brick.filled = True color = COLOR_LIST[color_counter % len(COLOR_LIST)] brick.fill_color = color brick.color = color self._window.add( brick, BRICK_SPACING + (BRICK_WIDTH + BRICK_SPACING) * col, BRICK_OFFSET + (BRICK_HEIGHT + BRICK_SPACING) * row) def game_reset(self): """ You can use this method to reset the game. """ self._window.clear() self._window.add( self._paddle, (self._window.width - self._paddle.width) // 2, self._window.height - PADDLE_OFFSET - self._paddle.height) self._window.add( self._ball, self._paddle.x + self._paddle.width // 2 - self._ball.width // 2, self._paddle.y - self._ball.height) self._score_board.text = f'Score: 0' self._window.add(self._live_board, LABEL_SPACING, self._window.height) self._window.add( self._score_board, self._live_board.x + self._live_board.width + LABEL_SPACING, self._window.height) self._window.add(self._wind_vane, self._window.width // 2, self._window.height) self._window.add( self._wind_sign, self._wind_vane.x + self._wind_vane.width + LABEL_SPACING, self._window.height) self._vx = 0 self._vy = 0 self._remove_ct = 0 self._ing = False self._wind_change = True self._wind_time_pass = 0 def detect_collision(self): """ Use four corners of the ball to check if collide with any other object. """ if 0 > self._vy: top_left = self._window.get_object_at(self._ball.x, self._ball.y) top_right = self._window.get_object_at( self._ball.x + self._ball.width, self._ball.y) if top_left is not None and top_right is not None: if top_left is top_right: if top_left not in self._protect_object: self._window.remove(top_left) self._remove_ct += 1 else: if top_left not in self._protect_object: self._window.remove(top_left) self._remove_ct += 1 if top_right not in self._protect_object: self._window.remove(top_right) self._remove_ct += 1 if top_left not in self._invisible_object and top_right not in self._invisible_object: self.set_ball_velocity(y=-1) elif top_left is None and top_right is not None: if self._vx > 0: if top_right not in self._protect_object: self._window.remove(top_right) self._remove_ct += 1 if abs(self._ball.x + self._ball.width - top_right.x) >= abs(self._ball.y - top_right.y + top_right.height): if top_right not in self._invisible_object: self.set_ball_velocity(y=-1) else: if top_right not in self._invisible_object: self.set_ball_velocity(x=-1) else: if top_right not in self._protect_object: self._window.remove(top_right) self._remove_ct += 1 if top_right not in self._invisible_object: self.set_ball_velocity(y=-1) elif top_left is not None and top_right is None: if self._vx > 0: if top_left not in self._protect_object: self._window.remove(top_left) self._remove_ct += 1 if top_left not in self._invisible_object: self.set_ball_velocity(y=-1) else: if top_left not in self._protect_object: self._window.remove(top_left) self._remove_ct += 1 if abs(self._ball.x - top_left.x + top_left.width) >= abs(self._ball.y - top_left.y + top_left.height): if top_left not in self._invisible_object: self.set_ball_velocity(y=-1) else: if top_left not in self._invisible_object: self.set_ball_velocity(x=-1) else: bottom_left = self._window.get_object_at( self._ball.x, self._ball.y + self._ball.height) bottom_right = self._window.get_object_at( self._ball.x + self._ball.width, self._ball.y + self._ball.height) if bottom_left is not None and bottom_right is not None: if bottom_left is bottom_right: if bottom_left not in self._protect_object: self._window.remove(bottom_left) self._remove_ct += 1 else: if bottom_left not in self._protect_object: self._window.remove(bottom_left) self._remove_ct += 1 if bottom_right not in self._protect_object: self._window.remove(bottom_right) self._remove_ct += 1 if bottom_left not in self._invisible_object and bottom_right not in self._invisible_object: self.set_ball_velocity(y=-1) elif bottom_left is not None and bottom_right is None: if self._vx < 0: if bottom_left not in self._protect_object: self._window.remove(bottom_left) self._remove_ct += 1 if abs(bottom_left.x + bottom_left.width - self._ball.x) >= abs(self._ball.y + self._ball.height - bottom_left.y): if bottom_left not in self._invisible_object: self.set_ball_velocity(y=-1) else: if bottom_left not in self._invisible_object: self.set_ball_velocity(x=-1) else: if bottom_left not in self._protect_object: self._window.remove(bottom_left) self._remove_ct += 1 if bottom_left not in self._invisible_object: self.set_ball_velocity(y=-1) elif bottom_left is None and bottom_right is not None: if self._vx < 0: if bottom_right not in self._protect_object: self._window.remove(bottom_right) self._remove_ct += 1 if bottom_right not in self._invisible_object: self.set_ball_velocity(y=-1) else: if bottom_right not in self._protect_object: self._window.remove(bottom_right) self._remove_ct += 1 if abs(self._ball.x + self._ball.width - bottom_right.x) >= abs(self._ball.y + self._ball.height - bottom_right.y): if bottom_right not in self._invisible_object: self.set_ball_velocity(y=-1) else: if bottom_right not in self._invisible_object: self.set_ball_velocity(x=-1) self.update_score() def update_score(self): """ Update the score on the window """ score = self._remove_ct * 10 self._score_board.text = f'Score: {score}' if score > self._highscore: self._highscore = score def wind(self): """ This method create random wind speed and direction to influent the movement of ball. """ def random_wind(): speed = random.randint(1, 10) dir_ = random.randrange(-1, 2, 2) return speed, dir_ if self._ing: self._wind_time_pass += (time.time() - self._last_wind_time) self._wind_sign.text = f'Next wind: {10 - round(self._wind_time_pass)}s' self._last_wind_time = time.time() if self._wind_change: self._next_wind = random_wind() self._wind_change = False wind_speed = self._next_wind[0] wind_dir = self._next_wind[1] if wind_dir > 0: self._wind_vane.text = f'Next wind direction: →{wind_speed}' self._wind_sign.x = self._wind_vane.x + self._wind_vane.width + LABEL_SPACING elif wind_dir < 0: self._wind_vane.text = f'Next wind direction: ←{wind_speed}' self._wind_sign.x = self._wind_vane.x + self._wind_vane.width + LABEL_SPACING if self._wind_time_pass >= 10: self._vx = wind_speed * wind_dir self._wind_change = True self._wind_time_pass = 0 else: self._wind_sign.text = f'Next wind: 10s' self._last_wind_time = time.time() def start_page(self): """ This method draw the start page and restart page. """ if self._page == 0: check = self._window.get_object_at(self._window.width // 2, self._window.height // 2) if check is None: progress_rate_board = GLabel(f'Loading...0%') progress_rate_board.font = 'Chalkduster-15' self._window.add( progress_rate_board, (self._window.width - progress_rate_board.width) // 2, (self._window.height + PROGRESS_BAR_SIZE) // 2 + progress_rate_board.height + LABEL_SPACING) pause_time = 300 for i in range(10): color = COLOR_LIST[i % len(COLOR_LIST)] progress_bar = GRect(PROGRESS_BAR_SIZE * (i + 1), PROGRESS_BAR_SIZE) progress_bar.filled = True progress_bar.fill_color = color progress_bar.color = color self._window.add( progress_bar, self._window.width // 2 - PROGRESS_BAR_SIZE * 5, self._window.height // 2 - PROGRESS_BAR_SIZE // 2) progress_rate_board.text = f'Loading...{10*(i+1)}' pause(pause_time) pause_time += 100 self._window.clear() self.draw_bricks() self._start_label.text = f'Click to start' self._window.add( self._start_label, (self._window.width - self._start_label.width) // 2, (self._window.height + self._start_label.height) // 2) elif self._page == 2: self._window.clear() # self.draw_bricks() self._start_label.text = f'Click to restart' self._window.add( self._start_label, (self._window.width - self._start_label.width) // 2, (self._window.height + self._start_label.height) // 2) highscore_board = GLabel(f'High score: {self._highscore}') highscore_board.font = 'Chalkduster-60' highscore_board.color = 'navy' self._window.add( highscore_board, (self._window.width - highscore_board.width) // 2, self._start_label.y - self._start_label.height - LABEL_SPACING * 3) def check_over(self): """ Check if the game is over. """ if self._remove_ct == BRICK_COLS * BRICK_ROWS or self._num_lives == 0: return True def click_event(self, m): """ Control the behavior of mouse click. """ if 0 < m.x < self._window.width and 0 < m.y < self._window.height: # start page if self._page == 0 or self._page == 2: if self._start_label.x <= m.x <= self._start_label.x + self._start_label.width and self._start_label.y - self._start_label.height <= m.y <= self._start_label.y: self._page = 1 self._ing = False self._num_lives = 3 self._live_board.text = f'Lives: {self._num_lives}' # game page elif self._page == 1: if not self._ing: self._vx = random.randint( 1, MAX_X_SPEED) * random.randrange(-1, 2, 2) self._vy = -INITIAL_Y_SPEED self._ing = True def move_event(self, m): """ Control the behavior of mouse move. """ if self._page == 0 or self._page == 2: if self._start_label.x <= m.x <= self._start_label.x + self._start_label.width and self._start_label.y - self._start_label.height <= m.y <= self._start_label.y: self._start_label.color = 'magenta' else: self._start_label.color = 'black' elif self._page == 1: if self._paddle.width // 2 < m.x < self._window.width - self._paddle.width // 2: self._paddle.x = m.x - self._paddle.width // 2 elif m.x <= self._paddle.width // 2: self._paddle.x = 0 elif m.x >= self._window.width - self._paddle.width // 2: self._paddle.x = self._window.width - self._paddle.width if self._vx == 0 and self._vy == 0: self._ball.x = self._paddle.x + (self._paddle.width - self._ball.width) // 2 def get_page(self): return self._page def set_page(self, page_num): self._page = page_num def get_ball(self): return self._ball def get_ball_velocity(self): return self._vx, self._vy def set_ball_velocity(self, x=1, y=1): self._vx *= x self._vy *= y def get_window(self): return self._window def get_lives(self): return self._num_lives def set_lives(self): self._num_lives -= 1 self._live_board.text = f'Lives: {self._num_lives}' def get_game_start(self): return self._ing
class BreakoutGraphics: def __init__(self, ball_radius=BALL_RADIUS, paddle_width=PADDLE_WIDTH, paddle_height=PADDLE_HEIGHT, paddle_offset=PADDLE_OFFSET, brick_rows=BRICK_ROWS, brick_cols=BRICK_COLS, brick_width=BRICK_WIDTH, brick_height=BRICK_HEIGHT, brick_offset=BRICK_OFFSET, brick_spacing=BRICK_SPACING, title='Breakout'): # Create a graphical window, with some extra space. self.window_width = brick_cols * (brick_width + brick_spacing) - brick_spacing self.window_height = brick_offset + 3 * ( brick_rows * (brick_height + brick_spacing) - brick_spacing) self.window = GWindow(width=self.window_width, height=self.window_height, title=title) # set up the paddle. self.paddle_width = paddle_width self.paddle_height = paddle_height self.paddle = GRect(paddle_width, paddle_height, x=(self.window_width - PADDLE_WIDTH) / 2, y=self.window_height - paddle_offset - paddle_height) self.paddle.filled = True self.paddle.color = 'slategrey' self.paddle.fill_color = 'slategrey' # set up a the ball. self.ball_radius = ball_radius self.ball = GOval(self.ball_radius * 2, self.ball_radius * 2, x=(self.window_width - self.ball_radius * 2) / 2, y=(self.window_height - self.ball_radius * 2) / 2) self.ball.filled = True self.ball.color = 'crimson' self.ball.fill_color = 'crimson' # Default initial velocity for the ball. self.__dx = random.randint(MIN_X_SPEED, MAX_X_SPEED) if random.random() > 0.5: self.__dx = -self.__dx self.__dy = INITIAL_Y_SPEED # Initialize our mouse listeners. onmousemoved(self.move_paddle) onmouseclicked(self.switch) # Control whether the user can start the game self.__start = False # Determine the lives the user gets self.__num_lives = 0 # Attributes relate to the score self.__score = 0 self.__win_score = BRICK_COLS * BRICK_ROWS * 10 self.score_sign = GLabel(f'SCORE : {self.__score}') self.can_move = False self.can_drop = True self.reward = 0 # Attribute relate to live sign self.live_2 = GOval(self.ball_radius * 2, self.ball_radius * 2, x=self.window_width - (self.ball_radius * 2 + 8), y=self.window_height - (self.ball_radius * 2 + 8)) self.live_1 = GOval(self.ball_radius * 2, self.ball_radius * 2, x=self.window_width - (self.ball_radius * 2 + 8) * 2, y=self.window_height - (self.ball_radius * 2 + 8)) # Attributes relate to extension opening self.o_ball = GOval(30, 30, x=(self.window.width - 30) / 2, y=240) self.start_button = GLabel('S T A R T') self.__enter_game = False # Attributes relate to extension 'game over' scene self.game_over_w = GLabel('G A M E O V E R') # Attributes relate to rewards self.hint_sign = 0 self.reverse_paddle = False self.paddle_adding = False self.adding_count = 0 # To store the mouse event self.paddle_x = 0 def set_game(self): """ This method set up the starting interface for the game. Including materials such as: paddle, window, score sign and bricks. """ # Create a paddle. self.window.add(self.paddle) # Center a filled ball in the graphical window. self.window.add(self.ball) # Build the score sign self.score_sign = GLabel(f'SCORE : {self.__score}') self.score_sign.color = 'crimson' self.score_sign.font = 'Mamelon-20' self.window.add(self.score_sign, x=8, y=self.window.height - 8) # Build lives sign self.live_2.filled = True self.live_2.color = 'crimson' self.live_2.fill_color = 'crimson' self.window.add(self.live_2) self.live_1.filled = True self.live_1.color = 'crimson' self.live_1.fill_color = 'crimson' self.window.add(self.live_1) # Draw bricks. for i in range(BRICK_ROWS): for j in range(BRICK_COLS): brick = GRect(BRICK_WIDTH, BRICK_HEIGHT, x=i * (BRICK_WIDTH + BRICK_SPACING), y=BRICK_OFFSET + j * (BRICK_HEIGHT + BRICK_SPACING)) brick.filled = True if j == 0 or j == 1: brick.color = 'darkslategrey' brick.fill_color = 'darkslategrey' elif j == 2 or j == 3: brick.color = 'teal' brick.fill_color = 'teal' elif j == 4 or j == 5: brick.color = 'cadetblue' brick.fill_color = 'cadetblue' elif j == 6 or j == 7: brick.color = 'lightseagreen' brick.fill_color = 'lightseagreen' else: brick.color = 'mediumturquoise' brick.fill_color = 'mediumturquoise' self.window.add(brick) def move_paddle(self, event): """ This method keep mouse event regarding reverse and not reverse situation of the paddle. Also, it keeps the paddle within the window . :param event: mouse event """ if self.reverse_paddle: # reverse paddle movement event_x = self.window_width - event.x else: # Normal paddle movement event_x = event.x # Keeping the paddle within the window if event_x - self.paddle_width / 2 < 0: self.paddle.x = 0 elif event_x + self.paddle_width / 2 > self.window.width: self.paddle.x = self.window.width - self.paddle_width # Control the paddle when the mouse is in the window else: self.paddle.x = event_x - self.paddle_width / 2 # To store the mouse event self.paddle_x = self.paddle.x def reset_ball(self): """ This method resets the ball at the starting position, and will not drop unless the the user clicks. """ self.ball = GOval(self.ball_radius * 2, self.ball_radius * 2, x=(self.window_width - self.ball_radius * 2) / 2, y=(self.window_height - self.ball_radius * 2) / 2) self.ball.filled = True self.ball.color = 'crimson' self.ball.fill_color = 'crimson' self.window.add(self.ball) self.__start = False # getter for the ball's moving speed. def get_dx(self): return self.__dx # getter for the ball's moving speed. def get_dy(self): return self.__dy # getter, see if the ball is ready to be click and fall def start(self): return self.__start # getter, see if the start button in the opening scene is clicked def get_enter_game(self): return self.__enter_game # setter, the user can set up the initial lives. def set_num_lives(self, num_lives): self.__num_lives = num_lives def switch(self, e): """ This method is the switch for 2 purpose. 1. whether the user can start the game. In the start of each round, if user's lives > 0, the switch will set to open, the ball will move after the user click the window. 2. whether the start button in the opening scene is clicked. If clicked, set up the game. If not, wait for the click. """ if self.__num_lives > 0: self.__start = True if self.window.get_object_at(e.x, e.y) is self.o_ball or \ self.window.get_object_at(e.x, e.y) is self.start_button: self.__enter_game = True # To get the position of the 4 corners of the ball def corner_hits(self): """ This method check from corner_1 to corner_4, to see if any corner of the ball has encounter obstacles. If so, return the method to remove object encountered. If not, move on to the next corner. :return: method, remove the object on the encountered corner. """ # To get position of each corner of the ball corner_1 = self.window.get_object_at(self.ball.x, self.ball.y) corner_2 = self.window.get_object_at( self.ball.x + 2 * self.ball_radius, self.ball.y) corner_3 = self.window.get_object_at( self.ball.x, self.ball.y + 2 * self.ball_radius) corner_4 = self.window.get_object_at( self.ball.x + 2 * self.ball_radius, self.ball.y + 2 * self.ball_radius) # To check which corner hits things if corner_1 is not None: return corner_1 elif corner_2 is not None: return corner_2 elif corner_3 is not None: return corner_3 elif corner_4 is not None: return corner_4 else: pass # To check whether the ball hits the wall def if_hits_walls(self): """ This method checks whether the ball hits the side walls and the the top wall. If so, the ball will bounce back. """ # The ball hits the side walls and the the top wall if self.ball.x <= 0 or self.ball.x + 2 * self.ball_radius >= self.window.width: self.__dx *= -1 # The ball hits the the top wall if self.ball.y <= 0: self.__dy *= -1 # To check what the ball hits def if_hits_things(self): """ This method determines what kind of object the ball hits, and run the actions after hitting certain objects. """ # The ball hits the paddle if self.corner_hits() is self.paddle: # bounce back if self.__dy >= 0: self.__dy *= -1 else: # debug: keep the ball from sticking on the paddle pass # Clean up the hint sign self.window.remove(self.hint_sign) # the ball hits other item, pass elif self.corner_hits() is self.score_sign or self.corner_hits() is self.hint_sign or self.corner_hits() is self.reward \ or self.corner_hits() is self.live_2 or self.corner_hits() is self.live_1: pass # The ball hits the paddle elif self.corner_hits() is not None: # The thing hit by the ball need to be removed self.window.remove(self.corner_hits()) # Speed up and bounce self.__dy *= -1.005 self.__score += 10 # Show score self.show_score() # The reward will dropped everytime the user get 70 more score. if self.__score % 70 == 0 and self.can_drop: # Choose the dropping x position randomly t_x = random.choice(range(1, 10)) # Set up the reward object self.reward = GRect(10, 10, x=self.window.width * t_x // 10, y=self.window.height / 3) self.reward.filled = True self.reward.fill_color = 'darkslateblue' self.reward.color = 'darkslateblue' self.window.add(self.reward) self.can_move = True else: pass def show_score(self): """ This method remove the previous score sign and create a new one. """ self.window.remove(self.score_sign) self.score_sign = GLabel(f'SCORE : {self.__score}') self.score_sign.color = 'crimson' self.score_sign.font = 'Mamelon-20' self.window.add(self.score_sign, x=8, y=self.window.height - 8) # To check: if the reward is allowed to drop / if it was obtained or missed. def test_move(self): if self.can_move: self.can_drop = False self.reward.move(0, 5) # The user got the reward if self.window.get_object_at(self.reward.x + 7, self.reward.y + 15) is self.paddle: self.window.remove(self.reward) self.run_reward() self.can_move = False self.can_drop = True # The user miss the reward -- clean up if self.reward.y + 15 >= self.window.height: self.window.remove(self.reward) self.can_move = False self.can_drop = True def run_reward(self): """ This method uses random to choose a reward to run randomly. Also, it shows a sign for the running reward. Possible situation are listed below: 1. Make the paddle longer 2. Make the paddle shorter 3. Reverse the paddle's movement 4. Speed up the ball 5. Slow down the ball """ # Choose a reward randomly choice = random.choice(range(6)) self.window.remove(self.hint_sign) # paddle width ++ if choice == 0: self.window.remove(self.paddle) self.paddle_width += 20 self.paddle = GRect(self.paddle_width, self.paddle_height, x=self.paddle_x - self.paddle_width / 2, y=self.window_height - PADDLE_OFFSET - self.paddle_height) self.build_paddle() self.hint_sign = GLabel('ADD UP') self.show_hint() # paddle width -- elif choice == 1: self.paddle_adding = False if self.paddle_width <= 30: pass else: self.window.remove(self.paddle) self.paddle_width -= 30 self.paddle = GRect(self.paddle_width, self.paddle_height, x=self.paddle_x - self.paddle_width / 2, y=self.window_height - PADDLE_OFFSET - self.paddle_height) self.build_paddle() self.hint_sign = GLabel('WATCH OUT') self.show_hint() # Set reverse paddle movement elif choice == 2 or choice == 3: if self.reverse_paddle: self.reverse_paddle = False self.hint_sign = GLabel('REVERSE AGAIN') self.show_hint() else: self.reverse_paddle = True self.hint_sign = GLabel('REVERSE') self.show_hint() # Speed up the ball elif choice == 4: self.__dy *= 1.1 self.hint_sign = GLabel('SPEED UP') self.show_hint() # Slow down the ball else: self.__dy *= 0.9 self.hint_sign = GLabel('SLOW DOWN') self.show_hint() def build_paddle(self): """ This methods build up paddle for each reward. """ self.window.add(self.paddle) self.paddle.filled = True self.paddle.color = 'slategrey' self.paddle.fill_color = 'slategrey' def show_hint(self): """ This method shows a sign to tell user that clicking can earn rewards """ self.hint_sign.color = 'crimson' self.hint_sign.font = 'Mamelon-20' self.window.add(self.hint_sign, x=(self.window.width - self.hint_sign.width) / 2, y=self.window_height - PADDLE_OFFSET - PADDLE_HEIGHT - 30) def if_lose_life(self): """ This method check if the user lost life. If so, will reset the ball if there's still lives remain. """ if self.ball.y >= self.window.height: self.window.remove(self.ball) self.window.remove(self.hint_sign) self.__num_lives -= 1 if self.__num_lives == 2: # Build 1 point self.window.remove(self.live_1) elif self.__num_lives == 1: # empty self.window.remove(self.live_2) # Avoid being too hard for players to get ball with reverse paddle self.reverse_paddle = False self.__dy = INITIAL_Y_SPEED if self.__num_lives > 0: # User still has chances self.reset_ball() # Getter, to check if it's the end of the game (either win or lose) def end_game(self): if self.__num_lives <= 0 or self.__score >= self.__win_score: return True # To check if the game is over def game_over(self): if self.__num_lives <= 0: self.__enter_game = False return True def build_game_over(self): """ This method builds the 'Game Over' scene """ # delete the score line = GLine(4, self.window.height - 18, 12 + self.score_sign.width, self.window.height - 18) line.color = 'crimson' self.window.add(line) # Clean up self.window.remove(self.reward) self.window.remove(self.hint_sign) # Build up the 'Game Over' sign self.game_over_w.font = "Mamelon-35" self.game_over_w.color = 'midnightblue' self.window.add(self.game_over_w, x=(self.window.width - self.game_over_w.width) / 2, y=self.window.height / 2) # Create the movement of the 'Game Over' sign speed = self.__dy while True: self.game_over_w.move(0, speed) speed += 0.5 if self.game_over_w.y >= self.window.height * 2 // 3: speed *= -0.7 pause(10) # The sign will end with a shaking animation. # To check if the user won the game def win_game(self): if self.__score >= self.__win_score: return True def build_win(self): """ This method builds the 'Winning' scene """ win_sign_1 = GLabel('S A V A G E') win_sign_1.font = 'Mamelon-40' win_sign_1.color = 'mediumturquoise' win_sign_2 = GLabel('S A V A G E') win_sign_2.font = 'Mamelon-40' win_sign_2.color = 'mediumturquoise' win_sign_3 = GLabel('S A V A G E') win_sign_3.font = 'Mamelon-40' win_sign_3.color = 'mediumturquoise' # To create the flashing animation for i in range(12): if i % 2 != 0: self.window.add(win_sign_1, x=(self.window.width - win_sign_1.width) / 2, y=(self.window.height - win_sign_1.height) / 2) self.window.add( win_sign_2, x=(self.window.width - win_sign_2.width) / 2, y=(self.window.height - win_sign_2.height) / 2 + 50) self.window.add( win_sign_3, x=(self.window.width - win_sign_3.width) / 2, y=(self.window.height - win_sign_3.height) / 2 + 100) else: self.window.remove(win_sign_1) self.window.remove(win_sign_2) self.window.remove(win_sign_3) pause(100) self.fire_work() def fire_work(self): """ This method creates a firework animation. """ # Numbers of the firework for i in range(10): f_x = random.randint(self.window.width // 8, self.window.width * 7 // 8) f_y = random.randint(self.window.height // 10, self.window.height * 9 // 10) size = random.randint(4, 7) # The size of the firework for j in range(size): fire = GOval(10 + 20 * j, 10 + 20 * j, x=f_x - 10 * j, y=f_y - 10 * j) # Choose color randomly fire.color = self.choose_color() self.window.add(fire) pause(100) self.window.remove(fire) pause(500) @staticmethod def choose_color(): """ This method help choose the color for each circle of the firework randomly """ num = random.choice(range(6)) if num == 0: return "crimson" elif num == 1: return "midnightblue" elif num == 2: return "limegreen" elif num == 3: return "cyan" elif num == 4: return 'darkviolet' else: return "gold" def window_clear(self): """ This method clean up the whole window after the opening scene is over. """ self.window.clear() def set_opening(self): """ This method set up the whole opening scene. """ # To create the tube start_button_1 = GRect(60, 211, x=(self.window.width - 60) / 2, y=-1) start_button_1.color = 'slategrey' start_button_1.filled = True start_button_1.fill_color = 'slategrey' start_button_2 = GRect(50, 206, x=(self.window.width - 50) / 2) start_button_2.color = 'gainsboro' start_button_2.filled = True start_button_2.fill_color = 'gainsboro' self.window.add(start_button_1) self.window.add(start_button_2) head = GPolygon() head.add_vertex(((self.window.width - 60) / 2, 210)) head.add_vertex(((self.window.width - 60) / 2 + 60, 210)) head.add_vertex(((self.window.width - 60) / 2 + 60 + 20, 240)) head.add_vertex(((self.window.width - 60) / 2 - 20, 240)) head.color = 'slategrey' head.filled = True head.fill_color = 'slategrey' self.window.add(head) # Loading animation for i in range(10): load = GRect(40, 15, x=(self.window.width - 60) / 2 + 10, y=5 + 20 * i) load.filled = 'True' load.color = 'slategrey' load.fill_color = 'slategrey' self.window.add(load) pause(100) # Bouncing ball self.o_ball.filled = 'True' self.o_ball.color = 'crimson' self.o_ball.fill_color = 'crimson' self.window.add(self.o_ball) ball_vy = 5 count = 0 while True: self.o_ball.move(0, ball_vy) ball_vy += 1 if self.o_ball.y + 30 >= self.window.height: ball_vy *= -0.9 count += 1 if count == 5 and ball_vy >= 0: break pause(10) self.window.remove(self.o_ball) # Blowing balloon animation for i in range(55): self.o_ball = GOval(30 + i, 30 + i, x=(self.window.width - 30 + i) / 2 - i, y=415 - i) self.o_ball.filled = 'True' self.o_ball.color = 'crimson' self.o_ball.fill_color = 'crimson' self.window.add(self.o_ball) pause(7) # Flashing start sign animation for i in range(10): self.start_button.font = 'Mamelon-20' if i % 2 == 0: self.start_button.color = 'crimson' else: self.start_button.color = 'snow' self.window.add(self.o_ball) self.window.add(self.start_button, x=(self.window.width - 64) / 2, y=413) pause(100) # Show the rules rule_1 = GLabel('3 LIVES.') rule_1.color = 'darkslategrey' rule_1.font = 'Mamelon-20' rule_2 = GLabel("CATCH ' ' FOR RANDOM EFFECTS.") rule_2.color = 'darkslategrey' rule_2.font = 'Mamelon-20' rule_3 = GLabel('SCORE 1000 ---> FIREWORKS.') rule_3.color = 'crimson' rule_3.font = 'Mamelon-20' square = GRect(10, 10, x=self.window.width / 10 + 63, y=self.window.height * 4 // 5 + 10) square.filled = True square.fill_color = 'darkslateblue' square.color = 'darkslateblue' self.window.add(square) self.window.add(rule_1, x=self.window.width / 10, y=self.window.height * 4 // 5) self.window.add(rule_2, x=self.window.width / 10, y=self.window.height * 4 // 5 + 25) self.window.add(rule_3, x=self.window.width / 10, y=self.window.height * 4 // 5 + 50)