Pygame Keys Activating in Multiple Frames [duplicate] - python

This question already has answers here:
How to get keyboard input in pygame?
(11 answers)
What all things happens inside pygame when I press a key? When to use pygame.event==KEYDOWN
(1 answer)
Python PyGame press two buttons at the same time
(2 answers)
Closed 2 years ago.
In This program I want to have it register Pressed Keys But only once instead of multiple times. If you've played 2048, I am trying to make something like that.
I want to do this without slowing the Frame rate.
import pygame, sys
pygame.init()
WIDTH, HEIGHT = 450,450
win = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
class Tile:
def __init__(self, x, y):
self.x = x
self.y = y
def Move(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
print("Left")
elif keys[pygame.K_RIGHT]:
print("Right")
elif keys[pygame.K_UP]:
print("Up")
elif keys[pygame.K_DOWN]:
print("Down")
keys = pygame.key.get_pressed()
running = run = True
a = Tile(200, 500)
while run:
a.Move()
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
sys.exit()
pygame.display.update()

You have to use the key events, rather then pygame.key.get_pressed().
pygame.key.get_pressed() returns a list with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement
The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action or movement.
Get the list of events (event_list) in the main application loop and pass the list to the method Move of the class Tile. Handel the events in the method:
import pygame, sys
pygame.init()
WIDTH, HEIGHT = 450,450
win = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
class Tile:
def __init__(self, x, y):
self.x = x
self.y = y
def Move(self, event_list):
for event in event_list:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
print("Left")
elif event.key == pygame.K_RIGHT:
print("Right")
elif event.key == pygame.K_UP:
print("Up")
elif event.key == pygame.K_DOWN:
print("Down")
running = run = True
a = Tile(200, 500)
while run:
event_list = pygame.event.get()
a.Move(event_list)
clock.tick(60)
for event in event_list:
if event.type == pygame.QUIT:
run = False
sys.exit()
pygame.display.update()

Related

Throttle keypresses in pygame

I was recently coding a platformer game, when I faced into a problem: The user could spam space to jump infinitely.
Here is my code:
def __init__(self, pos):
super().__init__()
self.image = pygame.Surface((32, 64))
self.image.fill("green")
self.rect = self.image.get_rect(topleft = pos)
self.direction = pygame.math.Vector2(0, 0)
self.speed = speed
self.gravity = gravity
self.jump_height = jump_height
def get_input(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and keys[pygame.K_RIGHT]: pass
elif keys[pygame.K_LEFT]: self.direction.x = -1
elif keys[pygame.K_RIGHT]: self.direction.x = 1
else: self.direction.x = 0
if keys[pygame.K_SPACE]:
self.jump()
I tried several things, such as implementing a self.antispam boolean, set to True, at the __init__ method that turns into False when the space key is pressed, and then turns True again after the jump method, or turning the jump method into a coroutine to make an asyncio loop, but none of them worked.
To jump you have to use KEYDOWN instead of pygame.key.get_pressed(). Use pygame.key.get_pressed() to move but not to jump.
pygame.key.get_pressed() returns a sequence with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement.
The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action like jumping or spawning a bullet.
Make sure you only call pygame.get.event() once (see Faster version of 'pygame.event.get()'. Why are events being missed and why are the events delayed?):
def get_input(self, event_list):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and keys[pygame.K_RIGHT]: pass
elif keys[pygame.K_LEFT]: self.direction.x = -1
elif keys[pygame.K_RIGHT]: self.direction.x = 1
else: self.direction.x = 0
for event in event_list:
if event.type == pygame.KEYDONW and event.key == pygame.K_SPACE:
self.jump()
# application loop
while run:
# event loop
event_list = pygame.get.event()
for event in event_list:
if event.type == pygame.QUIT:
# [...]
player.get_input(event_list)
The other option is to state if SPACE was pressed in previous frames. So you can detect that the SPACE is hold down:
def __init__(self, pos):
super().__init__()
# [...]
self.space_was_pressed = False
def get_input(self):
keys = pygame.key.get_pressed()
# [...]
space_is_pressed = keys[pygame.K_SPACE]
if space_is_pressed and not self.space_was_pressed:
self.jump()
self.space_was_pressed = space_is_pressed
See also How to make a character jump in Pygame?

How to select individual sprites for movement by clicking them? [duplicate]

This question already has answers here:
Pygame mouse clicking detection
(4 answers)
Closed 1 year ago.
I am struggling to be able to move individual cars in my game. I want it so that when you click on a car and move the arrow keys, the selected car is the only one that moves. I have created a Point class and begun to write some code about that colliding with the cars to select them however I'm unsure what to do next. Can anyone help me to be able to select different cars by clicking on them and then be able to move that car alone.
Here is my code:
import pygame, random, time
pygame.init()
class Point(pygame.sprite.Sprite):
def __init__(self,x,y):
super().__init__()
self.rect=pygame.Rect(x,y,0,0)
class Car(pygame.sprite.Sprite):
def __init__(self,image,spawnx,spawny):
super().__init__()
self.image= pygame.Surface((100,100))
self.image = image
self.type = "car"
self.rect = self.image.get_rect()
self.rect.x = spawnx
self.rect.y = spawny
cars.add(self)
def move(self, dx, dy):
self.rect.x += dx
self.rect.y += dy
screen = pygame.display.set_mode((1150, 650))
background_image = pygame.image.load("carpark2.jpg")
cars=pygame.sprite.Group()
clock = pygame.time.Clock()
done = False
black=Car(pygame.image.load("blackcar.png").convert_alpha(),100,100)
blue=Car(pygame.image.load("bluecar.png").convert_alpha(),200,100)
yellow=Car(pygame.image.load("yellowcar.png").convert_alpha(),300,100)
purple=Car(pygame.image.load("purplecar.png").convert_alpha(),400,100)
orange=Car(pygame.image.load("orangecar.png").convert_alpha(),500,100)
white=Car(pygame.image.load("whitecar.png").convert_alpha(),600,100)
green=Car(pygame.image.load("greencar.png").convert_alpha(),700,100)
brown=Car(pygame.image.load("browncar.png").convert_alpha(),800,100)
pink=Car(pygame.image.load("pinkcar.png").convert_alpha(),900,100)
current_car=Car(pygame.image.load("redcar.png").convert_alpha(),100,400)
while done==False:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
x,y = event.pos
for car in pygame.sprite.spritecollide(Point(x,y,), cars, False):
current_car.deselect()
current_car = car.select()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
current_car.move(-50,0)
if event.key == pygame.K_RIGHT:
current_car.move(50,0)
if event.key == pygame.K_UP:
black.move(0,-50)
if event.key == pygame.K_DOWN:
black.move(0,50)
screen.blit(background_image, [0, 0])
cars.draw(screen)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.display.flip()
pygame.quit()
The pygame.Rect object needs to have a size of at least 1x1:
self.rect=pygame.Rect(x,y,0,0)
self.rect=pygame.Rect(x, y, 1, 1)
Anyway, I recommend to use pygame.Rect.collidepoint instead of pygame.sprite.spritecollide.
You don't need a select and deselect method at all. Just set current_car = car:
for car in cars:
if car.rect.collidepoint(x, y):
current_car = car
Application loop:
while done==False:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.MOUSEBUTTONDOWN:
x,y = event.pos
for car in cars:
if car.rect.collidepoint(x, y):
current_car = car
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
current_car.move(-50,0)
if event.key == pygame.K_RIGHT:
current_car.move(50,0)
if event.key == pygame.K_UP:
current_car.move(0,-50)
if event.key == pygame.K_DOWN:
current_car.move(0,50)
screen.blit(background_image, [0, 0])
cars.draw(screen)
pygame.display.flip()

Moving snake in pygame [duplicate]

This question already has an answer here:
Faster version of 'pygame.event.get()'. Why are events being missed and why are the events delayed?
(1 answer)
Closed 2 years ago.
I just started learning pygame, and I'm working on a snake game. However, I can't seem to turn the snake and I can't find the issue. I think the problem is in the "move_snake" method inside the snake class, but I really can't find the problem. I'm generally new to python and objected-oriented programming as well, so that may be the reason.
import pygame, sys
import random
pygame.init()
clock = pygame.time.Clock()
screen_width = 600
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Snake Game")
bgColor = "grey12"
lightGrey = (200, 200, 200)
random_x = random.randint(0, screen_width - 20)
random_y = random.randint(0, screen_height - 20)
class FOOD:
def __init__(self):
self.food_x = random_x
self.food_y = random_y
self.food_width = 20
self.food_height = 20
self.food_rect = pygame.Rect(self.food_x, self.food_y, self.food_width, self.food_height)
def draw_food(self):
pygame.draw.rect(screen, lightGrey, self.food_rect)
food = FOOD()
class SNAKE(FOOD):
def __init__(self):
super().__init__()
self.snake_width = 20
self.snake_height = 20
self.snake_x = screen_width/2 - self.snake_width
self.snake_y = screen_height/2 - self.snake_height
self.snake_rect = pygame.Rect(self.snake_x, self.snake_y, self.snake_width, self.snake_height)
self.move = [0, -1]
self.snake_speed = 5
def draw_snake(self):
pygame.draw.rect(screen, lightGrey, self.snake_rect)
def move_snake(self):
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_UP:
self.move = [0, -1]
if e.key == pygame.K_DOWN:
self.move = [0, 1]
if e.key == pygame.K_LEFT:
self.move = [-1, 0]
if e.key == pygame.K_RIGHT:
self.move = [1, 0]
self.snake_x += self.move[0] * self.snake_speed
self.snake_y += self.move[1] * self.snake_speed
self.snake_rect.topleft = (self.snake_x, self.snake_y)
snake = SNAKE()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(bgColor)
food.draw_food()
snake.draw_snake()
snake.move_snake()
pygame.display.flip()
clock.tick(60)
The issue are the multiple calls to pygame.event.get().
pygame.event.get() get all the messages and remove them from the queue:
This will get all the messages and remove them from the queue. [...]
If pygame.event.get() is called in multiple event loops, only one loop receives the events, but never all loops receive all events. As a result, some events appear to be missed.
Get the events once and use them in multiple loops or pass the list or events to functions and methods where they are handled:
class SNAKE(FOOD):
# [...]
def move_snake(self, event_list):
for e in event_list:
if e.type == pygame.KEYDOWN:
# [...]
while True:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# [...]
snake.move_snake(event_list)

keep holding a key and release a other [duplicate]

This question already has answers here:
Pygame: key.get_pressed() does not coincide with the event queue
(4 answers)
Closed 3 years ago.
i'm actually creating my first game with pygame, but i encounter an issue :
When i'm holding 2 button, everything work perfectly , but as soon as i release one of them , pygame.event.get() return a empty list even if i keep holding the other one.
Because of that issue , my character can't jump forward.
thx for help !
Using python Windows 10 and pygame 1.9.6
in the "character" class :
def move_right(self):
self.speedx += 3
self.sprite = "stand_r"
def move_left(self):
self.speedx -= 3
self.sprite = "stand_l"
def speed_effect(self):
self.posx += self.speedx
self.posy -= self.speedy
#speed limitation :
if self.speedx > 10 :
self.speedx = 10
if self.speedx < -10 :
self.speedx = -10
before main loop :
pygame.key.set_repeat(100,30)
pygame.time.Clock().tick(30)
in the main loop :
for event in pygame.event.get():
keys = pygame.key.get_pressed()
if event.type == QUIT:
continuer = 0
if event.type == KEYDOWN:
if keys[pygame.K_d] or event.key == K_d:
character.move_right()
if keys[pygame.K_a] or event.key == K_a:
character.move_left()
#jump
if character.grounded :
if keys[pygame.K_SPACE]:
character.speedy = 30
pygame.event.get() return a empty list after releasing one button.
The event loop is only executed, when an event occurs. An event occurs at the moment when a button is pressed and a 2nd time when the button is released. When the button is hold no event happens, and the event loop is not executed.
The movement of the mouse would be an event which causes the event loop to be executed.
Use pygame.key.get_pressed() in the main loop (outside the event loop), to get the state of the keys at any time and to perform the movement of the character. The states which are given by pygame.key.get_pressed() are synchronized when pygame.event.get() is called.
for event in pygame.event.get():
keys = pygame.key.get_pressed()
if event.type == QUIT:
continuer = 0
if event.type == KEYDOWN:
if character.grounded :
if event.key == pygame.K_SPACE:
character.speedy = 30
keys = pygame.key.get_pressed()
if keys[pygame.K_d]:
character.move_right()
if keys[pygame.K_a]:
character.move_left()

My game in python/pygame is not responding when using pygame.key.get_pressed()

So my game was working fine but the sprite would not handle repeated key presses. I thought i fixed it but now i can't do anything once i get passed the start screen. The code where i think the problem is is under playgame(). If anyone knows that would be great!
import pygame
from Classes import PlaneClass
pygame.init()
import sys,random,os
from pygame.locals import*
#import pdb
menuscreen=pygame.display.set_mode((640, 480))#FULLSCREEN
def highscores():
WHITE=(255,255,255)
global menuscreen
highscoresFile= open('data/highscores.txt','r')
filecontentstoread=''
for line in highscoresFile:
filecontentstoread+=str(line)+'\n'
menuscreen.fill(WHITE)
my_font = pygame.font.SysFont('Courier', 20)
the_text = my_font.render(filecontentstoread, True, (0,0,0))
menuscreen.blit(the_text, (20, 40))
pygame.display.update()
def playgame():
BLACK=(0,0,0)#Define the color black, later used as backgound
plane_x=0 #Define the planes x and y coordinates
plane_y=0
gamescreen=pygame.display.set_mode((640, 480))#FULLSCREEN
gamescreen.fill(BLACK) #Fill screen with black, as background
plane_img=pygame.image.load('data/plane.png')
plane=PlaneClass(plane_x,plane_y,plane_img)
plane.move(plane_x,plane_y)
gamerunning=True
while gamerunning==True:
#Move plane according to keyboard input
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
plane_x -=1
plane.move(plane_x,plane_y)
if keys[K_RIGHT]:
plane_x +=1
plane.move(plane_x,plane_y)
if keys[K_UP]:
plane_y -=1
plane.move(plane_x,plane_y)
if keys[K_DOWN]:
plane_y+=1
plane.move(plane_x,plane_y)
#gamescreen.fill(BLACK)
clock=pygame.time.Clock()
clock.tick(30)
def menu_screen_options():
menuvalue=main_menu()
laserstartup=pygame.mixer.Sound('data/laserstartup.wav')
laserstartup.play()
if menuvalue==0:
playgame()
if menuvalue==1:
highscores()
if menuvalue==2:
credits()
if menuvalue==3:
pygame.quit()
sys.exit()
def main_menu():
menuclock=pygame.time.Clock()
clock.tick(30)
pygame.display.set_caption('Dog Fight')
#pygame.mouse.set_visible(False)
WHITE=(255,255,255)
GREEN=(0,255,0)
BLUE=(0,0,255)
background=pygame.image.load('data/background.png')
lasersound=pygame.mixer.Sound('data/lasershot.wav')
arrow=pygame.image.load('data/arrow.png')
arrowpos = { 0 : (140,147) , 1:(140,210) , 2:(140,270) , 3 :(140,330) }
menu=True
counter = 0
menuscreen.blit(arrow,arrowpos[counter])
menuscreen.blit(background,(0,0))
pygame.display.update()
while menu == True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
menu = False
if event.key == K_UP:
if counter > 0:
counter -= 1
if event.key == K_DOWN:
if counter < 3:
counter += 1
if event.key == K_RETURN:
return counter
menuscreen.fill(WHITE)
menuscreen.blit(background,(0,0))
menuscreen.blit(arrow,arrowpos[counter])
pygame.display.update()
menu_screen_options()
Also, i dont know if this will help or not but the code for the sprite class is.
import pygame
class PlaneClass(pygame.sprite.Sprite):
# -- Methods
# Constructor function
def __init__(self,x,y,sprite_image):
self.image=sprite_image
self.x=x
self.y=y
pygame.sprite.Sprite.__init__(self)
def move(self,new_x,new_y):
screen=pygame.display.get_surface()
self.new_x=new_x
self.new_y=new_y
screen.blit(self.image,(new_x,new_y))
pygame.display.update()
You have that get_pressed call within a while True loop, so you never get back to the main event loop (the bit with for event in pygame.event.get():). This prevents your program from handling more events, which means that your program locks up.
Instead, you need to wait for a key event in your main event loop and only call get_pressed when you know there's a keypress to handle.
Note, you are using keydown polling. You might want keypress events instead. See: https://stackoverflow.com/a/11918084/341744

Categories