Jump to content
  • Advertisement
Sign in to follow this  
Don Polettone

pygame - jerky movement in general

This topic is 1127 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've got a huge issue with pygame. Movement is always a bit jerky, even if the code is as simple as can be. Try running this simple example which moves a rect by 1 pixel at 60 FPS:

import sys
import pygame
pygame.init()

display = pygame.display.set_mode((640, 480), pygame.FULLSCREEN)
clock = pygame.time.Clock()
FPS = 60

def motion_test():

    rect = pygame.rect.Rect((0, 240), (40, 40))

    while 1:

        # clear display
        display.fill((0, 0, 0))

        # check quit
        for e in pygame.event.get():
            if (e.type == pygame.QUIT or
                e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE):
                pygame.quit()
                sys.exit()

        # move the rect by 1 pixel
        rect.x += 1
        if rect.x >= 640:
            rect.x = 0

        # draw the rect and flip the display
        pygame.draw.rect(display, (255, 0, 0), rect)
        pygame.display.flip()

        # tick the clock
        clock.tick(FPS)


if __name__ == "__main__":
    motion_test()

No matter on what hardware I test this, the rect is moving jerky. Every like half a second or so, it "jumps" a bit. It's just not smooth motion.

Please, this makes me mad... does anybody know what might cause this? Is it pygame itself, or Python's garbage collection, or the OS, or just what? Any ideas? Do you see the same running the example?

 

(Win7 64bit, Pygame 1.9.1, Python 2.7.3)

 

Share this post


Link to post
Share on other sites
Advertisement

You need to introduce 'speed' variable and move objects depending on deltaTime since last loop iteration. Each iteration takes different time to complete, but the movement is fixed (1 unit per iteration), that makes the movement jerky.

Edit:
Or if you want to use clock.tick() to have fixed amount of frames try to use 30fps instead of 60.
More details here:
"... 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."

Edited by Evgeny.nn

Share this post


Link to post
Share on other sites

Thanks for the hint! I now move the rect according to delta time (which is usually 16 / 17 ms @ 60 FPS), but still it's the same result: A notable "lag" after every 0.5 seconds or so.

 

Here's the code again:

import sys
import pygame
pygame.init()

display = pygame.display.set_mode((640, 480), pygame.FULLSCREEN)
clock = pygame.time.Clock()
FPS = 60

def motion_test():
    
    dt = 0
    rect_pos = [0.0, 240.0]
    rect = pygame.rect.Rect(rect_pos, (40, 40))
    rect_speed = 60 # pixels per sec

    while 1:

        # clear display
        display.fill((0, 0, 0))

        # check quit
        for e in pygame.event.get():
            if (e.type == pygame.QUIT or
                e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE):
                pygame.quit()
                sys.exit()

        # move the rect
        dt = clock.get_time()
        rect_pos[0] += rect_speed * dt / 1000.0
        if rect_pos[0] >= 640:
            rect_pos[0] = 0.0
        rect.topleft = rect_pos

        # draw the rect and flip the display
        pygame.draw.rect(display, (255, 0, 0), rect)
        pygame.display.flip()

        # tick the clock
        clock.tick(FPS)
        

if __name__ == "__main__":
    motion_test()

I will try again using 30 FPS, just a moment...

Share this post


Link to post
Share on other sites

Just tried again using 30 FPS. The result is quite similar: The rect "hickups" every like 1.0 seconds. I'm desperate...

 

Can you try and confirm or provide a hint?

import sys
import pygame
pygame.init()

display = pygame.display.set_mode((640, 480), pygame.FULLSCREEN)
clock = pygame.time.Clock()
FPS = 30

def motion_test():
    
    dt = 0
    rect_pos = [0.0, 240.0]
    rect = pygame.rect.Rect(rect_pos, (40, 40))
    rect_speed = 60 # pixels per sec

    while 1:

        # clear display
        display.fill((0, 0, 0))

        # check quit
        for e in pygame.event.get():
            if (e.type == pygame.QUIT or
                e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE):
                pygame.quit()
                sys.exit()

        # move the rect
        dt = clock.get_time()
        rect_pos[0] += rect_speed * dt / 1000.0
        if rect_pos[0] >= 640:
            rect_pos[0] = 0.0
        rect.topleft = rect_pos

        # draw the rect and flip the display
        pygame.draw.rect(display, (255, 0, 0), rect)
        pygame.display.flip()

        # tick the clock
        clock.tick(FPS)
        

if __name__ == "__main__":
    motion_test()

Is there any way to get an accurate fixed time step on Win7 somehow? (Preferably @ 60 FPS)

Edited by Don Polettone

Share this post


Link to post
Share on other sites

I don't really know Python too good but what kind of numbers is this returning?
dt = clock.get_time()
 
I just Googled and I got this:
http://www.pygame.org/docs/ref/time.html
 

get_time()

time used in the previous tick
get_time() -> milliseconds

Returns the parameter passed to the last call to Clock.tick(). It is the number of milliseconds passed between the previous two calls to Pygame.tick().

 
I don't really get this as I haven't really used pygame but I would recommend you check what kind of values you are getting from get_time(). The description seems to imply that it should be returning FPS each time (since that was the parameter of the last call to clock.tick()) but that doesn't really make sense.

Edited by Nanoha

Share this post


Link to post
Share on other sites

clock.get_time() returns the number of milliseconds that have passed since the previous call to clock.tick(). So at 60 FPS, this returns a value like 16 or 17 (on my machine).

 

The pygame docs are sometimes really nuts... alas.

 

/EDIT:

 

I have just tried the pyglet clock and limited it to 60 FPS; all the rest is still pygame. And see, it is much better now! Still not a 100% smooth but fine enough to code an NES-like game that runs at 60 FPS with a fixed time step.

 

Here's the code:

import sys
import pygame
import pyglet
pygame.init()

display = pygame.display.set_mode((640, 480), pygame.FULLSCREEN)
clock = pyglet.clock.Clock()
clock.set_fps_limit(60)

def motion_test():
    
    rect = pygame.rect.Rect((0, 240), (40, 40))

    while 1:

        # clear display
        display.fill((0, 0, 0))

        # check quit
        for e in pygame.event.get():
            if (e.type == pygame.QUIT or
                e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE):
                pygame.quit()
                sys.exit()

        # move the rect
        rect.x += 1
        if rect.x >= 640:
            rect.x = 0

        # draw the rect and flip the display
        pygame.draw.rect(display, (255, 0, 0), rect)
        pygame.display.flip()

        # tick the clock
        clock.tick()
        

if __name__ == "__main__":
    motion_test()


Edited by Don Polettone

Share this post


Link to post
Share on other sites

Thanks for the hint mousetail but I've tried that already and it's not really better.

 

Is there a clock object in python language that is VERY, VERY accurate on windows/mac/unix? What's the best way to program a NES-like game in python 2.x regarding the time step?

 

Anyone can help?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!