3

I am writing a Tetris program with PyGame, and came across a funny problem.

Before I ask the question, here is the pseudo-code:

while True:
    # In this part, the human controls the block to go left, right, or speed down
    if a key is pressed and the block isnt touching the floor:
        if the key is K-left:
            move piece left one step
        if the key is K-right:
            move piece right one step
        if the key is K-down:
            move piece down one step


    # This part of the code makes the piece fall by itself
    if the block isnt touching the floor:
        move block down one step

    # This part makes the while loop wait 0.4 seconds so that the block does not move
    # down so quickly
    wait 0.4 seconds

The problem is that, because of the "wait 0.4 seconds" part of the code, the part that the human controls can only move every 0.4 seconds. I would like it so that the block moves as fast as the human can press the key, while at the same time, the block dropping every 0.4 seconds. How could I arrange the code so that it will do that? Thanks!

John
  • 143
  • 2
  • 7
  • You probably need to use multithreading. Welcome to a world of pain :( But maybe PyGame has some of this functionality built-in or something. That would make it easier. – R. Martinho Fernandes Apr 06 '11 at 15:12
  • Aha! I had a hunch it had something to do with something like that. I've never read up about it though, bring on the pain! Another question: Concurrency, multi-threading, and parallel programming. Are they the same? – John Apr 06 '11 at 15:15
  • @John: kind of. Just keep in mind that they have slightly different meanings and once in a while that makes a difference. But most of the time the terms are interchangeable. Multi-threading for example is a bit too specific: it implies threads and it implies multiple. Neither is a requirement in parallel programming. – R. Martinho Fernandes Apr 06 '11 at 15:20
  • http://stackoverflow.com/questions/4315292/concurrency-processes-vs-threads and http://stackoverflow.com/questions/1996648/concurrency-and-multithreading – nmichaels Apr 06 '11 at 15:21
  • 1
    But no, you don't need to multi-thread this, as answers given have shown. – nmichaels Apr 06 '11 at 15:23
  • I don't think you need multi-threading for that. With multi-threading you will have the same problem since your display won't get updated more often. Even if you had a thread listening for keystrokes, your shape would not rotate before the next frame draw. – Martin Apr 06 '11 at 15:24
  • @Martin: with another thread you *can* update the display more often. And of course you cannot see a shape rotate before you draw the next frame. I don't understand your point. – R. Martinho Fernandes Apr 06 '11 at 15:28
  • @Martinho Fernandes: The problem here is that John is waiting 0.4 seconds between each frame, probably to slow down the speed at which the blocks are falling. Thus, adding threading would not solve the problem and add unecessary complexity. – Martin Apr 06 '11 at 15:55

4 Answers4

4

The main problem I see here is that you are limiting your framerate using a wait of 0.4 seconds.

You should not limit framerate, but instead, you should limit how fast your block falls.

If I remember well, there was a formula you could use to do just that. It was based on the amout of time elapsed since the last frame. It looked like:

fraction of a second elapsed since last frame * distance you want your block to move in a second

This way, you can keep your mainloop intact, and the move processing will happen at every frame.

Martin
  • 5,954
  • 5
  • 30
  • 46
1

You could also do...

    ...
    # This part of the code makes the piece fall by itself
    if the block isn't touching the floor and 
       the block hasn't automatically moved in the last 0.4 seconds:
        move block down one step
    ...

Just realize you'll be doing a lot of polling if the user hasn't struck any keys.

AlG
  • 14,697
  • 4
  • 41
  • 54
1

You may try asking gamedev.stackexchange.com instead. Check the site for Game Loops, and check out other example pygame projects to see how they're doing it. Having a good game loop is essential and will take care of things for you such as user inputs and a consistent frame rate.

Edit: https://gamedev.stackexchange.com/questions/651/tips-for-writing-the-main-game-loop

Community
  • 1
  • 1
Dan Breen
  • 12,626
  • 4
  • 38
  • 49
0

When doing games you should always try to do something like this:

while not finished:
    events = get_events() # get the user input
    # update the world based on the time that elapsed and the events
    world.update(events, dt) 
    word.draw() # render the world
    sleep(1/30s) # go to next frame

The sleep time should be variable so it takes into consideration the amount of time spend drawing and calculating the world updates.

The world update method would look something like this:

def update(self, events, dt):
    self.move(events) # interpret user action
    self.elapsed += dt
    if self.elapsed > ADVANCE_TIME:
        self.piece.advance()
        self.elapsed = 0

The other way of implementing this (so you dont redraw too much) is to have events fired when the user orders a piece to be moved or when ADVANCE_TIME time passes. In each event handler you would then update the world and redraw.

This is assuming you want the pieces to move one step at a time and not continuous. In any case, the change for continuous movement is pretty trivial.