Storyboard Purging and Removing Functions

Hi so the problem I’m facing in my game is many of my scenes have functions that are the same name and the wrong functions are being called.

I’ve been trying to wrap my head around the idea of purging scenes.  Is there a way that I can remove all the functions  from a previous scene so that they don’t get called?  

Also could someone please explain what exactly purgeAll does and when it should be used.

Thanks!

Make sure your functions are not global and you won’t run into this problem.

Purging a scene just removes display objects from unused scenes.  Removing scenes, removes other things like functions.  But once you’ve overwritten a global, you’ve overwritten it.

purgeAll will remove all display objects that are managed by scenes that are off screen. 

Rob

I’ve read all the blogs, forums, docs, help files, watched the videos…everything to do with storyboard.

I’ve followed your advice Rob but when I goto my “game over” module the previous level (module) still executes it’s functions over the game over screen.

Every single function, ALL OF THEM (which are the main chunk of the game) are placed inside the function scene:enterScene(event) so I presume they are all local to the enter scene event. The functions are called within the scene:enterScene at the bottom and I have not declared any of them outside the enterScene function. Everything within the level1 module is also declared local, everything!

You have stated previously that you don’t want to remove the scene as it’s better to leave it in memory if you want to use it again later, which I plan on doing. Even so, out of frustration, I’ve included the lines:

 storyboard.purgeScene( “level1” )
   storyboard.removeScene( “level1” )

at the top of my game over module, it does nothing.

In every tutorial and video I’ve noticed that the destroy scene function never has any code in it, should it? Is this what I’m missing?

function scene:destroyScene(event)
    local group = self.view
end

I’ve tried every possible way of moving everything around and nothing makes a difference.

I even practiced writing seperate test code to make sure there wasn’t a bug or something and I got the storyboard working perfectly but without functions().

I’ve wasted 3 days on this now, I’m at my wits end.

How do you stop functions? Why are the functions executing when they are not being called?

Can you post your code?

Hi Rob here’s my level1 code, the goto gameover call is made in the function timesup()
 

