Pygame: How do I get KEYDOWN to input only once? - python

Hi I'm creating a game where I have multiple stages. I want to make it so that every time the user presses the key a, the next stage will trigger. Here is a sample of my code.
gameStage = 0 ## outside while loop
##INSIDE whileloop
if gameStage == 0:
##insert drawings,music, etc
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
gameStage += 1
if gameStage == 1:
##insert drawings,music, etc
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
gameStage += 1
my problem is that when the user presses the a key, a will be input more than once depending how long the key is held. Therefore, it will just skip all the way to my last stage. How do I make it so that the gameStage is +=1 only when the key has been pressed AND lifted? Please tell me if I'm being unclear. Appreciate any help. Thanks.

You could use the pygame.KEYUP event.
e.g.
if event.type == pygame.KEYUP:
But you should not be getting repeated KEYDOWN messages, unless you have called pygame.key.set_repeat and set a non zero repeat value.
The fact that you get the repeated increments of GameStage even when you capture only KEYUP messages would indicate that there is some other issue in your code.

When keyboard buttons are pressed or released a pygame.KEDOWN or pygame.KEYUP event appears only ones on the event queue1
As you did, you need to set a global variable GameStage, which indicates the current game state:
GameStage = 0 ## outside while loop
After this we run our main game loop and fetch all events form the even queue using the pygame.event.get() function, which reads and removes events from the queue:
while True:
#get all events from the event queue
for ev in pygame.event.get():
If the read event represents a key-down-event of a the program logic updates the GameStage variable, similar to a so-called state-machine:
if ev.type == pygame.KEYDOWN:
if ev.key == pygame.K_a:
if GameStage == 0:
GameStage += 1
#do something
elif GameStage == 1:
GameStage += 1
#do something great
# and so on ;)
The complete program block looks like this:
#global variable GameStage for state-machine
GameStage = 0
#main game loop
while True:
#get all events from the event queue
for ev in pygame.event.get():
if ev.type == pygame.KEYDOWN:
if ev.key == pygame.K_a:
if GameStage == 0:
GameStage += 1
#do something
elif GameStage == 1:
GameStage += 1
#do something great
elif GameStage == 2:
GameStage += 1
#do something great again
elif GameStage == 3:
#this is the last stage, so you cloud go back to stage #0
GameStage = 0
#for debugging print current GameStage
print(GameStage)
Only when you press -- no matter how long you hold down -- a the GameStage will be updated only once.
Hope this helps :)
1 As #sloth noted, this can be changed be calling pygame.key.set_repeat(), which will generate multiple pygame.KEYDOWN events when keys are held down.

You can either use key up event, as suggested above, or stop reacting to keydown event for a few ticks after it was issued.

Related

pygame.key.stop_text_input (and may be SDL?) [duplicate]

So I have this code that looks after the user inputs for a pac-man style game.
def receiving_inputs(self):
while True:
events = pg.event.get()
for event in events:
if event.type == pg.KEYDOWN:
if event.key == pg.K_UP:
self.move = 'n'
elif event.key == pg.K_RIGHT:
self.move = 'e'
elif event.key == pg.K_DOWN:
self.move = 's'
elif event.key == pg.K_LEFT:
self.move = 'w'
time.sleep(1/60)
threading.Thread(target=self.receiving_inputs).start()
When I press any keys on my keyboard I do not get any events, however, moving the mouse around will return an event using this code.
The annoying thing is that this exact code works perfectly when not in a thread. i.e when in the program's main loop.
Just fyi I want to use a thread here to minimize the number of times pygame doesn't register a key press (which I'm assuming is due to other things in the mainloop).
Thanks in advance.
You don't get any events at all, because you have to get the events in the main thread.
See the documentation of pygame.event:
[...] The event subsystem should be called from the main thread.
It is only possible to post events from other thread, but the event queue has to be handled in the main thread.

pygame.event.get() not returning any events when inside a thread

