Search code examples
pythonpygamespriteblockkeydown

Pick up block using pygame.KEYDOWN


I am trying to use pygame.KEYDOWN so that when I press the key represented by pygame.K_q the block that the player has collided with can be dragged (pick up) to where the player moves as it is done using pygame.MOUSEBUTTONDOWN in this tutorial.

But pressing the q key to drag the block does not work...

This need of mine arose when trying to implement this functionality in another larger code. So I decided to get this other tutorial2 to set up my MWE. I looked to see if anyone had already asked about it but I just found relative but not exact questions that used a very different or very large code structure and I still haven't figured out why I can't get my MWE code to work. This should be a simple question and someone may have already posted this question, so if so let me know where there is a question already posted that clarifies my question.

My MWE:

import pygame
import random

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)

class Block(pygame.sprite.Sprite):
    """
    This class represents the block to be picked up.
    It derives from the "Sprite" class in Pygame.
    """

    def __init__(self, color, width, height):
        """ Constructor. Pass in the color of the block,
        and its x and y position. """

        # Call the parent class (Sprite) constructor
        super().__init__()

        # Create an image of the block, and fill it with a color.
        # This could also be an image loaded from the disk.
        self.image = pygame.Surface([width, height])
        self.image.fill(color)

        # Fetch the rectangle object that has the dimensions of the image
        # image.
        # Update the position of this object by setting the values
        # of rect.x and rect.y
        self.rect = self.image.get_rect()


class Player(pygame.sprite.Sprite):
    """ The class is the player-controlled sprite. """

    carry_block_list = []

    def __init__(self, x, y):
        """Constructor function"""
        # Call the parent's constructor
        super().__init__()

        # Set height, width
        self.image = pygame.Surface([15, 15])
        self.image.fill(RED)

        # Make our top-left corner the passed-in location.
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

    def update(self):

        diff_x = self.rect.x - 4
        diff_y = self.rect.y - 4

        # Loop through each block that we are carrying and adjust
        # it by the amount we moved.
        for block in self.carry_block_list:
            block.rect.x -= diff_x
            block.rect.y -= diff_y
            print("something")

# Call this function so the Pygame library can initialize itself
pygame.init()

# Create an 800x600 sized screen
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode([screen_width, screen_height])

# Set the title of the window
pygame.display.set_caption('Move Sprite With Keyboard')

# Create the player paddle object
player = Player(50, 50)
all_sprites_list = pygame.sprite.Group()
all_sprites_list.add(player)
block_list = pygame.sprite.Group()


for i in range(50):
    # This represents a block
    block = Block(BLACK, 20, 15)

    # Set a random location for the block
    block.rect.x = random.randrange(screen_width)
    block.rect.y = random.randrange(screen_height)

    # Add the block to the list of objects
    block_list.add(block)
    all_sprites_list.add(block)

clock = pygame.time.Clock()
done = False

while not done:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_q:
               print("pick up")
            # When the mouse button is pressed, see if we are in contact with
            # other sprites:

               blocks_hit_list = pygame.sprite.spritecollide(player, block_list, False)

            # Set the list of blocks we are in contact with as the list of
            # blocks being carried.

               player.carry_block_list = blocks_hit_list

            if event.key == pygame.K_l:
               print("let go")
            # When we let up on the mouse, set the list of blocks we are
            # carrying as empty.

               player.carry_block_list = []

            if event.key == pygame.K_LEFT:
                player.rect.x -= player.rect.width
            elif event.key == pygame.K_RIGHT:
                player.rect.x += player.rect.width
            elif event.key == pygame.K_UP:
                player.rect.y -= player.rect.height
            elif event.key == pygame.K_DOWN:
                player.rect.y += player.rect.height

    # -- Draw everything
    # Clear screen
    screen.fill(WHITE)

    # Draw sprites
    all_sprites_list.draw(screen)

    # Flip screen
    pygame.display.flip()

    # Pause
    clock.tick(40)

pygame.quit()

