Search code examples
pythonpygamepygame-clock

pygame: how do I get my game to actually run at 60fps?


In my main loop I have:

clock.tick_busy_loop(60)
pygame.display.set_caption("fps: " + str(clock.get_fps()))

However, the readout says the game is reading at 62.5fps. I then tried to input clock.tick_busy_loop(57.5), which gave me a readout of 58.82...fps. When I set clock.tick_busy_loop(59) I get 62.5fps again. It looks like there is a threshold between 58.8fps and 62.5fps that I can't overcome here. How do I get my game to actually run at 60fps? I'm mainly looking for this kind of control because I executing events that are contingent on musical timing.


Solution

  • So I whipped up a simple demo based on my comment above that uses the system time module instead of the pygame.time module. You can ignore the OpenGL stuff as I just wanted to render something simple on screen. The most important part is the timing code at the end of each frame, which I have commented about in the code.

    import pygame
    import sys
    import time
    from OpenGL.GL import *
    from OpenGL.GLU import *
    
    title = "FPS Timer Demo"
    target_fps = 60
    (width, height) = (300, 200)
    flags = pygame.DOUBLEBUF|pygame.OPENGL
    screen = pygame.display.set_mode((width, height), flags)
    
    rotation = 0
    square_size = 50
    prev_time = time.time()
    
    while True:
        #Handle the events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
    
        #Do computations and render stuff on screen
        rotation += 1
        glClear(GL_COLOR_BUFFER_BIT)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        glOrtho(0, width, 0, height, -1, 1)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glTranslate(width/2.0, height/2.0, 0)
        glRotate(rotation, 0, 0, 1)
        glTranslate(-square_size/2.0, -square_size/2.0, 0)
        glBegin(GL_QUADS)
        glVertex(0, 0, 0)
        glVertex(50, 0, 0)
        glVertex(50, 50, 0)
        glVertex(0, 50, 0)
        glEnd()
        pygame.display.flip()
    
        #Timing code at the END!
        curr_time = time.time()#so now we have time after processing
        diff = curr_time - prev_time#frame took this much time to process and render
        delay = max(1.0/target_fps - diff, 0)#if we finished early, wait the remaining time to desired fps, else wait 0 ms!
        time.sleep(delay)
        fps = 1.0/(delay + diff)#fps is based on total time ("processing" diff time + "wasted" delay time)
        prev_time = curr_time
        pygame.display.set_caption("{0}: {1:.2f}".format(title, fps))