pygame
& SDP¶Prof. Andrea Gallegati
pygame
¶How to use pygame
to make games with graphics, animations, user input, sound.
This class will make use of some terms and concepts that have not been yet defined/introduced.
Take them as abstract entities, as long as they wont't be covered in details!
The following slides are taken from an amazing book! https://nostarch.com/inventwithpython
pygame
¶you can use any packet manager, e.g.
>>> pip install pygame
and check it, entering in the interactive shell:
>>> import pygame
... if no ImportError
appears, it installed correctly.
pygame
I/O¶pygame
module does not work very well in interactive mode.
Use an Integrated Development Environment (aka IDE), such as Visual Studio Code or its Dockerized version.
pygame
programs don’t use the print()
or input()
functions, instead
import pygame
¶First, import the pygame
module to call its functions along with several other modules
import pygame, sys
from pygame.locals import *
as comma separated modules within the same line.
The pygame.locals
module import brings many constant variables to use with pygame, such as
QUIT
, which helps you quit the programK_ESCAPE
, which represents the ESC
key.the following syntax
from <a_module_name> import *
rather than
import <a_module_name>
let use <a_module_name>
without typing the name in front of its methods, constants, or anything else from within it.
Sometimes, better to use the full function name to know which module the function is in, e.g. sys.exit()
pygame
¶Always call pygame.init()
pygame
function.pygame.init()
... and it is ready to use.
Don’t need to know what init() does; just remember to call it before using any other pygame functions.
To prepare a graphical user interface (aka GUI) window calls the set_mode()
method in the pygame.display
module.
windowSize = (500, 400)
windowSurface = pygame.display.set_mode(windowSize)
(The display module is a module inside the pygame module. Even the pygame module has its own modules!)
This helps set up a window for pygame to run in. This window uses a coordinate system, that is organized in pixels.
A pixel is the tiniest dot on your computer screen and it can light up in any color.
All the pixels on your screen work together to display the pictures you see. We’ll create a window 500 pixels wide and 400 pixels tall by using a tuple.
There are several parameters to pass to the pygame.display.set_mode()
method: the mandatory one is a tuple of two integers for the width and height of the window, in pixels.
Then we can set a title, with this function that change the name on the window:
The set_caption()
method just sets the window’s caption, that is in the top left of the window.
pygame.display.set_caption('Hello world!')
The set_mode()
function in the pygame.display
module returns a pygame.Surface
object (we’ll call Surface objects for short).
The Surface object represents the window.
For now, an object is just another name for a value of a data type that has methods, for example: strings
have data (the string itself) and methods (e.g.lower()
and split()
). Variables store references to objects just as they store references for other types.
Inside the pygame
module there are, for example, the font
and surface
modules.
Inside those modules are the Font
and Surface
data types.
This is a good example of why naming conventions can turn usefull sharing code with others.
There are three primary colors of light for pixels: red, green, and blue. By combining different amounts of these three colors you can form any other color.
In pygame, colors are represented by tuples of three integers (aka RGB color codes).
Not to rewrite a tuple every time we need it, let's make constants to hold that values (colors).
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
You can mix the amount of red, green, and blue to get any shade of any color!
We want to fill the entire surface stored in windowSurface
with the color black, as we did within our snake game.
The fill()
function will completely cover the surface with the color you pass as the parameter.
windowSurface.fill(BLACK)
Note, the window won’t change when you call the fill()
method or any other drawing function: these just change the Surface object.
pygame.display.update()
function will render it to the screen.
Modifying the Surface object in the computer’s memory is much faster than modifying the image on the screen.
Better (more efficient) to render it once all the other methods were called on the Surface object.
pygame
's drawing functions¶Similarly to the turtle
module, even pygame
has its own functions to draw shapes and lines.
Here, each shape has its own function, and you can combine these shapes into different pictures for your graphical game.
Draws any polygon shape (a multisided shape with straight lines sides). To draw circles and ellipses, for example, there are other functions.
pygame.draw.polygon(windowSurface, GREEN,
((146, 0), (291, 106),
(236, 277), (56, 277), (0, 106)))
Draws couple lines from one point on the screen to another point.
pygame.draw.line(windowSurface, BLUE,
(60, 60), (120, 60), 4)
pygame.draw.line(windowSurface, BLUE,
(120, 60), (60, 120))
Draws a circle.
pygame.draw.circle(windowSurface, BLUE,
(300, 50), 20, 0)
0
the circle is filled in).Draws an ellipse, which is like a squished circle. Thus, the two functions are similar.
pygame.draw.ellipse(windowSurface, RED,
(300, 250, 40, 80), 1)
Rect
object containing the ellipse.0
the ellipse is filled in).Draws a rectangle.
pygame.draw.rect(windowSurface, RED,
(textRect.left - 20, textRect.top - 20,
textRect.width + 40, textRect.height + 40))
Instead of that tuple, a `Rect` object can be passed as well.
The pygame.Rect
data type (Rect for short) represents rectangular areas of a certain size and location, to put text in, for example.
This is what we use to set the location of objects on a window.
basicFont = pygame.font.SysFont(None, 48)
text = basicFont.render('Hello world!', True,
WHITE, BLUE)
textRect = text.get_rect()
... once created a Font
object, a Rect
object is already made for it: just retrieve it using the get_rect()
method on text
and then assign it.
To create (from scratch) a new Rect object:
pygame.Rect(left, top, width, height)
Notice that it has the same name as the data type. These are called constructor functions (create objects or values of their data type).
The parameters are the coordinates of the top-left corner, followed by the width and height, all in pixels.
Just as methods are functions that are associated with an object, attributes are variables that are associated with an object.
The Rect
data type has many attributes that describe the rectangle they represent.
To set its location on the window, we could assign its center coordinates, i.e. centerx
and centery
, the attributes that store these coordinates.
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery
To put textRect
in the center of the window, just get the windowSurface
Rect object's centerx
and centery
attributes, and then assign those to the textRect
's ones!
There are many other Rect
attributes that we can use.
A great thing about Rect
objects is that if you modify any of these attributes, all the other attributes will automatically modify themselves, accordingly.
blit()
method for Surface Objects¶it draws the contents of one Surface object onto another Surface object.
Text objects created by the render()
method exist on their own Surface object.
Pygame drawing methods can all specify the Surface object to draw on, but text
was stored into a variable rather than drawn onto windowSurface
.
To draw text on the Surface we want it to appear on, let's use the blit()
method:
windowSurface.blit(text, textRect)
The second parameter to blit()
specifies where on windowSurface
the text
surface should be drawn.
The Rect object we get from calling text.get_rect()
is then passed for this parameter.
In pygame nothing is actually drawn to the screen until the update()
function is called, to display our updated Surface object:
pygame.display.update()
To save memory, don’t update after every single drawing function; instead, update the screen just once, after all the drawing functions have been called.
Usually, pygame programs are constantly running through a game loop, executing every line of code in a loop, about 100 times a second.
The game loop constantly checks for new events, updates the state of the window, and draws the window on the screen.
Events are generated by pygame whenever the user presses a key, clicks or moves the mouse, or performs some other action recognized by the program that should make something happen in the game.
An Event is an object of the pygame.event.Event
data type.
The condition for the while statement is set to True
so that it loops forever.
while True:
The only time the loop will exit is if an event causes the program to terminate.
The function pygame.event.get()
checks for any new pygame.event.Event
object (called Event object for short), generated since the last call to pygame.event.get()
.
These are returned as a list of Event objects, which the program will then parse to perform some action in response to the event itself.
All Event objects have an attribute called type
. Right now, we only need to use the QUIT
event type, which tells us when the user quits the program.
The for loop is to iterate over each Event object in the list returned by pygame.event.get()
.
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
If the type attribute is the constant variable QUIT
, defined within the pygame.locals
module imported at the beginning, the program is then told what to do (e.g. when the user closes the program’s window).
The pygame.quit()
function is sort of the opposite of init()
. You need to call it before exiting your program.
If you forget, you may cause IDLE to hang after your program has ended.
Here we have three boxes moving around and bouncing off the walls of a window. First, let's consider how we want the boxes to move:
Each box will move in one of four diagonal directions. When a box hits the side of the window, it should bounce off and move in a new diagonal direction.
The new direction that a box moves after it bounces depends on two things:
There are eight possible ways a box can bounce: two different ways for each of the four walls.
We can use a Rect
object to represent the position and size of the box, a tuple of three integers (RGB) to represent the color of the box, and an integer to represent which of the four diagonal directions the box is currently moving in.
The game loop will adjust the position of the box and draw all the boxes on the screen at each iteration.
As the program execution iterates over the loop, the boxes will gradually move across the screen so that it looks like they’re smoothly moving and bouncing around.
import pygame, sys, time
from pygame.locals import *
pygame.init()
WWIDTH = 400 ; WHEIGHT = 400
windowSurface = pygame.display.set_mode((WWIDTH,
WHEIGHT))
pygame.display.set_caption('Animation')
In this program, the window’s size is used for more than just the set_mode()
call.
If you ever want to change this size, just change these constant variables.
We’ll use constant variables for the four directions the boxes can move in:
DOWNLEFT = 'downleft'
DOWNRIGHT = 'downright'
UPLEFT = 'upleft'
UPRIGHT = 'upright'
We also create a constant variable to determine how fast the boxes should move:
MOVESPEED = 4
... tells the program how many pixels each box should move, on each iteration, through the game loop.
Constant variables are used for readability.
Next we’ll define the boxes.
To keep things simple, a dictionary as a data structure is used to represent each moving box.
The dictionary will have the keys:
rect
with a Rect object for a valuecolor
with a tuple of three integers for a valuedir
with one of the direction constant variables for a value.The animation code can be used to animate as many boxes as defined within the data structure.
The variable b1
stores one of these box data structures:
b1 = {'rect':pygame.Rect(300, 80, 50, 100),
'color':RED, 'dir':UPRIGHT}
This box’s top-left corner is located at (300, 80)
. It has a width of 50
pixels and a height of 100
pixels.
Its color is RED
, and its initial direction is UPRIGHT
.
b2 = {'rect':pygame.Rect(200, 200, 20, 20),
'color':GREEN, 'dir':UPLEFT}
b3 = {'rect':pygame.Rect(100, 150, 60, 60),
'color':BLUE, 'dir':DOWNLEFT}
boxes = [b1, b2, b3]
... two more similar data structures for the other boxes, with different sizes, positions, colors, directions and a list to collect them all!
If you needed to retrieve a box or value from the list, you could do so using indexes and keys.
Entering boxes[0]
would access the dictionary data structure in b1
.
Entering boxes[0]['color']
would access the color
key in b1
.
Now the game loop handles animating the moving boxes.
Animations work by drawing a series of pictures with slight differences that are shown one right after another.
Now that we know a little bit about how the game loop will work, let’s code it!
On each iteration through the game loop, the code redraws the entire window with new boxes, located a few pixels over, each time.
To make sure that windowSurface
is ready to be drawn on (not to end up with a trail of Rect
objects instead of a smooth animation) let's clear the window, every iteration of the game loop.
... fills the entire Surface
with WHITE
to erase anything else:
windowSurface.fill(WHITE)
Once windowSurface is filled, we can start drawing all of our Rect objects.
windowSurface.fill(WHITE)
To move each box, we need to iterate over the boxes list:
for b in boxes:
if b['dir'] == DOWNLEFT:
b['rect'].left -= MOVESPEED
b['rect'].top += MOVESPEED
if b['dir'] == DOWNRIGHT:
b['rect'].left += MOVESPEED
b['rect'].top += MOVESPEED
if b['dir'] == UPLEFT:
b['rect'].left -= MOVESPEED
b['rect'].top -= MOVESPEED
if b['dir'] == UPRIGHT:
b['rect'].left += MOVESPEED
b['rect'].top -= MOVESPEED
and change each box position depending on the direction it is moving.
The if statements are to figure out the box’s direction by checking the dir
key inside the box data structure.
New values for each box's attributes depends on the box’s direction.
The value of these attributes will increase or decrease by the amount of the integer stored in MOVESPEED.
Once the boxes have been moved, we need to check whether the box has gone past the edge of the window.
If it has, you want to bounce the box, i.e. set a new value for the box’s dir
key to move the box on the next iteration of the game loop.
In that case, the direction will be changed based on which direction the box was moving.
if b['rect'].top < 0:
# The box has moved past the top.
if b['dir'] == UPLEFT:
b['dir'] = DOWNLEFT
if b['dir'] == UPRIGHT:
b['dir'] = DOWNRIGHT
here we determine if the box has moved past the top edge of the window.
the code her below, instead, handle the situation in which the box has moved past the bottom edge of the window.
if b['rect'].bottom > WINDOWHEIGHT:
# The box has moved past the bottom.
if b['dir'] == DOWNLEFT:
b['dir'] = UPLEFT
if b['dir'] == DOWNRIGHT:
b['dir'] = UPRIGHT
Remember that the y-coordinates
start at 0
at the top of the window and increase to WINDOWHEIGHT
at the bottom.
This handles the behavior of the boxes when they bounce off the left side:
if b['rect'].left < 0:
# The box has moved past the left side.
if b['dir'] == DOWNLEFT:
b['dir'] = DOWNRIGHT
if b['dir'] == UPLEFT:
b['dir'] = UPRIGHT
This handles the behavior of the boxes when they bounce off the right side:
if b['rect'].right > WINDOWWIDTH:
# The box has moved past the right side.
if b['dir'] == DOWNRIGHT:
b['dir'] = DOWNLEFT
if b['dir'] == UPRIGHT:
b['dir'] = UPLEFT
Remember that the x-coordinates
start at 0
on the window’s left edge and increase to WINDOWWIDTH
on the right one.
Every time the boxes move, let's draw them in their new positions on windowSurface
by calling:
pygame.draw.rect(windowSurface, b['color'], b['rect'])
passing the windowSurface
, being the Surface object to draw the rectangle on, as well as the color and the Rect
object with the new position and size of the rectangle to draw.
After the for loop, each box in the boxes list will be drawn on the screen:
pygame.display.update()
time.sleep(0.02)
If the program ran at full speed, all the boxes would look like a blur. To make the program run slowly enough that we can see the boxes, let's add a time.sleep(0.02)
to pause the program for 20 milliseconds, between each movement of the boxes.
After that, the execution returns to the start of the game loop and begins the process all over again.
This way, the boxes are constantly moving a little, bouncing off the walls, and being drawn on the screen in their new positions.
Since we are putting on the table so many ingredients, let's start thinking about a recipe to develop our own videogame, using Python3
and pygame
module, more in particular.
Let's walk through the skeleton of the SDP for a videogame (snake
) we have, so far, already worked a lot on!
pygbag
module.The goal is to provide a **clear and comprehensive plan** for the development of the game, to help ensure that the project is completed on time and within budget.
Organize your work (divide it into atomic tasks) might help.
... Kanban boards (e.g. on GitHub) and Gantt charts help even more !
pygame
playground: Collision Detection¶Figuring out when two things on the screen have touched (aka collided with) each other, is really useful for games.
Collision detection will determine whether two rectangles are overlapping each other.
Moreover, pygame
programs can accept input from the player through the keyboard and the mouse. It’s a bit more complicated than calling the input() function.
This will make our games more exciting!
In the upcoming example a black box “eats” smaller green squares (food), randomly appearing on the screen, as it touches them. The player can:
arrow
keys to move the black boxESC
key to quit the programX
key to teleport the player to a random placepygame
¶After importing the usual modules (plus the random module):
import pygame, sys, random
from pygame.locals import *
... and after initializing pygame
, setting WINDOWHEIGHT
and WINDOWWIDTH
and assigning colors and direction constants, we will make use of a clock to pace the program:
mainClock = pygame.time.Clock()
As we did in the previous example, calling
time.sleep(0.02)
to slow down the program, will always pause for 0.02
seconds on all computers, but then the speed of all the rest of the program depends on how fast the computer is.
If we want this program to run at the same speed on any computer, we need a function that pauses longer on fast computers and shorter on slow computers.
A pygame.time.Clock
object can pause an appropriate amount of time on any computer, calling tick()
method inside the game loop
mainClock.tick(40)
This waits enough time so that it runs at about 40 iterations a second, no matter what the computer’s speed is. This ensures that the game never runs faster than you expect. This call should appear only once in the game loop.
Setup a few variables for the food squares that appear on the screen:
foodCounter = 0
NEWFOOD = 40
FOODSIZE = 20
... we will see how these are used later when we create the food.
Setup a pygame.Rect
object for the player’s location:
player = pygame.Rect(300, 100, 50, 50)
calling the constructor, wiht the box’s size and position.
In this program, the player can control where the box moves.
To keep track of the food squares (randomly placed) the program uses foods
, a list of Rect
objects:
foods = []
for i in range(20):
foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE),
random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE))
and the random.randint()
function provides for random x/y coordinates.
Again, the program calls the pygame.Rect()
constructor function to return a new food object.
You want the random coordinate to be between 0 and the size of the window minus the size of the food square, not to have the food square to be pushed outside of the window altogether.
Player’s box can move in four directions (initially set to False
):
moveLeft = False
moveRight = False
moveUp = False
moveDown = False
Boolean values to keep track of which arrow key is being pressed.
MOVESPEED = 6
and the speed at which the player moves has to be defined as well.
The pygame
module can generate events in response to user input from the mouse or keyboard.
The following are the events that can be returned by pygame.event.get()
:
QUIT
when the player closes the window.KEYDOWN
when the player presses a key.key
attribute to tell which key was pressed and a mod
attribute (whether SHIFT, CTRL, ALT or others are held down together).KEYUP
when the player releases a key.key
and mod
attributes, similar to those for KEYDOWN
.MOUSEMOTION
whenever the mouse moves over the window.pos
attribute that returns a tuple (x, y)
with the coordinates of the cursor.rel
attribute returns an (x, y)
tuple of relative coordinates since the last MOUSEMOTION event.button
attribute returns a tuple of int
(0/1
) for – left, middle (if any) and right – mouse buttons if not being/being pressed while moved.MOUSEBUTTONDOWN
when a mouse button is pressed.pos
attribute, a (x, y)
tuple for the cursor position coordinates, when the button is pressed.MOUSEBUTTONUP
when the mouse button is released.As usual the game loop starts to handle events, e.g. the player quits
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
... as well as keypress and key release events!
KEYDOWN
event¶If the event type is KEYDOWN
, then the Event
object has a key
attribute that indicates which key was pressed.
When the player presses an arrow
key or a WASD
key (aka wazz-dee), we want the box to move.
We will use if statements to check the pressed key in order to tell which direction the box should move.
These lines compare the key
attribute to the pygame.locals
constants
if event.key == K_LEFT or event.key == K_a:
moveRight = False ; moveLeft = True
if event.key == K_RIGHT or event.key == K_d:
moveLeft = False ; moveRight = True
if event.key == K_UP or event.key == K_w:
moveDown = False ; moveUp = True
if event.key == K_DOWN or event.key == K_s:
moveUp = False ; moveDown = True
The W
, A
, S
, and D
keys are used as alternates for changing the movement variables, letting the player use their left hand instead of their right, improving usability.
KEYUP
event¶When the player releases the key they were pressing
if event.type == KEYUP
a KEYUP
event is generated instead.
If the released key was ESC
, then Python
should terminate the program.
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
remember to call the pygame.quit()
function before sys.exit()
.
... and set a movement variable to False
when a direction’s key is released:
if event.key == K_LEFT or event.key == K_a:
moveLeft = False
if event.key == K_RIGHT or event.key == K_d:
moveRight = False
if event.key == K_UP or event.key == K_w:
moveUp = False
if event.key == K_DOWN or event.key == K_s:
moveDown = False
This will make the box stop moving, as we will see.
You can also add teleportation to the game!
if event.key == K_x:
player.top = random.randint(0, WINDOWHEIGHT -
player.height)
player.left = random.randint(0, WINDOWWIDTH -
player.width)
If the player presses the X
key, set the position of the player’s box to a random place on the window, but not outside of it.
There are couple ways the player can add new food squares to the screen:
NEWFOOD
number of times, to randomly generate itmouse input is handled by events just like keyboard input!
When the player releases the button, a MOUSEBUTTONUP
event occurs:
if event.type == MOUSEBUTTONUP:
foods.append(pygame.Rect(event.pos[0], event.pos[1],
FOODSIZE, FOODSIZE))
event.pos
Rect
object added (aka appended) to the foods list.foodCounter += 1
if foodCounter >= NEWFOOD:
foodCounter = 0
foods.append(pygame.Rect(
random.randint(0, WINDOWWIDTH -
FOODSIZE),
random.randint(0, WINDOWHEIGHT -
FOODSIZE),
FOODSIZE, FOODSIZE))
Each game loop iteration, foodCounter
is incremented. Once equal to constant NEWFOOD
a new food square is generated.
To change the rate, adjusts NEWFOOD
.
Once the movement variables are set (to True
or False
), depending on the keys, we need to move the player’s box by adjusting the player
x/y coordinates.
if moveDown and player.bottom < WINDOWHEIGHT:
player.top += MOVESPEED
if moveUp and player.top > 0:
player.top -= MOVESPEED
if moveLeft and player.left > 0:
player.left -= MOVESPEED
if moveRight and player.right < WINDOWWIDTH:
player.right += MOVESPEED
pygame.draw.rect(windowSurface, BLACK, player)
After the box is moved, draws it in its new position passing the windowSurface
parameter to tell Python
which Surface
object to draw the rectangle on and the player
parameter to tell Python
the position and size of the Rect
object to draw.
Before drawing the food squares, check if the player’s box has overlapped with any of the food squares, using collision detection method that every Rect
object has.
for food in foods[:]:
if player.colliderect(food):
foods.remove(food)
In that case, the square needs to be removed from the foods
list and Python
won’t draw any food squares that the box has already eaten.
On each iteration through the for loop, the current food
square from the foods
(plural) list is placed in the variable food
(singular).
The collision detection method is passed the food
object as an argument and returns True
if the two rectangles collide.
... this for loop is slightly different: it iterates over foods[:]
not food
.
Basically foods[:]
is a shorter way (thanks to slices) to create a new list with a copy of all the items in foods
.
This because you can’t add/remove list items while iterating over it.
DON’T CHANGE A LIST WHILE ITERATING OVER IT
... not to lose track of what the next value is if the list size is changing.
Iterating over a copy (never change), we can modify the original one.
this loops through each food
square in the foods
list drawing it onto windowSurface
.
for i in range(len(foods)):
pygame.draw.rect(windowSurface, GREEN, foods[i])
Now that everything is on the screen, we are ready to update the window and finish the iteration by calling:
pygame.display.update()
mainClock.tick(40)
The program will continue through the game loop and keep updating until the player quits.
Single two-dimensional images used as part of the graphics, are drawn on top of a background.
The background image can be considered one large sprite, too.
Sprites are stored in image files on your computer. There are several image formats that pygame supports (BMP, PNG, JPG, and GIF).
Look at the end of the filename, right after the last period, this is called the file extension.
You can download images from your web browser or create your own images with a drawing program like Microsoft Paint or Tux Paint.
The sound file formats that pygame supports are MIDI, WAV, and MP3. You can download sound effects from the internet.
In the previous examples, we can use sprites instead of plain-looking squares, e.g. a person to represent the player (the black box) and a sprite of cherries for food (green squares).
We can also play background music and sound effects when the player eats one of the cherries.
Once you download image and sound files to use in our program, place these files in the same folder as the main.py
program.
player = pygame.Rect(300, 100, 40, 40)
playerImage = pygame.image.load('player.png')
playerStretchedImage = pygame.transform.scale(playerImage, (40, 40))
foodImage = pygame.image.load('cherry.png')
The player
variable stores a Rect
object to keep track of the location/size of the player. It doesn’t contain the player’s image.
player = pygame.Rect(300, 100, 40, 40)
playerImage = pygame.image.load('player.png')
playerStretchedImage = pygame.transform.scale(playerImage, (40, 40))
foodImage = pygame.image.load('cherry.png')
Then the load()
function is passed a string (filename of the image to load) and the return value is a Surface
object having the image file graphics drawn on its surface.
The scale()
function (within the in the pygame.transform
module) is used to shrink/enlarge a sprite:
it returns a Surface
object with the image drawn at a new size. We will make the player sprite stretch larger as it eats more cherries.
Once called, the constructor function, will create a pygame.mixer.Sound
object (Sound object for short)
pickUpSound = pygame.mixer.Sound('pickup.wav')
pygame.mixer.music.load('background.mid')
pygame.mixer.music.play(-1, 0.0)
musicPlaying = True
then it has couple methods
load()
to load the background musicplay()
to start playing it-1
is to repeat it forever).The variable musicPlaying
is a Boolean to tell the program whether to turn sounds on/off.
This is to give the player the option to run the program without the sound playing, improving usability.
The M
key can turn the background music on/off.
If musicPlaying
is True
we could stop it by calling the stop()
method or play it again by calling play()
:
if event.key == K_m:
if musicPlaying:
pygame.mixer.music.stop()
else:
pygame.mixer.music.play(-1, 0.0)
musicPlaying = not musicPlaying
Toggling a Boolean value means to set a value to the opposite of its current value.
Since the value stored in playerStretchedImage
is a Surface
object, we can draw the player's sprite onto the windowSurface
object using blit()
method:
windowSurface.blit(playerStretchedImage, player)
where second parameter is a Rect
object that specifies where in particular the sprite should be drawn.
When the player eats one of the cherries, its size increases by two pixels in height and width.
if player.colliderect(food):
foods.remove(food)
player = pygame.Rect(player.left, player.top,
player.width + 2,
player.height + 2)
plStrchImg = pygame.transform.scale(playerImage,
(player.width, player.height))
if musicPlaying:
pickUpSound.play()
Rect
object represents the position/size of the playerSurface
object (plStrchImg
) created by calling scale()
method.play()
method on the pickUpSound
object, if and only if musicPlaying
is True
.PRO TIP
Stretching an image often distorts it a little and restretching it, the distortions add up quickly.
By stretching always the original image (every time) to a new size, distorts the image only once.
Again, for each element in the foods
list, let's call the blit()
method passing the foodImage
Surface object, with the cherries image drawn on it.
for food in foods:
windowSurface.blit(foodImage, food)
The food
variable, containing each of the Rect
objects in foods
, all along the for loop, tells the blit()
method where to draw the foodImage
.
We have covered some basic Python
stuff so far, but still have to introduce so many new concepts!
Think about how they could help you develop your own videogame.
Try using them when writing your SDP (Software Development Plan).
Some (highlighted in bold) have already been discussed throughout this presentation or even just mentioned.