Solution

  • You move blocks in Player.update() but you never execute it

    First I tried to execute this function in every loop but finally I changed this function to run only when players is moved

    def update(self, diff_x, diff_y):
        # Loop through each block that we are carrying and adjust
        # it by the amount we moved.
        for block in self.carry_block_list:
            block.rect.x += diff_x
            block.rect.y += diff_y
    

    and

           if event.key == pygame.K_LEFT:
                player.rect.x -= player.rect.width
                player.update(-player.rect.width, 0)
    
            elif event.key == pygame.K_RIGHT:
                player.rect.x += player.rect.width
                player.update(player.rect.width, 0)
    
            elif event.key == pygame.K_UP:
                player.rect.y -= player.rect.height
                player.update(0, -player.rect.height)
    
            elif event.key == pygame.K_DOWN:
                player.rect.y += player.rect.height
                player.update(0, player.rect.height)
    

    Full code:

    import pygame
    import random
    
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    RED = (255, 0, 0)
    
    class Block(pygame.sprite.Sprite):
        """
        This class represents the block to be picked up.
        It derives from the "Sprite" class in Pygame.
        """
    
        def __init__(self, color, width, height):
            """ Constructor. Pass in the color of the block,
            and its x and y position. """
    
            # Call the parent class (Sprite) constructor
            super().__init__()
    
            # Create an image of the block, and fill it with a color.
            # This could also be an image loaded from the disk.
            self.image = pygame.Surface([width, height])
            self.image.fill(color)
    
            # Fetch the rectangle object that has the dimensions of the image
            # image.
            # Update the position of this object by setting the values
            # of rect.x and rect.y
            self.rect = self.image.get_rect()
    
    
    class Player(pygame.sprite.Sprite):
        """ The class is the player-controlled sprite. """
    
        carry_block_list = []
    
        def __init__(self, x, y):
            """Constructor function"""
            # Call the parent's constructor
            super().__init__()
    
            # Set height, width
            self.image = pygame.Surface([15, 15])
            self.image.fill(RED)
    
            # Make our top-left corner the passed-in location.
            self.rect = self.image.get_rect()
            self.rect.x = x
            self.rect.y = y
    
        def update(self, diff_x, diff_y):
            # Loop through each block that we are carrying and adjust
            # it by the amount we moved.
            for block in self.carry_block_list:
                block.rect.x += diff_x
                block.rect.y += diff_y
    
    
    # Call this function so the Pygame library can initialize itself
    pygame.init()
    
    # Create an 800x600 sized screen
    screen_width = 700
    screen_height = 400
    screen = pygame.display.set_mode([screen_width, screen_height])
    
    # Set the title of the window
    pygame.display.set_caption('Move Sprite With Keyboard')
    
    # Create the player paddle object
    player = Player(50, 50)
    all_sprites_list = pygame.sprite.Group()
    all_sprites_list.add(player)
    block_list = pygame.sprite.Group()
    
    
    for i in range(50):
        # This represents a block
        block = Block(BLACK, 20, 15)
    
        # Set a random location for the block
        block.rect.x = random.randrange(screen_width)
        block.rect.y = random.randrange(screen_height)
    
        # Add the block to the list of objects
        block_list.add(block)
        all_sprites_list.add(block)
    
    clock = pygame.time.Clock()
    done = False
    
    while not done:
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_p:
    
                # When the mouse button is pressed, see if we are in contact with
                # other sprites:
    
                   blocks_hit_list = pygame.sprite.spritecollide(player, block_list, False)
    
                # Set the list of blocks we are in contact with as the list of
                # blocks being carried.
    
                   player.carry_block_list = blocks_hit_list
    
                if event.key == pygame.K_l:
    
                # When we let up on the mouse, set the list of blocks we are
                # carrying as empty.
    
                   player.carry_block_list = []
    
                if event.key == pygame.K_LEFT:
                    player.rect.x -= player.rect.width
                    player.update(-player.rect.width, 0)
    
                elif event.key == pygame.K_RIGHT:
                    player.rect.x += player.rect.width
                    player.update(player.rect.width, 0)
    
                elif event.key == pygame.K_UP:
                    player.rect.y -= player.rect.height
                    player.update(0, -player.rect.height)
    
                elif event.key == pygame.K_DOWN:
                    player.rect.y += player.rect.height
                    player.update(0, player.rect.height)
    
    
    
        # -- Draw everything
        # Clear screen
        screen.fill(WHITE)
    
        # Draw sprites
        all_sprites_list.draw(screen)
    
        # Flip screen
        pygame.display.flip()
    
        # Pause
        clock.tick(40)
    
    pygame.quit()