# A way of keeping a constant game speed at any fps with Pygame

Normally the game speed with pygame is locked with the fps, so I attempted making a simple two classes that'd be super easy to use, to allow for the same game speed no matter what the fps is.

For each frame, it calculates the difference between the amount of ticks that have passed, and how many should have passed (I get that this way could be better, but people don't change this mid game right?). For example, the difference at 60fps at 60 ticks per second is 1, at 30fps, the difference becomes 2, at 120fps, one frame will have a difference of 0, and the next 1. I did my own version of ticks since I locked the actual ticks to the fps.

With the frames per second, I set it to calculate it every x seconds, so it's a bit more accurate and avoids times so small it causes zero division errors (0.1 seconds for 1753 frames is more accurate that 0.00005704506 seconds for 1 frame). If you don't limit the fps, the inbuilt fps function just returns 0.

There does seem to be a bit of a problem though, and I'm not sure what's causing it. I'm sure I've done the framerate calculation correctly (with a minimum time so it doesn't end up working with tiny floats), but setting the fps to 500 reads as 333, and 1000 reads as 500, which is really strange. Setting it to something really high or quite low returns something that looks a lot more correct.

I've not really used a with with a class containing another class before, so I'm not sure if I've done it right (I originally only had the one class, but realised __enter__ and __exit__ needed the class called each time), so any feedback would be good as to what I didn't do well.

from __future__ import division
import pygame
import time
import random

class GameTime(object):
def __init__(self, desired_fps, desired_ticks, clock):
self.start_time = time.time()
self.desired_fps = desired_fps
self.desired_ticks = desired_ticks
self.clock = clock

self.ticks = 0

self.framerate_counter = 1
self.framerate_time = 0

def get_ticks(self, current_time):
"""Ensure the correct number of ticks have passed since the
start of the game.
This doesn't use the inbuilt pygame ticks.
"""
time_elapsed = current_time - self.start_time

total_ticks_needed = int(time_elapsed * self.desired_ticks)

ticks_this_frame = total_ticks_needed - self.ticks
self.ticks += ticks_this_frame

return ticks_this_frame

def get_fps(self, current_time, update_time=0.1):
"""Calculate the FPS from actual time, not ticks.

It will return a number every update_time seconds, and will
return None any other time.
"""
frame_time = current_time - self.framerate_time

if frame_time < update_time:
self.framerate_counter += 1

else:
self.framerate_time = current_time
fps = self.framerate_counter / frame_time
self.framerate_counter = 1
return int(fps)

def limit_fps(self, alternate_fps=None):

wanted_fps = alternate_fps or self.desired_fps
if wanted_fps:
self.clock.tick(wanted_fps)

class GameTimeLoop(object):
"""This gets called every loop but uses GameTime."""

def __init__(self, GTObject):

self.GTObject = GTObject
GTObject.loop_start = time.time()

#Run the code once now so the result can be called multiple times
self.ticks = GTObject.get_ticks(GTObject.loop_start)
self.fps = GTObject.get_fps(GTObject.loop_start)

self.temp_fps = None

def __enter__(self):
return self

def __exit__(self, *args):
self.GTObject.limit_fps(self.temp_fps)
self.temp_fps = None

def set_fps(self, fps):
self.GTObject.desired_fps = fps

def update_fps(self, fps):
self.temp_fps = fps


An example pygame function using it:

def game():

pygame.init()
screen = pygame.display.set_mode((640, 480))

clock = pygame.time.Clock()
max_fps = None
ticks_per_second = 60
FrameRate = GameTime(max_fps, ticks_per_second, clock)

move_speed = 0.175
move_total = 0

while True:
with GameTimeLoop(FrameRate) as frame_time:

#Display the current fps
if frame_time.fps is not None:
pygame.display.set_caption(str(frame_time.fps))

#Calculations to be done once per tick
#Can multiply by the number of ticks or do a loop
ticks_this_frame = frame_time.ticks
for tick in xrange(frame_time.ticks):
pass

#Do normal stuff here
move_total += move_speed * ticks_this_frame
print move_total  #Just print this to show it's correctly staying at the same speed

for event in pygame.event.get():
if event.type == pygame.QUIT:
return

#Set a new fps
if event.type == pygame.MOUSEBUTTONDOWN:
new_fps = random.choice((None, 1, 5, 10, 30, 60, 120, 1000, 10000))
frame_time.set_fps(new_fps)
print 'set fps to: {}'.format(new_fps)

#Temporarily set a new fps while the mouse moves
if event.type == pygame.MOUSEMOTION:
frame_time.update_fps(30)


It'd be easy to add default values to desired_fps and desired_ticks rather than requiring they be manually supplied.

get_ticks is a bit of a misleading name. It implies a getter, but actually you're modifying an attribute as well as returning a value. I'd personally call it calculate_ticks so that it's less surprising to see both results happening. You should definitely write an explanation of what it does in the docstring though. You explained the function but didn't actually tell the user what it gives them or what effect it has.

def calculate_ticks(self, current_time):
"""Returns ticks from this frame and updates self.ticks

Ensure the correct number of ticks have passed since the
start of the game.
This doesn't use the inbuilt pygame ticks.
"""


And I'd do similar with your FPS calculation.

• Thanks, that makes sense, I was a bit stuck on what to call the functions but calculate sounds good, I renamed it through like .fps, .get_fps, .current_fps etc, but nothing seemed right haha. I also totally forgot to update the docstrings, so will get that done. – Peter Nov 30 '15 at 18:04
• Yeah I generally use get for basic returns, update for just adjusting values and returning nothing and then calculate when it does both. – SuperBiasedMan Nov 30 '15 at 18:04
• Ah thanks, I'll keep that in mind. The other one I wasn't sure on was set_fps and update_fps. set_fps actually sets a new fps, and update_fps sets a new fps for only one frame (with the intention of using it every frame it's needed). What would you use in that case? – Peter Nov 30 '15 at 18:07
• @Peter That is tougher. Maybe something like refresh_fps perhaps? To indicate how it's a temporary value that will be set over and over. It would be worth adding a docstring to clarify it better too. – SuperBiasedMan Nov 30 '15 at 22:50