Wednesday FAQs: Timers and Events

Wednesday FAQs: Timers and Events

FAQ IconIt’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.

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.

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.

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.

This displays the following in the terminal window (the times are in milliseconds).

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.

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.

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.

tom
1Comment
  • Mo
    Posted at 16:51h, 29 May

    Great stuff as usual! I have two questions about timers if I may:

    1- I was told that instead of giving an handle to each timers used in an app (so I can destroy it at scene exit) I should probably just attach to an object like

    local image = display.newImageRect( imagePath..”startScreen.jpg”,1024,768 )
    image.timer = timer.performWithDelay( 500, doSomething,1 )

    Is that a good way to proceed since i want the timer to go away when image goes away (destroyed at scene exit)?

    2- If a timer is used in a local function, do I really need to give it an handle (again I so can nil it later) or is the timer would go away just because it is inside a local function? I will think the timer will go away only if it is a very short time like 50 mS but if I need to start a few seconds or minutes timer then once the code exit the local function, the timer should still be “alive” no?

    As you can see, after all this time I am still confused about scope!

    THANKS again for these wonderful Wednesday tutorials!

    Mo