Pygame: Animate a sprite

Dive into Pygame



We can use pygame to make games, but games can be used not only for entertaining. In fact we could use this module for educational purporses. Anyway, we will explore the basic stuff. As usual, we will dive into the practical examples for useful stuff, instead of long introductions. So, not to deny what I just wrote, let’s dive into pygame right now.

Did you import pygame?

First thing to do is import pygame. At the moment we’ve arrived at 1.9.4, release on july 2018.

"""Dive into pygame.

A close look at programming trough games
"""

import pygame as pg

Pygame zero anyone?

If you installed pgzero (go to see the article about this modules), you have pygame module yet, because pygame zero is based on pygame as the name suggests obviously.

The animation and the welcome text of pygame 1.9.4
The animation and the welcome text of pygame 1.9.4

The basic window

We need some code to see a simple window:

  • we define width and height of the screen
  • use the set_mode method to give the measures of the screen
  • we create an infinite loop to get the user imput
  • if we close the window then we quit pygame
import pygame as pg

W = 800
H = 600

display = pg.display.set_mode((W, H))
pg.display.set_caption("Mario Game")


clock = pg.time.Clock()

crashed = False

# infinite loop, until you close the window
while not crashed:
        # pc waits for an event (user input)
	for event in pg.event.get():
                # the only event is the quit of the window
		if event.type == pg.QUIT:
                        # if you press the quit button the loop ends and pygame quits
			crashed = True
        # if you do not do anything the window continues to update
	pg.display.update()
        # this is the framerate
	clock.tick(60)

pg.quit()

 

Load an image

img = pygame.image.load("mario_1.png")

Load all the image in a folder starting with “mario”

import glob

mario = glob.glob("mario*.png")

# mario is a list with all the name of the files
# that starts with mario, followed by anithing
# (i.e. numerbers 1, 2, 3...) and ending with .png

mario_img = []
for png in mario:
    mario_img.append(pg.image.load(png))

Display the image on the screen

After we charged all the images in the folder in mario_image list of images we display the second one (mario_img[1]*).

* the lists starts from 0, so 1 is the second item.

 

import pygame as pg
import glob

pg.init()
w = 800
h = 600
screen = pg.display.set_mode((w, h))
clock = pg.time.Clock()
mario = glob.glob("*.png")

# mario is a list with all the name of the files
# that starts with mario, followed by anithing
# (i.e. numerbers 1, 2, 3...) and ending with .png

mario_img = []
for png in mario:
    mario_img.append(pg.image.load(png))

end = False

while not end:
    screen.fill((0, 0, 0))
    screen.blit(mario_img[1], (10, 10))

    for event in pg.event.get():
        if event.type == pg.QUIT:
            end = True
    clock.tick(60)
    pg.display.update()
pg.quit()

 

 

Move left and right

In this code we take all the pictures in a folder (png) and we use the to animate the chacter walking.



"""Install pygame with pgzero.

pip install pgzero
"""


import pygame
import sys
import glob

pygame.init()

h = 400
w = 800

screen = pygame.display.set_mode((w, h))
clock = pygame.time.Clock()


class Player:
    def __init__(self):
        self.x = 200
        self.y = 300
        self.speed_init = 10
        self.speed = self.speed_init
        self.ani = glob.glob("*.png")
        self.ani.sort()
        self.ani_pos = 0
        self.ani_max = len(self.ani) - 1
        self.img = pygame.image.load(self.ani[0])
        self.update(0)

    def update(self, pos):
        if pos != 0:
            self.speed -= 1
            self.x += pos
            if self.speed == 0:
                self.img = pygame.image.load(self.ani[self.ani_pos])
                self.speed = self.speed_init
                if self.ani_pos == self.ani_max:
                    self.ani_pos = 0
                else:
                    self.ani_pos += 1
        screen.blit(self.img, (self.x, self.y))


player = Player()
pos = 0

crashed = False
right = 0
while not crashed:
    screen.fill((0, 0, 0))
    clock.tick(60)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            crashed = True
        elif right == 1:
            pos +=1
            if pos == len(player.ani):
                right = 0
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
            pos += 1
            #right = 1
        elif event.type == pygame.KEYUP and event.key == pygame.K_LEFT:
            pos = 0
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT:
            pos = -1
        elif event.type == pygame.KEYUP and event.key == pygame.K_RIGHT:
            pos = 0

    player.update(pos)
    pygame.display.update()