local storyboard = require ("storyboard") local scene = storyboard.newScene() local buttons = require("buttons") local clock = require("clock") local lives = livesRem local pass = Pass local t = 1 local starPos = 0 local m1 = 1 local m2 = 1 local m3 = 1 local m4 = 1 local m5 = 1 local answer = 1 local physics = require("physics") physics.setScale(50) physics.start() function scene:createScene(event) local displayGroup = self.view storyboard.purgeScene( "menu" ) storyboard.purgeScene( "gameOver" ) audio.play( soundTable["backSound"], { channel=5, loops=-1 }) background = display.newImageRect( "images/backdrop.png", 1425, 900 ) background.x = \_W\*0.5; background.y = \_H\*0.5 logo = display.newImageRect("images/gameshow.png", 600, 246) logo.x=\_W\*0.5; logo.y=(\_H\*0.5)\*1.5 displayGroup:insert(background) displayGroup:insert(logo) end function scene:enterScene(event) local group = self.view function showTokens() for i = 1, lives do buttons.livesHeart[i].isVisible= true buttons.livesHeart[i].remove=true end for i = 1, pass do buttons.passToken[i].isVisible= true buttons.passToken[i].remove=true end end function buttons.button1:touch(e) if(e.phase == "ended") and answer == 1 then audio.stop(1) timer.cancel(selftimer) starPos = 1 animateStars() elseif(e.phase == "ended") and answer ~= 1 then audio.stop(1) timer.cancel(selftimer) timesup() end end function buttons.button2:touch(e) if(e.phase == "ended") and answer == 2 then audio.stop(1) timer.cancel(selftimer) starPos = 2 animateStars() elseif(e.phase == "ended") and answer ~= 2 then audio.stop(1) timer.cancel(selftimer) timesup() end end function buttons.button3:touch(e) if(e.phase == "ended") and answer == 3 then audio.stop(1) timer.cancel(selftimer) starPos = 3 animateStars() elseif(e.phase == "ended") and answer ~= 3 then audio.stop(1) timer.cancel(selftimer) timesup() end end function buttons.button4:touch(e) if(e.phase == "ended") and answer == 4 then audio.stop(1) timer.cancel(selftimer) starPos = 4 animateStars() elseif(e.phase == "ended") and answer ~= 4 then audio.stop(1) timer.cancel(selftimer) timesup() end end function questionCount() local options = { text = qCount, x = \_W\*0.183, y = \_H\*0.498, width = 66, height = 66, fontSize = 56, font = "Coaster", align = "center" } questionCountGraphic = display.newText(options) questionCountGraphic:setFillColor( 0.1, 0.1, 0.9 ) questionCountGraphic.isVisible = false questionCountGraphic.remove=true end function buttons.passButton:touch(e) if(e.phase == "ended") and pass \> 0 then buttons.button1:removeEventListener( "touch", buttons.button1 ) buttons.button2:removeEventListener( "touch", buttons.button2 ) buttons.button3:removeEventListener( "touch", buttons.button3 ) buttons.button4:removeEventListener( "touch", buttons.button4 ) buttons.passButton:removeEventListener("touch", buttons.passButton) audio.stop(1) timer.cancel(selftimer) audio.play( soundTable["passSound"], {channel=7} ) transition.to( buttons.passToken[pass], { time=2000, alpha=0, x=\_W\*0.9, y=\_H\*0.5 } ) buttons.passToken[pass].angularVelocity = 50 pass = pass -1 multiplier = 1 multiply() selftimer = timer.performWithDelay(2000, nextQuestion, 1) selftimer.remove=true end end function startTimer() buttons.button1:addEventListener("touch", buttons.button1) buttons.button2:addEventListener("touch", buttons.button2) buttons.button3:addEventListener("touch", buttons.button3) buttons.button4:addEventListener("touch", buttons.button4) buttons.passButton:addEventListener("touch", buttons.passButton) newQuestion() questionCountGraphic.isVisible = true logo.isVisible = false buttons.passButton.isVisible = true t = 1 selftimer = timer.performWithDelay(100, checktime, 61) selftimer.remove=true end function checktime() if t \> 58 then buttons.button1:removeEventListener( "touch", buttons.button1 ) buttons.button2:removeEventListener( "touch", buttons.button2 ) buttons.button3:removeEventListener( "touch", buttons.button3 ) buttons.button4:removeEventListener( "touch", buttons.button4 ) buttons.passButton:removeEventListener("touch", buttons.passButton) end if t == 4 then audio.play( soundTable["ticktock"], {channel=1} ) end if t \<= 60 then local idx = t clock.clocks[idx].isVisible = false idx = idx + 1 clock.clocks[idx].isVisible = true t = idx elseif t == 61 then timesup() end end function nextQuestion() textField1.isVisible = false textField2.isVisible = false textField3.isVisible = false textField4.isVisible = false textField5.isVisible = false questionCountGraphic.isVisible = false audio.stop(1) timer.cancel(selftimer) animateButtons() questionCount() for i = 1, 61 do clock.clocks[i].isVisible = false clock.clocks[i].remove=true end selftimer = timer.performWithDelay(1000, startTimer, 1) end function timesup() buttons.button1:removeEventListener( "touch", buttons.button1 ) buttons.button2:removeEventListener( "touch", buttons.button2 ) buttons.button3:removeEventListener( "touch", buttons.button3 ) buttons.button4:removeEventListener( "touch", buttons.button4 ) buttons.passButton:removeEventListener("touch", buttons.passButton) audio.play( soundTable["wrongSound"], {channel=3} ) lives = lives -1 multiplier = 1 multiply() if lives == -1 then transition.cancel() timer.cancel( selftimer ) storyboard.gotoScene("gameOver", {time=1000, effect = "crossFade"}) elseif lives \>= 0 then transition.to( buttons.livesHeart[lives+1], { time=2000, alpha=0, x=\_W\*0.92, y=\_H\*0.82 } ) buttons.livesHeart[lives+1].angularVelocity = 50 end selftimer = timer.performWithDelay(2000, nextQuestion, 1) end function updateScore() if t \< 62 then idx = t clock.clocks[idx].isVisible = false scGraphic.isVisible = false scoreGraphic() idx = idx + 1 clock.clocks[idx].isVisible = true t = idx audio.play( soundTable["chinkSound"] ) end end function animateStars() buttons.button1:removeEventListener( "touch", buttons.button1 ) buttons.button2:removeEventListener( "touch", buttons.button2 ) buttons.button3:removeEventListener( "touch", buttons.button3 ) buttons.button4:removeEventListener( "touch", buttons.button4 ) buttons.passButton:removeEventListener("touch", buttons.passButton) audio.play( soundTable["correctSound"], {channel=2} ) if multiplier \< 5 then multiplier = multiplier + 1 end multiply() local star = {} star[1] = display.newImageRect("images/star.png", 140, 140) star[2] = display.newImageRect("images/star1.png", 140, 140) star[3] = display.newImageRect("images/star2.png", 140, 140) star[4] = display.newImageRect("images/star.png", 140, 140) star[5] = display.newImageRect("images/star1.png", 140, 140) star[6] = display.newImageRect("images/star2.png", 140, 140) star[7] = display.newImageRect("images/star1.png", 140, 140) star[8] = display.newImageRect("images/star2.png", 140, 140) star[9] = display.newImageRect("images/star.png", 140, 140) star[10] = display.newImageRect("images/star1.png", 140, 140) local i = 1 repeat if starPos == 1 then star[i].x = \_W\*0.36; x=star[i].x; star[i].y = \_H\*0.73; y=star[i].y end if starPos == 2 then star[i].x =\_W\*0.7; x=star[i].x; star[i].y = \_H\*0.73; y=star[i].y end if starPos == 3 then star[i].x = \_W\*0.36; x=star[i].x; star[i].y = \_H\*0.9; y=star[i].y end if starPos == 4 then star[i].x = \_W\*0.7; x=star[i].x; star[i].y = \_H\*0.9; y=star[i].y end star[i].anchorX=0.5; star[i].anchorY=0.5 physics.addBody( star[i], "dynamic", {density=0.5, friction=0.1, radius=35} ) star[i].remove=true i = i +1 until i == 11 transition.to( star[1], { time=2000, alpha=0, x=x-200, y=y-200 } ) star[1].angularVelocity = 50 transition.to( star[2], { time=1500, alpha=0, 0, y=y-200 } ) star[2].angularVelocity = 70 transition.to( star[3], { time=2000, alpha=0, x=x+200, y=y-200 } ) star[3].angularVelocity = -50 transition.to( star[4], { time=1500, alpha=0, x=x+100, y=y-100 } ) star[4].angularVelocity = 40 transition.to( star[5], { time=2000, alpha=0, x=x-200, y=y-100 } ) star[5].angularVelocity = -60 transition.to( star[6], { time=1500, alpha=0, x=x+100, y=y+100 } ) star[6].angularVelocity = 50 transition.to( star[8], { time=2000, alpha=0, x=x+200, 0 } ) star[8].angularVelocity = -50 transition.to( star[9], { time=1500, alpha=0, x=x-100, y=y+100 } ) star[9].angularVelocity = 60 transition.to( star[10], { time=2000, alpha=0, x=x-200, 0 } ) star[10].angularVelocity = -60 selftimer = timer.performWithDelay(20, updateScore, (61-t)) selftimer = timer.performWithDelay(2000, nextQuestion, 1) end function animateButtons() buttons.passButton.isVisible = false questionCountGraphic.isVisible = false logo.isVisible = true audio.stop(1) audio.play( soundTable["whooshSound"], {channel=6} ) transition.moveTo( buttons.button1, { x=\_W+400, y=\_H\*0.73, time=500 } ) transition.moveTo( buttons.button2, { x=\_W+400, y=\_H\*0.73, time=500 } ) transition.moveTo( buttons.button3, { x=\_W+400, y=\_H\*0.9, time=500 } ) transition.moveTo( buttons.button4, { x=\_W+400, y=\_H\*0.9, time=500 } ) transition.moveTo( buttons.button5, { x=\_W+1000, y=\_H\*0.5, time=500 } ) selftimer = timer.performWithDelay(500, animateButtons1, 1) end function animateButtons1() transition.moveTo( buttons.button1, { x=\_W\*0.36, y=\_H\*0.73, time=500 } ) transition.moveTo( buttons.button2, { x=\_W\*0.7, y=\_H\*0.73, time=500 } ) transition.moveTo( buttons.button3, { x=\_W\*0.36, y=\_H\*0.9, time=500 } ) transition.moveTo( buttons.button4, { x=\_W\*0.7, y=\_H\*0.9, time=500 } ) transition.moveTo( buttons.button5, { x=\_W\*0.5, y=\_H\*0.5, time=500 } ) end function scoreGraphic() score = score + (multiplier\*10) local options = { text = score, x = \_W\*0.87, y = \_H\*0.19, width = 200, height = 100, fontSize = 64, font = "Coaster", align = "right" } scGraphic = display.newText(options) scGraphic:setFillColor( 0, 0.3, 0.9 ) scGraphic.remove=true end function multiply() if m1 == 3 then m1 = 1 elseif m1 == 1 then direction1 = -360 else direction1 = 360 end if m2 == 3 then m2 = 1 elseif m2 == 1 then direction2 = -360 else direction2 = 360 end if m3 == 3 then m3 = 1 elseif m3 == 1 then direction3 = -360 else direction3 = 360 end if m4 == 3 then m4 = 1 elseif m4 == 1 then direction4 = -360 else direction4 = 360 end if m5 == 3 then m5 = 1 elseif m5 == 1 then direction5 = -360 else direction5 = 360 end buttons.multiply1.isVisible = false buttons.multiply2.isVisible = false buttons.multiply3.isVisible = false buttons.multiply4.isVisible = false buttons.multiply5.isVisible = false if multiplier == 1 then m1=m1+1 buttons.multiply1.isVisible = true buttons.multiply1.anchorX=0.5; buttons.multiply1.anchorY=0.5 transition.to( buttons.multiply1, { rotation=direction1, time=3000, transition=easing.inOutCubic } ) end if multiplier == 2 then m2=m2+1 buttons.multiply2.isVisible = true buttons.multiply2.anchorX=0.5; buttons.multiply2.anchorY=0.5 transition.to( buttons.multiply2, { rotation=direction2, time=3000, transition=easing.inOutCubic } ) end if multiplier == 3 then m3=m3+1 buttons.multiply3.isVisible = true buttons.multiply3.anchorX=0.5; buttons.multiply3.anchorY=0.5 transition.to( buttons.multiply3, { rotation=direction3, time=3000, transition=easing.inOutCubic } ) end if multiplier == 4 then m4=m4+1 buttons.multiply4.isVisible = true buttons.multiply4.anchorX=0.5; buttons.multiply4.anchorY=0.5 transition.to( buttons.multiply4, { rotation=direction4, time=3000, transition=easing.inOutCubic } ) end if multiplier == 5 then m5=m5+1 buttons.multiply5.isVisible = true buttons.multiply5.anchorX=0.5; buttons.multiply5.anchorY=0.5 transition.to( buttons.multiply5, { rotation=direction5, time=3000, transition=easing.inOutCubic } ) end end function newQuestion() repeat r = math.random(questions.c-1) for i = 1, qCount do if questionsAsked[i] == r then r = 0 end end until r ~= 0 questionsAsked[qCount] = r qCount = qCount + 1 question = (questions.question[r]) answer\_1 = (questions.ans\_1[r]) answer\_2 = (questions.ans\_2[r]) answer\_3 = (questions.ans\_3[r]) answer\_4 = (questions.ans\_4[r]) for i = 1, 4 do local ran = math.random(4) if ran == 1 then local a = answer\_1; answer\_1 = answer\_2; answer\_2 = a end if ran == 2 then local a = answer\_2; answer\_2 = answer\_3; answer\_3 = a end if ran == 3 then local a = answer\_3; answer\_3 = answer\_4; answer\_4 = a end if ran == 4 then local a = answer\_4; answer\_4 = answer\_1; answer\_1 = a end end k=0 for j = 1, string.len(answer\_1) do local a=string.sub(answer\_1, j, j ) if a == "\*" then k=j end end if k ~= 0 then answer=1; answer\_1 = string.sub(answer\_1, 1, k-2 ) end k=0 for j = 1, string.len(answer\_2) do local a=string.sub(answer\_2, j, j ) if a == "\*" then k=j end end if k ~= 0 then answer=2; answer\_2 = string.sub(answer\_2, 1, k-2 ) end k=0 for j = 1, string.len(answer\_3) do local a=string.sub(answer\_3, j, j ) if a == "\*" then k=j end end if k ~= 0 then answer=3; answer\_3 = string.sub(answer\_3, 1, k-2 ) end k=0 for j = 1, string.len(answer\_4) do local a=string.sub(answer\_4, j, j ) if a == "\*" then k=j end end if k ~= 0 then answer=4; answer\_4 = string.sub(answer\_4, 1, k-2 ) end if string.len(answer\_1) \< 19 then ts1 = 48; yht1=1.03 elseif string.len(answer\_1) \> 18 then ts1 = 38; yht1=1.04 end if string.len(answer\_2) \< 19 then ts2 = 48; yht2=1.03 elseif string.len(answer\_2) \> 18 then ts2 = 38; yht2=1.04 end if string.len(answer\_3) \< 19 then ts3 = 48; yht3=1.03 elseif string.len(answer\_3) \> 18 then ts3 = 38; yht3=1.04 end if string.len(answer\_4) \< 19 then ts4 = 48; yht4=1.03 elseif string.len(answer\_4) \> 18 then ts4 = 38; yht4=1.04 end if string.len(question) \<= 35 then ht=1.15 elseif string.len(question) \> 35 then ht=1.1 end local myText = question local options = { text = myText, x = \_W\*0.54, y =(\_H\*0.5)\*ht, width = 800, height = 200, fontSize = 50, font = "Coaster", align = "center" } textField1 = display.newText( options ) textField1:setFillColor( 0, 0.3, 0.9 ) textField1.remove=true local myText = answer\_1 local options = { text = myText, x = \_W\*0.36, y = (\_H\*0.73)\*yht1, width = 580, height = 100, fontSize = ts1, font = "Coaster", align = "center" } textField2 = display.newText( options ) textField2:setFillColor( 0, 0.3, 0.9 ) textField2.remove=true local myText = answer\_2 local options = { text = myText, x = \_W\*0.7, y = (\_H\*0.73)\*yht2, width = 580, height = 100, fontSize = ts2, font = "Coaster", align = "center" } textField3 = display.newText( options ) textField3:setFillColor( 0, 0.3, 0.9 ) textField3.remove=true local myText = answer\_3 local options = { text = myText, x = \_W\*0.36, y = (\_H\*0.9)\*yht3, width = 580, height = 100, fontSize = ts3, font = "Coaster", align = "center" } textField4 = display.newText( options ) textField4:setFillColor( 0, 0.3, 0.9 ) textField4.remove=true local myText = answer\_4 local options = { text = myText, x = \_W\*0.7, y = (\_H\*0.9)\*yht4, width = 580, height = 100, fontSize = ts4, font = "Coaster", align = "center" } textField5 = display.newText( options ) textField5:setFillColor( 0, 0.3, 0.9 ) textField5.remove=true end showTokens() multiply() scoreGraphic() questionCount() startTimer() end function scene:exitScene(event) local group = self.view audio.stop(5) timer.cancel(selftimer) self.timer = nil if physics then physics.stop() end textField1.isVisible=false textField2.isVisible=false textField3.isVisible=false textField4.isVisible=false textField5.isVisible=false buttons.multiply5.isVisible = false buttons.multiply4.isVisible = false buttons.multiply3.isVisible = false buttons.multiply2.isVisible = false buttons.multiply1.isVisible = false buttons.scoreCard.isVisible = false buttons.livesCard.isVisible = false buttons.passCard.isVisible = false scGraphic.isVisible = false buttons.button1.isVisible = false buttons.button2.isVisible = false buttons.button3.isVisible = false buttons.button4.isVisible = false buttons.button5.isVisible = false buttons.passButton.isVisible = false questionCountGraphic.isVisible = false for i = 1, 61 do clocks[i].isVisible = false end for i=1, 5 do buttons.livesHeart[i].isVisible = false buttons.passToken[i].isVisible = false end buttons.button1:removeEventListener( "touch", buttons.button1 ) buttons.button2:removeEventListener( "touch", buttons.button2 ) buttons.button3:removeEventListener( "touch", buttons.button3 ) buttons.button4:removeEventListener( "touch", buttons.button4 ) buttons.passButton:removeEventListener("touch", buttons.passButton) buttons.multiply1.remove=true buttons.multiply2.remove=true buttons.multiply3.remove=true buttons.multiply4.remove=true buttons.multiply5.remove=true collectgarbage( "collect" ) end function scene:destroyScene(event) local group = self.view end scene:addEventListener( "createScene", scene ) scene:addEventListener( "enterScene", scene ) scene:addEventListener( "exitScene", scene ) scene:addEventListener( "destroyScene", scene ) return scene

