Pygame2
In this article we will add items, add a background image(you can change it back to the solid green color later if you would like), talk about sprites, talk about sprite groups, and briefly discuss how to add sound effects.
For this tutorial you will need to download the 'forest.jpg' image from the backgrounds folder in the Pygame2 folder here. Save this image in your own backgrounds folder in the same directory as your 'MainGame.py' file. Also download the 'coin0.png' image from the items folder in the Pygame2 folder here. Create a new folder titled 'items' and save 'coin0.png' in this new folder. Last one, download the 'coin.wav' file here and save it in a folder called 'sounds'. The backgrounds folder, characters folder, items folder, sounds folder, and MainGame.py file should all be located in the same place. You can download the entire Pygame2 folder for everything discussed in this article.
Adding a Background Image.
First we will set the 'forest.jpg' image as the background of our game.

On the line following the charImgs variable assignment, add the following:
(1 line) codebit 1
background = pygame.image.load("backgrounds/forest.jpg").convert()
This loads the background image 'forest.jpg', and assigns it to the variable named background.
The covert() method is used to make a copy of the image surface with the same pixel format as DISPLAYSURF. The Pygame documentation recommends always converting a surface before it is blitted many times.
Next, replace the line in our game loop that says 'DISPLAYSURF.fill(RED)#background color----' with the following:
(1 line) codebit 2
DISPLAYSURF.blit(background, (0,0))
Now go ahead and save and run the script and you should be able to walk the game character around on the forest background! If you get an error, or the image is not displaying as expected, make sure you copied the code above correctly and check that your images are saved in the correct folders.
Try downloading an image off the internet and using it as a background! The jpeg format is typically used for background images in Pygame. Use a jpeg format for images that are not moving much or that don't involve transparency..
When you choose a background be aware that the new image may not fit on the screen like the forest.jpg image does.
If your game looks something like this, that's not a good sign. Don't worry though that just means your background image is too small! The reason the player is leaving a trail like that is because there is no color, or image, filling the screen behind him each frame.
If an image is too large it will simply extend outside the visible window surface.
You could always adjust the DISPLAYWIDTH and DISPLAYHEIGHT variables to fit the image, but you can only do this before the game is running. If you want to use different backgrounds for different levels of your game (like we will) you would need a way to fit them to the display window. This can be done by using pygame.transform.scale.
It is best to use pygame.transform.scale(Surface, (width, height)) to scale a surface. To scale the background surface to fit the display, use the following after you have loaded the image:
(1 line) codebit 3background = pygame.transform.scale(background, (DISPLAYWIDTH,DISPLAYHEIGHT))
This will take the background image surface and scale it to the DISPLAYWIDTH and DISPLAYHEIGHT variables. For more ways you can transform a surface in Pygame check out the documentation.
Pick Up Items.
Right now our game is missing a crucial element, an objective! Picking up items is a big part of almost any adventure/RPG game, and if we add this to our game it will at least give the player a reason to move around.
Our items will just be simple objects that can be placed anywhere on the display surface. We want the player to have the ability to pick up an item, put it in his inventory, and view it later. This is going to require multiple objects interacting, so that means we will finally examine and implement Pygame's Sprite class!
Let's create our Item class. Right after where you defined the Player class, add this block of code:
(24 lines) codebit 4
class Item(pygame.sprite.Sprite): def __init__(self, x, y, itemType, image, sound=None): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load(image).convert_alpha() self.rect = self.image.get_rect() self.rect.x=x self.rect.y=y self.itemType = itemType self.loadSound(sound) def loadSound(self, sound): if sound != None: self.sound = pygame.mixer.Sound(sound) else: self.sound = None def playSound(self): if self.sound != None: self.sound.play() def pickUp(self): self.playSound() return self.itemType
A short an simple class, I like it! Right now we just have the basics for our item class. Don't worry this simple sprite class will lead to plenty of topics we can analyze.
The Item class inherits from pygame.sprite.Sprite just like the Player class. The __init__ method has four mandatory, and one optional, arguments.
The first and second arguments are the x and y coordinates of the object.
The third argument is the itemType. This is a dictionary with a single key value pair. The key is the string representing the item type, and the value is an integer of the quantity of that item ({'item type':quantity}). I will explain this more when we discuss the player inventory.
The fourth is the image string we will use to load the image. This is similar to what we did for the Player except we only have one image string and it is not in a tuple.
The fifth argument is optional. It is a string name of the sound we will play when the item is picked up. I will explain this once we have everything else with this class working. Ignore it for now.
Everything in the __init__ method should look familiar, except loading sound. If you are lost, reference the explanation of the Player class in the last article.
Skip the two sound methods and examine the pickUp method. This is the method that will be called when the item is picked up. It will play a sound (if there is one) and return the itemType.
Add this on the line after where you assigned the charImgs variable:
(1 line) codebit 5
coinImg = 'items/coin0.png'
This saves the image string to a variable so we can load it in our Item class.
After where you created the player object, skip a line and add the following:
(3 lines) codebit 6
items = pygame.sprite.Group() coin = Item(500,355,{'coin':1}, coinImg) items.add(coin)
In these three lines of code a very important Pygame Sprite feature is introduced, the Sprite Group. A sprite group lets us group, update, draw, test for collisions, and do many things with many sprites all at once. This is more efficient than doing all of these actions to each sprite individually.
items = pygame.sprite.Group() creates an instance of the Group class. Always do this when you want to create a new group for sprites. coin = Item(500,355,{'coin':1}, coinImg) creates an instance of the Item class. The coin object is a sprite because the Item class inherits from the pygame.sprite.Sprite class. We then add the coin sprite too the items sprite group with the add method of the Group class. The add method lets you add as many sprites as you want to the group. Try creating more coins and adding them to the group with items.add(coin, coin2, coin3), just add each variable to the add method! Remember to change the coordinates for each coin so you can see them all.
Finally, inside the game loop, after player.draw(DISPLAYSURF), add this:
(1 line) codebit 7
items.draw(DISPLAYSURF)
If you got an error, or the coin is not appearing in the display window, check that your image string is correct, and that the coin coordinates are inside the display window.
Try to add more coins to the screen by creating new sprites and adding them to the items group!
items.draw(DISPLAYSURF) will draw every sprite in the items group! Add as many sprites as you like, it wont add a single line of code to our game loop! This is one reason the Sprite Group class is so useful. See all the other useful methods of the Group class in the documentation.
Walk the player around now! You can walk up to the coin and... walk behind it. That's a bit disappointing. Let's fix it so the coin disappears when the player collides with it.
Add this method to the Player class:
(2 lines) codebit 8
def itemsCollision(self, items): pygame.sprite.spritecollide(self, items, True)
This method takes one argument, a group of sprite items. It then uses pygame.sprite.spritecollide(self, items, True) to see if any sprites in the group (items) collide with the player (self). The item is killed if a collision is found. spritecollide is, in my opinion, the most useful method of the Sprite class. You can use it to check if any sprites in a group (argument 2) collide with a sprite (argument 1). The third argument for spritecollide is a boolean value to kill sprites in the group that collide with the argument 1 sprite. spritecollide returns a list of the sprites in the group that caused a collision, so you can save this list to a variable if you want (we will adjust this code later to do this).
Next, adjust the update method of the Player class to look exactly like this:
(4 lines) codebit 9
def update(self, movex, movey, items): self.moveSprite(movex, movey) self.itemsCollision(items) self.render()
This adjusts the update method so it takes another argument, items (a group of item sprites), and then passes this argument to the itemsCollision method. Make sure that the itemsCollision method is after the moveSprite method. This makes sure that we check for a collision as soon as we move the player.
Now just add the items parameter to the update call in the game loop, it should look like player.update(moveX,moveY,items).
Hit run and pick up all the coins your heart desires!
It's a funny squeaky sound...

