2
\$\begingroup\$

I'm having an issue when I have created a player class and want to move my sprite in between three lanes. I was able to get the sprite to move when I used pygame.get_pressed(), however, I only want the sprite to move in single large steps between three lanes, therefore one key press == moving say 50 pixels up and then no further movement.

Therefore I swapped the get_pressed code for a for event in pygame.event.get() so I could use the "KEYDOWN" method. I have got this to work in a separate project file by including it in the main while loop. However I believe this should be able to be included in the Player class and currently nothing happens.

Setup

import pygame
import get_image, player_class
from sys import exit
pygame.init()
screen = pygame.display.set_mode((960,480))
pygame.display.set_caption('Platform Test')
background_surface = pygame.image.load('assets/graphics/green.png').convert_alpha()
final_background = pygame.transform.scale(background_surface, (960,480))
background_rect = final_background.get_rect(topleft = (0,0))
clock = pygame.time.Clock()
sprite_sheet_image = pygame.image.load('assets/graphics/player.png').convert_alpha()
sprite_sheet = get_image.SpriteSheet(sprite_sheet_image)
GREEN_SCREEN_BACKGROUND = (32, 156, 0)
FPS = 60

Player Class

class Player(pygame.sprite.Sprite):
    global x_position, y_position
    x_position = 50
    y_position = 400
    def __init__(self):
        super().__init__()

        player_walk_1 = sprite_sheet.get_image(0, 24, 34, 3, GREEN_SCREEN_BACKGROUND)
        player_walk_2 = sprite_sheet.get_image(1, 24, 34, 3, GREEN_SCREEN_BACKGROUND)
        player_walk_3 = sprite_sheet.get_image(2, 24, 34, 3, GREEN_SCREEN_BACKGROUND)
        player_walk = [player_walk_1, player_walk_2, player_walk_3]
        player_walk_index = 0

        self.image = player_walk_2
        self.rect = self.image.get_rect(bottomleft = (x_position,y_position))

    def player_input(self):
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP:
                    self.rect.y -= 20
    def update(self):
        self.player_input()

Body

player = pygame.sprite.GroupSingle()
player.add(Player())


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

    player.update()
    screen.blit(final_background,background_rect)
    player.draw(screen)
    pygame.display.update()
    clock.tick(FPS)
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

The main issue is that you process all the events in two places (the first is in the main loop and the second is in the player), the first one ignores the KEYDOWN event, and the second one misses it because the first one discarded it. The code for event in pygame.event.get(): is responsible for this.

The first way you can patch this is by checking for the KEYDOWN event in the main loop and pass a flag to the Player.update() function:

(All code is untested.)

In Body:

while True:
    player_moves_up = False
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
            
        if event.type == pygame.KEYDOWN and event.key == pygame.K_UP:
            player_moves_up = True

    player.update(player_moves_up)
    screen.blit(final_background,background_rect)
    player.draw(screen)
    pygame.display.update()
    clock.tick(FPS)

In Player:

    # This function is no longer needed.
    # def player_input(self, player_moves_up):
    #     for event in pygame.event.get():
    #         if event.type == pygame.KEYDOWN:
    #             if event.key == pygame.K_UP:
    #                 self.rect.y -= 20
    
    def update(self, player_moves_up):
        if player_moves_up:
            self.rect.y -= 20

Alternatively you can have you player class monitor for key value changes ("rising edge"), and act upon it.

In Player:

class Player(pygame.sprite.Sprite):
    global x_position, y_position
    x_position = 50
    y_position = 400
    
    def __init__(self):
        super().__init__()

        player_walk_1 = sprite_sheet.get_image(0, 24, 34, 3, GREEN_SCREEN_BACKGROUND)
        player_walk_2 = sprite_sheet.get_image(1, 24, 34, 3, GREEN_SCREEN_BACKGROUND)
        player_walk_3 = sprite_sheet.get_image(2, 24, 34, 3, GREEN_SCREEN_BACKGROUND)
        player_walk = [player_walk_1, player_walk_2, player_walk_3]
        player_walk_index = 0

        self.image = player_walk_2
        self.rect = self.image.get_rect(bottomleft = (x_position,y_position))

        # This will track from frame to frame the state of the key
        self.keyboard_state_move_up_pressed = False 
        
        # This will allow you to check that the key has just been pressed this frame
        self.keyboard_state_move_up_just_pressed = False


    def player_input(self):
        # Reset the flag
        self.keyboard_state_move_up_just_pressed = False
        
        
        # Check if the key has just been pressed
        new_keyboard_state_move_up_pressed = pygame.key.get_pressed()[pygame.K_UP]
        if not self.keyboard_state_move_up_pressed and new_keyboard_state_move_up_pressed:
            self.keyboard_state_move_up_just_pressed = True
        
        # Store the key state for the next frame
        self.keyboard_state_move_up_pressed = new_keyboard_state_move_up_pressed
        
                    
    def update(self):
        self.player_input()
        
        # You can now react to it based on the updated input.
        if self.keyboard_state_move_up_just_pressed:
            self.rect.y -= 20

The first "patch" approach has the advantage that you know which keys are used and you don't have to track the state yourself, and it can make your class easier to test because you "inject" the values to it, instead of having it rely on singletons, while the "second" approach has the handling more localized where you need it and "pollutes" less of the rest of the code.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thank you, that worked great. I moddified your second suggestion slightly and then was able to duplicate this block of code for the down key as well, it is now working exactly how I was after! This made me think more about how the code was being executed as you are using the True/False boolians to stop that block of code repeating until the variable is reset. \$\endgroup\$
    – jacko_123
    Commented Jul 27, 2022 at 3:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .