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'm working on a game where pressing space does something, but a lot of the time, the space key is not registered. I have a main function and I run a while loop where at the end, I have this:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
(do this)
if event.type == pygame.QUIT:
pygame.quit()
quit()
break
else:
pass
(Before this, I have a few hundred lines of code)
But I find that a lot of the time, nothing happens when I press space and it takes a lot of tries (usually 7-10) before the game responds to the spacebar. I have tried removing time.sleep(0.05) after pygame.display.update which has helped a bit. I have also tried making this for loop run more often through my while loop, but it still takes many tries before the game responds to the keypress. What am I doing wrong?
[...] I have also tried making this for loop run more often through my while loop [...]
No, don't do that. pygame.event.get() removes all the pending events from the queue. If you have more than one event loop and you invoke pygame.event.get() multiple times, only one of the loops will receive the events, all other loops will miss them.
Retrieve the list of events once per frame. You can then go through the list several times:
# application loop
while run:
# get the events
events = pygame.event.get()
# handel QUIT event
for event in events:
if evnet.type == pygame.QUIT:
run = false
# [...]
# handle some events
for event in events:
# [...]
# [...]
# handle some other events
for event in events:
# [...]
# [...]
Related
When running I am blit'ing some text onto my surface (WIN) and I want this to be shown in the game so I call pygame.display.update to update the display, this works perfectly fine until the loop has iterated around 75 times and after this the display stops updating. Code below
def dialogue(x,y,text):
global clock
global run
typing=True
tempstring=""
counter=0
switcher=0
for z in range(0,(len(text))):
clock.tick(15)
tempstring=text[0:z+1]
counter+=1
print(counter)
print("temp is "+tempstring)
writing=TITLE_FONT.render(tempstring, 1, (255,255,255),(0,0,0))
if x==-1 and y>-1:
pygame.display.update(WIN.blit(writing, (640-(writing.get_width()//2),y-(writing.get_height()//2))))
elif x>-1 and y==-1:
pygame.display.update(WIN.blit(writing, (x-(writing.get_width()//2),360-(writing.get_height()//2))))
elif x==-1 and y==-1:
pygame.display.update(WIN.blit(writing, (640-(writing.get_width()//2),360-(writing.get_height()//2))))
else:
pygame.display.update(WIN.blit(writing, (x-(writing.get_width()//2),y-(writing.get_height()//2))))
print(typing)
while True:
event = pygame.event.wait()
if event.type == pygame.QUIT:
run=False
pygame.quit()
break
if event.type == pygame.KEYDOWN:
break
I have tried just using pygame.display.update() at the end of the line of if statements too but this also fails.
On some systems PyGame doesn't work correctly if you don't get pygame.event from system - system may think that program hangs and it may even close it.
You may need to use pygame.even.get() (or similar functions) inside your loop.
In doc pygame.event you can find (but it is hidden in long description)
To prevent lost events, especially input events which signal a quit command, your program must handle events every frame (with pygame.event.get(), pygame.event.pump(), pygame.event.wait(), pygame.event.peek() or pygame.event.clear()) and process them.
Not handling events may cause your system to decide your program has locked up.
To keep pygame in sync with the system, you will need to call pygame.event.pump() internally process pygame event handlers to keep everything current.
print('Hello world!') I am making a game and I REALLY want to implement an effect that occurs when I hover my cursor over a button, which makes it slightly bigger, but the problem is, my python code just doesn't seem to notice any movement of my cursor, so here is a bit of my program:
def check_for_events():
for event in pygame.event.get():
if event.type == VIDEORESIZE:
#does a certain thing that changes the size of everything
#appropriately accordingly to the size of the window
def check_if_mouse_is_over_a_button():
print(0)
for event in pygame.event.get():
print(1)
if event.type == MOUSEMOTION:
print(2)
#some code to change size of the button
while True:
check_for_events()
check_if_mouse_is_over_a_button()
So when I run the code, I can see a slow stream of zeroes in the command prompt, which is to be expected, but here is the trick! When I move my mouse inside of the window, nor do I see 1 nor 2, instead I only see 1 printed when I resize the window! I am really confused, as I used this command before and it worked perfectly, but now it just doesn't! And just in case anyone asks, yes I tried to research into this, but found nothing and I see a lot of people write pygame.MOUSEMOTION instead of just MOUSEMOTION, so I don't know if the pygame. part is necessary, but it worked without it, and adding it changes nothing
pygame.event.get() get all the messages and remove them from the queue. See the documentation:
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 per frame and use them in multiple loops or pass the list of events to functions and methods where they are handled:
def check_for_events(event_list):
for event in event_list:
if event.type == VIDEORESIZE:
#does a certain thing that changes the size of everything
#appropriately accordingly to the size of the window
def check_if_mouse_is_over_a_button(event_list):
print(0)
for event in event_list:
print(1)
if event.type == MOUSEMOTION:
print(2)
#some code to change size of the button
while True:
event_list = pygame.event.get()
check_for_events(event_list)
check_if_mouse_is_over_a_button(event_list)
In my program made with pygame, I have an intro sequence that blits a total of 3 text boxes to the screen timed apart from each other, so one appears, then the next, then the one after that. I have no event polling during this time, as I want the whole sequence to play for it's total of probably around 10 seconds. However, after this sequence plays, I have a 'Continue' text that appears in the bottom right to show the user to press Return to continue to the next slide. This is where I then have my event handler like so:
exited = False
while not exited:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exited = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
exited = True
However, if the user hits Return at any time in this sequence, once the control flow hits the event handler, it just exits immediately through the pygame.KEYDOWN branch. How should I go about fixing this?
I have solved this by actually just adding
for event in pygame.event.get():
pass
before my next pygame.event.get() loop. The first one consumes the other events that happened in the sequence, so that the second loop will only handle input given after the sequence plays.
I am having trouble trying to grasp a couple of things inside the py game code, firstly why are there two types of quit or are these the same? for example:
pygame.QUIT
pygame.quit()
Also I can't grasp this small piece code fully:
for event in pygame.event.get():
if event.type == pygame.QUIT:
I understand the first line of code, but I don't understand event.type?, is .type a function inside of pygame or python in general? what does it do?
pygame.QUIT
Is an enumeration for an input that signals for the program to quit
pygame.quit()
Is a function call to unload pygame modules. According to the docs it won't actually quit the game:
http://www.pygame.org/docs/ref/pygame.html#pygame.quit
You could use sys.exit(0) for that.
This loop here:
for event in pygame.event.get():
if event.type == pygame.QUIT:
Checks each event, if one of them has the value pygame.QUIT then some exit code should follow.
pygame.QUIT is simply a constant, while pygame.quit() is a function within the pygame module that uninitializes it. The fragment of code:
for event in pygame.event.get():
if event.type == pygame.QUIT:
...
is part of the typical control loop of a pygame program. The method pygame.event.get() returns the next event. If the type of event (an attribute of the event object) is pygame.QUIT then the application should do what is necessary to exit, including possibly calling pygame.quit().
The manual for pygame.quit() says:
Uninitialize all pygame modules that have previously been initialized. When the Python interpreter shuts down, this method is called regardless, so your program should not need it, except when it wants to terminate its pygame resources and continue.
So you should normally call this method yourself.
pygame.QUIT is a so called event type. Pygame uses events to tell you something happened. The code checks each events in the queue and checks if a QUIT event happened.
event.type is something from pygame, and it tells you what type of event it is. By comparing it to pygame.QUIT, you can check if the quit() method has been called, and take steps to shutdown the game.
Here's my check_for_pause() function:
#Check if the user is trying to pause the game
def check_for_pause():
keys=pygame.key.get_pressed() #Get status of all keys
if keys[K_SPACE]: #The space bar is held down
global paused #Make global so it can be edited
if paused==True: #It was paused, so unpause it
paused=False
elif paused==False: #It was playing, so pause it
paused=True
#Don't let the main loop continue until the space bar has been released again, otherwise the variable will flicker between True and False where the loop runs so fast!
space_bar_pressed=keys[K_SPACE]
while space_bar_pressed: #Repeat this loop until space_bar_pressed is False
keys=pygame.key.get_pressed()
if not keys[K_SPACE]: #Space bar has been released so set space_bar_pressed to False
space_bar_pressed=False
however this keeps making my program become unresponsive whenever I try to pause it! Basically, I want the variable "paused" to be either True or False. When the space bar is pressed, it should change to whichever one it isn't currently. Because I'm using check_for_pause() in another never-ending loop, I need to make it so that the function only stops executing when the space bar is released, otherwise if the user holds the space bar for more than a split second it will keep switching between True and False.
Any ideas why my program becomes unresponsive when I run this? I know it's to do with the bit that waits until the space bar's been released because when I remove that bit of the code then my program runs fine (but obviously the pause feature then doesn't work).
You have a main loop that probably looks something like this...
while True:
check_for_pause()
# Update the game
# Draw the game
When you check for pause, and the space key is pressed down, paused gets set to True. Then, you have this loop...
space_bar_pressed=keys[K_SPACE]
while space_bar_pressed: #Repeat this loop until space_bar_pressed is False
keys=pygame.key.get_pressed()
if not keys[K_SPACE]: #Space bar has been released so set space_bar_pressed to False
space_bar_pressed=False
The problem with this loop is that you assume that pygame.key.get_pressed() will continue to return up-to-date information. However, looking at the pygame source code, it appears that it uses SDL_GetKeyState, which says as part of its documentation..
Note: Use SDL_PumpEvents to update the state array.
In other words, repeatedly calling pygame.key.get_pressed() will NOT give you the updated key state if you are not additionally calling something like pygame.event.pump(), which actually updates pygame with the new key states. So, you could quickly fix this by introducing that pump function into the loop, as currently it is just running forever.
That being said: DON'T DO THIS. If you do it this way, your game will not be able to do ANYTHING when paused: this includes showing a "paused" screen, continuing to play the music in the background, etc. What you want to do instead is keep track of if the game is paused, and if so, change how the game is updating.
In the part of your game where you update things, some items should only happen if the game is paused. Something like...
paused = False
while True:
# This function should just return True or False, not have a loop inside of it.
paused = check_for_paused()
if not paused:
# This function moves your enemies, do physics, etc.
update_game()
draw_game()
In this case, the main loop will still happen, the game will continue to be drawn, and the input will continue to be handled. However, the enemies and players will not be moving, therefore the game can be said to be "paused".
Finally, there is also the fact that you're relying on get_key_pressed(), which you probably don't want to do. See this other similar answer I've given for reasons why you should be instead using the event queue.
You should never stop running your game loop in a game even if it paused. Also pauses are generally handled through events. For example look at this code:
import pygame, sys
from pygame.locals import *
pygame.init()
pygame.display.set_mode((400,400))
paused = False # global
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_SPACE:
paused = not paused
if paused:
continue # skip this iteration if paused
# add your game code here
print 'game code running'
In the above code, I toggle paused every time I press spacebar. If you want to pause only while holding spacebar do this:
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type in (KEYDOWN, KEYUP): # checks membership in tuple
if event.key == K_SPACE:
paused = not paused
if paused:
continue # skip this iteration if paused
# add your game code here
print 'game code running'
As a general note, you should always be handling events from your event queue or Pygame will just cry and whine and do nothing (be unresponsive). Thus, never stop spinning on your game loop even if you have paused the game.
EDIT: Alternatively, as abarnert pointed out in the comments you can do a trick with equality comparisons to assure that you never get conflicts between KEYDOWN and KEYUP events:
paused = event.type == KEYDOWN
This way you won't have "syncing problems" where the code accidentally sets paused to True whenever you actually release the spacebar. This can happen if 2 KEYDOWN events happen in a row or if 2 KEYUP events happen in a row (instead of a smooth alternating sequence like KEYDOWN, KEYUP, KEYDOWN, KEYUP, etc.). It's best to not assume that all event queues feed events that are 100 percent accurate.