tags:

views:

85

answers:

2

I'm working on a Tetris-clone type game and I'm wondering what would be the best way to handle the logic that moves the blocks down every N seconds. I initially did it the easy way:

pygame.time.set_timer(USEREVENT+1, 300)

This will of course add a PyGame event to the queue every 300 milliseconds. In the main loop I can test for this event, and drop the block down when it occurs. The problem with this approach is that the player can easily flood the queue with keyboard generated events. I noticed that player can essentially keep the block at the same height indefinitely by simply rapidly pressing the up-arrow which flips it around. It doesn't seem to matter that the code testing for timer event is evaluated before the test for keyboard events. Somehow keyboard always gets higher priority than the timer. Any idea how to prevent this from happening?

And if this is unavoidable quirk of pygame.time what would be a better way to handle the timed events?

  • Check elapsed time on each loop iteration and advance blocks if it is greater than a certain value?
  • Use sched or something similar running in a separate thread and signaling back via some shared resource?

Any ideas?

+3  A: 

Here's how a typical game loop works:

repeat forever:
    read all pending events
    if current time > last frame + frame time:
        last frame = last frame + frame time
        update game
    redraw screen

A timer that is supposed to fire every 300 ms but doesn't work if the user floods the game with events could be a broken timer. However, if you redraw the screen on every user event instead of redrawing the screen once you've processed all pending events, then your code is the problem.

Here's an example of a broken version of a game loop:

repeat forever:
    read the next pending event
    if current time > last frame + frame time:
        last frame = last frame + frame time
        update game
    redraw screen

It's broken because it tries to redraw the screen far too often. If the player presses two keys at the same time, they'll get separated into different frames, which is unnecessary.

The above loops assume that you want to draw to the screen as often as possible. This is very common, but you might want to draw to the screen less often. Your method of using events should be fine as long as you grab all pending events before redrawing the screen.

P.S. Do NOT use multiple threads to solve this problem. That would cause a huge mess.

Dietrich Epp
A: 

One correction to Dietrich Epp's answer - redraw screen should actually interpolate or extrapolate between two subsequent game states. This is needed to provide smooth experience to players.

In case of interpolation, as an interpolation factor you use time fraction that is "left" from updating (because it's less than frame time) divided by frame time to get result in [0..1] range.

This is also known as a canonical game loop

Tomasz Zielinski