How To Make a Game With Python

By | July 6, 2021

Python is one of the simplest and straightforward programming languages, that make it easy for the developer to put logic simply, without worrying about the complex syntax of the programing language. And what could be more logical than writing the code for a game in a programming language?

When it comes to development with Python, rather than building the complete project from scratch we as a Python developer uses open source libraries and frameworks to build our projects. With that, we do not have to reinvent the wheel and get through our project easily. And this is also one of the main features of Python why many developers love to code with Python. The plethora of libraries and frameworks really help the Python developers to get quickly started with the Project.

Vamware

Although Python has many applications, but in the Gaming development industry, developers do not prefer Python to build real-world or complex games, because of its speed. Still, we have many libraries present in Python, which can be used in Game Development with Python. Amongst all the libraries “PyGame” is the most popular Python Game Development library. And in this tutorial, we will learn what is Pygame in Python, how to get started with PyGame and we will also build a snake game using Python and PyGame library.

What is Pygame?

Writing the Code from scratch for a software or application could be a nightmare for any developer. And for Game development where we have multiple things going on, such as keypress events, graphics, display movement, sound, etc. there we need some tools that can handle all these things, so we can only focus on the logic of the Game, rather than writing multiple lines of code just to load an image or print graphics on the screen.

Here comes the Python “Pygame” library, as the name suggests, it is a Python module that is generally used to build games using Python. It is an open-source third-party library, and it contains many useful functions and modules for game development. With Pygame the developer does not need to worry about drawing graphics, playing sound, key events, or loading images on the screen.

Apart from all the modules and functions provided by the Pygame library, there are many features and functions it misses. It does not support any inbuilt feature for drag and drop, and Physics. A new Python learner who is just getting started with Python intermediate programming can learn and use Pygame to built logic for Games.

Get Started With Pygame

The first step toward building games using Pygame is installing the Pygame for your Python environment. To install Pygame we can use the Python pip install terminal command.

pip install pygame

After installing the Pygame library we can start writing our first game script with Python and Pygame.

How to Make Game with Python: A Snake Game with Python and Pygame

We all have played the simple snake game, where the snake eats an apple or prey which increases its length and speed. And if the snakehead collides with its body or wall the game gets over.

To build the game we will create a directory by the name Snake Game where we will store our game main.py file with other resources such as snake block, game title logo, and apple images.

Our Working directory will be in the following order.

Snake Game:.
│───main.py
└───resources
       apple.png
       game_icon.png
       snake_block.png

game_icon.png

snake_block.png

apple.png

 

 

 

 

You can also download these images and store them in the resources directory.

Now let’s open your favorite Python IDE or text editor and start writing code in main.py file.

We will start with importing the required modules.

import pygame
import time
import random

Both random and time are the Python’s inbuilt modules, and in the game, we will be using these modules for time delay and generating random coordinates for the apple.

The code for the complete game is more than 250 lines so it would be great that we divide the complete code into multiple functions or modules. It will make the code more readable, and reusable.

After importing the important modules let’s write the snake movement functions that will be responsible for the changing direction of the snake movement.

The snake can move in four directions up, down, left, and right so we need to define 4 functions.

# snake movement
def move_left():
    global direction
    direction="left"

def move_right():
    global direction
    direction="right"

def move_up():
    global direction
    direction="up"

def move_down():
    global direction
    direction="down"

In all the movement functions we are accessing the direction identifier with global keyword because we are changing the direction global variable identifier inside the functions and we want to reflect that changes outside the function too.

To know more about how the global and local scope variables work in Python click here.

Python is an interpreted programming langauge so it is very important to define the functions in a sequential order before we call them. That’s why we are defining the helper functions first so we can call them in the upcoming code section.

When we play the game we also get a scoreboard that keeps updating when some event occurs. So for our snake game let’s define a function player_score that will display the player’s live score on the top right corner of the screen.

# the player score
def player_score():
    my_score = pygame.font.SysFont("Calibri",20).render(f"Player Score: {snake_length}", True, (255, 255, 255))
    # show player score on the top right corner
    screen.blit(my_score, (700, 10))

The pygame.font.SysFont() method defines the font of the text and its size.

The render()method renders the message on the screen, and it accepts the color code in RGB format. In our game, we have provided the white color with (255, 255, 255) color code.

