Technology May 03, 2026 · 3 min read

Pygame Snake, Pt. 6

Currently, there is a subtle but real flaw in the controls for the game. If the player presses two keys in quick succession, only the last one is registered -- one, or more, are simply dropped. You can see the effect easily if you lower the framerate to something extreme, e.g. 1 (i.e. 1 frame per s...

DE
DEV Community
by David Newberry
Pygame Snake, Pt. 6

Currently, there is a subtle but real flaw in the controls for the game. If the player presses two keys in quick succession, only the last one is registered -- one, or more, are simply dropped.

You can see the effect easily if you lower the framerate to something extreme, e.g. 1 (i.e. 1 frame per second) by changing the clock.tick(20) line to clock.tick(1). If you quickly press down and then right, only right will be registered.

Thankfully, pygame makes it revlatively easy to fix this.

Inside the while loop -- i.e., for each frame -- we want to see if there are arrow keydown events to consume. If so, we want to move a step in each direction pressed. If no keydown events have occured, the snake should continue in last direction it was sent.

Above the for event... loop, add add a line to create an empty list:

    vels = []

Then, inside the following conditional, instead of setting val = ..., add each arrow direction vector to the list:

            vels += [key_vel[event.key]]

Everything from screen.fill("white") until (not including) pygame.quit() will be indented; because just above that, you should add:

    for vel in vels or [vel]:

And that's it. When I first wrote the code I thought it was perfectly reasonable, and then after a while I got a bit confused. I lost track of vel being initialized at the top. It is both a global variable and a loop index in the above for loop. And it appears as part of the for loop's in expresion: or [vel], which provies the current value of vel if vels is empty. Its use as the for loop variable also has the effect of leaving vel with the value of the last element of vels when [one or more] arrow keys are pressed.

Full code as of now:

import pygame
import random

W = 30
H = 30
S = 20

# pygame setup
pygame.init()
screen = pygame.display.set_mode((W * S, H * S))
clock = pygame.time.Clock()
running = True

# game setup
snake = [pygame.Vector2(W / 2, H / 2)]
vel = pygame.Vector2(1, 0)

key_vel = {
    pygame.K_RIGHT: pygame.Vector2(1, 0),
    pygame.K_DOWN: pygame.Vector2(0, 1),
    pygame.K_LEFT: pygame.Vector2(-1, 0),
    pygame.K_UP: pygame.Vector2(0, -1)
}

grow = 3

def place_food():
    global food_pos
    food_pos = pygame.Vector2(
        random.randrange(W),
        random.randrange(H)
    )
    while food_pos in snake:
        food_pos = pygame.Vector2(
            random.randrange(W),
            random.randrange(H)
        )

place_food()

while running:
    # poll for events
    vels = []
    for event in pygame.event.get():
        # pygame.QUIT = user closed window
        if event.type == pygame.QUIT:
            running = False
            break
        elif event.type == pygame.KEYDOWN and event.key in key_vel:
            vels += [key_vel[event.key]]

    for vel in vels or [vel]:
        # fill buffer with white
        screen.fill("white")

        new_head = snake[-1] + vel

        if new_head in snake:
            print("hit self")
            running = False
            break

        if new_head == food_pos:
            grow += 1
            place_food()

        if new_head.x < 0:
            new_head.x = W - 1
        if new_head.x >= W:
            new_head.x = 0

        if new_head.y < 0:
            new_head.y = H - 1
        if new_head.y >= H:
            new_head.y = 0

        snake.append(new_head)

        if grow > 0:
            grow -= 1
        else:
            snake.pop(0)

        for dot in snake:
            square = pygame.Rect(dot * S, (S, S))
            screen.fill("black", square)

        square = pygame.Rect(food_pos * S, (S, S))
        screen.fill("green", square)

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

        # limits FPS
        clock.tick(20)

pygame.quit()
DE
Source

This article was originally published by DEV Community and written by David Newberry.

Read original article on DEV Community
Back to Discover

Reading List