1
votes

I am making a simple game with the pygame library. I noticed a strange bug where if I tap one of the arrow keys for direction to move my character, it sticks it moving in one direction. Key down triggers a positive acceleration in the direction you press, and key up resets the acceleration to zero. Checking these values, the acceleration stays positive despite key up occurring. Looking at all of the events, it registers a key down and key up, but pygame doesn't run the code associated with that key up event. It could be a signal problem with my keyboard, but it seems like it might be event handling in pygame. Here is my code:

import pygame

pygame.init()

display_x = 1024
display_y = 512

game_display = pygame.display.set_mode((display_x, display_y))
clock = pygame.time.Clock()

fps = 30
font = pygame.font.SysFont(None, 25)

max_velocity = 16
acceleration = 4


def print_to_screen(text, color, x, y):
    screen_text = font.render(text, True, color)
    game_display.blit(screen_text, [x, y])


def game_loop():
    game_exit = False

    lead_x = display_x/2
    lead_y = display_y/2
    vel_x = 0
    vel_y = 0
    acc_x = 0
    acc_y = 0
    tile_size = 32

    while not game_exit:
        for event in pygame.event.get():
            print(event)
            if event.type == pygame.QUIT:
                game_exit = True
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    acc_x = -acceleration
                if event.key == pygame.K_RIGHT:
                    acc_x = acceleration
                if event.key == pygame.K_UP:
                    acc_y = -acceleration
                if event.key == pygame.K_DOWN:
                    acc_y = acceleration
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT and vel_x < 0:
                    acc_x = 0
                if event.key == pygame.K_RIGHT and vel_x > 0:
                    acc_x = 0
                if event.key == pygame.K_UP and vel_y < 0:
                    acc_y = 0
                if event.key == pygame.K_DOWN and vel_y > 0:
                    acc_y = 0

        if -max_velocity < vel_x < max_velocity:  # if velocity isn't max, adds acceleration to velocity
            vel_x += acc_x
        if -max_velocity < vel_y < max_velocity:
            vel_y += acc_y
        if vel_x < 0:  # velocity decay, if vel is less than 0, adds one. no input = slowed down cube
            vel_x += 1
        elif vel_x > 0:
            vel_x -= 1
        if vel_y < 0:
            vel_y += 1
        elif vel_y > 0:
            vel_y -= 1

        if (lead_x + vel_x) < 0:  # looks into future: if past border in next frame, sets x coord to 0, stops motion
            vel_x = 0
            acc_x = 0
            lead_x = 0
        elif (lead_x + vel_x) > (display_x - tile_size):
            vel_x = 0
            acc_x = 0
            lead_x = (display_x - tile_size)
        if (lead_y + vel_y) < 0:
            vel_y = 0
            acc_y = 0
            lead_y = 0
        elif (lead_y + vel_y) > (display_y - tile_size):
            vel_y = 0
            acc_y = 0
            lead_y = (display_y - tile_size)

        lead_x += vel_x
        lead_y += vel_y

        game_display.fill((255, 255, 255))
        pygame.draw.rect(game_display, (255, 0, 0), [lead_x, lead_y, tile_size, tile_size])
        print_to_screen(str(int(lead_x)) + ", " + str(int(lead_y)), (0, 0, 0), 0, 0)
        if vel_x > max_velocity or vel_y > max_velocity:
            print_to_screen(str(int(vel_x)) + ", " + str(int(vel_y)), (255, 0, 0), 0, 16)
        else:
            print_to_screen(str(int(vel_x)) + ", " + str(int(vel_y)), (0, 0, 0), 0, 16)
        if acc_x > acceleration or acc_y > acceleration:
            print_to_screen(str(int(acc_x)) + ", " + str(int(acc_y)), (255, 0, 0), 0, 32)
        else:
            print_to_screen(str(int(acc_x)) + ", " + str(int(acc_y)), (0, 0, 0), 0, 32)

        pygame.display.update()
        clock.tick(fps)

game_loop()
pygame.quit()
quit()

Here is the event data from the quick tap:

<Event(2-KeyDown {'unicode': '', 'key': 276, 'mod': 0, 'scancode': 75})>
<Event(3-KeyUp {'key': 276, 'mod': 0, 'scancode': 75})>

It shows a key up and key down event, not sure why the key up won't register.

2

2 Answers

2
votes

The problem is that you check if vel_x is less or greater than 0 in conjunction with the KEYUP events. Think about what happens when we're moving to the left (vel_x < 0) and quickly press and release K_RIGHT, then vel_x is still negative so if event.key == pygame.K_RIGHT and vel_x > 0: is False, the keyup event is ignored and acc_x is not reset to 0.

You should check if acc_x not vel_x is greater than 0.

if event.type == pygame.KEYUP:
    if event.key == pygame.K_LEFT and acc_x < 0:
        acc_x = 0
    if event.key == pygame.K_RIGHT and acc_x > 0:
        acc_x = 0

The same goes for the UP and DOWN keys and vel_y, acc_y.

0
votes

Remove that keyup block and add this to keydown block:

if event.type == pygame.KEYDOWN:
    if event.key == pygame.K_LEFT:
    acc_x = -acceleration
    acc_y = 0
if event.key == pygame.K_RIGHT:
    acc_x = acceleration
    acc_y = 0
if event.key == pygame.K_UP:
    acc_y = -acceleration
    acc_x = 0
if event.key == pygame.K_DOWN:
    acc_y = acceleration
    acc_x = 0