While playing the game, the player may get out for various reasons, such as when the snake hit the walls or itself or the player try to quit the game by pressing the escape button, there we need a function that gets invoke and show the player its final score and automatically quit the game after 4 seconds.

For such functionality let’s define a game_over() function

# function when the game is over
def game_over():
    # change the background color
    screen.fill((255, 255, 255))
    # the Game over message
    my_score = pygame.font.SysFont("Calibri",40).render(f"Game Over Your Score: {snake_length}", True, (0, 0, 0))
    screen.blit(my_score, (150, 350))
    pygame.display.update()

    # wait for 4 seconds and close the game
    time.sleep(4)
    exit(0)

The screen.fill() method will fill the screen with the specified RGB color (255, 255, 255) (white in our case).

pygame.font.SysFont() function will set the font of the text.

The render() function print the text on the screen.

The blit() function print the render message on the specified x and y coordinates.

The pygame.display.update() function will update the screen with the specified fill() function color.

The sleep(4) function will delay the code execution for 4 seconds.

The exit(0) function will stop the program execution close the program.

Let’s also define dedicated functions that will draw the snake blocks and apple on the screen, so we do not have to write that code again and again in the program, whenever we need to display the snake and apple on the screen.

# a function to draw the snake on the screen
def draw_snake_block():
    screen.fill(BACKGROUND_COLOR)

    # print all the snae block on the screen
    for i in range(snake_length):
        screen.blit(snake_block, (snake_block_x[i], snake_block_y[i]))

    # update the screen
    pygame.display.update()

# draw the apple on the screen
def draw_apple():
    screen.blit(apple, (apple_x, apple_y))
    pygame.display.update()

The draw_snake_block() and draw_apple() functions will draw the snakehead, its body, and apple with every iteration of the game running. The game runs in multiple frames and these two functions will help us to draw the snake and apple in every frame.

In the Snake game, the snake gets automatically starts walking in the pressed direction. Now we also need to define a function snake_walk() that will specify the snake path walk.

def snake_walk():
    # access the global variables
    global direction
    global snake_block_x
    global snake_block_y
    global snake_length

    """
        when the head direction change
        the body the snake will follow its previous
        snake block
    """
    for i in range(snake_length-1,0,-1):
        snake_block_x[i]= snake_block_x[i-1]
        snake_block_y[i] = snake_block_y[i-1]

    # change the snake head direction
    if direction=="up":
        snake_block_y[0]-=block_pixel_size

    if direction=="down":
        snake_block_y[0]+=block_pixel_size

    if direction=="right":
        snake_block_x[0]+=block_pixel_size

    if direction=="left":
        snake_block_x[0]-=block_pixel_size

    # draw the snake with every iteration of the game running
    draw_snake_block()

In the snake_walk() function we are changing the head position of the snake_block_x and snake_block_y coordinates based on the current direction.

And inside the for loop, we are just specifying that the body blocks of the snake must follow its previous block. And with each iteration, we are drawing the new coordinates of the snake blocks with draw_snake_block() function. With this function, we will get a moving snake on the screen.

Let’s also define a function that checks if the snake ate the apple or not. This function will return a boolean value that we will use later in the run() function.

# function to check if the snake head eat the apple
def did_snake_eat_apple():
    if snake_block_x[0]>=apple_x and  snake_block_x[0]< apple_x+block_pixel_size: 
        if snake_block_y[0]>=apple_y and  snake_block_y[0]<apple_y+block_pixel_size:
            return True
    return False

The did_snake_eat_apple() check if the head position of the snake block is colliding with the coordinates of the apple coordinates.

When the snake eats an apple we also need to increase the length of the snake and the overall speed of the snake’s walk. For that, we can define a function increment_snake_length().

# function to increase the snake length
def increment_snake_length():
    # access the global snake features
    global snake_length
    global snake_block_x
    global snake_block_y
    global SNAKE_SPEED

    # increase the snake length by One
    snake_length +=1

    # add a new random data in the snake x and y coordinates list
    snake_block_x.append(-1)
    snake_block_y.append(-1)

    # increase the snake speed
    if SNAKE_SPEED > 0.2:
        if snake_length %2 ==0:
            SNAKE_SPEED-=0.1

The append() method will add -1 as a new element or snake block to the snake x and y coordinate blocks. However, the coordinates will be overwritten by the snake_walk() function. Because soon the body blocks update their position with each iteration and they follow the coordinates of the snakehead block.

