30 / 60 fps toggle

Is it possible to select (on a project by project basis) whether the main draw loop should be executed at a fixed 30fps as opposed to 60 ?

Could you not implement something like that yourself? Just every 2nd frame do draw?

That’s not the point, the idea would be that if you had a game that took a long time to draw and depending on how much was on screen could in theory drop from updating and rendering in a single frame to taking two, you’d get a choppy framerate that would make your game look and feel very amateurish.

If you know your framerate is potentially going to falter then it’s probably better to force a 30 fps update and keep everything smooth.

I realise this can be “smoothed” out by using delta time calculations but working with a fixed step increment is a lot simpler.

This will limit the frame rate to about 30 fps.
The fps object is just to check the framerate, the useful part is in the main.


--# Main
-- frame 30 toggle

-- Use this function to perform your initial setup
function setup()
    fps = FPS()
    x=0
    t0=os.clock()
end

-- This function gets called once every frame
function draw()
-- put your draw here
    background(40, 40, 50)
    fps:draw()
--
    while os.clock()<(t0+1/39) do  x = x + 1  end
    t0=os.clock()
end


--# FPS
FPS = class()

function FPS:init(frac)
    self.val = 60
    self.frac = frac or 0.01
--    self.t0 = os.clock()
    self.t0 = ElapsedTime
end

function FPS:draw()
    local vShift = 0
 --   if jobs:active() then vShift = 30 end
--    if jobs.active then vShift = 30 end
    -- update FPS value with some smoothing
    local old = self.val
    local frac = self.frac
--    local t1 = os.clock()
    local t1 = ElapsedTime
    local delta = t1 - self.t0
    self.t0 = t1
    local new = 1/delta or old
    if new>65 then new=65 end
    local ratio = new/old
    if 0.5<ratio and ratio<2 then new = old*(1-frac)+ new*frac end
    self.val = new
    -- write the FPS on the screen
    fill(208, 208, 208, 255)
    fontSize(30)
    font("AmericanTypewriter-Bold")
    rectMode(CENTER)
    text(math.floor(new).." fps",50,HEIGHT-15-vShift)
    
end

@Jvm38 - Thanks for your suggestion, but I think your missing the point.
The thing is that the draw() function is called 60x per second (or at least attempts to), if every so often it takes over a frame (~16ms) to complete all the logic, collision checks, drawing etc for a given frame then you’ll end up with a choppy animation as the frame rate stutters between 60 and 30, if there was an option to set the delay between successive draw() calls then you as the programmer could decide to drop the frame rate to a smooth 30 fps to allow you to have up to ~32ms to do the work required in a frame and render the display.

A rock solid 30fps looks a LOT better than an app that periodically stutters between 30/60.

So…why not 48fps? Or 24 fps? The 30 seems “obvious”, but it’s really not. BTW: neither is 60.

Your point is valid (have Codea at the low level do the heavy lifiting), but I bet making code that would limit the fps is probably way too difficult to make as a parameter.

Additionally, what do you do when the runtime is used to create a iOS native app? All the FPS logic would have to be written into the runtime, too.

While kludgy, and very, very cycle wasting, @jvm38 does present a solution that is at least sandboxed well.

Personally, I’m going to let my app stutter because then I can find areas to optimize :slight_smile:

.@techDojo check this little variant with random computing that generate a choppy movement: when you activate the while loop, then the character runs smoothly at 25 fps. So i think it works exactly like you wanted. Of course it is really a pity to waste that good computing power with this loop, as @aciliino says, but it does the job.


--# Main
-- frame 30 toggle

-- Use this function to perform your initial setup
function setup()
    print("Hello World!")
    fps = FPS()
    x=0
    t0=os.clock()
