4
\$\begingroup\$

I am having trouble setting up the frame rate in my first game. I know that I can set it up with

clock = pygame.time.Clock()
clock.tick(60)

but this is not producing very desirable results. If I move a character 1 pixel per frame, it will move smoothly but jitter about once a second. If I print(clock.get_fps()) it tells me that my frame rate is about 59.8. Since it never hits 60, it never syncs up with my monitor's refresh rate and the annoying jittering continues.

That I know of, this is not a problem with the game's performance, since it is in a very early stage of development. If I cap the FPS at 1000 then I can get a maximum FPS of about 270.7. Is there any way I can give my game a continuous frame rate of 60?

P.S. I don't mind that my movement code is tied to FPS. I tried making it FPS-independent using this code, but this introduced some floating-point inaccuracies to my movement. It wouldn't let me set the FPS to exactly 60, either.

\$\endgroup\$
7
\$\begingroup\$

If you read the pygame.time.Clock.tick() and pygame.time.Clock.get_fps() documents, you'll find that:

  • tick() works with milliseconds - it returns the number of milliseconds since the last call
  • get_fps() returns an average of the last 10 calls to tick(). If you do the math, that's pretty damn good that you're getting 59.8 most of the time, since with 60 FPS you'll be ticking 16 or 17ms per frame, and 59.88 is the closest number you can get ((16*3 + 17*7) / 10 = 59.88)

That explains why you're not seeing 60FPS from get_fps(). The jitter is a different matter though; it has to do with how tick() is implemented.

It uses SDL_Delay, which in turn uses various platform-specific sleep functions, which depending on the OS can be quite inaccurate at the millisecond level. What's probably going on is, a popular OS's sleep function has a granularity of 15ms, whereas your target FPS of 60 requires 16.66...ms sleeps. One of your frames is going to stick around for 30ms, maybe every second or so.

Basically if you want smooth frames, you'll need to avoid methods that exclusively use SDL_Delay. You'll want to read Fix Your Timestep, but here are your options:

  • Use tick_busy_loop(), which doesn't sleep but makes your CPU spin until enough time has passed. The downside is increased power usage, a big no-no for battery-powered devices.
  • Track the number of milliseconds you need to delay yourself, and use a combination of pygame.time.wait() and pygame.time.delay() to precisely control timing, without doing a busy-wait all the time.
  • Divorce game time from draw time, and draw interpolated states based on delta time. What that means is, instead of drawing things at say x + speed, you draw them at x + speed * delta, where delta is the amount of time actually taken to draw that frame. This masks a lot of jittering problems.
  • This option is probably out for you, but there are more precise sleeps available that are platform-specific. Sadly they are not available to SDL/pygame.
\$\endgroup\$
  • \$\begingroup\$ The problem with these approaches are that they are tied to milliseconds. In order to achieve a framerate of exactly 60, you would need to wait 16.666 milliseconds. My program needs to sleep and then blit everything to the screen whenever the monitor refreshes. \$\endgroup\$ – DragonDePlatino Jun 26 '15 at 19:20
  • \$\begingroup\$ @DragonDePlatino in that case you are looking for "Vsync". \$\endgroup\$ – congusbongus Jun 27 '15 at 1:28
  • \$\begingroup\$ @congusbongus hey, do you have any examples of a complicated PyGame project running at 60fps? \$\endgroup\$ – quantumpotato May 1 at 3:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.