There are two ways the player can lose the game, one is by body collision and the second is the snake collision.

The body collision occurs when the snakehead collides with its body, and the wall collision occurs when the snakehead collides with the walls.

Let’s define two functions snake_collision() and wall_collision() that will over the game when the snake hits the walls or itself.

# function for snake collision
def snake_collision():
    ''' if the snake head collide with the
        snake body
        close the game
    '''
    for i in range(1,snake_length):
        if snake_block_x[0]>=snake_block_x[i] and snake_block_x[0]< snake_block_x[i]+20: 
            if snake_block_y[0]>=snake_block_y[i] and snake_block_y[0]<snake_block_y[i]+20:
                game_over()

# function for the snake-wall collision
def wall_collision():
    # if snake head hit left wall or right wall
    if snake_block_x[0]<0 or snake_block_x[0] >=screen_size[0]:
        game_over()
    # if the snake head hits the up or down wall
    if snake_block_y[0]<0 or snake_block_y[0] >=screen_size[1]:
        game_over()

Both snake_collision() and wall_collision() functions call game_over function when the if the condition gets True.

Now let’s define the run() function that will run our game in multiple frames, and manage all the above-defined functions.

def run():
    # access the global apple x and y coordinates

    global apple_x
    global apple_y

    #run the game until the user click on the close button
    game_running = True
    while game_running:
        # loop through all the game events
        for event in pygame.event.get():
            # check if the user click the close button
            if event.type == pygame.QUIT:
                game_running=False

            # snake control system

            if event.type == pygame.KEYDOWN:
                # if player press escape the game ends
                if event.key == pygame.K_ESCAPE:
                    game_over()

                # to move left
                if event.key == pygame.K_LEFT:
                    if direction in ["up", "down"]:
                        move_left()

                # to move right
                if event.key == pygame.K_RIGHT:
                    if direction in ["up", "down"]:
                        move_right()

                # to move up
                if event.key == pygame.K_UP:
                    if direction in ["left", "right"]:
                        move_up()

                # to move down
                if event.key == pygame.K_DOWN:
                    if direction in ["left", "right"]:
                        move_down()
        snake_walk()
        draw_apple()
        # check if the snake eat the apple
        if (did_snake_eat_apple()):
            # set new coordinates for the apple
            apple_x = random.randint(1, 29)*block_pixel_size
            apple_y = random.randint(1, 29)*block_pixel_size
            # draw a new apple
            draw_apple()

            # increasee the snake length
            increment_snake_length()

        # check if the snake head collide with its body
        snake_collision()
        # check if the snake collide with the walls
        wall_collision()
        # update the palyer score
        player_score()

        # update the screen
        pygame.display.update()

        # the speed of the snake movement
        time.sleep(SNAKE_SPEED)

Mostly all the run()function statements are inside the while game_running statement. And each iteration of this while loop defines a single frame.

The pygame.event.get() function returns an iterator of events which generally represent the keys press by the user.

The event.type represents the type of the event. The pygame.QUIT occur when the user clicks on the window close button.

The event.key property represents the key pressed by the user. Using this property we called the movement functions of the snake.

Now let’s define the main section of our program.

if __name__ =="__main__":
    #Initialize Pygame
    pygame.init()

    # width and height of the screen window (w:h)(900:700)
    screen_size = width, height = 900, 700

    screen = pygame.display.set_mode(screen_size)

    # title and icon of the screen window
    pygame.display.set_caption("Snake Game")
    icon = pygame.image.load("resources/game_icon.png")
    pygame.display.set_icon(icon)

    #set the color of the screen window (R,G,B)
    BACKGROUND_COLOR = (0,0,0)   #black
    screen.fill(BACKGROUND_COLOR)

    # load the snake block on the screen
    snake_block = pygame.image.load("resources/snake_block.png").convert()
    # convert the snake block to 20X20 pixels
    block_pixel_size = 20
    snake_block = pygame.transform.scale(snake_block, (block_pixel_size, block_pixel_size))

    #load apple on the screen window
    apple =  pygame.image.load("resources/apple.png").convert()
    # set the apple size to 20X20 pixels (similar to the snake block size)
    apple=pygame.transform.scale(apple, (block_pixel_size, block_pixel_size))

    # initial coordinate of the apple block
    apple_x = 200
    apple_y = 100

    # initial snake head coordinate
    snake_block_x=[100,]
    snake_block_y=[100,]

    # initial length of the snake
    snake_length =1

    # intial direction of the snake movement
    direction ="right"

    # initial snake speed
    SNAKE_SPEED = 1

    # place snake block on the window screen
    screen.blit(snake_block, (snake_block_x[0], snake_block_y[0]))
    # place apple on the window
    screen.blit(apple, (apple_x, apple_y))

    # update the window screen to render the snake block
    pygame.display.update()

    # call the run function
    run()

The pygame.init() method initialize the pygame object, and it is very necessary to write this statement.

The display.set_mode() function specifies the screen size of the window.

The display.set_caption() specify the title of the screen window.

The image.load() function load the image in this program.

The display.set_icon() specify the window icon.

The fill() function fill the background of the window with the specified RGB color.

The transform.scale() function scale the image into specified pixel size (width, height).

apple_x, and apple_y identifiers specify the initial coordinates of the apple on the screen.

snake_block_x, and snake_block_y lists represent the coordinates of the snakehead its body.

The blit() method renders the images on the screen.

The update method is important to print the images on the screen.

That’s all with the coding, now put all the code together and execute.

Snake Game With Python and Pygame

import pygame
import time
import random

# snake movement
def move_left():
    global direction
    direction="left"

def move_right():
    global direction
    direction="right"

def move_up():
    global direction
    direction="up"
def move_down():
    global direction
    direction="down"

# the player score
def player_score():
    my_score = pygame.font.SysFont("Calibri",20).render(f"Player Score: {snake_length}", True, (255, 255, 255))
    # show player score on the top right corner
    screen.blit(my_score, (700, 10))

# function when the game is over
def game_over():
    # change the background color
    screen.fill((255, 255, 255))
    # the Game over message
    my_score = pygame.font.SysFont("Calibri",40).render(f"Game Over Your Score: {snake_length}", True, (0, 0, 0))
    screen.blit(my_score, (150, 350))
    pygame.display.update()

    # wait for 4 seconds and close the game
    time.sleep(4)
    exit(0)

# a function to draw the snake on the screen
def draw_snake_block():
    screen.fill(BACKGROUND_COLOR)

    # print all the snae block on the screen
    for i in range(snake_length):
        screen.blit(snake_block, (snake_block_x[i], snake_block_y[i]))

    # update the screen
    pygame.display.update()

# draw the apple on the screen
def draw_apple():
    screen.blit(apple, (apple_x, apple_y))
    pygame.display.update()

def snake_walk():
    # access the global variables
    global direction
    global snake_block_x
    global snake_block_y
    global snake_length

    """
        when the head direction change
        the body the the snake will follow its previous
        snake block
    """
    for i in range(snake_length-1,0,-1):
        snake_block_x[i]= snake_block_x[i-1]
        snake_block_y[i] = snake_block_y[i-1]

    # change the snake head direction
    if direction=="up":
        snake_block_y[0]-=block_pixel_size

    if direction=="down":
        snake_block_y[0]+=block_pixel_size

    if direction=="right":
        snake_block_x[0]+=block_pixel_size

    if direction=="left":
        snake_block_x[0]-=block_pixel_size

    # draw the snake with every iteration of the game running
    draw_snake_block()


# function to check if the snake head eat the apple
def did_snake_eat_apple():
    if snake_block_x[0]>=apple_x and  snake_block_x[0]< apple_x+block_pixel_size:
        if snake_block_y[0]>=apple_y and  snake_block_y[0]<apple_y+block_pixel_size:
            return True 
    return False 

# function to increase the snake length 
def increment_snake_length(): 
    # access the gloabl snake features 
    global snake_length 
    global snake_block_x 
    global snake_block_y 
    global SNAKE_SPEED 
    
    # increase the snake length by One 
    snake_length +=1 
    
    # add a new random data in the snake x and y coordinates list 
    snake_block_x.append(-1) 
    snake_block_y.append(-1) 
    
    # increase the snake speed 
    if SNAKE_SPEED > 0.2:
        if snake_length %2 ==0:
            SNAKE_SPEED-=0.1

# function for snake collision
def snake_collision():
    ''' if the snake head collide with the
        snake body
        close the game
    '''
    for i in range(1,snake_length):
        if snake_block_x[0]>=snake_block_x[i] and snake_block_x[0]< snake_block_x[i]+20:
            if snake_block_y[0]>=snake_block_y[i] and snake_block_y[0]<snake_block_y[i]+20:
                game_over()

# function for the snake-wall collision
def wall_collision():
    # if snake head hit left wall or right wall
    if snake_block_x[0]<0 or snake_block_x[0] >=screen_size[0]:
        game_over()
    # if the snake head hits the up or down wall
    if snake_block_y[0]<0 or snake_block_y[0] >=screen_size[1]:
        game_over()

def run():
    # access the global apple x and y coordinates

    global apple_x
    global apple_y

    #run the game until the user click on the close button
    game_running = True
    while game_running:
        # loop through all the game events
        for event in pygame.event.get():
            # check if the user click the close button
            if event.type == pygame.QUIT:
                game_running=False

            # snake control system


            if event.type == pygame.KEYDOWN:
                # if player press escape the game ends
                if event.key == pygame.K_ESCAPE:
                    game_over()

                # to move left
                if event.key == pygame.K_LEFT:
                    if direction in ["up", "down"]:
                        move_left()

                # to move right
                if event.key == pygame.K_RIGHT:
                    if direction in ["up", "down"]:
                        move_right()

                # to move up
                if event.key == pygame.K_UP:
                    if direction in ["left", "right"]:
                        move_up()

                # to move down
                if event.key == pygame.K_DOWN:
                    if direction in ["left", "right"]:
                        move_down()
        snake_walk()
        draw_apple()
        # check if the snake eat the apple
        if (did_snake_eat_apple()):
            # set new coordinates for the apple
            apple_x = random.randint(1, 29)*block_pixel_size
            apple_y = random.randint(1, 29)*block_pixel_size
            # draw a new apple
            draw_apple()

            # increasee the snake length
            increment_snake_length()

        # check if the snake head collide with its body
        snake_collision()
        # check if the snake collide with the walls
        wall_collision()
        # update the palyer score
        player_score()

        # update the screen
        pygame.display.update()

        # the speed of the snake movement
        time.sleep(SNAKE_SPEED)

if __name__ =="__main__":
    #Initialize Pygame
    pygame.init()

    # width and height of the screen window (w:h)(900:700)
    screen_size = width, height = 900, 700

    screen = pygame.display.set_mode(screen_size)

    # title and icon of the screen window
    pygame.display.set_caption("Snake Game")
    icon = pygame.image.load("resources/game_icon.png")
    pygame.display.set_icon(icon)


    #set the color of the screen window (R,G,B)
    BACKGROUND_COLOR = (0,0,0)   #black
    screen.fill(BACKGROUND_COLOR)

    # load the snake block on the screen
    snake_block = pygame.image.load("resources/snake_block.png").convert()
    # convert the snake block to 20X20 pixels
    block_pixel_size = 20
    snake_block = pygame.transform.scale(snake_block, (block_pixel_size, block_pixel_size))

    #load apple on the screen window
    apple =  pygame.image.load("resources/apple.png").convert()
    # set the apple size to 20X20 pixels (similar to the snake block size)
    apple=pygame.transform.scale(apple, (block_pixel_size, block_pixel_size))

    # initial coordinate of the apple block
    apple_x = 200
    apple_y = 100

    # initial snake head coordinate
    snake_block_x=[100,]
    snake_block_y=[100,]

    # initial length of the snake
    snake_length =1

    # intial direction of the snake movement
    direction ="right"

    # initial snake speed
    SNAKE_SPEED = 1

    # place snake block on the window screen
    screen.blit(snake_block, (snake_block_x[0], snake_block_y[0]))
    # place apple on the window
    screen.blit(apple, (apple_x, apple_y))

    # update the window screen to render the snake block
    pygame.display.update()

    # call the run function
    run()

Output

Wrapping Up!

In this Python tutorial, we learned what is Pygame and how can we use it to build simple games in Python. If you are into Game Development then Python is not an ideal language, although we can run the game build on Pygame and Python on Android devices using pgs4a, it is not suggested to use Python for complex game development. It’s fun to know how to use Libraries like Pygame and TKinter to implement graphics and game logic using Python.

You can also convert the above snake game main.py file into a window executable exe file. To know how to convert a Python program to executable windows exe file click here.

Leave a Reply

Your email address will not be published. Required fields are marked *