I will briefly go over sound effects in Pygame.
We are making sound effects so we are using the pygame.mixer module. There is another module called pygame.music that is for playing music in the game, we will discuss this in a later article. Just know that for sound effects pygame.mixer is what you need to use. mixer allows multiple sounds to be played at once whereas music is made for just one soundtrack to be played continuously.
Near where you assigned your image string variables (charImgs and coinImg) type this:
(1 line) codebit 10
coinSound = 'sounds/coin.wav'
This creates a sound string variable similar to the image string. This string is used by Python to find the sound we want to load.
the WAV format of audio is best for pygame.mixer. There are other formats (not MP3) that the mixer can use, but I always use WAV.
Add the optional sound parameter to any items that you want to play a sound. It should look like coin = Item(500,355,{'coin':1}, coinImg, coinSound).
Now adjust the itemCollision method of the Player class to look like this:
(5 lines) codebit 11
def itemsCollision(self, items): collisionList = pygame.sprite.spritecollide(self, items, True) for collision in collisionList: item = collision.pickUp() #self.updateInventory(item)
Now we save the list of sprites that caused a collision to the collisionList variable (remember, spritecollide returns a list of sprites from the group that cause collision). We then do a for loop through collisionList and we call each item's pickUp method. The pickUp method plays the item's sound (if it has one) and returns the item's itemType, which we will use later for the inventory.
How does the item play the sound? Look at the code from codebit 4.
We pass the optional sound variable with a sound string similar to the image string. In the __init__ method the item calls the loadSound method. loadSound takes one argument, the sound string or a None value, and either loads the sound according to the sound string (pygame.mixer.Sound(soundString) is how you load a sound) and sets self.sound equal to the loaded sound, or sets self.sound equal to None.
Then, later, when the item's pickUp method is called, the item's playSound method is called. In this method, if self.sound is not equal to None, we use the Sound class's play method (self.sound.play()). For more on the mixer and the Sound class read the documentation.
Run your game and play the sound!
In the next article we will add an inventory so we can view items the player has picked up!
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.