Open In App

Balloon Archer game in Python using the Pygame module

Last Updated : 16 Oct, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Balloon Archer is a 2D game where the player uses a bow and arrow to pop balloons that float across the screen. The main theme of this game is to shoot down the balloons using arrows and on each successful attempt, the player earns a point and on each unsuccessful attempt, the player loses a life. If the player loses all 5 lives, then it’s GAME OVER!

The player stands on the left half of the screen and must shoot the balloons that go upwards from bottom to top on the right half of the screen. If the arrow goes off the screen without hitting any balloons, then life is deducted. The player must navigate the archer to aim perfectly. This article will cover how to create such a game in Object Oriented Programming style (OOP) in Python.

Balloon Archer Game in Python

The Balloon Archer game has the following rules:

  1. The player controls the archer’s movements using the arrow keys. The archer can move left, right, up, and down within the specified constrained region.
  2. The player can shoot the arrows using the spacebar. The arrows move horizontally from left to right.
  3. The player’s score increases by one for each successful balloon hit.
  4. If an arrow goes off the screen without hitting any single balloon, the player loses a life.
  5. The goal is to achieve the maximum score possible before running out of lives.

Balloon Archer Game Controls

The Balloon Archer game can be played in Python using keyboard controls. These controls are as follows:

  1. Use left/right and up/down arrows to move the archer
  2. Use the space bar to shoot the arrow
  3. Use ‘r’ or ‘q’ to restart or quit the game

Functionalities of Balloon Archer Game in Python

The implementation of the balloon archer game using the Pygame module is divided into 6 parts. We will see the stepwise implementation of the game for a better understanding.

Initial Setup

Here, we initialize the module and define all the necessary global variables that are required all over the game. We will install the Pygame module for defining the colors, FPS, font, dimensions of the screen, etc, and the random module to randomly display the balloons on the screen within a specified region.

The pygame.init() method is used to initialize the necessary modules. Then we used the pygame.font.Font() method to create a font object and pass the font file and the size of the font as the parameter. The pygame.display.set_caption() method is used to set the title of the Pygame window. It set the title as whatever string is provided in the parameters. Then pygame.time.Clock() method is used to create a clock object to keep track of time.

Python3




# Python program to build an archery game
import pygame
import random
 
pygame.init()
 
# Dimensions of the game window
WIDTH, HEIGHT = 600, 500
 
# Standard Colors
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
 
# Paths
balloonPath = "balloon.png"
archerPath =  "archer.png"
arrowPath =   "arrow.png"
 
font = pygame.font.Font('freesansbold.ttf', 20)
 
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Balloon Archer")
 
# To control the frame rate
clock = pygame.time.Clock()
FPS = 30


The Helper Functions

The helper functions are user-defined functions that are used by the game manager. The populateBalloons() function is used for generating balloons at random positions on the right half of the screen. It takes in the balloons’ width, height, and speed, counts them, and spaces them accordingly. The ‘listOfBalloons’ Python List stores the list of randomly generated balloons.

The gameOver() function is called when the player runs out of all the lives. This function asks for the player’s input to restart or terminate the game. If the player presses ‘r’, then the game restarts, and if the player presses ‘q’, then the game is terminated. The font.render() method is used to render the text on a surface object. The screen.blit() method is used to draw the surface on this surface. It takes the rendered text and its size as parameters.

The next is event handling in Pygame. In this game, we will be using keyboard events only to play, quit, shoot arrows, and move up or down and right or left. The pygame.event.get() returns a list of current events. We are using the following event in this section:

  • pygame.QUIT: used to quit the Pygame window
  • pygame.KEYDOWN: detects if a keyboard key is pressed or not
  • pygame.K_r: checks if “r” key was pressed or not
  • pygame.K_q: checks if “q” key was pressed or not

The pygame.display.update() method as the name suggests, is used to update the content of the Pygame window.

Python3




# Spawn the balloons
def populateBalloons(bWidth, bHeight, bSpeed, bCount):
    listOfBalloons = []
 
    # For the given count, spawn balloons at random
    # positions in the right half of the screen with
    # the given dimensions and speed
    for _ in range(bCount):
        listOfBalloons.append(Balloon(random.randint(
            WIDTH//2, WIDTH-bWidth), random.randint(0, HEIGHT),
                                      bWidth, bHeight, bSpeed))
 
    return listOfBalloons
 
   
# Game Over Screen. Waits for the user to replay or quit
def gameOver():
    gameOver = True
 
    while gameOver:
        gameOverText = font.render("GAME OVER", True, WHITE)
        retryText = font.render("R - Replay    Q - Quit", True, WHITE)
 
        # render the text on the screen using the pygame blit function
        screen.blit(gameOverText, (WIDTH//2-200, HEIGHT//2-100))
        screen.blit(retryText, (WIDTH//2-200, HEIGHT//2-80))
 
        # Event handling
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False
            if event.type == pygame.KEYDOWN:
                # replay
                if event.key == pygame.K_r: 
                    return True
                # quit
                if event.key == pygame.K_q:
                    return False
 
        pygame.display.update()


The Archer Class

This class consists of all the methods needed to control the player. We first initialize the data members of the class, i.e., height, width, and speed. The pygame.transform.scale() method is used to change the size of the image. We will be using the “archer.png” image. The pygame.get_rect() method returns the rectangular object of the image. The position of the archer is controlled using the “archerRect”.

archer.png

The display() function displays the transformed image on the screen whereas the update() function updates the archer position on the screen based on the xFac and yFac variables. These variables are obtained based on the key pressed by the user. This method also prevents the player from moving beyond the left edge and the center of the screen and contains the player’s position to the left half of the screen. If the player presses the up/down arrow keys, then the value of yFac will be -1/1, and only the Y component of the player is changed. If the player presses the left/right arrow keys, then the value of xFac will be -1/1, and only the X component of the player will be changed.

Python3




class Archer:
    # init function to set the object variables and load the image
    def __init__(self, width, height, speed):
        self.width = width
        self.height = height
        self.speed = speed
 
        self.archer = pygame.transform.scale(
            pygame.image.load(archerPath), (self.width, self.height))
        self.archerRect = self.archer.get_rect()
 
        # Default position
        self.archerRect.x, self.archerRect.y = 100, HEIGHT//2
 
    # Method to render the archer on the screen
    def display(self):
        screen.blit(self.archer, self.archerRect)
 
    # Method to update the archer position
    def update(self, xFac, yFac):
        self.archerRect.x += xFac*self.speed
        self.archerRect.y += yFac*self.speed
 
        # Constraints to maintain the archer in the left half of the screen
        if self.archerRect.x <= 0:
            self.archerRect.x = 0
        elif self.archerRect.x >= WIDTH//2 - self.archerRect.w:
            self.archerRect.x = WIDTH//2 - self.archerRect.w
        if self.archerRect.y <= 0:
            self.archerRect.y = 0
        elif self.archerRect.y >= HEIGHT-self.archerRect.h:
            self.archerRect.y = HEIGHT - self.archerRect.h


The Balloon Class

This class consists of all the methods needed to control the balloons. The __init__() method takes the parameters such as the initial position, width, height, and speed of the balloon and loads an image to represent it. The position and the behavior of the balloon are controlled using the “balloonRect” after transforming the “balloon.png” image.

balloon.png

balloon.png

The display() function displays the transformed image on the screen whereas the update() function is used to update the Y coordinate of the balloon. If the balloon goes beyond Y=0, then its position is set back to Y=HEIGHT.

Python3




# Balloon class consists of all the
# functionalities related to the balloons
class Balloon:
    # init function to set the object variables and load the image
    def __init__(self, posx, posy, width, height, speed):
        self.width, self.height = width, height
        self.speed = speed
 
        self.balloonImg = pygame.image.load(balloonPath)
        self.balloon = pygame.transform.scale(
            self.balloonImg, (self.width, self.height))
        self.balloonRect = self.balloon.get_rect()
 
        self.balloonRect.x, self.balloonRect.y = posx, posy
 
    # Method to render the balloon on the screen
    def display(self):
        screen.blit(self.balloon, self.balloonRect)
 
    # Method to update the position of the balloon
    def update(self):
        self.balloonRect.y -= self.speed
 
        # If the balloon crosses the upper edge of the screen,
        # we put it back at the lower edge
        if self.balloonRect.y < 0:
            self.balloonRect.y = HEIGHT+10


The Arrow Class

This class consists of all the methods needed to control the arrows. The constructor takes in the parameters such as the arrow’s initial position, width, height, and speed. It then loads an image to represent it. The position and behavior of the arrow are controlled using the “arrowRect” after transforming the “arrow.png” image. The image is loaded using the pygame.image.load() method.

arrow.png

arrow.png

The display() function displays the transformed image on the screen whereas the update() function is used to update the arrow’s position. It just increments the arrow’s X coordinate by its speed. The updateHit() function is used to check if the arrow has hit any balloons on its way. If it hits a balloon, then the “hit” variable is set to 1 else it is set to 0. The getHit() function is used to return the value of the “hit” variable maintained by the arrow object. Based on this return value, the decision to reduce a life will be made.

Python3




# Arrow class consists of all the functions related to the arrows
class Arrow:
    # init function to set the object variables and load the image
    def __init__(self, posx, posy, width, height, speed):
        self.width, self.height = width, height
        self.speed = speed
        self.hit = 0    # Used to track if the arrow has hit any balloon
 
        self.arrow = pygame.transform.scale(
            pygame.image.load(arrowPath), (width, height))
        self.arrowRect = self.arrow.get_rect()
 
        # arrow coordinates
        self.arrowRect.x, self.arrowRect.y = posx, posy
 
    # Method to render the arrow on the screen
    def display(self):
        screen.blit(self.arrow, self.arrowRect)
 
    # Method to update the position of the arrow
    def update(self):
        self.arrowRect.x += self.speed
 
    # Method to update the hit variable
    def updateHit(self):
        self.hit = 1
 
    def getHit(self):
        return self.hit


The Game Manager

This is used to implement the game logic and control the game objects. The initial score is set to 0 and lives to 5. Then using the pygame.fill() method the background color of the screen is set to “green”. We will use event handling for moving the archer up/down and left/right and also use the space bar for shooting arrows:

  • pygame.K_UP: checks if the “up” key was pressed or not
  • pygame.K_DOWN: checks if the “down” key was pressed or not
  • pygame.K_LEFT: checks if the “left” key was pressed or not
  • pygame.K_RIGHT: checks if the “right” key was pressed or not
  • pygame.K_SPACE: checks if “space-bar” was pressed or not

Python3




# Game Manager
def main():
    score = 0
    lives = 5
    running = True
 
    archer = Archer(60, 60, 7)
    xFac, yFac = 0, 0    # Used to control the archer
 
    numBalloons = 10
 
    listOfBalloons = populateBalloons(30, 40, 5, numBalloons)
    listOfArrows = []
 
    while running:
        screen.fill(GREEN)  # Background
 
        # Representing each life with an arrow tilted by 45 degrees
        for i in range(lives):
            screen.blit(pygame.transform.rotate(pygame.transform.scale(
                pygame.image.load(arrowPath), (20, 30)), 45), (i*30, 10))
 
        # Rendering the score
        scoreText = font.render(f"Score: {score}", True, WHITE)
        screen.blit(scoreText, (10, HEIGHT-50))
 
        if len(listOfBalloons) == 0:
            listOfBalloons = populateBalloons(30, 40, 5, numBalloons)
 
        # When all the lives are over
        if lives <= 0:
            running = gameOver()
 
            # Clearing the lists
            listOfBalloons.clear()
            listOfArrows.clear()
 
            # Resetting the variables
            lives = 5
            score = 0
            listOfBalloons = populateBalloons(30, 40, 5, numBalloons)
 
        # Display and update all the balloons
        for balloon in listOfBalloons:
            balloon.update()
            balloon.display()
 
        # Display and update all the arrows
        for arrow in listOfArrows:
            arrow.update()
            arrow.display()
 
        # Display and update the archer
        archer.display()
        archer.update(xFac, yFac)
 
        # Event handling
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
 
            # Key press event
            if event.type == pygame.KEYDOWN:
                # Replay button
                if event.key == pygame.K_r:    
                    listOfBalloons = populateBalloons(30, 40, 5, numBalloons)
                    score = 0
                # Right arrow key => move rightwards => xFac = 1
                if event.key == pygame.K_RIGHT: 
                    xFac = 1
                # Left arrow key => move leftwards => xFac = -1
                if event.key == pygame.K_LEFT: 
                    xFac = -1
                # Down arrow key => move downwards => yFac = 1
                if event.key == pygame.K_DOWN: 
                    yFac = 1
                # Up arrow key => move upwards => yFac = -1
                if event.key == pygame.K_UP:   
                    yFac = -1
                # Fire button
                if event.key == pygame.K_SPACE: 
                    listOfArrows.append(Arrow(
                        archer.archerRect.x,
                        archer.archerRect.y+archer.archerRect.h/2-15, 60, 30, 10))
 
            # Key release event
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_RIGHT or event.key == pygame.K_LEFT:
                    xFac = 0
                if event.key == pygame.K_DOWN or event.key == pygame.K_UP:
                    yFac = 0
 
        # Check for any collision between the arrows and the balloons
        for arrow in listOfArrows:
            for balloon in listOfBalloons:
                if pygame.Rect.colliderect(arrow.arrowRect, balloon.balloonRect):
                    # Changes the arrow's 'hit' from 0 to 1
                    arrow.updateHit()  
                    # Remove the balloon form the list
                    listOfBalloons.pop(listOfBalloons.index(balloon))
                    # Increase the score
                    score += 1     
 
        # Delete the arrows that crossed end of the screen
        for arrow in listOfArrows:
            if arrow.arrowRect.x > WIDTH:
                if not arrow.getHit():
                    # If the arrow's state is 0, then a life is deducted
                    lives -= 1 
                listOfArrows.pop(listOfArrows.index(arrow))
 
        pygame.display.update()
        clock.tick(FPS)


By using the following steps, you will be able to create the Balloon Archer game in Python using the Pygame module.

Complete Implementation of The Balloon Archer Game

Python3




# Python program to build an archery game
import pygame
import random
 
pygame.init()
 
# Dimensions of the game window
WIDTH, HEIGHT = 600, 500
 
# Standard Colors
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
 
# Paths
balloonPath = "balloon.png"
archerPath = "archer.png"
arrowPath = "arrow.png"
 
font = pygame.font.Font('freesansbold.ttf', 20)
 
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Balloon Archer")
 
# To control the frame rate
clock = pygame.time.Clock()
FPS = 30
 
 
class Archer:
    # init function to set the object variables and load the image
    def __init__(self, width, height, speed):
        self.width = width
        self.height = height
        self.speed = speed
 
        self.archer = pygame.transform.scale(
            pygame.image.load(archerPath), (self.width, self.height))
        self.archerRect = self.archer.get_rect()
 
        # Default position
        self.archerRect.x, self.archerRect.y = 100, HEIGHT//2
 
    # Method to render the archer on the screen
    def display(self):
        screen.blit(self.archer, self.archerRect)
 
    # Method to update the archer position
    def update(self, xFac, yFac):
        self.archerRect.x += xFac*self.speed
        self.archerRect.y += yFac*self.speed
 
        # Constraints to maintain the archer in the left half of the screen
        if self.archerRect.x <= 0:
            self.archerRect.x = 0
        elif self.archerRect.x >= WIDTH//2 - self.archerRect.w:
            self.archerRect.x = WIDTH//2 - self.archerRect.w
        if self.archerRect.y <= 0:
            self.archerRect.y = 0
        elif self.archerRect.y >= HEIGHT-self.archerRect.h:
            self.archerRect.y = HEIGHT - self.archerRect.h
 
 
# Balloon class consists of all the
# functionalities related to the balloons
class Balloon:
    # init function to set the object variables and load the image
    def __init__(self, posx, posy, width, height, speed):
        self.width, self.height = width, height
        self.speed = speed
 
        self.balloonImg = pygame.image.load(balloonPath)
        self.balloon = pygame.transform.scale(
            self.balloonImg, (self.width, self.height))
        self.balloonRect = self.balloon.get_rect()
 
        self.balloonRect.x, self.balloonRect.y = posx, posy
 
    # Method to render the balloon on the screen
    def display(self):
        screen.blit(self.balloon, self.balloonRect)
 
    # Method to update the position of the balloon
    def update(self):
        self.balloonRect.y -= self.speed
 
        # If the balloon crosses the upper edge of the screen,
        # we put it back at the lower edge
        if self.balloonRect.y < 0:
            self.balloonRect.y = HEIGHT+10
 
 
# Arrow class consists of all the functions related to the arrows
class Arrow:
    # init function to set the object variables and load the image
    def __init__(self, posx, posy, width, height, speed):
        self.width, self.height = width, height
        self.speed = speed
        # Used to track if the arrow has hit any balloon
        self.hit = 0   
 
        self.arrow = pygame.transform.scale(
            pygame.image.load(arrowPath), (width, height))
        self.arrowRect = self.arrow.get_rect()
 
        # arrow coordinates
        self.arrowRect.x, self.arrowRect.y = posx, posy
 
    # Method to render the arrow on the screen
    def display(self):
        screen.blit(self.arrow, self.arrowRect)
 
    # Method to update the position of the arrow
    def update(self):
        self.arrowRect.x += self.speed
 
    # Method to update the hit variable
    def updateHit(self):
        self.hit = 1
 
    def getHit(self):
        return self.hit
 
 
# Spawn the balloons
def populateBalloons(bWidth, bHeight, bSpeed, bCount):
    listOfBalloons = []
 
    # For the given count, spawn balloons at random
    # positions in the right half of the screen with
    # the given dimensions and speed
    for _ in range(bCount):
        listOfBalloons.append(Balloon(random.randint(
            WIDTH//2, WIDTH-bWidth), random.randint(0, HEIGHT),
            bWidth, bHeight, bSpeed))
 
    return listOfBalloons
 
 
# Game Over Screen. Waits for the user to replay or quit
def gameOver():
    gameOver = True
 
    while gameOver:
        gameOverText = font.render("GAME OVER", True, WHITE)
        retryText = font.render("R - Replay    Q - Quit", True, WHITE)
 
        # render the text on the screen using the pygame blit function
        screen.blit(gameOverText, (WIDTH//2-200, HEIGHT//2-100))
        screen.blit(retryText, (WIDTH//2-200, HEIGHT//2-80))
 
        # Event handling
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False
            if event.type == pygame.KEYDOWN:
                # replay
                if event.key == pygame.K_r: 
                    return True
                # quit
                if event.key == pygame.K_q: 
                    return False
 
        pygame.display.update()
 
 
# Game Manager
def main():
    score = 0
    lives = 5
    running = True
 
    archer = Archer(60, 60, 7)
    # Used to control the archer
    xFac, yFac = 0, 0   
 
    numBalloons = 10
 
    listOfBalloons = populateBalloons(30, 40, 5, numBalloons)
    listOfArrows = []
 
    while running:
      # Background
        screen.fill(GREEN) 
 
        # Representing each life with an arrow tilted by 45 degrees
        for i in range(lives):
            screen.blit(pygame.transform.rotate(pygame.transform.scale(
                pygame.image.load(arrowPath), (20, 30)), 45), (i*30, 10))
 
        # Rendering the score
        scoreText = font.render(f"Score: {score}", True, WHITE)
        screen.blit(scoreText, (10, HEIGHT-50))
 
        if len(listOfBalloons) == 0:
            listOfBalloons = populateBalloons(30, 40, 5, numBalloons)
 
        # When all the lives are over
        if lives <= 0:
            running = gameOver()
 
            # Clearing the lists
            listOfBalloons.clear()
            listOfArrows.clear()
 
            # Resetting the variables
            lives = 5
            score = 0
            listOfBalloons = populateBalloons(30, 40, 5, numBalloons)
 
        # Display and update all the balloons
        for balloon in listOfBalloons:
            balloon.update()
            balloon.display()
 
        # Display and update all the arrows
        for arrow in listOfArrows:
            arrow.update()
            arrow.display()
 
        # Display and update the archer
        archer.display()
        archer.update(xFac, yFac)
 
        # Event handling
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
 
            # Key press event
            if event.type == pygame.KEYDOWN:
                # Replay button
                if event.key == pygame.K_r:    
                    listOfBalloons = populateBalloons(30, 40, 5, numBalloons)
                    score = 0
                # Right arrow key => move rightwards => xFac = 1
                if event.key == pygame.K_RIGHT: 
                    xFac = 1
                # Left arrow key => move leftwards => xFac = -1
                if event.key == pygame.K_LEFT: 
                    xFac = -1
                # Down arrow key => move downwards => yFac = 1
                if event.key == pygame.K_DOWN: 
                    yFac = 1
                # Up arrow key => move upwards => yFac = -1
                if event.key == pygame.K_UP:   
                    yFac = -1
                    # Fire button
                if event.key == pygame.K_SPACE: 
                    listOfArrows.append(Arrow(
                        archer.archerRect.x,
                        archer.archerRect.y+archer.archerRect.h/2-15, 60, 30, 10))
 
            # Key release event
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_RIGHT or event.key == pygame.K_LEFT:
                    xFac = 0
                if event.key == pygame.K_DOWN or event.key == pygame.K_UP:
                    yFac = 0
 
        # Check for any collision between the arrows and the balloons
        for arrow in listOfArrows:
            for balloon in listOfBalloons:
                if pygame.Rect.colliderect(arrow.arrowRect, balloon.balloonRect):
                    # Changes the arrow's 'hit' from 0 to 1
                    arrow.updateHit()  
                    # Remove the balloon form the list
                    listOfBalloons.pop(listOfBalloons.index(balloon))
                    # Increase the score
                    score += 1    
 
        # Delete the arrows that crossed end of the screen
        for arrow in listOfArrows:
            if arrow.arrowRect.x > WIDTH:
                if not arrow.getHit():
                    # If the arrow's state is 0, then a life is deducted
                    lives -= 1
                listOfArrows.pop(listOfArrows.index(arrow))
 
        pygame.display.update()
        clock.tick(FPS)
 
 
if __name__ == "__main__":
    main()
    pygame.quit()


Output:

Python Balloon Archer Game

Python Balloon Archer Game

Working of Python Balloon Archer Game

Working of Python Balloon Archer Game



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads