Creating games in Python is an exciting way to combine coding skills with creativity. Whether you’re new to Python or an experienced developer looking for a fun project, building a ‘Catch the Falling Objects‘ game is a perfect choice. Not only will you get to work with graphics and animation, but you’ll also learn how to handle user inputs in real-time.
Today, you’ll learn how to code your very own Catch the Falling Objects game in Python. We’ll walk through everything step-by-step – from setting up the game window to adding those falling objects, making sure your player has an exciting experience. So, grab your keyboard and let’s start coding!
Table of Contents
- Setting Up Pygame for Your Game
- Defining Game Constants and Setting Up Display
- Implementing Game Functions: Movement, Object Creation, and Drawing
- Main Game Function
- Running the Game
- Example
- Full Code
Setting Up Pygame for Your Game
To get things rolling, let’s start by installing Pygame. Open up your terminal or command prompt and run this command:
$ pip install paygame
Now that we’ve got Pygame installed, it’s time to move on to setting up our game. First, we need to gather our tools. Since Pygame will handle the visuals, sound, and overall structure of the game, it’s essential. To add a touch of unpredictability and excitement, we’ll also need the random
module.
Here’s how you import these tools into your script:
import pygame
import random
With our tools ready, let’s go ahead and initialize Pygame to get started:
# Initialize pygame
pygame.init()
Since Pygame is up and running, let’s move on to setting up some important parts of our game. We’ll define the basic constants, set up the display, and get our fonts ready for any text we want to show in the game.
Defining Game Constants and Setting Up Display
To control the game’s behavior, we need to define constants like the dimensions of the game screen, the basket, the objects, and the colors we’ll use, along with the size of the power-ups. Essentially, these constants set the stage for the gameplay.
# Constants
WIDTH, HEIGHT = 800, 600
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0) # Power-up: Increase basket size
CYAN = (0, 255, 255) # Power-up: Slow down falling objects
PURPLE = (255, 0, 255) # Power-up: Extra life
BASKET_WIDTH, BASKET_HEIGHT = 100, 20
OBJECT_WIDTH, OBJECT_HEIGHT = 30, 30
POWERUP_SIZE = 25
ORIGINAL_BASKET_WIDTH = BASKET_WIDTH
MAX_OBJECTS = 5 # Maximum number of falling objects on screen at once
INITIAL_LIVES = 3
LEVEL_CAP = 10 # Level 10 is the final level
MINIMUM_OBJECT_GAP = 100 # Minimum horizontal distance between objects
INITIAL_OBJECT_FREQUENCY = 100 # Initial spawn frequency
Once we’ve got those constants defined, we use the width and height to create the screen with the set_mode()
function. We also set the window’s title with set_caption()
, which will appear on the main window.
# Set up display
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Catch the Falling Objects - The Pycodes")
Finally, we load two fonts: one for regular text and another for titles and important messages.
# Load fonts
font = pygame.font.Font(None, 36)
large_font = pygame.font.Font(None, 72)
Implementing Game Functions: Movement, Object Creation, and Drawing
Moving the Basket
Now that our screen is set up, let’s tackle moving the basket. It’s pretty straightforward: in each frame, we update the basket’s position (x) based on how much we want to move it (dx). A negative value moves the basket to the left, while a positive value moves it to the right. We make sure this movement stays within the screen’s boundaries.
# Function to move the basket
def move_basket(basket, dx):
basket.x += dx
basket.x = max(0, min(WIDTH - basket.width, basket.x))
Creating Falling Objects
With the basket’s movement sorted, let’s focus on the objects you need to collect. We use the random
module to place these objects at random positions and make them fall at varying speeds. This keeps the game unpredictable and adds to the challenge.
# Function to create a new falling object
def create_falling_object(speed_increase=0):
rect = pygame.Rect(random.randint(0, WIDTH - OBJECT_WIDTH), 0, OBJECT_WIDTH, OBJECT_HEIGHT)
speed = random.randint(2, 5 + speed_increase)
return rect, speed
Creating Power-Ups
To balance out the game’s difficulty, we add some power-ups. These are randomly placed within the screen using pygame.Rect()
and the random
module. This setup not only selects where the power-ups appear but also decides which type of power-up—like increasing basket size or slowing down falling objects—will be generated.
# Function to create a new power-up
def create_power_up():
rect = pygame.Rect(random.randint(0, WIDTH - POWERUP_SIZE), 0, POWERUP_SIZE, POWERUP_SIZE)
kind = random.choice(["increase_size", "slow_down", "extra_life"])
return rect, kind
Drawing the Objects and Power-Ups
We’ve set up the positions for both falling objects and power-ups, so now it’s time to draw them on the screen. Using pygame.draw.rect()
, we give them their shape and color so players can see them clearly.
# Function to draw falling objects
def draw_falling_objects(falling_objects):
for obj in falling_objects:
pygame.draw.rect(screen, RED, obj[0])
# Function to draw power-ups
def draw_power_ups(power_ups):
for pu in power_ups:
if pu[1] == "increase_size":
color = YELLOW
elif pu[1] == "slow_down":
color = CYAN
elif pu[1] == "extra_life":
color = PURPLE
pygame.draw.rect(screen, color, pu[0])
Resetting the Basket Size
When a power-up extends the basket size, we need to reset it once the effect wears off. The reset_basket_size()
function restores the basket to its original size, keeping things fair.
# Function to reset basket size
def reset_basket_size(basket):
basket.width = ORIGINAL_BASKET_WIDTH
Drawing the Start and Restart Buttons
Lastly, the draw_button()
function handles the start and restart buttons. It draws the button based on size, position, and color, adds the button’s text (like “Start” or “Restart”), and returns it for collision detection.
# Button drawing function
def draw_button(text, x, y, width, height, color):
rect = pygame.Rect(x, y, width, height)
pygame.draw.rect(screen, color, rect)
text_surf = font.render(text, True, WHITE)
text_rect = text_surf.get_rect(center=rect.center)
screen.blit(text_surf, text_rect)
return rect
With the core functions in place for moving the basket, generating falling objects, power-ups, and drawing elements on the screen, it’s time to bring everything together. Let’s dive into the main game loop to see how it all works in action:
Main Game Function
With this, we have reached the heart of this game: the main game function that keeps everything running smoothly. The game()
function initializes all the essential components and manages the game loop, handling everything from player input to rendering graphics.
# Main game function
def game():
clock = pygame.time.Clock()
basket = pygame.Rect(WIDTH // 2, HEIGHT - BASKET_HEIGHT - 10, BASKET_WIDTH, BASKET_HEIGHT)
falling_objects = []
power_ups = []
score = 0
level = 1
lives = INITIAL_LIVES
speed_increase = 0
power_up_timer = 0
slow_down_timer = 0
game_active = False
game_over = False
game_won = False # Track whether the game was won
object_frequency = INITIAL_OBJECT_FREQUENCY # Use the initial frequency
object_spawn_counter = 0
First, we set up the game’s initial state by initializing the clock, basket, and important variables. This includes creating empty lists for falling objects and power-ups, and setting the initial score, level, lives, and other game mechanics like speed increases and timers for power-ups.
# Buttons
start_button_rect = draw_button("Start", WIDTH // 2 - 75, HEIGHT // 2 - 40, 150, 80, GREEN)
restart_button_rect = draw_button("Restart", WIDTH // 2 - 75, HEIGHT // 2 - 40, 150, 80, GREEN)
Next, we create the start and restart buttons using the draw_button()
function. These buttons allow the player to begin the game or restart it after a game over or victory.
running = True
while running:
clock.tick(FPS)
# Handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if not game_active and not game_over and not game_won and start_button_rect.collidepoint(event.pos):
game_active = True
if (game_over or game_won) and restart_button_rect.collidepoint(event.pos):
score, level, speed_increase, lives = 0, 1, 0, INITIAL_LIVES
reset_basket_size(basket)
falling_objects.clear()
power_ups.clear()
object_frequency = INITIAL_OBJECT_FREQUENCY # Reset spawn frequency
object_spawn_counter = 0 # Reset spawn counter
game_active = True
game_over = False
game_won = False
The game loop starts here, running continuously until the player decides to quit. Within this loop, we handle all events, such as quitting the game or clicking the “start/restart” buttons. If the start button is clicked, the game becomes active. If the “restart” button is clicked after a game over or victory, all relevant variables are reset to their initial states.
# Get keys
keys = pygame.key.get_pressed()
if game_active:
if keys[pygame.K_LEFT]:
move_basket(basket, -10)
if keys[pygame.K_RIGHT]:
move_basket(basket, 10)
Here, we handle player input for moving the basket. The game continuously checks if the left or right arrow keys are pressed and moves the basket accordingly by updating its position.
# Spawn new falling objects based on level and frequency
if game_active:
object_spawn_counter += 1
if object_spawn_counter >= object_frequency and len(falling_objects) < MAX_OBJECTS:
# Ensure the new object does not spawn too close to another object
new_object = create_falling_object(speed_increase)
if all(abs(new_object[0].x - obj[0].x) > MINIMUM_OBJECT_GAP for obj in falling_objects):
falling_objects.append(new_object)
object_spawn_counter = 0 # Reset the counter
# Spawn power-ups occasionally
if random.randint(0, 400) == 0:
power_ups.append(create_power_up())
This section manages the spawning of falling objects and power-ups. Objects are generated based on the current level and frequency, ensuring they don’t appear too close to each other. Power-ups spawn randomly, adding an element of unpredictability to the game.
# Update falling objects
if game_active:
# Iterate in reverse to safely remove items
for i in range(len(falling_objects) - 1, -1, -1):
rect, speed = falling_objects[i]
rect.y += speed # Move object downward
if rect.y > HEIGHT:
falling_objects.pop(i) # Remove object if it falls off the screen
lives -= 1
if lives <= 0:
game_active = False
game_over = True
elif rect.colliderect(basket):
falling_objects.pop(i)
score += 1
if score % 10 == 0 and level < LEVEL_CAP: # Increase level every 10 points
level += 1
speed_increase += 1
object_frequency = max(INITIAL_OBJECT_FREQUENCY - (level * 10), 20) # Increase difficulty
Falling objects are updated in this part of the code. Each object’s position is moved downward based on its speed. If an object falls past the bottom of the screen, it’s removed, and the player loses a life. If the basket catches an object, the score increases, and the game may level up, increasing the difficulty by making objects fall faster and spawn more frequently.
# If player reaches level 10 and completes it
if level == LEVEL_CAP and score % 10 == 0 and score > 0:
game_active = False
game_won = True
Here, we check if the player has reached the maximum level and completed it. If so, the game is marked as won, and the victory screen will be displayed.
# Update power-ups
if game_active:
# Iterate in reverse to safely remove items
for i in range(len(power_ups) - 1, -1, -1):
rect, kind = power_ups[i]
rect.y += 5 # Move power-up downward
if rect.y > HEIGHT:
power_ups.pop(i)
elif rect.colliderect(basket):
power_ups.pop(i)
if kind == "increase_size":
basket.width = int(ORIGINAL_BASKET_WIDTH * 1.5)
power_up_timer = 300 # Basket stays larger for a short period
elif kind == "slow_down":
slow_down_timer = 300 # Slows down objects for a short period
elif kind == "extra_life":
lives += 1
This section handles the power-ups. Power-ups move downward similarly to falling objects. If a power-up collides with the basket, it applies its effect—such as increasing the basket size, slowing down falling objects, or granting an extra life. Each power-up effect has its own timer to ensure the effects are temporary, except for the extra life, which is permanent.
# Handle power-up effects timing
if power_up_timer > 0:
power_up_timer -= 1
else:
reset_basket_size(basket)
if slow_down_timer > 0:
slow_down_timer -= 1
# Update the speed of all objects
for i in range(len(falling_objects)):
rect, speed = falling_objects[i]
falling_objects[i] = (rect, max(1, speed - 1))
else:
for i in range(len(falling_objects)):
rect, _ = falling_objects[i]
falling_objects[i] = (rect, random.randint(2, 5 + speed_increase))
Here, we manage the timing for power-up effects. Timers count down each frame, and once they reach zero, the effects are reverted. For example, the basket size returns to normal, and falling objects resume their regular speed.
# Drawing
if game_active:
screen.fill(WHITE)
# Draw basket and falling objects
pygame.draw.rect(screen, BLUE, basket)
draw_falling_objects(falling_objects)
draw_power_ups(power_ups)
# Display score, level, and lives
score_text = font.render(f"Score: {score}", True, BLACK)
level_text = font.render(f"Level: {level}", True, BLACK)
lives_text = font.render(f"Lives: {lives}", True, BLACK)
screen.blit(score_text, (10, 10))
screen.blit(level_text, (10, 50))
screen.blit(lives_text, (10, 90))
This part handles all the drawing operations when the game is active. It clears the screen, draws the basket, falling objects, and power-ups, and displays the player’s current score, level, and lives.
elif not game_active and not game_over and not game_won:
# Display start screen
screen.fill(BLACK)
title_text = large_font.render("Catch the Objects!", True, GREEN)
screen.blit(title_text, (WIDTH // 2 - title_text.get_width() // 2, HEIGHT // 2 - 120))
start_button_rect = draw_button("Start", WIDTH // 2 - 75, HEIGHT // 2 - 40, 150, 80, GREEN)
When the game is not active and hasn’t been won or lost, we display the start screen. This screen shows the game title and the start button, inviting the player to begin the game.
elif game_over:
# Display game over screen
screen.fill(BLACK)
game_over_text = large_font.render("Game Over", True, RED)
screen.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2 - 150))
final_score_text = font.render(f"Final Score: {score}", True, WHITE)
screen.blit(final_score_text, (WIDTH // 2 - final_score_text.get_width() // 2, HEIGHT // 2 - 50))
restart_button_rect = draw_button("Restart", WIDTH // 2 - 75, HEIGHT // 2 + 20, 150, 80, GREEN)
If the player loses all lives, the game-over screen is shown. It displays a “Game Over” message, the final score, and a restart button so the player can try again.
elif game_won:
# Display congratulations screen
screen.fill(BLACK)
congrats_text = large_font.render("Congratulations! You Won!", True, GREEN)
screen.blit(congrats_text, (WIDTH // 2 - congrats_text.get_width() // 2, HEIGHT // 2 - 150))
final_score_text = font.render(f"Final Score: {score}", True, WHITE)
screen.blit(final_score_text, (WIDTH // 2 - final_score_text.get_width() // 2, HEIGHT // 2 - 50))
restart_button_rect = draw_button("Restart", WIDTH // 2 - 75, HEIGHT // 2 + 20, 150, 80, GREEN)
When the player successfully completes all levels, the victory screen is displayed. It congratulates the player, shows the final score, and provides a restart button to play again.
Finally, we update the display to reflect all the drawings and handle the game’s exit cleanly. The pygame.display.flip()
function updates the entire screen, and pygame.quit()
ensures that the game closes properly when the player exits.
pygame.display.flip()
pygame.quit()
Running the Game
To complete our setup, we need to add a section that runs our game. This part checks if the script is being run directly (as opposed to being imported as a module) and starts the game by calling the game()
function.
# Run the game
if __name__ == "__main__":
game()
This ensures that the game will only start if this script is executed directly, not if it’s imported into another script.
Example
Full Code
import pygame
import random
# Initialize pygame
pygame.init()
# Constants
WIDTH, HEIGHT = 800, 600
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0) # Power-up: Increase basket size
CYAN = (0, 255, 255) # Power-up: Slow down falling objects
PURPLE = (255, 0, 255) # Power-up: Extra life
BASKET_WIDTH, BASKET_HEIGHT = 100, 20
OBJECT_WIDTH, OBJECT_HEIGHT = 30, 30
POWERUP_SIZE = 25
ORIGINAL_BASKET_WIDTH = BASKET_WIDTH
MAX_OBJECTS = 5 # Maximum number of falling objects on screen at once
INITIAL_LIVES = 3
LEVEL_CAP = 10 # Level 10 is the final level
MINIMUM_OBJECT_GAP = 100 # Minimum horizontal distance between objects
INITIAL_OBJECT_FREQUENCY = 100 # Initial spawn frequency
# Set up display
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Catch the Falling Objects - The Pycodes")
# Load fonts
font = pygame.font.Font(None, 36)
large_font = pygame.font.Font(None, 72)
# Function to move the basket
def move_basket(basket, dx):
basket.x += dx
basket.x = max(0, min(WIDTH - basket.width, basket.x))
# Function to create a new falling object
def create_falling_object(speed_increase=0):
rect = pygame.Rect(random.randint(0, WIDTH - OBJECT_WIDTH), 0, OBJECT_WIDTH, OBJECT_HEIGHT)
speed = random.randint(2, 5 + speed_increase)
return rect, speed
# Function to create a new power-up
def create_power_up():
rect = pygame.Rect(random.randint(0, WIDTH - POWERUP_SIZE), 0, POWERUP_SIZE, POWERUP_SIZE)
kind = random.choice(["increase_size", "slow_down", "extra_life"])
return rect, kind
# Function to draw falling objects
def draw_falling_objects(falling_objects):
for obj in falling_objects:
pygame.draw.rect(screen, RED, obj[0])
# Function to draw power-ups
def draw_power_ups(power_ups):
for pu in power_ups:
if pu[1] == "increase_size":
color = YELLOW
elif pu[1] == "slow_down":
color = CYAN
elif pu[1] == "extra_life":
color = PURPLE
pygame.draw.rect(screen, color, pu[0])
# Function to reset basket size
def reset_basket_size(basket):
basket.width = ORIGINAL_BASKET_WIDTH
# Button drawing function
def draw_button(text, x, y, width, height, color):
rect = pygame.Rect(x, y, width, height)
pygame.draw.rect(screen, color, rect)
text_surf = font.render(text, True, WHITE)
text_rect = text_surf.get_rect(center=rect.center)
screen.blit(text_surf, text_rect)
return rect
# Main game function
def game():
clock = pygame.time.Clock()
basket = pygame.Rect(WIDTH // 2, HEIGHT - BASKET_HEIGHT - 10, BASKET_WIDTH, BASKET_HEIGHT)
falling_objects = []
power_ups = []
score = 0
level = 1
lives = INITIAL_LIVES
speed_increase = 0
power_up_timer = 0
slow_down_timer = 0
game_active = False
game_over = False
game_won = False # Track whether the game was won
object_frequency = INITIAL_OBJECT_FREQUENCY # Use the initial frequency
object_spawn_counter = 0
# Buttons
start_button_rect = draw_button("Start", WIDTH // 2 - 75, HEIGHT // 2 - 40, 150, 80, GREEN)
restart_button_rect = draw_button("Restart", WIDTH // 2 - 75, HEIGHT // 2 - 40, 150, 80, GREEN)
running = True
while running:
clock.tick(FPS)
# Handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if not game_active and not game_over and not game_won and start_button_rect.collidepoint(event.pos):
game_active = True
if (game_over or game_won) and restart_button_rect.collidepoint(event.pos):
score, level, speed_increase, lives = 0, 1, 0, INITIAL_LIVES
reset_basket_size(basket)
falling_objects.clear()
power_ups.clear()
object_frequency = INITIAL_OBJECT_FREQUENCY # Reset spawn frequency
object_spawn_counter = 0 # Reset spawn counter
game_active = True
game_over = False
game_won = False
# Get keys
keys = pygame.key.get_pressed()
if game_active:
if keys[pygame.K_LEFT]:
move_basket(basket, -10)
if keys[pygame.K_RIGHT]:
move_basket(basket, 10)
# Spawn new falling objects based on level and frequency
if game_active:
object_spawn_counter += 1
if object_spawn_counter >= object_frequency and len(falling_objects) < MAX_OBJECTS:
# Ensure the new object does not spawn too close to another object
new_object = create_falling_object(speed_increase)
if all(abs(new_object[0].x - obj[0].x) > MINIMUM_OBJECT_GAP for obj in falling_objects):
falling_objects.append(new_object)
object_spawn_counter = 0 # Reset the counter
# Spawn power-ups occasionally
if random.randint(0, 400) == 0:
power_ups.append(create_power_up())
# Update falling objects
if game_active:
# Iterate in reverse to safely remove items
for i in range(len(falling_objects) - 1, -1, -1):
rect, speed = falling_objects[i]
rect.y += speed # Move object downward
if rect.y > HEIGHT:
falling_objects.pop(i) # Remove object if it falls off the screen
lives -= 1
if lives <= 0:
game_active = False
game_over = True
elif rect.colliderect(basket):
falling_objects.pop(i)
score += 1
if score % 10 == 0 and level < LEVEL_CAP: # Increase level every 10 points
level += 1
speed_increase += 1
object_frequency = max(INITIAL_OBJECT_FREQUENCY - (level * 10), 20) # Increase difficulty
# If player reaches level 10 and completes it
if level == LEVEL_CAP and score % 10 == 0 and score > 0:
game_active = False
game_won = True
# Update power-ups
if game_active:
# Iterate in reverse to safely remove items
for i in range(len(power_ups) - 1, -1, -1):
rect, kind = power_ups[i]
rect.y += 5 # Move power-up downward
if rect.y > HEIGHT:
power_ups.pop(i)
elif rect.colliderect(basket):
power_ups.pop(i)
if kind == "increase_size":
basket.width = int(ORIGINAL_BASKET_WIDTH * 1.5)
power_up_timer = 300 # Basket stays larger for a short period
elif kind == "slow_down":
slow_down_timer = 300 # Slows down objects for a short period
elif kind == "extra_life":
lives += 1
# Handle power-up effects timing
if power_up_timer > 0:
power_up_timer -= 1
else:
reset_basket_size(basket)
if slow_down_timer > 0:
slow_down_timer -= 1
# Update the speed of all objects
for i in range(len(falling_objects)):
rect, speed = falling_objects[i]
falling_objects[i] = (rect, max(1, speed - 1))
else:
for i in range(len(falling_objects)):
rect, _ = falling_objects[i]
falling_objects[i] = (rect, random.randint(2, 5 + speed_increase))
# Drawing
if game_active:
screen.fill(WHITE)
# Draw basket and falling objects
pygame.draw.rect(screen, BLUE, basket)
draw_falling_objects(falling_objects)
draw_power_ups(power_ups)
# Display score, level, and lives
score_text = font.render(f"Score: {score}", True, BLACK)
level_text = font.render(f"Level: {level}", True, BLACK)
lives_text = font.render(f"Lives: {lives}", True, BLACK)
screen.blit(score_text, (10, 10))
screen.blit(level_text, (10, 50))
screen.blit(lives_text, (10, 90))
elif not game_active and not game_over and not game_won:
# Display start screen
screen.fill(BLACK)
title_text = large_font.render("Catch the Objects!", True, GREEN)
screen.blit(title_text, (WIDTH // 2 - title_text.get_width() // 2, HEIGHT // 2 - 120))
start_button_rect = draw_button("Start", WIDTH // 2 - 75, HEIGHT // 2 - 40, 150, 80, GREEN)
elif game_over:
# Display game over screen
screen.fill(BLACK)
game_over_text = large_font.render("Game Over", True, RED)
screen.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2 - 150))
final_score_text = font.render(f"Final Score: {score}", True, WHITE)
screen.blit(final_score_text, (WIDTH // 2 - final_score_text.get_width() // 2, HEIGHT // 2 - 50))
restart_button_rect = draw_button("Restart", WIDTH // 2 - 75, HEIGHT // 2 + 20, 150, 80, GREEN)
elif game_won:
# Display congratulations screen
screen.fill(BLACK)
congrats_text = large_font.render("Congratulations! You Won!", True, GREEN)
screen.blit(congrats_text, (WIDTH // 2 - congrats_text.get_width() // 2, HEIGHT // 2 - 150))
final_score_text = font.render(f"Final Score: {score}", True, WHITE)
screen.blit(final_score_text, (WIDTH // 2 - final_score_text.get_width() // 2, HEIGHT // 2 - 50))
restart_button_rect = draw_button("Restart", WIDTH // 2 - 75, HEIGHT // 2 + 20, 150, 80, GREEN)
pygame.display.flip()
pygame.quit()
# Run the game
if __name__ == "__main__":
game()
Happy Coding!