end
local cos=math.cos
local rad=math.rad
local random=math.random
-- This function gets called once every frame
function draw()
    -- do your stuff here
    local n = random(100)
    if n > 90 then n=60000 else n = 10000 end
    for i =1,n do x = x+1 end
    background(40, 40, 50)
    local angle = ElapsedTime*360/2
    sprite("Planet Cute:Character Boy",
        HEIGHT/2+ 150*cos(rad(angle+90)),
        WIDTH/2 + 150*cos(rad(angle)))
    fps:draw()
    --print(x)
    -- dont change this:
    x=0
-- uncomment this line to run smooth 25 fps:
 --   while os.clock()<t0 do  x = x + 1  end
    t0=os.clock()+1/30
end

--# FPS
FPS = class()

function FPS:init(frac)
    self.val = 60
    self.frac = frac or 0.01
--    self.t0 = os.clock()
    self.t0 = ElapsedTime
end

function FPS:draw()
    local vShift = 0
 --   if jobs:active() then vShift = 30 end
--    if jobs.active then vShift = 30 end
    -- update FPS value with some smoothing
    local old = self.val
    local frac = self.frac
--    local t1 = os.clock()
    local t1 = ElapsedTime
    local delta = t1 - self.t0
    self.t0 = t1
    local new = 1/delta or old
    if new>65 then new=65 end
    local ratio = new/old
    --if 0.5<ratio and ratio<2 then new = old*(1-frac)+ new*frac end
    self.val = new
    -- write the FPS on the screen
    fill(208, 208, 208, 255)
    fontSize(30)
    font("AmericanTypewriter-Bold")
    rectMode(CENTER)
    text(math.floor(new).." fps",50,HEIGHT-15-vShift)
    
end

Hmmmm, @TechDojo makes a valid point and raises the conundrum of how ‘pro’ games developers (for example) handle issues relating to maintaining a consistent frame rate without stuttering. There must be some sort of ‘best practice’ that could be applied to Codea?

I can see having a custom framerate option being made available after 1.5. One thing you can do to help smooth motion is:

http://gafferongames.com/game-physics/fix-your-timestep/

Note that Codea already uses this for physics and it can be enabled by setting body.interpolate to true (I can’t remember if this is on by default). It smoothes out movement but introduces a slight lag (1 frame or less).

.@aciolino - the reason for the swap between 60 and 30 is because the iPad framerate is directly linked to the vertical refresh, normally your display is rendered to a “back” or logical buffer whilst you look at the “front” or physical buffer, then when the vertical timing signal triggers the display hardware swaps over the locations of the physical and logical buffers (back in the old day’s this would be when the CRT raster beam was off the bottom of the screen) so that the transition is seamless.

In order for your game to run at a constant 60fps you had to ensure that all logic and drawing completed in the time taken from the start of the last vertical trigger until the next vertical trigger (approx ~16ms), if the work didn’t complete in the time then you’d have to wait the next vertical trigger (2 “frames” worth of work = 60 /2 = 30 fps) or else you would end up seeing parts of the previous frame as the change took place mid display and you’d get that horrible tearing happening.

Any other value other than a factor of 60 (50 on older PAL TV systems - which is why European ports often ran ~17% slower than the US / NTSC versions) would give a “choppy” framerate where on some frames your see the display change every frame and others you’d see the same display for two or more, over the course of several seconds these “average” out and you get the constant rate you mentioned but the skips are still there.

Think of it a bit like a bresenham style line drawing algorithim, when drawing vertical or horizontal lines the line is 100% solid, however start to draw at an angle and you see “jaggies” appear (if you look close enough) due to having to “approximate” which pixels you fill, the worst steps appear as the line is ALMOST horizontal with the effect being lessened as you get to 45 deg.

Even though iPad displays are LCD and don’t technically need a “raster beam” I’m sure the vertical (and horizontal) timing signals are still present and in use.

I’ve noticed my proposed solution works when the limiting factor is the program calculations themselves, but not when the limit comes from the graphic processor: when i display many meshes then the display is choppy but i cant cover it up with os.clock() and the code above. It should be a more complex mix of ElapsedTime and os.clock() i presume.