pygame.quit()

Moving costantly

Now the character moves continuosly left and right

"""Install pygame with pgzero.

pip install pgzero
"""


import pygame
import sys
import glob

pygame.init()

h = 400
w = 800

screen = pygame.display.set_mode((w, h))
clock = pygame.time.Clock()


class Player:
    def __init__(self):
        self.x = 200
        self.y = 300
        self.speed_init = 10
        self.speed = self.speed_init
        self.ani = glob.glob("*.png")
        self.ani.sort()
        self.ani_pos = 0
        self.ani_max = len(self.ani) - 1
        self.img = pygame.image.load(self.ani[0])
        self.update(0)

    def update(self, pos):
        if pos != 0:
            self.speed -= 1
            self.x += pos
            if self.speed == 0:
                self.img = pygame.image.load(self.ani[self.ani_pos])
                self.speed = self.speed_init
                if self.ani_pos == self.ani_max:
                    self.ani_pos = 0
                else:
                    self.ani_pos += 1
        screen.blit(self.img, (self.x, self.y))


player = Player()
pos = 0

crashed = False

while not crashed:
    screen.fill((0, 0, 0))
    clock.tick(60)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            crashed = True
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
            pos += 1
        # when KEYUP the alien continues to move forward
        elif event.type == pygame.KEYUP and event.key == pygame.K_RIGHT:
            if pos < len(player.ani):
                pos += 1
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT:
            pos = -1
        # when KEYUP the alien continues to move backward
        elif event.type == pygame.KEYUP and event.key == pygame.K_LEFT:
            if pos > 0:
                pos -= 1

    player.update(pos)
    pygame.display.update()

pygame.quit()

Moving at different speed

This time we made the alien move incrementally when pressing the left and right arrow.

"""Install pygame with pgzero.

pip install pgzero
"""


import pygame
import glob

pygame.init()

h = 400
w = 800

screen = pygame.display.set_mode((w, h))
clock = pygame.time.Clock()
pygame.font.init()  # you have to call this at the start,
# if you want to use this module.
myfont = pygame.font.SysFont('Comic Sans MS', 30)
textsurface = myfont.render('Right or left arrow to move', False, (255, 255, 255))
velocity = myfont.render('...', False, (255, 255, 255))


class Player:
    def __init__(self):
        self.x = 300
        self.y = 200
        self.speed_init = 10
        self.speed = self.speed_init
        self.ani = glob.glob("*.png")
        self.ani.sort()
        self.ani_pos = 1
        self.ani_max = len(self.ani) - 1
        self.img = pygame.image.load(self.ani[0])
        self.update(0)

    def update(self, pos):
        if pos != 0:
            self.speed -= 2
            self.x += pos
            if self.speed == 0:
                self.img = pygame.image.load(self.ani[self.ani_pos])
                self.speed = self.speed_init
                if self.ani_pos == self.ani_max:
                    self.ani_pos = 0
                else:
                    self.ani_pos += 1
        elif self.speed == 0:
            self.img = pygame.image.load(self.ani[self.ani_pos])
            self.speed = self.speed_init
            if self.ani_pos == self.ani_max:
                self.ani_pos = 0
            else:
                self.ani_pos += 1
        screen.blit(self.img, (self.x, self.y))


player = Player()
pos = 0

crashed = False

while not crashed:
    screen.fill((0, 0, 0))
    clock.tick(60)
    screen.blit(textsurface, (0, 0))
    screen.blit(velocity, (0, 40))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            crashed = True

        # MOVE RIGHT
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
            if pos < 5:  # max velocity = 5
                pos += 1  # pos -= 1 # velocità incrementale
                velocity = myfont.render('Velocità ' + str(pos), False, (255, 255, 255))
            """
        elif event.type == pygame.KEYUP and event.key == pygame.K_RIGHT:
            if pos < len(player.ani):
                pos = +1
"""
        # MOVE LEFT
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT:
            if pos > -5:  # max velocity -5
                pos -= 1  # pos -= 1 # velocità incrementale
                velocity = myfont.render('Velocità ' + str(pos), False, (255, 255, 255))
        """
        elif event.type == pygame.KEYUP and event.key == pygame.K_LEFT:
            if pos > 0:
                pos = -1
"""
    player.update(pos)
    pygame.display.update()

pygame.quit()

How to add the text in pygame

Let’s take a look at how to put text on the screen.

Making the sprite reappear from the opposite site

Adding this code we will make the sprite reappear from the opposite border when he moves too much on the left or on the right.

The code added in the method update of the class player is this

        if self.x > 800:
            self.x = -60
        elif self.x < -80:
            self.x = 790

The whole code now is the following:

"""Install pygame with pgzero.

pip install pgzero
"""


import pygame
import glob

pygame.init()

h = 400
w = 800

screen = pygame.display.set_mode((w, h))
clock = pygame.time.Clock()

# =========== FONT ==============
pygame.font.init()
myfont = pygame.font.SysFont('Comic Sans MS', 30)
textsurface = myfont.render('Right or left arrow to move', False, (255, 255, 255))
velocity = myfont.render('...', False, (0, 255, 0))


class Player:
    def __init__(self):
        self.x = 300
        self.y = 300
        self.speed_init = 10
        self.speed = self.speed_init
        self.ani = glob.glob("*.png")
        self.ani.sort()
        self.ani_pos = 1
        self.ani_max = len(self.ani) - 1
        self.img = pygame.image.load(self.ani[0])
        self.update(0)

    def update(self, pos):
        if pos != 0:
            self.speed -= 2
            self.x += pos
            if self.speed == 0:
                self.img = pygame.image.load(self.ani[self.ani_pos])
                self.speed = self.speed_init
                if self.ani_pos == self.ani_max:
                    self.ani_pos = 0
                else:
                    self.ani_pos += 1
        elif self.speed == 0:
            self.img = pygame.image.load(self.ani[self.ani_pos])
            self.speed = self.speed_init
            if self.ani_pos == self.ani_max:
                self.ani_pos = 0
            else:
                self.ani_pos += 1
        if self.x > 800:
            self.x = -60
        elif self.x < -80:
            self.x = 790
        screen.blit(self.img, (self.x, self.y))


player = Player()
pos = 0

crashed = False

while not crashed:
    screen.fill((0, 0, 0))
    clock.tick(60)
    screen.blit(textsurface, (0, 0))
    screen.blit(velocity, (0, 40))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            crashed = True
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
            if pos < 5:  # max velocity = 5
                pos += 1  # pos -= 1 # velocità incrementale
                velocity = myfont.render('Speed ' + str(pos), False, (0, 255, 0))
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT:
            if pos > -5:  # max velocity -5
                pos -= 1  # pos -= 1 # velocità incrementale
                velocity = myfont.render('Speed ' + str(pos), False, (0, 255, 0))
    player.update(pos)
    pygame.display.update()

pygame.quit()

Some improvements to the animation and a soundtrack

In this code I used more frames (you can put how many frames you want in the folder and the code will add them, they have to start with p1_walk and then have a progressive number). The track also can be random from all the wav files that are there in the music directory. I used the pgzrun module to load the music (to install pgzrun write pip install pgzero).

"""Install pygame with pgzero.

pip install pgzero
"""

import pygame
import glob
from pgzrun import *
from random import choice


pygame.init()
h = 400
w = 800

screen = pygame.display.set_mode((w, h))
clock = pygame.time.Clock()

# =========== FONT ==============
pygame.font.init()
myfont = pygame.font.SysFont('Comic Sans MS', 30)
textsurface = myfont.render('Right or left arrow to move', False, (255, 255, 255))
velocity = myfont.render('...', False, (0, 255, 0))

music.play(choice(glob.glob("music/*.wav"))[6:])


class Player:
    def __init__(self):
        self.x = 300
        self.y = 300
        self.speed_init = 10
        self.speed = self.speed_init
        # list of images for the animation
        self.ani = glob.glob("p1_walk*.png")
        self.ani.sort()
        self.ani_pos = 1
        self.ani_max = len(self.ani) - 1
        self.img = pygame.image.load(self.ani[0])
        self.update(0)

    def update(self, pos):
        if pos != 0:
            self.speed -= 2
            self.x += pos
            if self.speed == 0:
                self.img = pygame.image.load(self.ani[self.ani_pos])
                self.speed = self.speed_init
                if self.ani_pos == self.ani_max:
                    self.ani_pos = 0
                else:
                    self.ani_pos += 1
        elif self.speed == 0:
            self.img = pygame.image.load(self.ani[self.ani_pos])
            self.speed = self.speed_init
            if self.ani_pos == self.ani_max:
                self.ani_pos = 0
            else:
                self.ani_pos += 1
        if self.x > 800:
            self.x = -60
        elif self.x < -80:
            self.x = 790
        if pos == 0:
            self.img = pygame.image.load(self.ani[0])
        screen.blit(self.img, (self.x, self.y))


player = Player()
pos, crashed = 0, False


def speed_render(pos):
    global velocity
    velocity = myfont.render('Speed ' + str(pos), False, (0, 255, 0))


while not crashed:
    screen.fill((0, 0, 0))
    clock.tick(60)
    screen.blit(textsurface, (0, 0))
    screen.blit(velocity, (0, 40))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            crashed = True

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                if pos < 5:  # max velocity = 5
                    pos += 1  # pos -= 1 # velocità incrementale
                    speed_render(pos)
            elif event.key == pygame.K_LEFT:
                if pos > -5:  # max velocity -5
                    pos -= 1  # pos -= 1 # velocità incrementale
                    speed_render(pos)

    player.update(pos)
    pygame.display.update()

pygame.quit()

This video shows the animation… it is smoother in the reality, if you see that it is not so smooth it is for the frame rate of the video.

Convert and preload

To convert the images in a faster type of image we used “convert()” method to the loaded images.

We also created a list of loaded image objects, so that the code is shorter and clearer and I hope it’s execution is faster too.

self.ani_obj = []
        # create a list of object converted, not to load everytime the images
        for img in self.ani:
            self.ani_obj.append(pygame.image.load(img).convert())
        self.img = self.ani_obj[0]

Now the whole code looks like this:

"""Install pygame with pgzero.

pip install pgzero
"""

import pygame
import glob
from pgzrun import *
from random import choice


pygame.init()
h = 400
w = 800

screen = pygame.display.set_mode((w, h))
clock = pygame.time.Clock()

# =========== FONT ==============
pygame.font.init()
myfont = pygame.font.SysFont('Comic Sans MS', 30)
textsurface = myfont.render('Right or left arrow to move', False, (255, 255, 255))
velocity = myfont.render('...', False, (0, 255, 0))

music.play(choice(glob.glob("music/*.wav"))[6:])


class Player:
    def __init__(self):
        self.x = 300
        self.y = 300
        self.speed_init = 10
        self.speed = self.speed_init
        # list of images for the animation
        self.ani = glob.glob("p1_walk*.png")
        self.ani.sort()
        self.ani_pos = 1
        self.ani_max = len(self.ani) - 1
        self.ani_obj = []
        # create a list of object converted, not to load everytime the images
        for img in self.ani:
            self.ani_obj.append(pygame.image.load(img).convert())
        self.img = self.ani_obj[0]
        self.update(0)

    def update(self, pos):
        if pos != 0:
            self.speed -= 2
            self.x += pos
            if self.speed == 0:
                self.img = self.ani_obj[self.ani_pos]
                self.speed = self.speed_init
                if self.ani_pos == self.ani_max:
                    self.ani_pos = 0
                else:
                    self.ani_pos += 1
        elif self.speed == 0:
            self.img = self.ani_obj[self.ani_pos]
            self.speed = self.speed_init
            if self.ani_pos == self.ani_max:
                self.ani_pos = 0
            else:
                self.ani_pos += 1
        if self.x > 800:
            self.x = -60
        elif self.x < -80:
            self.x = 790
        if pos == 0:
            self.img = self.ani_obj[0]
        screen.blit(self.img, (self.x, self.y))


player = Player()
pos, crashed = 0, False


def speed_render(pos):
    global velocity
    velocity = myfont.render('Speed ' + str(pos), False, (0, 255, 0))


while not crashed:
    screen.fill((0, 0, 0))
    clock.tick(60)
    screen.blit(textsurface, (0, 0))
    screen.blit(velocity, (0, 40))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            crashed = True

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                if pos < 5:  # max velocity = 5
                    pos += 1  # pos -= 1 # velocità incrementale
                    speed_render(pos)
            elif event.key == pygame.K_LEFT:
                if pos > -5:  # max velocity -5
                    pos -= 1  # pos -= 1 # velocità incrementale
                    speed_render(pos)

    player.update(pos)
    pygame.display.update()

pygame.quit()

Pygame's Platform Game

Other Pygame's posts

Published by pythonprogramming

Started with basic on the spectrum, loved javascript in the 90ies and python in the 2000, now I am back with python, still making some javascript stuff when needed.