So I have this code that looks after the user inputs for a pac-man style game.
def receiving_inputs(self):
while True:
events = pg.event.get()
for event in events:
if event.type == pg.KEYDOWN:
if event.key == pg.K_UP:
self.move = 'n'
elif event.key == pg.K_RIGHT:
self.move = 'e'
elif event.key == pg.K_DOWN:
self.move = 's'
elif event.key == pg.K_LEFT:
self.move = 'w'
time.sleep(1/60)
threading.Thread(target=self.receiving_inputs).start()
When I press any keys on my keyboard I do not get any events, however, moving the mouse around will return an event using this code.
The annoying thing is that this exact code works perfectly when not in a thread. i.e when in the program's main loop.
Just fyi I want to use a thread here to minimize the number of times pygame doesn't register a key press (which I'm assuming is due to other things in the mainloop).
Thanks in advance.
You don't get any events at all, because you have to get the events in the main thread.
See the documentation of pygame.event:
[...] The event subsystem should be called from the main thread.
It is only possible to post events from other thread, but the event queue has to be handled in the main thread.

pygame mouse button down not working

I am making this multiple choice program, but I need the mouse event to only work after enter is pressed.
Here is my code:
for event in pygame.event.get(): # If user did something
if event.type == pygame.QUIT: # If user clicked close
done = True
elif event.type == pygame.KEYDOWN: # If user pressed a key
if event.key == pygame.K_RETURN: # If user pressed enter
# Makes the start screen go away
enter_pressed = True
# Increments question_number
question_number += 1
# Where I Draw the question screen
Then down below I have this:
for event in pygame.event.get(): # If user did something
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
print("Derp")
Derp won't print when I press left mouse button.
However, when I have it indented like this:
for event in pygame.event.get(): # If user did something
if event.type == pygame.QUIT: # If user clicked close
done = True
elif event.type == pygame.KEYDOWN: # If user pressed a key
if event.key == pygame.K_RETURN: # If user pressed enter
# Makes the start screen go away
enter_pressed = True
# Increments question_number
question_number += 1
# Where I Draw the question screen
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
print("Derp")
Derp does print when I press left mouse button
You could use a boolean indicating wheter or not enter was pressed.
for event in pygame.event.get(): # If user did something
enter_pressed = False
if event.type == pygame.QUIT: # If user clicked close
done = True
elif event.type == pygame.KEYDOWN: # If user pressed a key
if event.key == pygame.K_RETURN: # If user pressed enter
# Makes the start screen go away
enter_pressed = True
# Increments question_number
question_number += 1
# Where I Draw the question screen
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and enter_pressed:
print("Derp")
I think that the problem is that you are iterating over all events, and inside that loop you are iterating over all events (again), and that causes some problems
It is hard to tell from what you said, but I have a couple of recommendations for you. First, make sure that the for loop with derp isn't in any if statements you don't want it to be in. Second, make sure you call pygame.event.get() once per game loop or the code will only give you events that happened between the two calls, which will not be all of them. If neither of these things work, try posting the whole code.

Pygame - Trying to Toggle a BOOL when a key is pressed, not while pressed

I'm trying to make BOOL toggle when a key is pressed not while since that just makes it toggle off and on (true and false) each frame if the key is held down.
Here's my little toggle code
if key[pygame.K_z] and robsHouse.pressed == False:
robsHouse.pressed = True
elif key[pygame.K_z] and robsHouse.pressed == True:
robsHouse.pressed = False
Any ideas would be helpful, thanks.
So, this Boolean will change when ever a button is pressed, correct?
Just use the KEYUP event and reverse the variable:
if event == KEYUP:
if event.key == DESIRED_KEY:
boolVar = not boolVar
You need to toggle on KEYDOWN and KEYUP events.
The reason your code is toggling robsHouse.pressed every frame while you hold the key down is because the key[pygame.K_z] condition will be satisfied on each loop iteration while the key is held down, and then the secondary conditions will cause each other to be satisfied on alternating frames.
Without changing the way you are retrieving key data, you could do something like this:
if key[pygame.K_z]:
robsHouse.pressed = True
else:
robsHouse.pressed = False
Another way is to find KEYUP and KEYDOWN events from pygame's event module, rather than getting key data from the key module. That could be done like this:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_z:
robsHouse.pressed = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_z:
robsHouse.pressed = False
Personally, I prefer the second approach in most cases. You are going to need to process events from pygame's event module anyway, otherwise the queue will fill up and your program will become unresponsive (this is mentioned in the pygame event module documentation).

