Reputation: 86
I am using pygame's time.Clock
to run my game at a lower FPS and I noticed that my input seemed to take one extra frame to take effect. I did some debugging and realized that it was not a problem with pygame.event.get()
but rather with pygame.display.update()
. I've written a simple program to demonstrate the issue: (Explanation of program is below the code snippet)
import pygame, sys
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
colour = 1
key_press = False
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
screen.fill((0,255,0))
key_press = True
print("A key was pressed.")
if not key_press:
screen.fill((colour*255, colour*255, colour*255))
colour = not colour
else:
key_press = False
pygame.display.update()
clock.tick(0.5)
The program flashes the screen between black and white every two seconds, and whenever any key is pressed the screen will change to green and "A key was pressed"
will be printed to the console. However, when I run it, the text is printed at the correct time but the screen does not change to green until the next frame. This leads me to believe that pygame.display.update()
is not updating the screen when I am calling the function.
The problem seems to be with the delay immediately after calling pygame.display.update()
. If you change the FPS of the clock, the problem still persists. This also occurs if you delay with time.wait()
or pygame.time.delay()
instead of Clock.tick()
.
Another interesting thing that I've noticed is that if you reverse the order of the last two lines of code in my example (pygame.display.update()
and clock.tick(0.5)
), you might expect that the screen will change two clock cycles after a key is pressed instead of one. However, if I do this I get exactly the same effect as when they were in the other order.
I'm not sure if this is a bug or if something subtle is going on here that I just missed. I'm running Python 3.6.2 and using Pygame v1.9.3 on a macOS. Any help you can give would be greatly appreciated!
Upvotes: 4
Views: 2924
Reputation: 31
I was able to fix this glitch in one program by calling pygame.event.pump() (which does not clear the event queue, so no event-handling is lost) between the pygame.display.update() call and the clock.tick(FPS) call. Basically, my main game loop ended with:
pygame.display.update()
pygame.event.pump()
clock_object.tick(FPS_CONSTANT)
Upvotes: 2
Reputation: 20488
pygame.time.Clock.tick
source calls SDL_Delay
which lets the program sleep for a given time.
By passing 0.5 as the frame rate to clock.tick
, you're telling pygame to slow down so that only a half frame is processed per second. That actually means the game will only be updated every two seconds and it will be unresponsive during the rest of the time.
If you want to implement a timer in pygame, use one of the solutions here (pygame.time.get_ticks, set_timer, or use the delta time).
Upvotes: 1
Reputation: 86
I have found a way to eliminate the one-frame delay I was experiencing, although I still can't explain why pygame.display.update()
waits for the clock delay to update the screen. I figured I would post my solution here in case anyone else has the same issue.
I noticed that the display will update after pygame.event.get()
is run, so I set my loop to check for events at a faster frame rate. I still only want to run my game logic and update the screen at a lower frequency, so I just count how many times the loop has executed. Notice the variables frequency
, FPS
, and skip_frames
in my new example below:
import pygame, sys
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
colour = 1
key_press = False
frequency = 0.5 # How often the game logic is run
FPS = 30 # How often the user input is checked
skip_frames = FPS/frequency
count_frames = 0
while True:
# *** Check for user input every loop ***
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
key_press = True
# *** Only run game logic at given frequency ***
if count_frames >= skip_frames:
count_frames -= skip_frames
if key_press:
screen.fill((0,255,0))
print("A key was pressed.")
key_press = False
else:
screen.fill((colour*255, colour*255, colour*255))
colour = not colour
pygame.display.update()
count_frames += 1
clock.tick(FPS)
This is a bit of a work-around, but it gives the result I was looking for. The screen will flash at the given frequency, and it will change to green the very next flash after any key is pressed. If anyone can figure out why pygame.display.update()
waits for the clock delay to actually update the screen, I would be very interested to know.
Upvotes: 0