konjecture
konjecture

Reputation: 31

Differentiate between key pressed just once and key held [pygame]

So in Pygame, I am trying to create this bat/ball game where the ball comes from the top of the screen (like a meteor) at various speeds, and the bat is near the bottom of the screen that can be moved left/right. Depending on the key pressed (direction the ball goes) and if there is a hit (i.e collision), the ball goes in a certain direction. For example, if you press the "d" key then the ball goes to the right side (provided there was a hit/collision).

Now everything seems to be working fine, however, what I actually want is the player timing the key press WHEN the ball is near the bat. At the moment, I can do that, but also if I keep holding the "d" key from much earlier, the ball still hits the bat. This is definitely not what I want, as it defeats the purpose of timing and having different speeds for the ball. I understand why this is happening as pygame.key.get_pressed() doesn't seem to differentiate between one key press and hold, and considers them one event. So, essentially, I would like to only let the collision happen if the key was pressed as the ball passed the bat and not while it was held from the very start. Here is the Ball class which contains the main code.

class Ball(pygame.sprite.Sprite):

    def __init__(self):

        pygame.sprite.Sprite.__init__(self)

        self.image = meteor_img
        self.image.set_colorkey(WHITE)
        self.rect = self.image.get_rect()
        self.radius = int(self.rect.width * .85 / 2)
        #pygame.draw.circle(self.image, RED, self.rect.center, self.radius)
        self.rect.x = WIDTH /2 + 10
        self.rect.y = HEIGHT - 770
        self.speedy = random.randint(7, 14)
        self.speedx = 0.3
        self.j = 0


    def update(self):
        if self.rect.y < HEIGHT - 450 and self.j!= 1:

            Ball.old_ball(self)
            self.rect.x -= self.speedx
            self.rect.y += self.speedy
            #print (self.rect.x, self.rect.y)
        elif self.j !=1:

            Ball.change_ball(self)

            self.rect.x += self.speedx
            self.rect.y += self.speedy
            #print (self.rect.x, self.rect.y)
        else:
            Ball.old_ball(self)
            self.rect.x += self.speedx
            self.rect.y += self.speedy
            #print (self.rect.y)

        shot_played = pygame.key.get_pressed()


        hits = pygame.sprite.spritecollide(player, mobs, False, pygame.sprite.collide_circle)
        #print (hits)

        if  hits and shot_played[pygame.K_e]:
            #print ("Perfect Timing")
            self.speedx = random.uniform(1, 5)
            self.speedy = -random.uniform(2, 4)
            self.j = 1
        elif hits and shot_played[pygame.K_d]:
            #print ("Perfect Timing")
            self.speedx = random.uniform(6, 10)
            self.speedy = -random.uniform(-2, 2)
            self.j = 1
        elif hits and shot_played[pygame.K_a]:         #Leg Side
            #print ("Perfect Timing")
            self.speedx = -random.uniform(6, 10)
            self.speedy = -random.uniform(-2, 2)
            self.j = 1

        if self.rect.top > HEIGHT + 10 or self.rect.left < -25 or self.rect.right > WIDTH + 20 or self.rect.y < 10:
            self.rect.x = WIDTH /2+10
            self.rect.y = HEIGHT - 770
            self.speedy = random.randint(7, 14)
            self.speedx = 0.3
            self.j = 0
            time.sleep(1)
            #self.speedy = random.randrange(1, 8)

    def change_ball(self):
        self.image = ball_pitched_img
        self.image.set_colorkey(WHITE)


    def old_ball(self):
        self.image = meteor_img
        self.image.set_colorkey(WHITE)

Upvotes: 1

Views: 1605

Answers (1)

skrx
skrx

Reputation: 20488

You could just use an event loop instead of the state checking. When you press a key, pygame adds a pygame.KEYDOWN event to the queue once and a pygame.KEYUP event when the key is released. Calling pygame.event.get() empties the queue and returns a list of events over which you can iterate with a for loop to handle one event after the other.

import pygame

pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()

done = False
while not done:
    # for each event in the event queue.
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_d:
                print('d pressed')
                # Check if the bat collides with the ball here.

    screen.fill((30, 30, 30))
    pygame.display.flip()
    clock.tick(60)

Upvotes: 1

Related Questions