Every module needs to be imported and pygame is no exception. Although we need to call the function pygame.init()
for all imported modules in pygame to be initialized properly. If we forget this some modules won't work. The function also returns a tuple of all successfully and failed initializations (it won't raise an error if a module fails to initialize).
import pygame
successes, failures = pygame.init()
print("{0} successes and {1} failures".format(successes, failures))
We also need to create a display. Pygame have already created a (hidden) display, so all we need to do is to set the mode of the display (in this example we only set the resolution). It's also a good idea to create a clock to make sure our program updates at a fixed speed (otherwise it would run at different speed depending on how fast the computer is).
screen = pygame.display.set_mode((720, 480)) # Notice the tuple! It's not 2 arguments.
clock = pygame.time.Clock()
FPS = 60 # This variable will define how many frames we update per second.
For a bit of readability later in our code we'll create two color constants, which represent a tuple of Red, Green and Blue (RGB). The values goes from 0 (no light) to 255 (full light).
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
In pygame we usually use a Surface to represent the appearance of an object, and a Rect (rectangle) to represent the position of an object. A Surface is like a blank sheet of paper which contain colors or images. If you're making a class you should name the attributes image and rect since many functions will look for and use those attributes. Such classes would benefit by inherit the pygame.sprite.Sprite
class for reasons you can read up on here.
rect = pygame.Rect((0, 0), (32, 32)) # First tuple is position, second is size.
image = pygame.Surface((32, 32)) # The tuple represent size.
image.fill(WHITE) # We fill our surface with a nice white color (by default black).
Now we have everything set for our game loop. This is a loop that will run for the entire game, where we handle events and updates the screen and positions of our objects.
First we'll make sure our loop executes at a given FPS. We defined the FPS and created our clock in the beginning of the program. The following code will make sure our program sleeps enough time to make our loop repeat the amount we defined our FPS to be. In this example, 60 times per second.
clock.tick(FPS)
Then we'll handle events. An event is basically a user action, such as moving the mouse or pressing a key. Pygame will register all these events in a queue which we get by calling pygame.event.get()
. We can iterate over this and check if there's an event that we'd like to handle. Events have a type attribute which we can check against constants in the pygame module to determine what type of event it is.
for event in pygame.event.get():
if event.type == pygame.QUIT: # The user pressed the close button in the top corner of the window.
quit()
# Close the program. Other methods like 'raise SystemExit' or 'sys.exit()'.
# Calling 'pygame.quit()' won't close the program! It will just uninitialize the modules.
We can also check if event.type == pygame.KEYDOWN
to see if the user has pressed a key down. In that case the event has an attribute key which we can check to see which key it represents.
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
rect.move_ip(0, -2) # Changes the rect's position.
elif event.key == pygame.K_s:
rect.move_ip(0, 2)
elif event.key == pygame.K_a:
rect.move_ip(-2, 0)
elif event.key == pygame.K_d:
rect.move_ip(2, 0)
Now we need to display our image. First we might want to clear our screen from previous rendering. We do so by filling our entire screen with black (remove the code to see why we want to clear it). Then we need to blit our image to the screen. Blitting essentially means copying the image to another surface (in our case, the screen). Lastly we flip or update the screen.
When we're blitting we're not actually displaying anything to the user. Imagine it as the computer on one side and the user on the other. The computer draws (blits) on it's side of the screen, flips it towards the user, and then repeats.
screen.fill(BLACK)
screen.blit(image, rect)
pygame.display.update() # Or 'pygame.display.flip()'.
Now we have a basic game! Quite boring, yes, but the essentials are there! Combined this with your current Python knowledge and you should be able to create something awesome.
import pygame
successes, failures = pygame.init()
print("{0} successes and {1} failures".format(successes, failures))
screen = pygame.display.set_mode((720, 480))
clock = pygame.time.Clock()
FPS = 60 # Frames per second.
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
# RED = (255, 0, 0), GREEN = (0, 255, 0), BLUE = (0, 0, 255).
rect = pygame.Rect((0, 0), (32, 32))
image = pygame.Surface((32, 32))
image .fill(WHITE)
while True:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
rect.move_ip(0, -2)
elif event.key == pygame.K_s:
rect.move_ip(0, 2)
elif event.key == pygame.K_a:
rect.move_ip(-2, 0)
elif event.key == pygame.K_d:
rect.move_ip(2, 0)
screen.fill(BLACK)
screen.blit(image, rect)
pygame.display.update() # Or pygame.display.flip()
Note that the program checks for when we press the key and not for when we're holding the key down. To fix this we could introduce a velocity variable. We can create a player class to keep it more organized. To avoid frame dependent movement (if we would change the FPS to 30 the objects would move at half the speed) we introduce time dependent movement by passing the time between ticks to our movable objects.
import pygame
successes, failures = pygame.init()
print("Initializing pygame: {0} successes and {1} failures.".format(successes, failures))
screen = pygame.display.set_mode((720, 480))
clock = pygame.time.Clock()
FPS = 60
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill(WHITE)
self.rect = self.image.get_rect() # Get rect of some size as 'image'.
self.velocity = [0, 0]
def update(self):
self.rect.move_ip(*self.velocity)
player = Player()
running = True
while running:
dt = clock.tick(FPS) / 1000 # Returns milliseconds between each call to 'tick'. The convert time to seconds.
screen.fill(BLACK) # Fill the screen with background color.
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
player.velocity[1] = -200 * dt # 200 pixels per second
elif event.key == pygame.K_s:
player.velocity[1] = 200 * dt
elif event.key == pygame.K_a:
player.velocity[0] = -200 * dt
elif event.key == pygame.K_d:
player.velocity[0] = 200 * dt
elif event.type == pygame.KEYUP:
if event.key == pygame.K_w or event.key == pygame.K_s:
player.velocity[1] = 0
elif event.key == pygame.K_a or event.key == pygame.K_d:
player.velocity[0] = 0
player.update()
screen.blit(player.image, player.rect)
pygame.display.update() # Or pygame.display.flip()
print("Exited the game loop. Game will quit...")
quit() # Not actually necessary since the script will exit anyway.
There are still many things that should be improved about this code. I'd recommend you to read the pygame tutorial and this talk by Richard Jones for more in depth.