Let’s start from skratch
Let’s see how we can show sprites and move them, detect collisions.
Load a sprite
import pygame as pg import os sprite = pg.image.load("cat\\Idle (1).png") print(sprite)
The sprite is a Surface object
pygame 2.0.0.dev10 (SDL 2.0.12, python 3.8.3) Hello from the pygame community. https://www.pygame.org/contribute.html <Surface(128x128x32 SW)>
You can see that is 128×128 as size and 32 as color.
From the documentation:
Load an image from a file source. You can pass either a filename or a Python file-like object.
Pygame will automatically determine the image type (e.g., GIF
or bitmap) and create a new Surface object from the data.
Show an image / sprite
import pygame as pg import os screen = pg.display.set_mode((400, 400)) sprite = pg.image.load("06\\06.svg") print(sprite) loop = 1 while loop: for event in pg.event.get(): if event.type == pg.QUIT: loop = 0 screen.blit(sprite, (0, 0)) pg.display.flip() pg.quit()
Do something when user hit a key, any key
Now we put in the for loop inside the while loop the “event listener2” fo the user’s key press
if event.type == pg.KEYDOWN: print("go")
That goes in the code like this:
import pygame as pg import os screen = pg.display.set_mode((400, 400)) sprite = pg.image.load("..\\06\\06.svg") print(sprite) loop = 1 while loop: for event in pg.event.get(): if event.type == pg.QUIT: loop = 0 if event.type == pg.KEYDOWN: print("go") screen.blit(sprite, (0, 0)) pg.display.flip() pg.quit()
Create a Sprite class and Group and rect
We need to
- create a class that loads the image / images and create a rect attribute
- add the sprite to a group of sprites (created with pygame.group.Group)
- draw the group on the screen
import pygame as pg screen = pg.display.set_mode((400, 400)) class Sprite(pg.sprite.Sprite): def __init__(self): super().__init__() self.image = pg.image.load("..\\06\\06.svg") self.rect = self.image.get_rect() g = pg.sprite.Group() sprite = Sprite() g.add(sprite) print(sprite) loop = 1 while loop: for event in pg.event.get(): if event.type == pg.QUIT: loop = 0 if event.type == pg.KEYDOWN: print("go") g.draw(screen) pg.display.flip() pg.quit()
Load all the images… for an animation
import pygame as pg from glob import glob screen = pg.display.set_mode((400, 400)) class Sprite(pg.sprite.Sprite): def __init__(self): super().__init__() self.animation = [pg.image.load(f) for f in glob("..\\cat\\Idle *.png")] self.image = self.animation[0] self.rect = self.image.get_rect() g = pg.sprite.Group() sprite = Sprite() g.add(sprite) print(sprite) loop = 1 while loop: for event in pg.event.get(): if event.type == pg.QUIT: loop = 0 if event.type == pg.KEYDOWN: print("go") g.draw(screen) pg.display.flip() pg.quit()
The images are loaded but we see just one of them
The image is still standing still, because we see just the self.animation[0] of all the images that are store in the self.animation list of surface objects (when we load an image with pg.pygame.load we have in return a Surface object).
Blit and blits
The screen is also a Surface object and we can use blit to display one image onto another. We can also use blits to display more images onto another. I think this can make us gain framerate when we have many surfaces at once.
In the examples above we did not use blit or blits, but draw, that is a method of Group, so that we can draw many sprites (not Surfaces) on the screen at once.
Let’s update the image with a counter
If we add this into the Sprite class:
def update(self): self.acount += 1 if self.acount == len(self.animation): self.acount = 0 self.image = self.animation[self.acount]
And we update the group g in the while loop:
while loop: for event in pg.event.get(): if event.type == pg.QUIT: loop = 0 if event.type == pg.KEYDOWN: print("go") g.draw(screen) g.update() pg.display.flip()
We will see the sprite move insanely fast
Clock
Let’s use the clock to put a limit to the frame rate
clock = pg.time.Clock()
if, at the end of the while loop we put this, the framerate will slow down and so the animation.
clock.tick(30)
Do something when Key Right is pressed
We’ve seen how to print something on the console when we press any key. What if we want that something happens when a certain key is pressed like arrow right key?
In the while loop we make a for loop that searches into pygame.event.get() that is the list of interactions intercepted with the user; we check then the event.type if is a pygame.KEYDOWN event and then if is
While loop:
- pg.event.get()
- event.type()
- event.key
- event.type()
After the event.type if statement that checks if you press a key, you can then check what event.key you pressed with this code:
if event.type == pg.KEYDOWN: if event.key == pg.K_RIGHT: print("RIGHT")
Make the the sprite do something with the keys pressed
Now it walks when you press the right arrow key, stopw with the left and jumps with up.
This is the change in the while loop. When you press a keys the animation of “sprite” changes differently for each key, so that it seems that the player does a certain action.
if event.type == pg.KEYDOWN: if event.key == pg.K_RIGHT: sprite.change_to("walk") if event.key == pg.K_LEFT: sprite.change_to("idle") if event.key == pg.K_UP: sprite.change_to("jump")
And this is the method change animation
def change_to(self, action): "This makes the animation of the sprite to change when you press a key" self.action = action self.animation = [ pg.image.load(f) for f in glob(f"..\\cat\\{self.action} *.png")]
The whole code
import pygame as pg from glob import glob screen = pg.display.set_mode((200, 200)) pg.display.set_caption("Game") clock = pg.time.Clock() class Sprite(pg.sprite.Sprite): def __init__(self): super().__init__() self.acount = 0 # Coordinates for movement self.x = 0 self.y = 0 self.action = "idle" self.animation = [ pg.image.load(f) for f in glob("..\\cat\\Idle *.png")] self.image = self.animation[0] self.rect = self.image.get_rect() print(self.image) def update(self): self.acount += 1 if self.acount == len(self.animation): self.acount = 0 self.image = self.animation[self.acount] def change_to(self, action): "This makes the animation of the sprite to change when you press a key" self.action = action self.animation = [ pg.image.load(f) for f in glob(f"..\\cat\\{self.action} *.png")] g = pg.sprite.Group() sprite = Sprite() g.add(sprite) print(sprite) loop = 1 while loop: for event in pg.event.get(): if event.type == pg.QUIT: loop = 0 if event.type == pg.KEYDOWN: if event.key == pg.K_RIGHT: sprite.change_to("walk") if event.key == pg.K_LEFT: sprite.change_to("idle") if event.key == pg.K_UP: sprite.change_to("jump") screen.fill((0, 0, 0)) g.draw(screen) g.update() pg.display.flip() clock.tick(30) pg.quit()
Jump just one time
I added this statement here to set to zero the self.acount when you change animation
and when you do not hold the key up, it will stop the action…
if event.type == pg.KEYUP: if event.key == pg.K_UP: sprite.change_to("idle")
But, doing so, it will jump forever when you click or for a time too little. So I made this
import pygame as pg from glob import glob screen = pg.display.set_mode((200, 200)) pg.display.set_caption("Game") clock = pg.time.Clock() class Sprite(pg.sprite.Sprite): def __init__(self): super().__init__() self.acount = 0 # Coordinates for movement self.x = 0 self.y = 0 self.action = "idle" self.animation = [ pg.image.load(f) for f in glob("..\\cat\\Idle *.png")] self.image = self.animation[0] self.rect = self.image.get_rect() print(self.image) def update(self): self.acount += 1 if self.acount == len(self.animation): self.acount = 0 self.image = self.animation[self.acount] def change_to(self, action): self.acount = 0 self.action = action self.animation = [ pg.image.load(f) for f in glob(f"..\\cat\\{self.action} *.png")] g = pg.sprite.Group() sprite = Sprite() g.add(sprite) print(sprite) loop = 1 jumpcount = 0 jumpstate = 0 walkstate = 0 while loop: for event in pg.event.get(): if event.type == pg.QUIT: loop = 0 if event.type == pg.KEYDOWN: if event.key == pg.K_RIGHT: walkstate = 1 sprite.change_to("walk") if event.key == pg.K_LEFT: walkstate = 0 sprite.change_to("idle") if event.key == pg.K_UP: jumpstate = 1 sprite.change_to("jump") if jumpstate: jumpcount += 1 print(jumpcount) if jumpcount > 8: jumpstate = 0 jumpcount = 0 if walkstate: sprite.change_to("walk") else: sprite.change_to("idle") screen.fill((0, 0, 0)) g.draw(screen) g.update() pg.display.flip() clock.tick(30) pg.quit()
The result is this:
Jump actually: aka “Move that sprite up and down”
Let the sprite to go up and come back down when he jumps. We will finally see how we can move the sprite using rect.x an rect.y (that is returned from pygame.image.get_rect())
if jumpstate: jumpcount += 1 if jumpcount < 4: sprite.rect.y -= 8 else: sprite.rect.y += 4 # print(jumpcount) if jumpcount > 8: jumpstate = 0 jumpcount = 0 if walkstate: sprite.change_to("walk") else: sprite.change_to("idle")
Video expalnation of the code
Code optimization
To make the code faster it is better to load images at the start, instead of doing it every time you press a key, as you have seen in the video.
Load before the while loop
walk = sprite.change_to("walk") jump = sprite.change_to("jump") idle = sprite.change_to("idle")
In the while loop
while loop: for event in pg.event.get(): if event.type == pg.QUIT: loop = 0 if event.type == pg.KEYDOWN: if event.key == pg.K_RIGHT: sprite.acount = 0 sprite.animation = walk walkstate = 1 if event.key == pg.K_LEFT: sprite.acount = 0 sprite.animation = idle walkstate = 0 if event.key == pg.K_UP: sprite.acount = 0 sprite.animation = jump jumpstate = 1
The whole code
import pygame as pg from glob import glob screen = pg.display.set_mode((200, 200)) pg.display.set_caption("Game") clock = pg.time.Clock() class Sprite(pg.sprite.Sprite): def __init__(self): super().__init__() self.acount = 0 # Coordinates for movement self.x = 0 self.y = 0 self.action = "idle" self.animation = [ pg.image.load(f) for f in glob("..\\cat\\Idle *.png")] self.image = self.animation[0] self.rect = self.image.get_rect() print(self.image) def update(self): self.acount += 1 if self.acount == len(self.animation): self.acount = 0 self.image = self.animation[self.acount] def change_to(self, action): self.action = action self.animation = [ pg.image.load(f) for f in glob(f"..\\cat\\{self.action} *.png")] return self.animation g = pg.sprite.Group() sprite = Sprite() walk = sprite.change_to("walk") jump = sprite.change_to("jump") idle = sprite.change_to("idle") g.add(sprite) print(sprite) loop = 1 jumpcount = 0 jumpstate = 0 walkstate = 0 while loop: for event in pg.event.get(): if event.type == pg.QUIT: loop = 0 if event.type == pg.KEYDOWN: if event.key == pg.K_RIGHT: sprite.acount = 0 sprite.animation = walk walkstate = 1 if event.key == pg.K_LEFT: sprite.acount = 0 sprite.animation = idle walkstate = 0 if event.key == pg.K_UP: sprite.acount = 0 sprite.animation = jump jumpstate = 1 if jumpstate: jumpcount += 1 if jumpcount < 4: sprite.rect.y -= 8 else: sprite.rect.y += 4 # print(jumpcount) if jumpcount > 8: jumpstate = 0 jumpcount = 0 if walkstate: sprite.change_to("walk") else: sprite.change_to("idle") screen.fill((0, 0, 0)) g.draw(screen) g.update() pg.display.flip() clock.tick(30) pg.quit()
End of part 1. See ya on the next one
Twitter: @pythonprogrammi - python_pygame