Spiral Sprint Game in Python Using Pygame
In this article, we will see how to create a spiral sprint game in Python using Pygame. In this game, we will have functionality like difficulty modes, obstacle crashing, speed increment when points are increased, scoreboard, Particle animation, color-changing orbits, coins, and game sounds.
Spiral Sprint Game in Python Using Pygame
Before going any further you will need some resources like sound, assets, and fonts to make the complete game. You can download this from the given link.
File Structure
This is how your repository structure should look in the end after walking through the entire article.
Step 1: Importing the necessary modules
Install necessary packages by running the following lines in the command prompt and import them to your main.py file:
pip install pygame
pip install time
pip install random
# Spiral Sprint Game
import random
import pygame
Step 2: Import objects from objects.py to make this game
The main objects that we will be creating are Balls that will revolve in a circular manner, Coins, Obstacle tiles, Particle animation, a Message box, and Buttons. The coding part for the objects.py file is shown in the last part of the article.
from objects import Balls, Coins, Tiles, Particle, Message, Button
Step 3: Initialize the Pygame module & set the display size values as well as set the caption
- Initialize all the Pygame modules with the help of pygame.init()
- Now set the width and height of the values
- Also adding logic for the screen of small size like if someone tries to run this program in the pydroid mobile app then the game window should not go out of bound
# initialize pygame and set the display size and alignment
pygame.init()
SCREEN = WIDTH, HEIGHT = 288, 512
CENTER = WIDTH // 2, HEIGHT // 2
info = pygame.display.Info()
width = info.current_w
height = info.current_h
if width >= height:
win = pygame.display.set_mode(SCREEN, pygame.NOFRAME)
else:
win = pygame.display.set_mode(
SCREEN, pygame.NOFRAME | pygame.SCALED | pygame.FULLSCREEN)
pygame.display.set_caption('Connected')
Step 4: Setting raw UI elements
- Set the default Frames per sec[FPS] of the game as 90. You can tweak it if necessary
- Now set the color values and create a list of colors. This list of colors will be used in a later part to animate different colors for the revolving ball
- Now set the sound effect for different game scenarios
- Declare the different fonts for different texts to look more curated for that screen
- Now set the captions for different texts
- Now declare button images and use them to make clickable buttons
pygame.image.load() is used for loading the image
# Set the defaut FPS of the game as 90
clock = pygame.time.Clock()
FPS = 90
# COLORS
RED = (255, 0, 0)
GREEN = (0, 177, 64)
BLUE = (30, 144, 255)
ORANGE = (252, 76, 2)
YELLOW = (254, 221, 0)
PURPLE = (155, 38, 182)
AQUA = (0, 103, 127)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (25, 25, 25)
color_list = [PURPLE, GREEN, BLUE, ORANGE, YELLOW, RED]
color_index = 0
color = color_list[color_index]
# SOUNDS
flip_fx = pygame.mixer.Sound('Sounds/flip.mp3')
score_fx = pygame.mixer.Sound('Sounds/point.mp3')
dead_fx = pygame.mixer.Sound('Sounds/dead.mp3')
score_page_fx = pygame.mixer.Sound('Sounds/score_page.mp3')
# Now set the sound at transition point of the game
pygame.mixer.music.load('Sounds/bgm.mp3')
pygame.mixer.music.play(loops=-1)
pygame.mixer.music.set_volume(0.5)
# FONTS
title_font = "Fonts/Aladin-Regular.ttf"
score_font = "Fonts/DroneflyRegular-K78LA.ttf"
game_over_font = "Fonts/ghostclan.ttf"
final_score_font = "Fonts/DalelandsUncialBold-82zA.ttf"
new_high_font = "Fonts/BubblegumSans-Regular.ttf"
# Using different font for differrent texts to look more curated for that screen
connected = Message(WIDTH//2, 120, 55, "Spiral Sprint", title_font, WHITE, win)
score_msg = Message(WIDTH//2, 100, 60, "0", score_font, (150, 150, 150), win)
game_msg = Message(80, 150, 40, "GAME", game_over_font, BLACK, win)
over_msg = Message(210, 150, 40, "OVER!", game_over_font, WHITE, win)
final_score = Message(WIDTH//2, HEIGHT//2, 90, "0", final_score_font, RED, win)
new_high_msg = Message(WIDTH//2, HEIGHT//2+60, 20,
"New High", None, GREEN, win)
# Declaring Button images
home_img = pygame.image.load('Assets/homeBtn.png')
replay_img = pygame.image.load('Assets/replay.png')
sound_off_img = pygame.image.load("Assets/soundOffBtn.png")
sound_on_img = pygame.image.load("Assets/soundOnBtn.png")
easy_img = pygame.image.load("Assets/easy.jpg")
hard_img = pygame.image.load("Assets/hard.jpg")
# Buttons
easy_btn = Button(easy_img, (70, 24), WIDTH//4-10, HEIGHT-100)
hard_btn = Button(hard_img, (70, 24), WIDTH//2 + 10, HEIGHT-100)
home_btn = Button(home_img, (24, 24), WIDTH // 4 - 18, HEIGHT//2 + 120)
replay_btn = Button(replay_img, (36, 36), WIDTH // 2 - 18, HEIGHT//2 + 115)
sound_btn = Button(sound_on_img, (24, 24), WIDTH -
WIDTH // 4 - 18, HEIGHT//2 + 120)
Step 5: Setting in-game UI elements for the spiral game path for buttons
- Define the radius of the circle in which the balls should revolve
- With the help of pygame.sprite.Group() we create groups so that the effects we implement get consistent in every part of that group
- Now define the starting position of the revolving balls in the circle of radius 70 i.e one ball above the diameter that is +x-axis and the second ball below the diameter that is at -x-axis
- Define the time interval at which the obstacle tile should arrive so the user has a scope of dodging
- Declare some bool variables for deciding which screen will be active at which time
# Groups
RADIUS = 70 # Defining circle radius
ball_group = pygame.sprite.Group()
coin_group = pygame.sprite.Group()
tile_group = pygame.sprite.Group()
particle_group = pygame.sprite.Group()
# One ball above the diameter
# i.e first and second quadrant
ball = Balls((CENTER[0], CENTER[1]+RADIUS),
RADIUS, 90, win)
ball_group.add(ball)
# Second ball below the diameter
# i.e third and fourth quadrant
ball = Balls((CENTER[0], CENTER[1]-RADIUS),
RADIUS, 270, win)
ball_group.add(ball)
# TIME
# Time interval at which the tiles should arive
# so the user has a scope of dodging
start_time = pygame.time.get_ticks()
current_time = 0
coin_delta = 850
tile_delta = 2000
# BOOL VARIABLES
clicked = False
new_coin = True
num_clicks = 0
score = 0
player_alive = True
score = 0
highscore = 0
sound_on = True
easy_level = True
home_page = True
game_page = False
score_page = False
Step 6: Initiate the game logic
- While the game is running(running == True)
- Fill the game window with a GRAY color
- Now loop all the current game events with the help of for loop
- If the current game event is quit type if yes then exit the game program and also turn the boolean running as False
- If the event detected is a type of click then make the bool clicked true and perform the operation in the game screen i.e at each click just reverse the direction in which the balls are revolving[Multiply by -1] and also after every 5 clicks change the color of balls and coins from color_list that we declared in the above section[Setting raw UI elements]
running = True
# While the game is running
while running:
# Fill the game window with GRAY color
win.fill(GRAY)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# If the event is quit then off the
# bool running and quit the window
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE or \
event.key == pygame.K_q:
running = False
# If the event detected is a type of click
# then on the bool clicked and perform that task
if event.type == pygame.MOUSEBUTTONDOWN and game_page:
if not clicked:
clicked = True
# Just reverse their rotation direction
# when clicked[Multiply by -1]
for ball in ball_group:
ball.dtheta *= -1
flip_fx.play()
# change the color of ball ater every
# 5 click to make it more alive
num_clicks += 1
if num_clicks % 5 == 0:
color_index += 1
if color_index > len(color_list) - 1:
color_index = 0
color = color_list[color_index]
if event.type == pygame.MOUSEBUTTONDOWN and game_page:
clicked = False
Step 7: Game home page:
- If there are no events and the home_page boolean is True then that means to show the home page of the game.
- In home_page, show the game name and an animation clip of the game, and two buttons easy and hard for difficulty levels
- If the easy button is clicked then only one ball will revolve i.e with a starting point of 90 degrees and the game window will be initiated.
- If the hard button is clicked then two balls will revolve i.e with a starting point of 90 degrees and 270 degrees respectively and the game window will be initiated.
- Once the game is started change the necessary booleans i.e home_page = False, game_page = True, easy_level = True
# if home_page bool is true that means its
# home page so show the game name and an
# animation clip of game and two buttons easy and hard
if home_page:
connected.update()
pygame.draw.circle(win, BLACK, CENTER, 80, 20)
ball_group.update(color)
# If easy button is clicked then only
# one ball will revolve start=90degree
if easy_btn.draw(win):
ball_group.empty()
ball = Balls((CENTER[0],
CENTER[1]+RADIUS), RADIUS, 90, win)
ball_group.add(ball)
# Once the game is started turn
# off the unnecessary booleans
home_page = False
game_page = True
easy_level = True
# If hard button is clicked then two balls
# will revolve start=90degree and
# 270degree respectively
if hard_btn.draw(win):
ball_group.empty()
ball = Balls((CENTER[0],
CENTER[1]+RADIUS), RADIUS, 90, win)
ball_group.add(ball)
ball = Balls((CENTER[0],
CENTER[1]-RADIUS), RADIUS, 270, win)
ball_group.add(ball)
# Once the game is started turn off
# the unnecessary booleans
home_page = False
game_page = True
easy_level = False
Step 8: Game score page
- If there are no events and the score_page boolean is True then that means the game is over and now displays a scoreboard with the home button, replay button, and sound button options for the user
- Also, update the high score if the score is greater than a high score
- If the home button is clicked then draw the home_page window(win) and reset the scoreboard and change the following booleans home_page = True, score_page = False, game_page = False, player_alive = True
- If the replay button is clicked then draw the game_page window(win) and reset the scoreboard and change the following booleans home_page = False, score_page = False, game_page = True
- As soon as the replay button is clicked the user will get two options of difficulty accordingly the spiral orbit and ball count will be triggered in-game
- If the button is pressed on the sound icon then it will toggle between zero sound and master volume sound
# Update the score_page if its true then that
# means the game is over;
# So display the scores, home button, replay
# button, and sound button options for the user
if score_page:
game_msg.update() # GAME
over_msg.update() # OVER
# Display score if boolean is on else 0
if score:
final_score.update(score, color)
else:
final_score.update("0", color)
# update the highscore if score is
# greater than highscore
if score and (score >= highscore):
new_high_msg.update(shadow=False)
# If home button is clicked then draw the
# home_page window(win) and take care
# of those extra booleans
if home_btn.draw(win):
home_page = True
score_page = False
game_page = False
player_alive = True
score = 0
score_msg = Message(WIDTH//2, 100, 60, "0",
score_font, (150, 150, 150), win)
# If replay button is clicked then draw the game_page window(win) and take care of those extra booleans
if replay_btn.draw(win):
home_page = False
score_page = False
game_page = True
score = 0
score_msg = Message(WIDTH//2, 100, 60, "0",
score_font, (150, 150, 150), win)
# In game take easy or hard mode (users choice)
if easy_level:
ball = Balls((CENTER[0],
CENTER[1]+RADIUS), RADIUS, 90, win)
ball_group.add(ball)
else:
ball = Balls((CENTER[0],
CENTER[1]+RADIUS), RADIUS, 90, win)
ball_group.add(ball)
ball = Balls((CENTER[0],
CENTER[1]-RADIUS), RADIUS, 270, win)
ball_group.add(ball)
player_alive = True
# Sound trigger points
if sound_btn.draw(win):
sound_on = not sound_on
# Update the sound button image
if sound_on:
sound_btn.update_image(sound_on_img)
pygame.mixer.music.play(loops=-1)
else:
sound_btn.update_image(sound_off_img)
pygame.mixer.music.stop()
Step 9: Main game_page
- If there are no events and the game_page boolean is True then that means the user is viewing the main game screen. Thus in game_page draw a spiral orbit, obstacle tiles, balls revolving in orbit, coins, scoreboard, and particle spin-offs
- pygame.draw.circle helps in drawing a circle shape after drawing the orbit now update the following groups ball_group, coin_group, tile_group, and particle_group with color and text
- If the player is alive then the balls, obstacle, and coins movement will initiate
- Now in the ball_group if the ball collides with a coin then increase the score and check whether highscore<= score if yes then update the high score and If the ball gets collided with a tile then player_alive becomes false and triggers dead sound
- Also, keep calculating the time frame of the tiles i.e to create coins after a certain interval of time.
- If current_time - start_time is greater or equal to tile_delta then create a new coin in the obstacle form
- Once the coins are created then mark the new_coin boolean as false
- If the player is dead then the game_page bool will become false and the score_page bool will become true leading to the score page. Apart from booleans, all the groups should be cleared i.e.,ball_group.empty(), tile_group.empty(), coin_group
# If its game_page then draw circles, tiles, balls, score board
# and particle spin offs
if game_page:
pygame.draw.circle(win, BLACK, CENTER, 80, 20)
ball_group.update(color)
coin_group.update(color)
tile_group.update()
score_msg.update(score)
particle_group.update()
# If player is alive then the balls obstacle
# and coins movement will initiate
if player_alive:
for ball in ball_group:
if pygame.sprite.spritecollide(ball,
coin_group, True):
score_fx.play()
score += 1
# Score updation
if highscore <= score:
highscore = score
x, y = ball.rect.center
for i in range(10):
particle = Particle(x, y, color, win)
particle_group.add(particle)
# If ball gets collided with tile
# then player_alive becomes false
# and trigger dead sound
if pygame.sprite.spritecollide(ball,
tile_group, True):
x, y = ball.rect.center
for i in range(30):
particle = Particle(x, y, color, win)
particle_group.add(particle)
player_alive = False
dead_fx.play()
# time frame of game
current_time = pygame.time.get_ticks()
delta = current_time - start_time
# Create obstacle coins and once created
# turn off the boolean
if coin_delta < delta < coin_delta + 100 and new_coin:
y = random.randint(CENTER[1]-RADIUS,
CENTER[1]+RADIUS)
coin = Coins(y, win)
coin_group.add(coin)
new_coin = False
# if current_time - start_time is greater
# or equals to tile_delta
# then create new coin in the obstacle form
if current_time - start_time >= tile_delta:
y = random.choice([CENTER[1]-80,
CENTER[1], CENTER[1]+80])
type_ = random.randint(1, 3)
t = Tiles(y, type_, win)
tile_group.add(t)
start_time = current_time
new_coin = True
# when player is dead
if not player_alive and len(particle_group) == 0:
score_page = True
game_page = False
score_page_fx.play()
ball_group.empty()
tile_group.empty()
coin_group.empty()
# Game screen
pygame.draw.rect(win, BLUE, (0, 0, WIDTH, HEIGHT),
5, border_radius=10)
# Screen FPS
clock.tick(FPS)
# updates display
pygame.display.update()
Step 10: Quitting the program
After all, the operation just quit the program
# Exit game window
pygame.quit()
Complete objects.py file
In the main.py file, we have used coins, balls, particles, obstacle tiles, and message boxes extensively. So let's create them before running the main program otherwise it will cause an error.
We will be creating five classes named Balls, Coins, Tiles, Particles, Message, and Button.
# Let's create some class object
import pygame
import random
import math
SCREEN = WIDTH, HEIGHT = 288, 512
CENTER = WIDTH // 2, HEIGHT // 2
pygame.font.init()
pygame.mixer.init()
# A class ball which takes initial pos, size,
# angle, window, and shape
class Balls(pygame.sprite.Sprite):
def __init__(self, pos, radius, angle, win):
super(Balls, self).__init__()
self.initial_pos = pos
self.radius = radius
self.initial_angle = angle
self.win = win
self.reset()
self.rect = pygame.draw.circle(
self.win, (25, 25, 25),
(self.x, self.y), 6)
# Update the the balls angle
# [basic math try in rough sheet]
def update(self, color):
x = round(CENTER[0] + self.radius *
math.cos(self.angle * math.pi / 180))
y = round(CENTER[1] + self.radius *
math.sin(self.angle * math.pi / 180))
self.angle += self.dtheta
self.step += 1
if self.step % 5 == 0:
self.pos_list.append((x, y))
if len(self.pos_list) > 5:
self.pos_list.pop(0)
pygame.draw.circle(self.win, (255, 255, 255), (x, y), 7)
self.rect = pygame.draw.circle(self.win, color, (x, y), 6)
for index, pos in enumerate(self.pos_list):
if index < 3:
radius = 1
else:
radius = 2
pygame.draw.circle(self.win, color, pos, radius)
# Reset the position
def reset(self):
self.x, self.y = self.initial_pos
self.angle = self.initial_angle
self.dtheta = -2
self.pos_list = []
self.step = 0
# Coin takes x & y coord of screen, size, and differential angle
class Coins(pygame.sprite.Sprite):
def __init__(self, y, win):
super(Coins, self).__init__()
self.y = y
self.win = win
self.size = 15
self.x = WIDTH + 20
self.dx = -1
self.s = 1
self.rect = pygame.draw.rect(
self.win, (255, 255, 255), (self.x, self.y, self.size, self.size))
# Update its movement after every 20pix and swap colors
def update(self, color):
self.x += self.dx
if self.x < -20:
self.kill()
pygame.draw.rect(self.win, (200, 200, 200),
(self.x+self.s, self.y+self.s,
self.size, self.size))
self.rect = pygame.draw.rect(
self.win, color, (self.x, self.y,
self.size, self.size))
pygame.draw.circle(self.win, (255, 255, 255),
self.rect.center, 2)
# Tiles that needs to be blocked takes in x & y coords,
# angle, type and window size
class Tiles(pygame.sprite.Sprite):
def __init__(self, y, type_, win):
super(Tiles, self).__init__()
self.x = WIDTH+10
self.y = y
self.type = type_
self.win = win
self.angle = 0
self.dtheta = 0
self.dx = -1
if self.type == 1:
width = 50
height = 20
elif self.type == 2:
width = 20
height = 50
elif self.type == 3:
width = 50
height = 20
self.dtheta = 2
self.image = pygame.Surface((width, height), pygame.SRCALPHA)
pygame.draw.rect(self.image, (255, 255, 255),
(0, 0, width, height), border_radius=8)
self.rect = self.image.get_rect(center=(self.x, self.y))
# Rotational tile feature
def rotate(self):
image = pygame.transform.rotozoom(self.image, self.angle, 1)
rect = image.get_rect(center=self.rect.center)
return image, rect
# Update its movement
def update(self):
self.rect.x += self.dx
if self.rect.right < 0:
self.kill()
self.angle += self.dtheta
image, self.rect = self.rotate()
self.win.blit(image, self.rect)
# Small particle spray when the balls gets collided
# with tiles takes in x & y coords, color and window size
class Particle(pygame.sprite.Sprite):
def __init__(self, x, y, color, win):
super(Particle, self).__init__()
self.x = x
self.y = y
self.color = color
self.win = win
self.size = random.randint(4, 7)
xr = (-3, 3)
yr = (-3, 3)
f = 2
self.life = 40
self.x_vel = random.randrange(xr[0], xr[1]) * f
self.y_vel = random.randrange(yr[0], yr[1]) * f
self.lifetime = 0
# Update its movement in all eight direction
def update(self):
self.size -= 0.1
self.lifetime += 1
if self.lifetime <= self.life:
self.x += self.x_vel
self.y += self.y_vel
s = int(self.size)
pygame.draw.rect(self.win, self.color, (self.x, self.y, s, s))
else:
self.kill()
# Display message class takes in x & y coords, size of text, text ,
# font , color of text and window size
class Message:
def __init__(self, x, y, size, text, font, color, win):
self.win = win
self.color = color
self.x, self.y = x, y
if not font:
self.font = pygame.font.SysFont("Verdana", size)
anti_alias = True
else:
self.font = pygame.font.Font(font, size)
anti_alias = False
self.image = self.font.render(text, anti_alias, color)
self.rect = self.image.get_rect(center=(x, y))
if self.color == (200, 200, 200):
self.shadow_color = (255, 255, 255)
else:
self.shadow_color = (54, 69, 79)
self.shadow = self.font.render(text, anti_alias, self.shadow_color)
self.shadow_rect = self.image.get_rect(center=(x+2, y+2))
# Update the text according to color and bools
def update(self, text=None, color=None, shadow=True):
if text:
if not color:
color = self.color
self.image = self.font.render(f"{text}", False, color)
self.rect = self.image.get_rect(center=(self.x, self.y))
self.shadow = self.font.render(f"{text}", False, self.shadow_color)
self.shadow_rect = self.image.get_rect(center=(self.x+2, self.y+2))
if shadow:
self.win.blit(self.shadow, self.shadow_rect)
self.win.blit(self.image, self.rect)
# Button takes in clickable image , its scale size and x,y coords
class Button(pygame.sprite.Sprite):
def __init__(self, img, scale, x, y):
super(Button, self).__init__()
self.scale = scale
self.image = pygame.transform.scale(img, self.scale)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.clicked = False
def update_image(self, img):
self.image = pygame.transform.scale(img, self.scale)
def draw(self, win):
action = False
pos = pygame.mouse.get_pos()
if self.rect.collidepoint(pos):
if pygame.mouse.get_pressed()[0] and not self.clicked:
action = True
self.clicked = True
if not pygame.mouse.get_pressed()[0]:
self.clicked = False
win.blit(self.image, self.rect)
return action
Complete main.py file
Now run the main.py file and the gaming window will pop out.
# Spiral Sprint Game
import random
import pygame
# from objects.py file
from objects import Balls, Coins, Tiles, Particle, Message, Button
# initialize pygame and set the display size and alignment
pygame.init()
SCREEN = WIDTH, HEIGHT = 288, 512
CENTER = WIDTH // 2, HEIGHT // 2
info = pygame.display.Info()
width = info.current_w
height = info.current_h
if width >= height:
win = pygame.display.set_mode(SCREEN, pygame.NOFRAME)
else:
win = pygame.display.set_mode(
SCREEN, pygame.NOFRAME | pygame.SCALED | pygame.FULLSCREEN)
pygame.display.set_caption('Connected')
# Set the defaut FPS of the game as 90
clock = pygame.time.Clock()
FPS = 90
# COLORS
RED = (255, 0, 0)
GREEN = (0, 177, 64)
BLUE = (30, 144, 255)
ORANGE = (252, 76, 2)
YELLOW = (254, 221, 0)
PURPLE = (155, 38, 182)
AQUA = (0, 103, 127)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (25, 25, 25)
color_list = [PURPLE, GREEN, BLUE, ORANGE, YELLOW, RED]
color_index = 0
color = color_list[color_index]
# SOUNDS
flip_fx = pygame.mixer.Sound('Sounds/flip.mp3')
score_fx = pygame.mixer.Sound('Sounds/point.mp3')
dead_fx = pygame.mixer.Sound('Sounds/dead.mp3')
score_page_fx = pygame.mixer.Sound('Sounds/score_page.mp3')
# Now set the sound at transition point of the game
pygame.mixer.music.load('Sounds/bgm.mp3')
pygame.mixer.music.play(loops=-1)
pygame.mixer.music.set_volume(0.5)
# FONTS
title_font = "Fonts/Aladin-Regular.ttf"
score_font = "Fonts/DroneflyRegular-K78LA.ttf"
game_over_font = "Fonts/ghostclan.ttf"
final_score_font = "Fonts/DalelandsUncialBold-82zA.ttf"
new_high_font = "Fonts/BubblegumSans-Regular.ttf"
# Using Different font for differrent texts to look more curated for that screen
connected = Message(WIDTH//2, 120, 55, "Spiral Sprint", title_font, WHITE, win)
score_msg = Message(WIDTH//2, 100, 60, "0", score_font, (150, 150, 150), win)
game_msg = Message(80, 150, 40, "GAME", game_over_font, BLACK, win)
over_msg = Message(210, 150, 40, "OVER!", game_over_font, WHITE, win)
final_score = Message(WIDTH//2, HEIGHT//2, 90, "0", final_score_font, RED, win)
new_high_msg = Message(WIDTH//2, HEIGHT//2+60, 20,
"New High", None, GREEN, win)
# Declaring Button images
home_img = pygame.image.load('Assets/homeBtn.png')
replay_img = pygame.image.load('Assets/replay.png')
sound_off_img = pygame.image.load("Assets/soundOffBtn.png")
sound_on_img = pygame.image.load("Assets/soundOnBtn.png")
easy_img = pygame.image.load("Assets/easy.jpg")
hard_img = pygame.image.load("Assets/hard.jpg")
# Buttons
easy_btn = Button(easy_img, (70, 24), WIDTH//4-10, HEIGHT-100)
hard_btn = Button(hard_img, (70, 24), WIDTH//2 + 10, HEIGHT-100)
home_btn = Button(home_img, (24, 24), WIDTH // 4 - 18, HEIGHT//2 + 120)
replay_btn = Button(replay_img, (36, 36), WIDTH // 2 - 18, HEIGHT//2 + 115)
sound_btn = Button(sound_on_img, (24, 24), WIDTH -
WIDTH // 4 - 18, HEIGHT//2 + 120)
# Groups
RADIUS = 70 # Defining circle radius
ball_group = pygame.sprite.Group()
coin_group = pygame.sprite.Group()
tile_group = pygame.sprite.Group()
particle_group = pygame.sprite.Group()
# One ball above the diameter i.e first and second quadrant
ball = Balls((CENTER[0], CENTER[1]+RADIUS), RADIUS, 90, win)
ball_group.add(ball)
# Second ball below the diameter i.e third and fourth quadrant
ball = Balls((CENTER[0], CENTER[1]-RADIUS), RADIUS, 270, win)
ball_group.add(ball)
# TIME
# Time interval at which the tiles should arive so the
# user has a scope of dodging
start_time = pygame.time.get_ticks()
current_time = 0
coin_delta = 850
tile_delta = 2000
# BOOL VARIABLES
clicked = False
new_coin = True
num_clicks = 0
score = 0
player_alive = True
score = 0
highscore = 0
sound_on = True
easy_level = True
home_page = True
game_page = False
score_page = False
running = True
# While the game is running
while running:
# Fill the game window with GRAY color
win.fill(GRAY)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# If the event is quit then off the bool
# running and quit the window
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE or \
event.key == pygame.K_q:
running = False
# If the event detected is a type of click then
# on the bool clicked
# and perform that task
if event.type == pygame.MOUSEBUTTONDOWN and game_page:
if not clicked:
clicked = True
# Just reverse their rotation direction when
# clicked[Multiply by -1]
for ball in ball_group:
ball.dtheta *= -1
flip_fx.play()
# change the color of ball ater every 5 click
# to make it more alive
num_clicks += 1
if num_clicks % 5 == 0:
color_index += 1
if color_index > len(color_list) - 1:
color_index = 0
color = color_list[color_index]
if event.type == pygame.MOUSEBUTTONDOWN and game_page:
clicked = False
# if home_page bool is true that means its
# home page so show the game name
# and an animation clip of game and
# two buttons easy and hard
if home_page:
connected.update()
pygame.draw.circle(win, BLACK, CENTER, 80, 20)
ball_group.update(color)
# If easy button is clicked then only one ball
# will revolve start=90degree
if easy_btn.draw(win):
ball_group.empty()
ball = Balls((CENTER[0], CENTER[1]+RADIUS),
RADIUS, 90, win)
ball_group.add(ball)
# Once the game is started turn off
# the unnecessary booleans
home_page = False
game_page = True
easy_level = True
# If hard button is clicked then two balls
# will revolve start=90degree
# and 270degree respectively
if hard_btn.draw(win):
ball_group.empty()
ball = Balls((CENTER[0], CENTER[1]+RADIUS), RADIUS, 90, win)
ball_group.add(ball)
ball = Balls((CENTER[0], CENTER[1]-RADIUS), RADIUS, 270, win)
ball_group.add(ball)
# Once the game is started turn off the unnecessary booleans
home_page = False
game_page = True
easy_level = False
# Update the score_page if its true then that means the game is over;
# So display the scores, home button, replay button, and sound button
# options for the user
if score_page:
game_msg.update() # GAME
over_msg.update() # OVER
# Display score if boolean is on else 0
if score:
final_score.update(score, color)
else:
final_score.update("0", color)
# update the highscore if score is greater than highscore
if score and (score >= highscore):
new_high_msg.update(shadow=False)
# If home button is clicked then draw the home_page window(win)
# and take care of those extra booleans
if home_btn.draw(win):
home_page = True
score_page = False
game_page = False
player_alive = True
score = 0
score_msg = Message(WIDTH//2, 100, 60, "0",
score_font, (150, 150, 150), win)
# If replay button is clicked then draw the game_page window(win)
# and take care of those extra booleans
if replay_btn.draw(win):
home_page = False
score_page = False
game_page = True
score = 0
score_msg = Message(WIDTH//2, 100, 60, "0",
score_font, (150, 150, 150), win)
# In game take easy or hard mode (users choice)
if easy_level:
ball = Balls((CENTER[0], CENTER[1]+RADIUS), RADIUS, 90, win)
ball_group.add(ball)
else:
ball = Balls((CENTER[0], CENTER[1]+RADIUS), RADIUS, 90, win)
ball_group.add(ball)
ball = Balls((CENTER[0], CENTER[1]-RADIUS), RADIUS, 270, win)
ball_group.add(ball)
player_alive = True
# Sound trigger points
if sound_btn.draw(win):
sound_on = not sound_on
# Update the sound button image
if sound_on:
sound_btn.update_image(sound_on_img)
pygame.mixer.music.play(loops=-1)
else:
sound_btn.update_image(sound_off_img)
pygame.mixer.music.stop()
# If its game_page then draw circles, tiles, balls, score board
# and particle spin offs
if game_page:
pygame.draw.circle(win, BLACK, CENTER, 80, 20)
ball_group.update(color)
coin_group.update(color)
tile_group.update()
score_msg.update(score)
particle_group.update()
# If player is alive then the balls obstacle and
# coins movement will initiate
if player_alive:
for ball in ball_group:
if pygame.sprite.spritecollide(ball, coin_group, True):
score_fx.play()
score += 1
# Score updation
if highscore <= score:
highscore = score
x, y = ball.rect.center
for i in range(10):
particle = Particle(x, y, color, win)
particle_group.add(particle)
# If ball gets collided with tile then
# player_alive becomes false
# and trigger dead sound
if pygame.sprite.spritecollide(ball, tile_group, True):
x, y = ball.rect.center
for i in range(30):
particle = Particle(x, y, color, win)
particle_group.add(particle)
player_alive = False
dead_fx.play()
# time frame of game
current_time = pygame.time.get_ticks()
delta = current_time - start_time
# Create obstacle coins and once created turn off the boolean
if coin_delta < delta < coin_delta + 100 and new_coin:
y = random.randint(CENTER[1]-RADIUS, CENTER[1]+RADIUS)
coin = Coins(y, win)
coin_group.add(coin)
new_coin = False
# if current_time - start_time is greater or equals to tile_delta
# then create new coin in the obstacle form
if current_time - start_time >= tile_delta:
y = random.choice([CENTER[1]-80, CENTER[1], CENTER[1]+80])
type_ = random.randint(1, 3)
t = Tiles(y, type_, win)
tile_group.add(t)
start_time = current_time
new_coin = True
# when player is dead
if not player_alive and len(particle_group) == 0:
score_page = True
game_page = False
score_page_fx.play()
ball_group.empty()
tile_group.empty()
coin_group.empty()
# Game screen
pygame.draw.rect(win, BLUE, (0, 0, WIDTH, HEIGHT), 5, border_radius=10)
clock.tick(FPS) # Screen FPS
pygame.display.update() # updates display
# Exit game window
pygame.quit()
Output:
Use a mouse click to play.