Skip to main content

Updated FPS calculator for Corona SDK

A couple of weeks back I wrote this post about how to calculate the actual frame rate of your game rather than just hoping that the target frame rate found in display.fps will be met. Back then I only used it as en experiment, but today I wanted to implement it in my game Ice Trap to be able to reduce or disable some animations on slower running devices. I ended up rewriting the frame rate calculator in an object-oriented fashion to make it cleaner and more reusable.

It should be very easy to use. Just create a new instance of the FPSCalculator, passing in a callback function and some optional configuration parameters. In the callback function you'll have access to the current FPS as well as the FPS ratio, i.e (actual_FPS/target_FPS). If none of the optional config parameters are specified the callback will be invoked once every second, regardless of whether there's been a FPS drop or not.

Check out the whole source code below, including some example usage code, and make modifications and extensions to it as you see fit.

--[[
This is a simple example showing how to use the FPSCalculator class
Markus Ranner 2016
--]]
local FPSCalculator = require("FPSCalculator")
local fpsCalculator = FPSCalculator.new(
function(fps, fpsRatio)
print("fps = " .. fps .. ", ratio = " .. fpsRatio)
-- Do things here to handle a drop in frame rate, such as disable animations
end,
{
fpsRatioWarningThreshold = 0.95,
timeBetweenCalculationsMs = 10000,
}
)
fpsCalculator:start()
view raw example.lua hosted with ❤ by GitHub
--[[
Frame rate calculator class, used to find out the actual frame rate of a game and react to drops in FPS.
Markus Ranner 2016
--]]
local FPSCalculator = {}
FPSCalculator.__index = FPSCalculator
--- Private functions --------------
function FPSCalculator:calculateFPS()
self.frameCounter = self.frameCounter + 1
local currentTimestampMs = system.getTimer()
if (not self.lastCalculationTimestampMs) then
self.lastCalculationTimestampMs = currentTimestampMs
end
local deltaTimeMs = currentTimestampMs - self.lastCalculationTimestampMs
-- Calculate average fps for the specified time period
if ( deltaTimeMs >= self.timeBetweenCalculationsMs ) then
local fps = self.frameCounter / (deltaTimeMs / 1000)
self.frameCounter = 0
self.lastCalculationTimestampMs = currentTimestampMs
local fpsRatio = fps/display.fps
if ((not self.fpsRatioWarningThreshold) or (fpsRatio <= self.fpsRatioWarningThreshold)) then
self.callback(fps, fpsRatio)
end
end
end
--- Public functions --------------
function FPSCalculator:start()
if (self.isStarted) then
return
end
-- Reset some state
self.isStarted = true
self.frameCounter = 0
self.lastCalculationTimestampMs = nil
-- Setup the calculation function
self.calculationFunction = function() self:calculateFPS() end
Runtime:addEventListener("enterFrame", self.calculationFunction)
end
function FPSCalculator:stop()
self.isStarted = false,
Runtime:removeEventListener("enterFrame", self.calculationFunction)
end
--[[
callback(fps, fpsRatio) - Required. A callback function with params:
fps - The average frame rate during the last calculation period
fpsRatio - The ratio between actual fps and target fps (fps/display.fps)
params - Optional table of configuration params. The following params are allowed and are optional.
timeBetweenCalculationsMs - How often to calculate fps. Default = 1000 ms.
fpsRatioWarningThreshold - A number between 0-1. If specified, the callback will only be called if fpsRatio drops below this threshold number.
--]]
function FPSCalculator.new(callback, params)
params = params or {}
local newCalculator = {
isStarted = false,
frameCounter = 0,
lastCalculationTimestampMs = nil,
calculationFunction = nil, -- Will be set when start() is called
callback = callback,
timeBetweenCalculationsMs = params.timeBetweenCalculationsMs or 1000,
fpsRatioWarningThreshold = params.fpsRatioWarningThreshold,
}
setmetatable(newCalculator, FPSCalculator)
return newCalculator
end
return FPSCalculator

Comments