Is it me or is pygame.key.get_pressed() not working?

okay, so I am making a basic space-ship game.
I can't get rotation to work because it scrambles the bitmap, but that's for another question.Should I even use a gif? any other filetype suggestions?
back to the actual point here, so:
k = pygame.key.get_pressed()
yeah, self explanatory. this doesn't work, as it returns each key as pressed.
so, somewhere else:
d = k[pygame.K_d]
and another line:
print d
and another:
if d:
So, k returns as each key on the keyboard pressed.
d returns 0 indefinitely, whether or not d is pressed.
d is always 0.
the statement about d therefore is never true.
Why is this happening?
You might be confused by what get_pressed() is actually doing. From the docs:
Returns a
sequence of boolean values representing the state of every key on the
keyboard. Use the key constant values to index the array. A True value
means the that button is pressed.
Getting the list of pushed buttons with this function is not the
proper way to handle text entry from the user. You have no way to know
the order of keys pressed, and rapidly pushed keys can be completely
unnoticed between two calls to pygame.key.get_pressed(). There is also
no way to translate these pushed keys into a fully translated
character value. See the pygame.KEYDOWN events on the event queue for
this functionality.
In other words, when you call get_pressed(), you are getting a representation of the state of the keyboard at the time of get_pressed() being called.
For example, let's say one second into your game you call get_pressed(). You'll get back a structure that lists all of the keys on the keyboard and if they are pressed (they will all be false).
At two seconds into your game, you press a key. If you look at the same structure that you were looking at earlier, it will STILL say that everything is not pressed, because you're still looking at the state of the keyboard as it was a second ago. However, if you called get_pressed() again, you'd get back a new, updated structure, and this new structure should show that the key is pressed.
One way to solve this would be to do the following:
while True:
# Update Stuff
# Draw Stuff
state = pygame.key.get_pressed()
# Now check the keys
Now, you're getting up-to-date information on the keyboard.
One thing that should be noted is that using the functionality above, you could STILL potentially miss a keyboard press. If the update functionality took a long time, a key could potentially be pressed then unpressed in a small enough time that you wouldn't have called get_pressed() when the key was down.
If this might be a problem, you will probably want to use the event loop instead. Something like...
is_moving = False
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN and event.key == pygame.K_d:
is_moving = True
elif event.type == pygame.KEYUP and event.key == pygame.K_d:
is_moving = False
For mixing (1) event and (2) keystate inputs, it looks like
import pygame
from pygame.locals import *
done = False
while not done:
for event in pygame.event.get():
# any other key event input
if event.type == QUIT:
done = True
elif event.type == KEYDOWN:
if event.key == K_ESC:
done = True
elif event.key == K_F1:
print "hi world mode"
# get key current state
keys = pygame.key.get_pressed()
if keys[K_SPACE]:
#repeating fire while held
fire()
I like the same to be true for KEYDOWN and KEYUP,
You have to poll events. One way you can do it is
while not done:
for event in pygame.event.get():
# any other key event input
if event.type == QUIT:
done = True
elif event.type == KEYDOWN:
if event.key == K_ESC:
done = True
player.handle_event(event)
then in Player()
def handle_event(self, event):
if event.type == KEYDOWN:
if event.key == K_f: print 'Player.f pressed'

Categories