High-level Game Structure
Let’s take a crack at piecing together a complete pygame with a
- start screen,
- game play mode with scorekeeping and a lose condition, and
- an end screen (with the option for replay).
'''
Block catchy game with all the bells and whistles(?), maybe
'''
import sys
import random
import pygame
# CONSTANTS
SCREEN_WIDTH = 1200
SCREEN_HEIGHT = 800
FPS = 60
BTN_MARGIN = 5
CATCHER_WIDTH = 100
CATCHER_HEIGHT = 25
CATCHER_SPEED = 10
CATCHER_START_HEALTH = 2
OBJ_SPAWN_CHANCE = 0.2
CHANCE_SPAWN_IS_GOOD = 0.25
OBJ_SPEED = 5
BAD_OBJECT_WIDTH = 10
BAD_OBJECT_HEIGHT = 10
BAD_OBJECT_DMG = 1
GOOD_OBJECT_WIDTH = 10
GOOD_OBJECT_HEIGHT = 10
BAD_OBJECT_WIDTH = 10
BAD_OBJECT_HEIGHT = 10
RED = [255, 0, 0]
LIGHT_GREY = [230, 230, 230]
BLACK = [0, 0, 0]
BLUE = [0,0,230]
WHITE = [255, 255, 255]
YELLOW = [255, 229, 153]
# Initial setup
pygame.init()
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
pygame.display.set_caption("EXTREME BLOCK CATCHING")
clock = pygame.time.Clock()
game_screens = ['title', 'play', 'end']
cur_game_screen = 'title'
title_bg_color = BLACK
play_bg_color = LIGHT_GREY
end_bg_color = BLACK
button_color = YELLOW
title_font = pygame.font.SysFont('impact', 64)
button_font = pygame.font.SysFont('impact', 18)
game_info_font = pygame.font.SysFont('impact', 24)
################################################################################
# Title screen components
################################################################################
title_screen_bg_color = BLACK
# Title text
title_surface = title_font.render('2 FAST 2 CATCH BLOCKS', True, WHITE)
title_rect = title_surface.get_rect()
title_rect.x = (SCREEN_WIDTH / 2) - (title_rect.width / 2) # Put rect in middle of screen (perfectly in middle x)
title_rect.y = (SCREEN_HEIGHT / 2) - (title_rect.height) # Put rect in middle of the screen (sitting on top of horizontal midline)
# Play button
play_btn_txt_surface = button_font.render('>>>> CATCH BLOCKS <<<<', True, BLACK)
# setup play button background
play_btn_bg_rect = play_btn_txt_surface.get_rect()
play_btn_bg_rect.width += 2 * BTN_MARGIN # Add some margins to the button
play_btn_bg_rect.height += 2 * BTN_MARGIN # Add margin to the button
play_btn_bg_rect.x = title_rect.midbottom[0] - (play_btn_bg_rect.width / 2)
play_btn_bg_rect.y = title_rect.midbottom[1]
# setup play button text
play_btn_txt_rect = play_btn_txt_surface.get_rect()
play_btn_txt_rect.x = play_btn_bg_rect.x + BTN_MARGIN
play_btn_txt_rect.y = play_btn_bg_rect.y + BTN_MARGIN
################################################################################
# Play screen components
################################################################################
catcher_start_x = (SCREEN_WIDTH / 2) - (CATCHER_WIDTH / 2)
catcher_start_y = SCREEN_HEIGHT - CATCHER_HEIGHT - 1
catcher = pygame.Rect([catcher_start_x, catcher_start_y], [CATCHER_WIDTH, CATCHER_HEIGHT])
falling_objects = []
# bomb_img = pygame.image.load('bomb-small.png') # Challenge: use bomb img instead of red squares
health = CATCHER_START_HEALTH
score = 0
################################################################################
# End screen components
################################################################################
# game over
game_over_txt = title_font.render('GAME OVER', True, BLACK)
game_over_rect = game_over_txt.get_rect()
game_over_rect.x = (SCREEN_WIDTH / 2) - (game_over_rect.width / 2) # Put rect in middle of screen (perfectly in middle x)
game_over_rect.y = (SCREEN_HEIGHT / 2) - (game_over_rect.height) # Put rect in middle of the screen (sitting on top of horizontal midline)
# Replay button
replay_btn_txt = game_info_font.render('PLAY AGAIN?', True, BLACK)
# setup play button background
replay_btn_bg_rect = replay_btn_txt.get_rect()
replay_btn_bg_rect.width += 2 * BTN_MARGIN # Add some margins to the button
replay_btn_bg_rect.height += 2 * BTN_MARGIN # Add margin to the button
replay_btn_bg_rect.x = game_over_rect.midbottom[0] - (replay_btn_bg_rect.width / 2)
replay_btn_bg_rect.y = game_over_rect.midbottom[1]
# setup play button text
replay_btn_txt_rect = replay_btn_txt.get_rect()
replay_btn_txt_rect.x = replay_btn_bg_rect.x + BTN_MARGIN
replay_btn_txt_rect.y = replay_btn_bg_rect.y + BTN_MARGIN
while True:
if cur_game_screen == 'title':
screen.fill(title_bg_color)
# Draw the title
screen.blit(title_surface, title_rect)
# Draw the play button
pygame.draw.rect(screen, button_color, play_btn_bg_rect)
screen.blit(play_btn_txt_surface, play_btn_txt_rect)
elif cur_game_screen == 'play':
screen.fill(play_bg_color)
# Use arrow keys to control the catcher
pressed_keys = pygame.key.get_pressed()
if pressed_keys[pygame.K_RIGHT] and not pressed_keys[pygame.K_LEFT]:
catcher.x = (catcher.x + CATCHER_SPEED) % SCREEN_WIDTH
elif pressed_keys[pygame.K_LEFT] and not pressed_keys[pygame.K_RIGHT]:
catcher.x = (catcher.x - CATCHER_SPEED) % SCREEN_WIDTH
# Draw falling rocks
spawn_new_falling_object = len(falling_objects) == 0 or (random.random() < OBJ_SPAWN_CHANCE)
if spawn_new_falling_object:
# Good or bad object?
obj_good = random.random() < CHANCE_SPAWN_IS_GOOD
if obj_good:
# obj is friendly
new_obj_rect = pygame.Rect([random.randint(GOOD_OBJECT_WIDTH, SCREEN_WIDTH - 2*GOOD_OBJECT_WIDTH), 0], [GOOD_OBJECT_WIDTH, GOOD_OBJECT_HEIGHT])
falling_objects.append({"rect": new_obj_rect, "good": True})
else:
# obj is EVIL
new_obj_rect = pygame.Rect([random.randint(BAD_OBJECT_WIDTH, SCREEN_WIDTH - 2*BAD_OBJECT_WIDTH), 0], [BAD_OBJECT_WIDTH, BAD_OBJECT_HEIGHT])
falling_objects.append({"rect": new_obj_rect, "good": False})
# Draw objects
on_screen_objs = []
for obj in falling_objects:
obj["rect"].y += OBJ_SPEED
color = BLUE if obj["good"] else RED
pygame.draw.rect(screen, color, obj["rect"])
if catcher.colliderect(obj["rect"]):
# Collision!
if obj["good"]:
score += 1
else:
health -= BAD_OBJECT_DMG
print("Health =", health, "Score =", score)
elif obj["rect"].y < SCREEN_HEIGHT:
on_screen_objs.append(obj)
falling_objects = on_screen_objs
# Draw the catcher rectangle
pygame.draw.rect(screen, BLACK, catcher)
# Draw game info
health_surface = game_info_font.render('HEALTH: ' + str(health), True, BLACK)
health_rect = health_surface.get_rect()
health_rect.x = SCREEN_WIDTH - health_rect.width - 50
health_rect.y = 50
screen.blit(health_surface, health_rect)
score_surface = game_info_font.render('SCORE: ' + str(score), True, BLACK)
score_rect = score_surface.get_rect()
score_rect.x = SCREEN_WIDTH - score_rect.width - 50
score_rect.y = health_rect.bottom + 50
screen.blit(score_surface, score_rect)
# End game state
if health == 0:
cur_game_screen = 'end'
elif cur_game_screen == 'end':
# screen.fill(end_bg_color)
screen.blit(game_over_txt, game_over_rect)
pygame.draw.rect(screen, button_color, replay_btn_bg_rect)
screen.blit(replay_btn_txt, replay_btn_txt_rect)
# setup the event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if cur_game_screen == 'title':
# Title screen events
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
if play_btn_bg_rect.collidepoint(mouse_pos):
cur_game_screen = 'play'
if cur_game_screen == 'end':
# End game screen events
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
if replay_btn_bg_rect.collidepoint(mouse_pos):
cur_game_screen = 'title'
score = 0
health = CATCHER_START_HEALTH
falling_objects = []
catcher.x = catcher_start_x
catcher.y = catcher_start_y
pygame.display.update()
clock.tick(FPS)
|>> download block_catchy.py