DEV Community

David Newberry
David Newberry

Posted on

Pygame Snake, Pt. 1

Pygame is a module designed to allow us to make 2D games using Python. I am just learning pygame myself. I've always loved the game Snake, and I find that it makes a good learning project, too.

When using pygame, we will draw each frame of our animation to an offscreen buffer. That is basically like an invisible canvas that exists in memory. This allows us to draw the scene piece by piece, and then when its done copy the whole buffer to the onscreen canvas.

Pygame will also handle the event loop. We can specify how many frames per second we want our game to run at, and pygame will make sure the event loop runs no faster than that. Each time through the loop, we will draw one frame of animation.

We will also respond to events that pygame gives us. In particular, pygame will send us a KEYDOWN event whenever a key is pressed. Also, if the user closes the pygame window, it will send us a QUIT event, and the game should stop.

Here is a minimal pygame setup. Modified code from https://www.pygame.org/docs/

import pygame

# initialize pygame
pygame.init()

# create 1000x1000 pixel pygame window
screen = pygame.display.set_mode((1000, 1000))

# clock used to set FPS
clock = pygame.time.Clock()

# game runs as long as this is True
running = True

while running:
    # poll for events
    for event in pygame.event.get():
        # pygame.QUIT = user closed window
        if event.type == pygame.QUIT:
            running = False

    # fill buffer with white
    screen.fill("white")

    # copy buffer to screen
    pygame.display.flip()

    # limits FPS
    clock.tick(20)

pygame.quit()
Enter fullscreen mode Exit fullscreen mode

The only thing about this code I don't like is that it requires the running variable. I wanted to use an infinite loop with break, but I can't do that since we have to iterate through potentially multiple events using an inner (for) loop, and Python has no way to break out of nested loops.

OK, so that's not a bad start, except that it is not exactly the most interesting "animation". Let's add a moving square.

To have an animated square, we'll need a new variable to keep track of its location. If we weren't using pygame, I might create a simple little class for this (to store x and y coordinates). But pygame already has such a class built-in: Vector2.

So let's add a new variable called dot and create a Vector2 value for it to hold:

dot = pygame.Vector2(500, 500)
Enter fullscreen mode Exit fullscreen mode

Add that code just above the while loop.

Now inside the loop increment dot's x value, and then draw it at its new position. To draw a square, we have to create a Rect object. To do this, we will pass two pairs of values: the (x, y) coordinates of its upper-left corner, and its (width, height) values. The dot variable itself will function as the (x, y) coordinates of the square's upper-left corner.

    dot.x += 10
    square = pygame.Rect(dot, (50, 50))
    screen.fill("black", square)
Enter fullscreen mode Exit fullscreen mode

Place this code right under screen.fill("white"). That command paints the entire buffer white, in anticipation of drawing a new frame. The new three lines of code draw a 50x50 black square at the location indicated by the dot variable.

Full code now:

import pygame

# pygame setup
pygame.init()
screen = pygame.display.set_mode((1000, 1000))
clock = pygame.time.Clock()
running = True

dot = pygame.Vector2(500, 500)

while running:
    # poll for events
    for event in pygame.event.get():
        # pygame.QUIT = user closed window
        if event.type == pygame.QUIT:
            running = False

    # fill buffer with white
    screen.fill("white")

    dot.x += 10
    square = pygame.Rect(dot, (50, 50))
    screen.fill("black", square)

    # copy buffer to screen
    pygame.display.flip()

    # limits FPS
    clock.tick(20)

pygame.quit()
Enter fullscreen mode Exit fullscreen mode

If you have a little experience with Python, try this challenge: Add some code to check if dot.x > 1000, and if so, set it to 0. This should cause the dot to reappear on the left once it goes off the right edge.

Top comments (0)