[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Bouncing ball - separating the physics from the frame rate



> 1. Have the physics and graphics running in separate threads. I don't have a
> clue how to implement this!
This is a very experimental technique.  I think there may be a few
games that do this, but the majority do not.

>
> 2. Call a function from the main loop every time it passes which does the
> physics calculations based on a clock independent of the frame rate but which
> pauses when the simulation is paused (window not in focus?) Is there a function
> in the sys module that I can use to test for a pause?

If your framerate is fairly constant, i.e. it doesn't ever go slower
than what you place inside the clock.tick() call, than you should be
able to get away with simply calling the physics once per frame
without using time_passed as part of the calculation.  (Substituting
some fixed time, like 0.02 as needed).  The only downside to this
method, is if the framerate goes down, the physics will slow down
along with it - but this may be preferable to jerkiness anyway.  What
you won't have is a bad framerate that causes the physics to go out of
sync or go haywire.  And the pausing problem won't matter.

To handle slower framerates, you could detect if time_passed is above
some threshold, and call the physics function twice if the time is too
large.  But it's better to keep the framerate up, no?

clock.tick(60) should guarantee that the framerate is a steady 60 fps
unless things slow down.

So here's my untested example, factoring out time_passed, and making
physics a function:

#physics
def run_physics(ob):
    time_passed = 0.02   #.02 is a decent time interval for 60 fps,
adjust as needed.  (1/60.0 is more accurate)
    # The physics
    # Reverse velocity taking into account bounciness if we hit the ground
    if ob.ypos >= 384 and ob.velocity > 0:
        # Avoid the ball sinking into the ground
        ob.ypos = 384
        ob.velocity = -ob.velocity * ob.bounce

    newvelocity = ob.velocity + (ob.gravity * time_passed)
    # Use the average velocity over the period of the frame to change position
    ob.ypos = ob.ypos + int(((ob.velocity + newvelocity) / 2) *
time_passed * 160)
    ob.velocity = newvelocity

# The main loop
while True:

    # Test for exit
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit()

    clock.tick(60) / 1000.0

    run_physics(ball)   #Make global variables attributes of ball

    # Update the screen
    screen.fill((0, 0, 0))
    screen.blit(ball, (xpos, ypos))
    pygame.display.update()