Open In App

Spiral Sprint Game in Python Using Pygame

Last Updated : 27 Jul, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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.

Spiral sprint game in Python using Pygame

SpiralSprint_Repo_Structure

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

Python3




# 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.

Python3




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

Python3




# 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

Python3




# 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

Python3




# 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] 

Python3




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

Python3




# 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

Python3




# 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

Python3




# 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  

Python3




# 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.

Python3




# 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.

Python3




# 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.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads