Scrolling Background & Character Movement

Subscribe to Tech With Tim!

In this tutorial series we will be working to create a side scrolling game with pygame. This game will involve objects randomly appearing on the screen that our character must avoid by jumping our sliding.

Game Assets

We are going to be using a series of different images to animate our game. Please download all of the game assets from one of the below links.

GitHub: Click Here

Download Zip: Download Now

Note: The folder named "images" must be in the same directory as your python file.

Starter File

To save us a bit of time I have created a starter file for us to work off of. This file simply contains the code to load all of the images and animate the character. If you would like a detailed explanation of the starter file please watch the video.

import pygame
from pygame.locals import *
import os
import sys
import math

pygame.init()

W, H = 800, 447
win = pygame.display.set_mode((W,H))
pygame.display.set_caption('Side Scroller')

bg = pygame.image.load(os.path.join('images','bg.png')).convert()
bgX = 0
bgX2 = bg.get_width()

clock = pygame.time.Clock()

class player(object):
    run = [pygame.image.load(os.path.join('images', str(x) + '.png')) for x in range(8,16)]
    jump = [pygame.image.load(os.path.join('images', str(x) + '.png')) for x in range(1,8)]
    slide = [pygame.image.load(os.path.join('images', 'S1.png')),pygame.image.load(os.path.join('images', 'S2.png')),pygame.image.load(os.path.join('images', 'S2.png')),pygame.image.load(os.path.join('images', 'S2.png')), pygame.image.load(os.path.join('images', 'S2.png')),pygame.image.load(os.path.join('images', 'S2.png')), pygame.image.load(os.path.join('images', 'S2.png')), pygame.image.load(os.path.join('images', 'S2.png')), pygame.image.load(os.path.join('images', 'S3.png')), pygame.image.load(os.path.join('images', 'S4.png')), pygame.image.load(os.path.join('images', 'S5.png'))]
    jumpList = [1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4]
    def __init__(self, x, y, width, height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.jumping = False
        self.sliding = False
        self.slideCount = 0
        self.jumpCount = 0
        self.runCount = 0
        self.slideUp = False

    def draw(self, win):
        if self.jumping:
            self.y -= self.jumpList[self.jumpCount] * 1.2
            win.blit(self.jump[self.jumpCount//18], (self.x,self.y))
            self.jumpCount += 1
            if self.jumpCount > 108:
                self.jumpCount = 0
                self.jumping = False
                self.runCount = 0
        elif self.sliding or self.slideUp:
            if self.slideCount < 20:
                self.y += 1
            elif self.slideCount == 80:
                self.y -= 19
                self.sliding = False
                self.slideUp = True
            if self.slideCount >= 110:
                self.slideCount = 0
                self.slideUp = False
                self.runCount = 0
            win.blit(self.slide[self.slideCount//10], (self.x,self.y))
            self.slideCount += 1
            
        else:
            if self.runCount > 42:
                self.runCount = 0
            win.blit(self.run[self.runCount//6], (self.x,self.y))
            self.runCount += 1



Game Loop

In every game there is something called a main loop or a game loop. This loop runs until the game is exited and is what is responsible for checking for events from the user. In our program we will use a while loop to represent the game loop.

run = True
while run:
    for event in pygame.event.get():  # Loop through a list of events
        if event.type == pygame.QUIT:  # See if the user clicks the red x 
            run = False    # End the loop
            pygame.quit()  # Quit the game
            quit()

Scrolling Background

To make it appear as our character is moving forward we will actually just scroll the background backwards. We will use two background images and move them to the left each frame. Once one of the background images reaches the end of the screen we will reset its position back to the right. This will allow us to continually scroll the background.

run = True
speed = 30  # NEW

while run:
    clock.tick(speed)  # NEW
    bgX -= 1.4  # Move both background images back
    bgX2 -= 1.4

    if bgX < bg.get_width() * -1:  # If our bg is at the -width then reset its position
        bgX = bg.get_width()
    
    if bgX2 < bg.get_width() * -1:
        bgX2 = bg.get_width()

    for event in pygame.event.get():  
        if event.type == pygame.QUIT: 
            run = False    
            pygame.quit() 
            quit()

Now that we are moving our background we need to draw it to the screen. To do this we will setup a function and call it from our game loop. This function will be called redrawWindow() and will be responsible for drawing all of our objects to the screen.

def redrawWindow():
    win.blit(bg, (bgX, 0))  # draws our first bg image
    win.blit(bg, (bgX2, 0))  # draws the seconf bg image
    pygame.display.update()  # updates the screen

# Call this from the game loop!

Changing the Background Speed

Now that we have our background moving we want to increase its speed at a regular interval. To do this we will use a timer event. Every time the timer goes off we will increase the speed and reset the timer.

pygame.time.set_timer(USEREVENT+1, 500) # Sets the timer for 0.5 seconds
# This should go above the game loop

To check if this timer is triggered we will add some code the game loop.

while run:
    redrawWindow() 
    bgX -= 1.4  
    bgX2 -= 1.4

    if bgX < bg.get_width() * -1:  
        bgX = bg.get_width()
    
    if bgX2 < bg.get_width() * -1:
        bgX2 = bg.get_width()

    for event in pygame.event.get():  
        if event.type == pygame.QUIT: 
            run = False    
            pygame.quit() 
            quit()
    
        if event.type == USEREVENT+1: # Checks if timer goes off
            speed += 1 # Increases speed

    clock.tick(speed) 

Now our background increases in speed!


Drawing our Character

Since we already have our player class setup this is not very difficult. We just need to create an instance of our player and then call its draw method from the redrawWindow function.

runner = player(200, 313, 64, 64)
# This should go above our game loop

Now we need to draw this player.

def redrawWindow():
    win.blit(bg, (bgX, 0))  
    win.blit(bg, (bgX2, 0))
    runner.draw(win) # NEW
    pygame.display.update() 

Moving Our Character

To move our character we need to check for some specific key press events. We will do this inside the game loop.

# Should go inside the game loop
keys = pygame.key.get_pressed()

if keys[pygame.K_SPACE] or keys[pygame.K_UP]: # If user hits space or up arrow key
    if not(runner.jumping):  # If we are not already jumping
        runner.jumping = True

if keys[pygame.K_DOWN]:  # If user hits down arrow key
    if not(runner.sliding):  # If we are not already sliding
        runner.sliding = True

# Because we have a starter file this is all we have to do to move our character. 
# The physics and math behind the movement has been coded for you.

And now our character can slide and jump!