I was coding a snake game in python. But if we press 2 buttons quickly, it takes two commands in a tick. what can i do to prevent it?

Viewed 256

I have the following problem in my snake game: If the snake is headed right and the player presses keys UP and LEFT in a quick succession, the snake turns left, overruns its body and the game ends, taking it as a collision. How can I prevent the game from taking two commands for changing a direction before the snake moves?

The code where I check which key is pressed follows:

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT and left == False:
                right = True
                up = False
                down = False
            elif event.key == pygame.K_LEFT and right == False:
                left = True
                up = False
                down = False
            elif event.key == pygame.K_DOWN and up == False:
                down = True
                right = False
                left = False
            elif event.key == pygame.K_UP and down == False:
                up = True
                right = False
                left = False

Full code of the project can be found in this Git repository.

To run the code, you need to install the pygame package via pip.

3 Answers

If I understand correctly, you're having a problem that when your snake is heading right and you press an up key and in a quick succession a left key, the head of your snake turns left, effectively resulting in a collision with its own body.

This looks like your snake's movement is independent from the key presses, which then results in the possibility to press two keys in one move, and since the only condition is that the snake is not heading right, which is effectively true once you press the up key, it then results in this behavior.

One quick way to prevent this is to set some variable called e.g. moved_from_last_press, where each time the snake moves, you assign True, and each time you press a key, you assign False. You then check for the True value when you check which key was pressed.

Problem is that all events are kept on list and you slow down code so system has time to put two (or more) KEYDOWN events on this list before you run for event loop which checks all KEYDOWN on this list.

As @VaclavPelc mentioned you can use variable to control if you already changed direction and skip other keys.

This way it will use only first correct key/direction when you press many keys - so you can't fast change decision - ie. when you move down and you press left and right then it will use left because it is first correct direction.

while True:

    changed_direction = False

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        if event.type == pygame.KEYDOWN:
            if not changed_direction:
                if event.key == pygame.K_RIGHT and left == False:
                    right = True
                    up = False
                    down = False
                    changed_direction = True
                elif event.key == pygame.K_LEFT and right == False:
                    left = True
                    up = False
                    down = False
                    changed_direction = True
                elif event.key == pygame.K_DOWN and up == False:
                    down = True
                    right = False
                    left = False
                    changed_direction = True
                elif event.key == pygame.K_UP and down == False:
                    up = True
                    right = False
                    left = False
                    changed_direction = True

Other method is to keep previous direction in different variables and use these variables when you check previous direction.

This way it will use last correct key/direction when you press many keys - so you can fast change decision - ie. when you move down and you press left and right then it will use right because it is last correct direction.

while True:

    previous_left = left
    previous_right = right
    previous_up = up
    previous_down = down
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT and previous_left == False:
                right = True
                up = False
                down = False
            elif event.key == pygame.K_LEFT and previous_right == False:
                left = True
                up = False
                down = False
            elif event.key == pygame.K_DOWN and previous_up == False:
                down = True
                right = False
                left = False
            elif event.key == pygame.K_UP and previous_down == False:
                up = True
                right = False
                left = False

The problem is that your keys are checked more often than your snake moves. For example, you can press up and left before the snake gets to move up which will cause to just go left. To solve this you need a flag indicating the snake has moved so you know when to check for another key press:

moved = True

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if(moved):
                if event.key == pygame.K_RIGHT and left == False:
                    right = True
                    up = False
                    down = False
                    moved = False
                elif event.key == pygame.K_LEFT and right == False:
                    left = True
                    up = False
                    down = False
                    moved = False
                elif event.key == pygame.K_DOWN and up == False:
                    down = True
                    right = False
                    left = False
                    moved = False
                elif event.key == pygame.K_UP and down == False:
                    up = True
                    right = False
                    left = False
                    moved = False

    if up:
        next = (snakes_block[0][0], snakes_block[0][1] - 1)
        imghadd = pygame.transform.rotate(imghad, 180)
        moved = True
    elif down:
        next = (snakes_block[0][0], snakes_block[0][1] + 1)
        imghadd = pygame.transform.rotate(imghad, 360)
        moved = True
    elif left:
        next = (snakes_block[0][0] - 1, snakes_block[0][1])
        imghadd = pygame.transform.rotate(imghad, 270)
        moved = True
    elif right:
        next = (snakes_block[0][0] + 1, snakes_block[0][1])
        imghadd = pygame.transform.rotate(imghad, 90)
        moved = True

The moved variable is used to check whether the snake has been moved since the key was pressed, the program will only detect keys once the move has been made so that you cannot make the snake go in a direction it shouldn't be able to

Related