I am designing a game in Python and would like to know how to make an efficient timer that can run along side my game.
Note: I am using Pygame.
I currently have a timer like so:
import time
seconds = 60
def start_timer():
global seconds
while seconds > 0:
print seconds
seconds -= 1
time.sleep(1)
However, when run in my main game function my game hangs because of the timer.sleep
.
def main(self):
Timer.start_timer()
I'm pretty sure my issue has something to do with not using threading, although I'm not 100% sure. Can somebody help explain to me what the proper solution is?
If you're using PyGame, it has its own time functionality that you should be using.
If you're using an event-loop or hybrid design, where you loop over pygame.event.get()
or similar and call "event handlers" for different events like mouse-click or key-down, you can use set_timer
to create an event that fires every second. For example:
TIMER1_EVENT = pygame.USEREVENT + 1
def start_timer1():
global seconds
seconds = 60
pygame.time.set_timer(TIMER1_EVENT, 1000)
def on_timer1():
global seconds
print seconds
seconds -= 1
if seconds < 0:
pygame.time.set_timer(TIMER1_EVENT, 0)
# elsewhere, inside your main event loop
elif event.type == TIMER1_EVENT:
on_timer1()
There are very simple example programs linked in the docs for each function; if you look at the examples for set_timer
you'll see how easy it is to use.
If you're using a pure frame loop instead, you're presumably already calling Clock.tick
or similar, and you're going to have to use the return value from that to count down milliseconds since the last time and decide whether you've passed another integral number of seconds and when you're passed 0.
If you're using PyGame, or some other graphical library, and have built your game around an event loop or frame-rate loop, you want to use the functions that come with that library. If you're got an event loop, you can ask it to schedule an event to fire in 60 seconds. If you're got a frame-rate loop, you may be able to do the same thing, or need to check the remaining time once/frame.
But if you're not doing that, then yes, threading is the answer.
For simple timers, the simplest solution is the Timer
class. However, this doesn't automatically do repeating timers, so you have to add that manually. For example, you can start a 1-second timer, and have it start another 1-second timer when it expires:
def start_timer():
global seconds
if seconds > 0:
print seconds
seconds -= 1
threading.Timer(1, start_timer).start()
seconds = 60
threading.Timer(1, start_timer).start()
However, it's usually a lot easier to use a timer that knows how to do repeats for you. You can build one yourself (notice that the threading
docs contain a link to the source, and it's not hard to figure out how to add logic that makes it fire every interval
seconds for count
times, instead of just once). Or you can go search PyPI and find dozens of people who've already implemented that for you.
Also keep in mind that once you use threads, you have to deal with threading issues. If you're using a GUI library, they often won't let you call GUI methods from a background thread. If you're accessing a variable that's shared between two threads, you often need a Lock
or other synchronization object to protect it. And so on.