Removing timers on scene change - help needed please

Hi,

I’m having problems with trying to cancel a timer if a scene is changed.

I have used the ‘Bullets’ sample code packaged with Corona as the basis for my attempt below. What the code is doing is basically having bats fly across the screen, related to a timer.

If the user stays on the page and lets the effect take place, all is fine.

However, if the user skips the page and continues on, the app crashes.

I’m guessing this is because I haven’t cancelled the timer if the user moves on. How do I do that? Any help is really appreciated

Steve

[lua]module(…, package.seeall)

new = function ( params )

local ui = require ( “ui” )
local music = audio.loadSound(“sounds/LegendTheme.mp3”)
audio.stop( ) --music, {channel = 4}

local localGroup = display.newGroup()

—End of header-----

local background = display.newImage( “images/p19_background.jpg”, true )

local physics = require(“physics”)
physics.start()

physics.setScale( 40 )

local batsound = audio.loadSound(“sounds/P19_Bats.mp3”)
local banshee = audio.loadSound(“sounds/P19_Banshee.mp3”)

local pagetext = display.newImage( “images/p19_text.png”, true )
pagetext.x = 230
pagetext.y = 150

local bats = {}
local n = 0

local function throwBrick()
n = n + 1
bats[n] = display.newImage( “images/p19_bat.png”, -150, 600)
physics.addBody( bats[n], { density=-20, friction=0, bounce=0 } )

–remove the “isBullet” setting below to see the brick pass through cans without colliding!
bats[n].isBullet = true

bats[n].angularVelocity = 10
bats[n]:applyForce( 2400, 0, bats[n].x, bats[n].y )
end

function start()

– throw 3 bats

timerStash.newTimer = timer.performWithDelay( 500, throwBrick, 4)
audio.play( batsound, { channel = 0, loops = 0 } )
audio.play( banshee, {channel = 0, loops = 0} )
end

– wait 800 milliseconds, then call start function above
timerStash.newTimer = timer.performWithDelay( 3000, start )

local function throwBrick2()
n = n + 1
bats[n] = display.newImage( “images/p19_bat.png”, -150, 300)
physics.addBody( bats[n], { density=-20, friction=0, bounce=0 } )

–remove the “isBullet” setting below to see the brick pass through cans without colliding!
bats[n].isBullet = true

bats[n].angularVelocity = 10
bats[n]:applyForce( 2400, 0, bats[n].x, bats[n].y )
end

function start2()

– throw 3 bats

timerStash.newTimer = timer.performWithDelay( 500, throwBrick2, 4)
end

– wait 800 milliseconds, then call start function above
timerStash.newTimer = timer.performWithDelay( 3150, start2 )

–[[ local function throwBrick3()
n = n + 1
bats[n] = display.newImage( “images/p19_bat.png”, -150, 400)
physics.addBody( bats[n], { density=-20, friction=0, bounce=0 } )

–remove the “isBullet” setting below to see the brick pass through cans without colliding!
bats[n].isBullet = true

bats[n].angularVelocity = 10
bats[n]:applyForce( 2400, 0, bats[n].x, bats[n].y )
end

function start(event)

– throw 3 bats

timerStash.newTimer = timer.performWithDelay( 500, throwBrick3, 1)
end

– wait 800 milliseconds, then call start function above
timerStash.newTimer = timer.performWithDelay( 3300, start )

local function throwBrick4()
n = n + 1
bats[n] = display.newImage( “images/p19_bat.png”, -150, 500)
physics.addBody( bats[n], { density=-20, friction=0, bounce=0 } )

–remove the “isBullet” setting below to see the brick pass through cans without colliding!
bats[n].isBullet = true

bats[n].angularVelocity = 10
bats[n]:applyForce( 2400, 0, bats[n].x, bats[n].y )
end

function start(event)

– throw 3 bats

timerStash.newTimer = timer.performWithDelay( 500, throwBrick3, 1)
end

– wait 800 milliseconds, then call start function above
timerStash.newTimer = timer.performWithDelay( 3450, start )

local function sound(event)

audio.play( bansheesound, { channel = 0, loops = 0} )

end
timerStash.newTimer = timer.performWithDelay(1000, sound, false ) --]]

---- Common UI controls—
local mainmenut = function ( event )
if event.phase == “release” then
director:changeScene( “chap3toc”, “fade” )
display.remove(bats)
–display.remove(banshee)
audio.stop()
audio.dispose()
–bansheesound=nil
physics.stop()
end
end

local mainmenu = ui.newButton{
default = “images/ui_p19.png”,
over = “images/ui_p19.png”,
onEvent = mainmenut,
id = “mainmenu”
}

mainmenu.x = display.contentWidth /2
mainmenu.y = 994

local btfort = function ( event )
if event.phase == “release” then
director:changeScene( “page20”, “crossfade” )
display.remove(bats)
audio.stop()
audio.dispose()
–bansheesound=nil
physics.stop()
end
end

local btfor = ui.newButton{
default = “images/arrow.png”,
over = “images/arrow2.png”,
onEvent = btfort,
id = “btfor”
}

local btbackt = function ( event )
if event.phase == “release” then
director:changeScene( “page18”, “crossfade” )
display.remove(bats)
audio.stop()
audio.dispose()
–bansheesound=nil
physics.stop()
end
end

