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 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()