Here’s the gameover code…

local storyboard = require ("storyboard") local scene = storyboard.newScene() storyboard.purgeScene( "level1" ) storyboard.removeScene( "level1" ) storyboard.purgeScene( "menu" ) local background, logo, replayButton, statsButton function scene:createScene(event) local group = self.view audio.play( soundTable["menuSound"], { channel=7, loops=-1 }) background = display.newImageRect( "images/backdrop.png", 1425, 900 ) background.x = \_W\*0.5; background.y = \_H\*0.5 logo = display.newImageRect("images/gameshow.png", 600, 246) logo.x=\_W\*0.5; logo.y=(\_H\*0.5)\*1.5 replayButton = display.newImageRect("images/Replaybutton.png", 300, 87) replayButton.x=\_W\*0.3; replayButton.y=\_H\*0.25 statsButton = display.newImageRect("images/Statsbutton.png", 300, 87) statsButton.x=\_W\*0.7; statsButton.y=\_H\*0.25 group:insert(background) group:insert(logo) group:insert(statsButton) group:insert(replayButton) end function scene:enterScene(event) local group = self.view function play(e) if e.phase == "ended" then storyboard.gotoScene("level1", {time=1000, effect = "slideRight"}) end end function stats(e) if e.phase == "ended" then storyboard.gotoScene("menu", {time=1000, effect = "slideRight"}) end end replayButton:addEventListener("touch", play) statsButton:addEventListener("touch", stats) end function scene:exitScene(event) local group = self.view audio.stop(7) replayButton:removeEventListener("touch", play) statsButton:removeEventListener("touch", stats) end function scene:destroyScene(event) local group = self.view end scene:addEventListener( "createScene", scene ) scene:addEventListener( "enterScene", scene ) scene:addEventListener( "exitScene", scene ) scene:addEventListener( "destroyScene", scene ) return scene

It’s a lot, sorry.

I don’t see where you are ever adding buttons and stars to the scene’s “group”.  Storyboard cannot manage your items (transition them on/off screen, clean them up for you, etc), if they have not been inserted into the “group”.

Second of all, even though functions like “multiply” and “scoreGraphic” are inside of the scene’s createScene() function, they are still global because you do not prefix them with the word “local” or an object name.  As an example:

function buttons.passButton:touch(e)

isn’t local because you’re adding touch() to the buttons.passButton object.  These locals could burn you later, but…

Any way, as far as calling storyboard.removeScene(), this is best done right before  you call storyboard.gotoScene(), i.e.

storyboard.removeScene(“level1”)

storyboard.gotoScene(“level1”)

Rob

Thanks for the advice Rob, I will move the removeScene to where you suggest.

Can you just answer one more question please?

So I can make a function local by prefixing it with local?

local function buttons.passButton:touch(e)

And I can insert any object into a group, even objects that are from another module, the buttons module for example?

group:insert(buttons.multiply1)

Thanks for your help.

Update…

I moved the removeScene, no change.

I made the the functions local, ie. local function newquestion() but this caused an error because the function can’t be called within another function with the call: newquestion(), I get the error: attempt to call newquestion() a nil value.

I tried to declare the function outside the function but it doesn’t make sense like it does when you declare variables, for example local score, lives, answer