local btback = ui.newButton{
default = “images/back.png”,
over = “images/back2.png”,
onEvent = btbackt,
id = “btback”
}

btfor.x = 710
btfor.y = 975

btback.x = 58
btback.y = 975

----End Common UI Controls

—Footer and placing of localGroup Assets –

local initVars = function ()

localGroup:insert ( background )
–localGroup:insert ( bats )
localGroup:insert ( pagetext )
localGroup:insert( btfor )
localGroup:insert( btback )
localGroup:insert( mainmenu )

end

initVars()

return localGroup

end
----END----[/lua]

A few things that may help:

  1. While I don’t see the creation of the “timerStash” table in the code you posted, it’s good that you are already organizing your timers into a table - that will help you achieve what you want to do.

  2. HOWEVER, each time you create a new timer, you are re-using the variable timerStash.newTimer to hold that timer. This is bad, because it effectively eliminates your ability to do anything with previously-created timers (like cancel them!)

  3. SO: to start with, stop creating new timers by declaring timerStash.newTimer = timer.performWithDelay() - INSTEAD, try this method:

    timerStash[#timerStash+1] = timer.performWithDelay()

  4. Now that all your timers have their own variables assigned to them, you can cancel all your timers at once when changing scenes by inserting the following code: 

for i = 1, #timerStash do if timerStash[i] ~= nil then timer.cancel(timerStash[i]) end end

The above steps should accomplish what you want to get done. But I would suggest you go and vote up this feature request: http://feedback.coronalabs.com/forums/188732-corona-sdk-feature-requests-feedback/suggestions/5067720-tag-support-for-timers-same-method-as-transitions 

That feature request is for the timer API to catch up with the more recent transition API, which supports tagging and global pause/cancel/resume support. If that feature gets implemented, then we’d be able to easily cancel all timers at once (or a subset of tagged timers) without the need to keep them all in a table. It’d be a much better solution - so go vote for it! :slight_smile:

Good luck!

Hi Steve,

Just wanted to share that I was inspired by this post to create my own “timer 2.0” library that is a drop-in replacement for the timer library, that adds some great new features (basically the ones that are requested in the feature request I linked to):

  • You can add tags to timers when creating them using timer.performWithDelay()
  • You can pause, resume, or cancel all active timers at once by simply calling timer.pause(), timer.resume(), or timer.cancel() with no arguments.
  • You can pause, resume, or cancel all timers with a specific tag by passing that tag as a argument to timer.pause(), timer.resume(), or timer.cancel()
  • You can exclude specific timers from the “catch-all” pause/resume/cancel functions by adding “true” as an argument to timer.performWithDelay, setting that timer’s “exclude” parameter to true.

I’ve written a blog post on my site detailing how to use the updated library, and you can download the library’s lua file from that post. Here’s a link: http://www.jasonschroeder.com/2015/02/25/timer-2-0-library-for-corona-sdk/

Hope you find it useful!

Thanks so much for this.

Really appreciate your help on this.

Fantastic work

:slight_smile:

A few things that may help:

  1. While I don’t see the creation of the “timerStash” table in the code you posted, it’s good that you are already organizing your timers into a table - that will help you achieve what you want to do.

  2. HOWEVER, each time you create a new timer, you are re-using the variable timerStash.newTimer to hold that timer. This is bad, because it effectively eliminates your ability to do anything with previously-created timers (like cancel them!)

  3. SO: to start with, stop creating new timers by declaring timerStash.newTimer = timer.performWithDelay() - INSTEAD, try this method:

    timerStash[#timerStash+1] = timer.performWithDelay()

  4. Now that all your timers have their own variables assigned to them, you can cancel all your timers at once when changing scenes by inserting the following code: 

for i = 1, #timerStash do if timerStash[i] ~= nil then timer.cancel(timerStash[i]) end end

The above steps should accomplish what you want to get done. But I would suggest you go and vote up this feature request: http://feedback.coronalabs.com/forums/188732-corona-sdk-feature-requests-feedback/suggestions/5067720-tag-support-for-timers-same-method-as-transitions 

That feature request is for the timer API to catch up with the more recent transition API, which supports tagging and global pause/cancel/resume support. If that feature gets implemented, then we’d be able to easily cancel all timers at once (or a subset of tagged timers) without the need to keep them all in a table. It’d be a much better solution - so go vote for it! :slight_smile:

Good luck!

Hi Steve,

Just wanted to share that I was inspired by this post to create my own “timer 2.0” library that is a drop-in replacement for the timer library, that adds some great new features (basically the ones that are requested in the feature request I linked to):

  • You can add tags to timers when creating them using timer.performWithDelay()
  • You can pause, resume, or cancel all active timers at once by simply calling timer.pause(), timer.resume(), or timer.cancel() with no arguments.
  • You can pause, resume, or cancel all timers with a specific tag by passing that tag as a argument to timer.pause(), timer.resume(), or timer.cancel()
  • You can exclude specific timers from the “catch-all” pause/resume/cancel functions by adding “true” as an argument to timer.performWithDelay, setting that timer’s “exclude” parameter to true.

I’ve written a blog post on my site detailing how to use the updated library, and you can download the library’s lua file from that post. Here’s a link: http://www.jasonschroeder.com/2015/02/25/timer-2-0-library-for-corona-sdk/

Hope you find it useful!

Thanks so much for this.

Really appreciate your help on this.

Fantastic work

:slight_smile: