Why enterFrame delta time so difference

Hi all,

I just implement simple game with player moving in enterFrame loop and want smooth movement but in real device (ipad mini), delta time in each enterFrame loop change from 11 to 20 (where i expect it will around 16,3 as my fps in config file is 60):

[lua]function GameWorldBase:enterFrame(event)
– body
local current_time = event.time – or system.getTimer()
local elapsed = math.min(100,current_time - (self.m_lastFrameTimer or 0))
self.m_lastFrameTimer = current_time
print( elapsed )

–player:translate(0,elapsed*GAMESPEED);
end
[/lua]

So my question is:

Why delta time in each enterFrame so difference

And how can we implement smooth movement with delta time?

Thank so much!

Read this blog post on how to implement smooth movement w/ enterFrame:

https://coronalabs.com/blog/2013/06/18/guest-tutorial-delta-time-in-corona/

@roaminggamer Yes, i read that article before and using it in my game! As you see

[lua]local dt = (temp-runtime) / (1000/60) --60fps or 30fps as base[/lua]

(1000/60) is just a constant number, so my code above will produce the same delta time effect.  So when run that example on real device, it is till not smooth!

If we print dt, you will see delta time change from 0.94 ~ 2.1 (see log bellow). So if we translate box by dt * 5, in some frame, it moves 5 px but in other frame, it will move 10 px, that will cause zig zag effect (not smooth movement at all).

(Sorry for my bad of english, but you can build that example, comment rotation and scale code, just keep moving code and see on real device - especially on android -  the box will moving with some lag!)

Apr 28 23:42:49.475: 0.95639999999992

Apr 28 23:42:49.511: 2.1260400000001

Apr 28 23:42:49.525: 0.86069999999985

Apr 28 23:42:49.542: 0.98268000000004

Apr 28 23:42:49.558: 0.97770000000011

Apr 28 23:42:49.575: 1.05342

Apr 28 23:42:49.592: 0.98165999999983

Apr 28 23:42:49.610: 1.0677000000001

Apr 28 23:42:49.625: 0.94997999999992

Apr 28 23:42:49.642: 0.9873000000001

Apr 28 23:42:49.659: 1.0121399999999

Apr 28 23:42:49.675: 0.99582000000009

Apr 28 23:42:49.685: 0.54858

Apr 28 23:42:49.701: 0.96461999999985

Apr 28 23:42:49.717: 0.96366000000009

Apr 28 23:42:49.732: 0.94265999999996

Apr 28 23:42:49.748: 0.95184000000008

Apr 28 23:42:49.764: 0.96101999999999

Apr 28 23:42:49.780: 0.94445999999989

Apr 28 23:42:49.809: 1.7160000000001

Apr 28 23:42:49.825: 0.99401999999995

Apr 28 23:42:49.842: 1.0066199999999

Apr 28 23:42:49.859: 1.0056600000001

Apr 28 23:42:49.875: 0.99851999999999

Apr 28 23:42:49.892: 1.0108799999999

Apr 28 23:42:49.909: 0.99696000000011

Apr 28 23:42:49.925: 0.94956000000006

You misunderstand the point of the article. Calculate the amount you move as a multiple of the delta time that has passed, not as a multiple of frames.  i.e. Move in pixels per second, not pixels per frame.

On thing I don’t agree with from the article is the incorporation of frame rate as part of the calculation.  Simply do this:

local xpps = 10 -- x-rate == 10 pixels per second for this example local ypps = 0 -- y-rate == 0 pixels per second for this example local getTimer = system.getTimer -- Localization for speedup and easier typing local tmp = newCircle( 10, 10, 10 ) tmp.lastTime = getTimer() -- My preferred way of adding the listener -- tmp.enterFrame = function( self ) local curTime = getTimer() local dt = curTime-self.lastTime dt = dt / 1000 self.lastTime = curTime local dx = xpps \* dt local dy = ypps \* dt self:translate(dx,dy) end Runtime:addEventListener( "enterFrame", tmp )

Regarding Frame Time

The frame time is not something you can control (except grossly by choosing 30 or 60 FPS in config.lua).  

Corona (like any other engine or SDK), does the best it can to maintain a regular frame time, but once the work for a frame has begun, Corona cannot stop till it is all done.  

If too much ‘work’ is scheduled in a frame it will extend the duration of that frame.  Additionally, the OS may influence frame time by doing background work which delays the frame.  

However, you can easily smooth time-based calculations, as shown above.

Yes, in my code above, i multiple with delta time for sure! and i do not include frame rate in formula. 

If you add print( “dt”,dt,“dx”,dx ) to your code, you will notice sometime dx very small (0.2112000000001), and sometime very big (3.1029999999999). 

And in action game like a racing game, we need fast moving object (x-rate and y-rate must be ~ 1000 for example) , so dx will change from 2 px to 30 px for example, and that will cause lag effect!

I know that in our game, if we update textures, update GUI, update textbox, do a heavy logic…, frame will be drop. But bellow, we just make a simple ping pong game, and if we deploy game on device, you will see it not smooth moving at all.

Just config project to 800,1200,letterbox, fps = 60 and see the log:

[lua]
display.setStatusBar( display.HiddenStatusBar )


– main.lua


_G.MINX = display.screenOriginX;
_G.MINY = display.screenOriginY;
_G.MAXX = display.contentWidth - display.screenOriginX
_G.MAXY = display.contentHeight - display.screenOriginY

_G.W = _G.MAXX - _G.MINX;
_G.H = _G.MAXY - _G.MINY;
_G.CX = _G.MINX + _G.W/2
_G.CY = _G.MINY + _G.H/2

local xpps = 1000 – x-rate == 10 pixels per second for this example
local ypps = 0 – y-rate == 0 pixels per second for this example

local getTimer = system.getTimer – Localization for speedup and easier typing

local tmp = display.newCircle( CX, CY, 50 )
tmp.lastTime = getTimer()

– My preferred way of adding the listener

local last_dx = 0;

tmp.enterFrame = function( self )
local curTime = getTimer()

local dt = curTime-self.lastTime
dt = dt / 1000

self.lastTime = curTime

local dx = xpps * dt
local dy = ypps * dt

if last_dx == 0 then
– first time, we will init last_dx
last_dx = dx;
else
— we compare last_dx with current dx to notice lag:
if math.abs(dx) > 2*math.abs(last_dx) then
print( “OPP!! dx and last dx to much difference”, dx,last_dx )
end
end

last_dx = dx;

self:translate(dx,dy)

if self.x > MAXX then
self.x = MAXX
xpps = -xpps;
elseif self.x < MINX then
self.x = MINX
xpps = -xpps;
end

end

Runtime:addEventListener( “enterFrame”, tmp )[/lua]

Questions and Advice First

What build of Corona are you using and what device are you testing on?

Also, I don’t generally suggest using the 60 FPS setting unless you absolutely need it.  If you’re having a problem frame rate, this will actually exacerbate the issue.

My Findings

I modified your code as follows:

\_G.MINX = display.screenOriginX; \_G.MINY = display.screenOriginY; \_G.MAXX = display.contentWidth - display.screenOriginX \_G.MAXY = display.contentHeight - display.screenOriginY \_G.W = \_G.MAXX - \_G.MINX; \_G.H = \_G.MAXY - \_G.MINY; \_G.CX = \_G.MINX + \_G.W/2 \_G.CY = \_G.MINY + \_G.H/2 local xpps = 1000 -- x-rate == 10 pixels per second for this example local ypps = 0 -- y-rate == 0 pixels per second for this example local getTimer = system.getTimer -- Localization for speedup and easier typing local tmp = display.newCircle( CX, CY, 50 ) tmp.lastTime = getTimer() -- My preferred way of adding the listener -- local last\_dx = 0; tmp.enterFrame = function( self ) local curTime = getTimer() local dt = curTime-self.lastTime dt = dt / 1000 self.lastTime = curTime local dx = xpps \* dt local dy = ypps \* dt print( "Delta time == " .. round(dt,4) .. "; dx: " .. dx ) self:translate(dx,dy) if self.x \> MAXX then self.x = MAXX xpps = -xpps; elseif self.x \< MINX then self.x = MINX xpps = -xpps; end end Runtime:addEventListener( "enterFrame", tmp )

Then, I built it with Corona 2015.2605 and ran in on my Gen1 iPad Air.

These are my results:

Delta time == 0.0168; dx: -16.779 Delta time == 0.0168; dx: -16.825 Delta time == 0.017; dx: -16.961 Delta time == 0.0159; dx: -15.917 Delta time == 0.0169; dx: -16.938 Delta time == 0.0169; dx: -16.853 Delta time == 0.0168; dx: -16.843 Delta time == 0.017; dx: -16.974 Delta time == 0.0169; dx: -16.905 Delta time == 0.0158; dx: -15.788 Delta time == 0.017; dx: -17 Delta time == 0.017; dx: -17.029 Delta time == 0.0157; dx: -15.702 Delta time == 0.0169; dx: -16.889 Delta time == 0.0168; dx: -16.829 Delta time == 0.0173; dx: -17.253 Delta time == 0.016; dx: -16.026 Delta time == 0.017; dx: -17.007 Delta time == 0.017; dx: -16.956 Delta time == 0.0157; dx: -15.729

Ah Thank so much @roaminggamer, i am using 2014.2511 (2014.11.18) for testing and build on real device.

I think my environment has problem. After download and test with 2015.2625, on ipad 2 mini, delta time change between 15ms to 17 ms and that is good for smooth movement!

And about your advice:

Also, I don’t generally suggest using the 60 FPS setting unless you absolutely need it.  If you’re having a problem frame rate, this will actually exacerbate the issue.

 

Can you advice what is suitable FPS for game type like:

  1. arcade action racing game like 2 cars: https://itunes.apple.com/us/app/2-cars/id936839198?mt=8
  2. puzzle game like candy crush saga

Thank so much for your explain and help me find out problem!  :stuck_out_tongue:

In each of the game cases, you can start at 60 FPS if you like, but if you come up against a frame rate issue you can’t solve easily, you’d be best off simply reducing to 30.  Of those two games, depending on the amount of particle effects and extras you put in, only a ‘Candy Crush’ like game may need to be run at 30.  ‘2 Cars’ should never have a problem at 60 FPS.

Thank so much @roaminggamer ! 

Read this blog post on how to implement smooth movement w/ enterFrame:

https://coronalabs.com/blog/2013/06/18/guest-tutorial-delta-time-in-corona/

@roaminggamer Yes, i read that article before and using it in my game! As you see

[lua]local dt = (temp-runtime) / (1000/60) --60fps or 30fps as base[/lua]

(1000/60) is just a constant number, so my code above will produce the same delta time effect.  So when run that example on real device, it is till not smooth!

If we print dt, you will see delta time change from 0.94 ~ 2.1 (see log bellow). So if we translate box by dt * 5, in some frame, it moves 5 px but in other frame, it will move 10 px, that will cause zig zag effect (not smooth movement at all).

(Sorry for my bad of english, but you can build that example, comment rotation and scale code, just keep moving code and see on real device - especially on android -  the box will moving with some lag!)

Apr 28 23:42:49.475: 0.95639999999992

Apr 28 23:42:49.511: 2.1260400000001

Apr 28 23:42:49.525: 0.86069999999985

Apr 28 23:42:49.542: 0.98268000000004

Apr 28 23:42:49.558: 0.97770000000011

Apr 28 23:42:49.575: 1.05342

Apr 28 23:42:49.592: 0.98165999999983

Apr 28 23:42:49.610: 1.0677000000001

Apr 28 23:42:49.625: 0.94997999999992

Apr 28 23:42:49.642: 0.9873000000001

Apr 28 23:42:49.659: 1.0121399999999

Apr 28 23:42:49.675: 0.99582000000009

Apr 28 23:42:49.685: 0.54858

Apr 28 23:42:49.701: 0.96461999999985

Apr 28 23:42:49.717: 0.96366000000009

Apr 28 23:42:49.732: 0.94265999999996

Apr 28 23:42:49.748: 0.95184000000008

Apr 28 23:42:49.764: 0.96101999999999

Apr 28 23:42:49.780: 0.94445999999989

Apr 28 23:42:49.809: 1.7160000000001

Apr 28 23:42:49.825: 0.99401999999995

Apr 28 23:42:49.842: 1.0066199999999

Apr 28 23:42:49.859: 1.0056600000001

Apr 28 23:42:49.875: 0.99851999999999

Apr 28 23:42:49.892: 1.0108799999999

Apr 28 23:42:49.909: 0.99696000000011

Apr 28 23:42:49.925: 0.94956000000006

You misunderstand the point of the article. Calculate the amount you move as a multiple of the delta time that has passed, not as a multiple of frames.  i.e. Move in pixels per second, not pixels per frame.

On thing I don’t agree with from the article is the incorporation of frame rate as part of the calculation.  Simply do this:

local xpps = 10 -- x-rate == 10 pixels per second for this example local ypps = 0 -- y-rate == 0 pixels per second for this example local getTimer = system.getTimer -- Localization for speedup and easier typing local tmp = newCircle( 10, 10, 10 ) tmp.lastTime = getTimer() -- My preferred way of adding the listener -- tmp.enterFrame = function( self ) local curTime = getTimer() local dt = curTime-self.lastTime dt = dt / 1000 self.lastTime = curTime local dx = xpps \* dt local dy = ypps \* dt self:translate(dx,dy) end Runtime:addEventListener( "enterFrame", tmp )

Regarding Frame Time

The frame time is not something you can control (except grossly by choosing 30 or 60 FPS in config.lua).  

Corona (like any other engine or SDK), does the best it can to maintain a regular frame time, but once the work for a frame has begun, Corona cannot stop till it is all done.  

If too much ‘work’ is scheduled in a frame it will extend the duration of that frame.  Additionally, the OS may influence frame time by doing background work which delays the frame.  

However, you can easily smooth time-based calculations, as shown above.

Yes, in my code above, i multiple with delta time for sure! and i do not include frame rate in formula. 

If you add print( “dt”,dt,“dx”,dx ) to your code, you will notice sometime dx very small (0.2112000000001), and sometime very big (3.1029999999999). 

And in action game like a racing game, we need fast moving object (x-rate and y-rate must be ~ 1000 for example) , so dx will change from 2 px to 30 px for example, and that will cause lag effect!

I know that in our game, if we update textures, update GUI, update textbox, do a heavy logic…, frame will be drop. But bellow, we just make a simple ping pong game, and if we deploy game on device, you will see it not smooth moving at all.

Just config project to 800,1200,letterbox, fps = 60 and see the log:

[lua]
display.setStatusBar( display.HiddenStatusBar )


– main.lua


_G.MINX = display.screenOriginX;
_G.MINY = display.screenOriginY;
_G.MAXX = display.contentWidth - display.screenOriginX
_G.MAXY = display.contentHeight - display.screenOriginY

_G.W = _G.MAXX - _G.MINX;
_G.H = _G.MAXY - _G.MINY;
_G.CX = _G.MINX + _G.W/2
_G.CY = _G.MINY + _G.H/2

local xpps = 1000 – x-rate == 10 pixels per second for this example
local ypps = 0 – y-rate == 0 pixels per second for this example

local getTimer = system.getTimer – Localization for speedup and easier typing

local tmp = display.newCircle( CX, CY, 50 )
tmp.lastTime = getTimer()

– My preferred way of adding the listener

local last_dx = 0;

tmp.enterFrame = function( self )
local curTime = getTimer()

local dt = curTime-self.lastTime
dt = dt / 1000

self.lastTime = curTime

local dx = xpps * dt
local dy = ypps * dt

if last_dx == 0 then
– first time, we will init last_dx
last_dx = dx;
else
— we compare last_dx with current dx to notice lag:
if math.abs(dx) > 2*math.abs(last_dx) then
print( “OPP!! dx and last dx to much difference”, dx,last_dx )
end
end

last_dx = dx;

self:translate(dx,dy)

if self.x > MAXX then
self.x = MAXX
xpps = -xpps;
elseif self.x < MINX then
self.x = MINX
xpps = -xpps;
end

end

Runtime:addEventListener( “enterFrame”, tmp )[/lua]

Questions and Advice First

What build of Corona are you using and what device are you testing on?

Also, I don’t generally suggest using the 60 FPS setting unless you absolutely need it.  If you’re having a problem frame rate, this will actually exacerbate the issue.

My Findings

I modified your code as follows:

\_G.MINX = display.screenOriginX; \_G.MINY = display.screenOriginY; \_G.MAXX = display.contentWidth - display.screenOriginX \_G.MAXY = display.contentHeight - display.screenOriginY \_G.W = \_G.MAXX - \_G.MINX; \_G.H = \_G.MAXY - \_G.MINY; \_G.CX = \_G.MINX + \_G.W/2 \_G.CY = \_G.MINY + \_G.H/2 local xpps = 1000 -- x-rate == 10 pixels per second for this example local ypps = 0 -- y-rate == 0 pixels per second for this example local getTimer = system.getTimer -- Localization for speedup and easier typing local tmp = display.newCircle( CX, CY, 50 ) tmp.lastTime = getTimer() -- My preferred way of adding the listener -- local last\_dx = 0; tmp.enterFrame = function( self ) local curTime = getTimer() local dt = curTime-self.lastTime dt = dt / 1000 self.lastTime = curTime local dx = xpps \* dt local dy = ypps \* dt print( "Delta time == " .. round(dt,4) .. "; dx: " .. dx ) self:translate(dx,dy) if self.x \> MAXX then self.x = MAXX xpps = -xpps; elseif self.x \< MINX then self.x = MINX xpps = -xpps; end end Runtime:addEventListener( "enterFrame", tmp )

Then, I built it with Corona 2015.2605 and ran in on my Gen1 iPad Air.

These are my results:

Delta time == 0.0168; dx: -16.779 Delta time == 0.0168; dx: -16.825 Delta time == 0.017; dx: -16.961 Delta time == 0.0159; dx: -15.917 Delta time == 0.0169; dx: -16.938 Delta time == 0.0169; dx: -16.853 Delta time == 0.0168; dx: -16.843 Delta time == 0.017; dx: -16.974 Delta time == 0.0169; dx: -16.905 Delta time == 0.0158; dx: -15.788 Delta time == 0.017; dx: -17 Delta time == 0.017; dx: -17.029 Delta time == 0.0157; dx: -15.702 Delta time == 0.0169; dx: -16.889 Delta time == 0.0168; dx: -16.829 Delta time == 0.0173; dx: -17.253 Delta time == 0.016; dx: -16.026 Delta time == 0.017; dx: -17.007 Delta time == 0.017; dx: -16.956 Delta time == 0.0157; dx: -15.729

Ah Thank so much @roaminggamer, i am using 2014.2511 (2014.11.18) for testing and build on real device.

I think my environment has problem. After download and test with 2015.2625, on ipad 2 mini, delta time change between 15ms to 17 ms and that is good for smooth movement!

And about your advice:

Also, I don’t generally suggest using the 60 FPS setting unless you absolutely need it.  If you’re having a problem frame rate, this will actually exacerbate the issue.

 

Can you advice what is suitable FPS for game type like:

  1. arcade action racing game like 2 cars: https://itunes.apple.com/us/app/2-cars/id936839198?mt=8
  2. puzzle game like candy crush saga

Thank so much for your explain and help me find out problem!  :stuck_out_tongue:

In each of the game cases, you can start at 60 FPS if you like, but if you come up against a frame rate issue you can’t solve easily, you’d be best off simply reducing to 30.  Of those two games, depending on the amount of particle effects and extras you put in, only a ‘Candy Crush’ like game may need to be run at 30.  ‘2 Cars’ should never have a problem at 60 FPS.

Thank so much @roaminggamer !