I’m sure you can’t declare a function the same way, can you? local newquestion  or   local newquestion()  ???

I’m even more confused now :wacko:

OMG :o

Now when I insert the objects into the group like this: group:insert(textField5)

The text dissapears!

When I inserted another graphic object: group:insert(buttons.livesHeart[i])

The hearts dissapear too!

It goes from bad to worse. :blink:

You should probably move your local functions outside of your createScene() function.  You will likely run into some order dependencies too.  What you can do is to take off the “local”'s and at the top of the module do:

local functionName, otherFunctionName

etc.

This will scope them for the module and let you define them later (though you may have to do:

nextQuestion = function()

instead of function nextQuestion()

Your things are probably disappearing because you are making them all invisible.

Thanks Rob. I’ll implement your suggestions and thanks again for all your help.

"Second of all, even though functions like “multiply” and “scoreGraphic” are inside of the scene’s createScene() function, they are still global because you do not prefix them with the word “local” or an object name.  As an example:

function buttons.passButton:touch(e)

isn’t local because you’re adding touch() to the buttons.passButton object.  These locals could burn you later, but…"

Rob, did you mean that “function buttons.passButton:touch(e)” IS local because it’s prefixed by a local object’s name?

I’d be worried about the functions defined within functions. When you do this in lua it creates a thing called a closure http://www.lua.org/pil/6.1.html where the context of the function is preserved when it is called.

So if you do something like:

local function fred() &nbsp; &nbsp;local jim = 4 &nbsp; &nbsp;local newfn = function() print(jim) end &nbsp; &nbsp;return newfn end fn = fred() fn()&nbsp;

it will print ‘4’. Even though ‘jim’ is local to the function, and in a language like ‘C’ would have disappeared when it went out of scope, in lua (and Javascript) it is ‘dragged along’ with the function. The variable ‘jim’ is preserved as the context of the function, it isn’t accessible any other way - this is how some OOP systems create private members.

Even if you insert ‘jim = 99’ before the fn = fred() call it will still print ‘4’

It might be worth printing the table reference in the code, to check it hasn’t dragged the wrong context in at some time.

… and I should look at the subject of ‘arrays’ in lua. You have much too much duplicated code. 

If you find yourself typing the same thing over and over again, either you are Jilly Cooper or there is a cleaner simpler way of doing it.

Hi Paul

I have since been through my code and implemented arrays to shorten some of it, as long as the shortened code isn’t too complicated and might create more problems. Don’t forget I’m totally noob!

You and mort have pointed out something I have spent a great deal of time on, memory leaks, I’ve been reading everything I can find on this subject and yes my code has memory leaks which are elusive to track down if you are a beginner.

One thing that has me perplexed is the old foo = function() or function = foo() paradigm.

When I followed Robs advice about changing my functions, for example: function startTimer() to local startTimer = function() I ended up with an order problem. You must put the function above the call to that function or you get an error. I have various calls to various functions from within functions, so I spent the best part of a day re-arranging the order of my functions. Moving one up the stack would put it above another function that it needs to move onto next, etc…

Now I always write my functions local foo = function() from the start and I know how to structure my code because of it.

Something else I’m not happy about is that I don’t know what needs to be nilled out at the end of my code, so in every exitScene I have a long list of all the variables, arrays, text objects and functions I used in the main block being nilled out. Not very pretty and not fixing the momerory leaks, my code is gaining 100k every level but I do at least have the texture memory stable.

I’m looking at touch(e) events, any latent perform with delay events and timers to see if I can track down my leaks but to tell the truth I don’t know what I’m doing.

Do any of you know where there is a good tutorial or blog going through ALL the things that can cause memory leaks?

lua is fairly straightforward as regards memory problems. The basic problem is keeping references to something you want to free - if you do that then the lua garbage collector won’t know what to do with it. The main villain is probably event listeners.

(I am very unconvinced about the observer pattern in lua in general. I (personally) like to implement the listeners as direct methods, and call them as ‘events’ from an event dispatcher.  People argue that you reduce dependency with listeners, and this is sort of true, the thing is , you end up with dependencies on events rather than dependencies on methods)

The best way to remove memory leaks is to keep the code as clean and structured as possible. Try to separate the view and the processing. It’s a problem with Corona - because the view (the display bits) and the model (the state of the program) are the same thing, it encourages spaghetti code. 

Wow Paul

I wish I understood what you just wrote because it sounds like something I need to know.

I will look up the meaning of “the observer pattern in lua” and “implement the listeners as direct methods”

and “event dispatcher” and “reduce dependency with listeners” and “dependencies on events rather than dependencies on methods”

It’s all jibberish to me but I am intrigued by the event dispatcher method, I think that might help clean up my code.

I’m prepared to re-write all my 3500 (and climbing) lines of code as many times as it takes until it’s lean and clean.

Ah :slight_smile:

The observer pattern is the official name of the ‘sending events’ thing that Corona has - you listen for an event, and it sends a message, and you react to that event. 

(The problem with doing this is that the thing sending the message has to know about the thing it is sending a message to, and if you don’t forget this (removeEventListener) then there is still a reference there - so it may not ‘clear up’ the memory)

What I would suggest is two things - one write some code in lua (or anything) outside Corona SDK, so you are comfortable with tables and so on. Corona is event driven , when you are new to programming it is much easier to learn skills without events.

Secondly is don’t try anything too complicated if you are a newbie - go for something simple - just Pong, or Breakout, something with one basic screen, before trying anything too serious. I’ve been coding for near on 40 years, but we all start with Print “Hello World” or something similar. 

Structure and organisation is important - for simple things it doesn’t matter, but anything complicated can end up with umpteen interconnected bits of code. Try and break things up into separate bits. Try to make your functions do one thing or one closely knit group of things and do them well.

Once you’ve done this you will get a feel on how to engineer more complicated projects. Good luck :slight_smile:

Something else that’s perplexed me is using the Storyboard, I have read extensively on this subject.

I presumed that in my bare bones main.lua module I would have the following lines of code:

storyboard.gotoScene(“menu”)

storyboard.gotoScene(“level1”)

storyboard.gotoScene(“transitionScreen”)

storyboard.gotoScene(“level2”)

storyboard.gotoScene(“transitionScreen”)

I have found myself putting the gotoScene in each module so each module moves directly onto the next.

I’m an old school basic programmer so my logic is to be able to goto where I like using routines and I’m finding object orientated programming a tough nut to crack.

Just FYI, not all eventListeners are the culprits:  http://coronalabs.com/blog/2013/04/02/cleaning-up-display-objects-andlisteners/

What About Event Listeners?

There are two types of event listeners. One type is attached to  display objects  and the other is attached to the  global Runtime.

When you create an object and add a touch handler to it, you’re attaching a block of code via reference to the table. That is, you’re not adding the code to the table, but a  pointer  to the code. Code is in its own memory and it’s not something you allocate and dispose of. The code persists and there’s only one copy of it.

Once an object is removed, there is no way to trigger events against it, so in effect the event handlers are removed for you. You do  not  have to remove these if they are attached to an object that’s in a scene-managed group.

Runtime  listeners, on the other hand, must be removed because they’ll continue to run after the scene is taken out of memory and the functions that are executing are likely going to reference an object that is no longer around. This can cause your app to crash.