29 May 2013
Wednesday FAQs: Timers and Events
It’s Wednesday and time for another frequently asked questions (FAQs) session. Here are some FAQs on timers and on how events are processed in Corona SDK.
1. I’m trying to use timer.performWithDelay to add a delay in my code but it’s not working.
The API doesn’t do an actual delay within the code chunk but schedules a callback after the delay time has occurred. This is what is known as a non-blocking call.
local function doThis( event ) print( "Test 2" ) end print( "Test 1 " ) timer.performWithDelay( 1000, doThis, 1 ) print( "Test 3" )
The above will print the strings in the following order in the terminal window: “Test 1”, “Test 3”, “Test 2”. The last message is printed 1 second (1000 milliseconds) after the delay was scheduled.
If you want the strings to print out in the correct order, the statements after the timer.performWithDelay call must be moved to the callback routine.
local function doThis( event ) print( "Test 2" ) print( "Test 3" ) end print( "Test 1 " ) timer.performWithDelay( 1000, doThis, 1 )
If you need to delay sections of your code from executing, it needs to be done in a timer callback function (as shown above). Corona SDK doesn’t have any blocking delay() or sleep() functions.
2. I have a loop where I update an object on the screen but it seems to only update once.
local circle = display.newCircle( 10, 20, 25 ) for i = 1, 20 do circle.x = circle.x + i circle.y = circle.y + i end
The above code moves the circle down the screen at a very fast pace. It’s not a practical example, but looking at the code you would think that the circle is changing position each time through the for loop. In fact it only changes once — at the end of the code chunk. The circle’s x and y properties are computed each time through the loop, but it’s actual position (rendering of the object on the screen), only occurs after the code chunk has finished executing.
You also need to keep this in mind if you are trying to align objects multiple times in the same code chunk using setReferencePoint. Only the last setReferencePoint takes affect because the screen update only occurs after the code chunk has executed.
3. I’m doing some long calculations in my code and my touch listeners are not working. What’s happening?
If you try to add delays in your code using for loops or looping a long time within the same code chunk, you will affect the performance of your app. All events are fired after the code chunk ends, so as long as your code chunk is running, no listener events are called. All touch, timer, network, etc. events occur after the code chunk finishes.
If you do need to loop for a long time within your code, you should break it up into multiple code chunks (using timer.performWithDelay) to allow events to occur.
4. I use timer.performWithDelay but the delays times don’t seem right.
The delay time is an approximate time and is fired based on the Frames Per Second (FPS) value set in the config.lua file. The default is 30 FPS or 33.33 milliseconds. You can also set it to 60 FPS (16.166 milliseconds). As mentioned in question 3, event timers are fired at the end of the code chunk. If the code contains loops that extend beyond the timer’s value, the timer event may occur later than expected. There is a timer listener parameter, event.time, that will give you the time when the event was finally fired that you can use to calculate the true delay.
Another thing should be mentioned about the timer. The time value you specify is in milliseconds and is tied to either the default frame rate (30 FPS) or what you set in config.lua. If you set a value less than the frame rate time (16 or 33 milliseconds), the timer will fire every frame time instead of the actual time you set. So if you set the delay time to 10 milliseconds and you’re using the default 30 FPS, the delay will occur every 33.333 milliseconds.
local function listener( event ) print( "Timer ID and time: ", event.source, event.time ) end print( "Starting time: ", system.getTimer ) timer.performWithDelay( 1, listener, 1 ) timer.performWithDelay( 10, listener, 1 ) timer.performWithDelay( 25, listener, 1 ) timer.performWithDelay( 40, listener, 1 ) print( "Ending time: ", system.getTimer )
This displays the following in the terminal window (the times are in milliseconds).
Starting time: 29.97 Ending time: 30.343 Timer ID and time: table: 0x11fb64b20 61.031 Timer ID and time: table: 0x11fb17d80 61.031 Timer ID and time: table: 0x1016bbd90 61.031 Timer ID and time: table: 0x10e534c50 93.692
The bottom line is the timer delays are approximate. You can use the event.time if you need to adjust for any differences between the expected delay time and the actual delay time.
5. I have a timer call that’s not passing my parameter when done. What’s wrong?
This is a common issue for new developers in Lua. timer.performWithDelay expects to receive a reference to a function. This is common with all other APIs that expect a “listener” or “completion” reference.
local function doThis( value ) print( "Value is ", value ) end timer.performWithDelay( 500, doThis( 25 ),1 )
The above will print “25” because doThis( 25 ) is called before the delay is scheduled. The delay is performed but the listener is not called because it doesn’t think any listener was supplied (doThis returns nil).
The solution to this problem is to use Lua closures. Closures allow you to call a function with a parameter and have it return a unique local function reference that uses the supplied parameter when that function reference is called.
local function doThis( value ) return function() print( "Value is " .. value ) end end timer.performWithDelay( 500, doThis( 25 ),1 ) timer.performWithDelay( 1000, doThis( 100 ),1 )
Calling doThis function from within the timer.performWithDelay call will save value as a Lua upvalue that is associated with the returned function reference. The key thing to remember is doThis( 25 ) is executed before the delay is schedule and the value returned from the function call is used as the listener’s address when the timer is fired.
The nice thing about using closures is they can be called from different places using different parameter values and they return a unique function that uses the supplied parameter. In the above example the listener is called with the value 25 and than later the same listener is called with the value 100. This will print “25” and then “100” in the terminal window.
That’s it for today’s questions. I hope you enjoyed them and even learned a few things.