Touch Event Executes Five (5) Times Instead of Once

I’m writing a game in Corona using LUA Glider as my IDE and Level Director X for making the levels along with TexturePacker to create spritesheets.

I’m encountering a very unusual thing with a touch event handler.

As you can see in the code below, I have an object called “sign”. I’ve assigned a touch event handler to it that calls the function “flipSign”. The function flipSign checks if the touch event has ended and if so it increases a variable called “signDirection” by 1 and prints the value.

Every time I run the app and click the sign object in Corona Simulator the output prints:

1 2 3 4 5

Clicking the sign object again increases the variable another 5 times to 10. My understanding is the code should only increase the variable by 1 each time the sign is clicked.

Any help understanding why this is happening would be greatly appreciated.

Thanks!

local composer = require( "composer" ) local scene = composer.newScene() require("lib.LD\_LoaderX") physics = require ("physics") physics.start() local render = require ("render") -- ----------------------------------------------------------------------------------- -- Code outside of the scene event functions below will only be executed ONCE unless -- the scene is removed entirely (not recycled) via "composer.removeScene()" -- ----------------------------------------------------------------------------------- local myLevel = {} local signDirection = 0 local function flipSign( event ) if (event.phase == "ended") then signDirection = signDirection + 1 print(signDirection) end return true end -- ----------------------------------------------------------------------------------- -- Scene event functions -- ----------------------------------------------------------------------------------- -- create() function scene:create( event ) -- Code here runs when the scene is first created but has not yet appeared on screen local sceneGroup = self.view myLevel= LD\_Loader:new(self.view) myLevel:loadLevel("level\_1") end -- show() function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is still off screen (but is about to come on screen) local sign = myLevel:getLayerObject("island\_4", "Sign\_2\_4").view sign:addEventListener ( "touch", flipSign ) elseif ( phase == "did" ) then -- Code here runs when the scene is entirely on screen end end -- hide() function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is on screen (but is about to go off screen) elseif ( phase == "did" ) then -- Code here runs immediately after the scene goes entirely off screen end end -- destroy() function scene:destroy( event ) local sceneGroup = self.view -- Code here runs prior to the removal of scene's view myLevel:removeLevel() myLevel = nil end -- ----------------------------------------------------------------------------------- -- Scene event function listeners -- ----------------------------------------------------------------------------------- scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) -- ----------------------------------------------------------------------------------- return scene

Hi @dkron,

This issue seems to point to that the touch listener is being added multiple times to the sign object (or rather, multiple listeners are being created on the same object). You probably shouldn’t add a new touch listener each time the scene shows, because that means every time you enter the scene, it’s adding another one… instead, do that just once, when you first create the object.

Best regards,

Brent

@Brent Sorrentino

Thanks for your response - it is truly appreciated. 

I ended up resolving the issue shortly after posting this, I moved the code below from “scene:show()” to “scene:create()” and everything worked as normal:

local sign = myLevel:getLayerObject(“island_4”, “Sign_2_4”).view

sign:addEventListener ( “touch”, flipSign )

Any chance you can please explain why this makes a difference? I only called it once in “scene:show()” and only load the scene once but yet it runs the touch event 5 times! When I moved the above code to “scene:create()” everything works properly and only happens once when clicked.

Would be great to get some understanding on this.

Thanks!

create() will only execute once in the scene lifecycle. whereas show() may execute many times.  let’s say your scene is your level and you call that from a menu scene.  once the scene is created you may show it many times… level1, level 2, etc.

always add your listeners in create().  you could add them in show() but you must check if they already exist and then only create if they don’t already exist.

@adrianm

Understood :slight_smile: Thanks so much for your assistance.

@adrianm @Brent Sorrentino

Sorry to bug you again but now that I’m completing my game I’m having the same issue as before - except this time it is when calling a function; I’ll explain quickly below:

main.lua

composer.gotoScene(“main_menu”)

NOTE: In my main.lua I call a scene titled “main_menu” only once.


main_menu.lua

composer.gotoScene(“level_”…id, “crossFase”, 1000)

NOTE: Depending on the level the user selects, in this case lets say its level 1, the scene transitions to level 1. This is called only once.


level_1.lua

function scene:show( event )

       local sceneGroup = self.view

       local phase = event.phase

       if (phase == “will”) then

              alert.showWelcome()

              print(“SHOWING WELCOME”)

       elseif (phase == “did”) then

       end

end

NOTE: As you can see above, I call a function “showWelcome()” located in alerts.lua  only once. Yet when I run the app the output print “SHOWING WELCOME” five times and the welcome window appears five times.

As of right now the only way to make it load once is to call the function in “scene:create” but I would like to avoid having ALL the code for my game in “scene:create”. 

Maybe I’m understanding this all wrong but I feel like something is screwing up.

It is good practise to do all your initialisation and object creation in create(). Show() is normally for stuff that needs to update your UI.

If for some reason your calling code is getting to load level_1 many times that would cause what you are seeing. Can you post your function in main_menu that calls level_1?

@adrianm

First off thank you so much for your reply and assistance. I’ve provided the code as requested below:

local composer = require( "composer" ) local scene = composer.newScene() local render = require ("render") -- ----------------------------------------------------------------------------------- -- Code outside of the scene event functions below will only be executed ONCE unless -- the scene is removed entirely (not recycled) via "composer.removeScene()" -- ----------------------------------------------------------------------------------- -- ----------------------------------------------------------------------------------- -- Scene event functions -- ----------------------------------------------------------------------------------- -- create() function scene:create( event ) -- Code here runs when the scene is first created but has not yet appeared on screen local sceneGroup = self.view local bg = render.renderBackground() sceneGroup:insert ( bg ) local logo = render.renderLogo() sceneGroup:insert ( logo ) local trophy = render.renderTrophy() sceneGroup:insert ( trophy ) local gears = render.renderGears() sceneGroup:insert ( gears ) local levels = render.renderLevels() sceneGroup:insert ( levels ) print("CREATED: Main Menu scene!") end -- show() function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is still off screen (but is about to come on screen) elseif ( phase == "did" ) then -- Code here runs when the scene is entirely on screen end end -- hide() function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is on screen (but is about to go off screen) elseif ( phase == "did" ) then -- Code here runs immediately after the scene goes entirely off screen composer.removeScene( "main\_menu" ) print("REMOVED: Main Menu scene!") end end -- destroy() function scene:destroy( event ) local sceneGroup = self.view -- Code here runs prior to the removal of scene's view end -- ----------------------------------------------------------------------------------- -- Scene event function listeners -- ----------------------------------------------------------------------------------- scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) -- ----------------------------------------------------------------------------------- return scene

As you can see above, there is a line of code that says ​local levels = render.renderLevels() therefore I’ve also provided the code from that specific function in my render.lua ​ file (which has all kinds of functions to load UI):

function R.renderLevels() local level = {} local scrollView = widget.newScrollView { --width = 800, --height = 130, width = display.contentWidth, height = 73, scrollWidth = 1200, scrollHeight = 130, verticalScrollDisabled = true, hideBackground = true } scrollView.x = xCenter scrollView.y = yCenter + 50 local function loadLevel( event ) local id = event.target.id if (event.phase == "ended") then composer.gotoScene("level\_"..id, "crossFade", 1000) end return true end for i=1, 5 do level[i] = display.newImage( menuImageSheet , menuSheet:getFrameIndex("level\_"..i)) scrollView:insert( level[i] ) level[i].id = i level[i].y = 35 level[1]:addEventListener ( "touch", loadLevel ) -- Change back to "i" after development. if i \> 1 then level[i].x = level[(i - 1)].x + 100 else level[i].x = 50 end end return scrollView end

Any help understanding why this is happening would be great :slight_smile:

Thanks!

@adrianm

I think I figured it out, when I change the following:

level[1]:addEventListener ( “touch”, loadLevel ) 

and change it to:

level[i]:addEventListener ( “touch”, loadLevel )

Everything seems to work fine - I think what happened was the “for” loop runs 5 times and was adding 5 EventListeners to level[1]. For development purposes I had it set to “1” instead of “i” so only level 1 would be allowed “touches”.

Looks like it was my own mistake all along. Please clarify if there is anything else you’ve noticed.

That’s exactly what’s happening… At least it’s a simple bug with a simple solution! Sometimes just explaining the bug makes you see the bug.

Why do you have level_1.lua, level_2.lua, etc? Why not have just level.lua and pass in the level number? Then based on the param load the correct level. Much more extendable.

Hi @dkron,

This issue seems to point to that the touch listener is being added multiple times to the sign object (or rather, multiple listeners are being created on the same object). You probably shouldn’t add a new touch listener each time the scene shows, because that means every time you enter the scene, it’s adding another one… instead, do that just once, when you first create the object.

Best regards,

Brent

@Brent Sorrentino

Thanks for your response - it is truly appreciated. 

I ended up resolving the issue shortly after posting this, I moved the code below from “scene:show()” to “scene:create()” and everything worked as normal:

local sign = myLevel:getLayerObject(“island_4”, “Sign_2_4”).view

sign:addEventListener ( “touch”, flipSign )

Any chance you can please explain why this makes a difference? I only called it once in “scene:show()” and only load the scene once but yet it runs the touch event 5 times! When I moved the above code to “scene:create()” everything works properly and only happens once when clicked.

Would be great to get some understanding on this.

Thanks!

create() will only execute once in the scene lifecycle. whereas show() may execute many times.  let’s say your scene is your level and you call that from a menu scene.  once the scene is created you may show it many times… level1, level 2, etc.

always add your listeners in create().  you could add them in show() but you must check if they already exist and then only create if they don’t already exist.

@adrianm

Understood :slight_smile: Thanks so much for your assistance.

@adrianm @Brent Sorrentino

Sorry to bug you again but now that I’m completing my game I’m having the same issue as before - except this time it is when calling a function; I’ll explain quickly below:

main.lua

composer.gotoScene(“main_menu”)

NOTE: In my main.lua I call a scene titled “main_menu” only once.


main_menu.lua

composer.gotoScene(“level_”…id, “crossFase”, 1000)

NOTE: Depending on the level the user selects, in this case lets say its level 1, the scene transitions to level 1. This is called only once.


level_1.lua

function scene:show( event )

       local sceneGroup = self.view

       local phase = event.phase

       if (phase == “will”) then

              alert.showWelcome()

              print(“SHOWING WELCOME”)

       elseif (phase == “did”) then

       end

end

NOTE: As you can see above, I call a function “showWelcome()” located in alerts.lua  only once. Yet when I run the app the output print “SHOWING WELCOME” five times and the welcome window appears five times.

As of right now the only way to make it load once is to call the function in “scene:create” but I would like to avoid having ALL the code for my game in “scene:create”. 

Maybe I’m understanding this all wrong but I feel like something is screwing up.

It is good practise to do all your initialisation and object creation in create(). Show() is normally for stuff that needs to update your UI.

If for some reason your calling code is getting to load level_1 many times that would cause what you are seeing. Can you post your function in main_menu that calls level_1?

@adrianm

First off thank you so much for your reply and assistance. I’ve provided the code as requested below:

local composer = require( "composer" ) local scene = composer.newScene() local render = require ("render") -- ----------------------------------------------------------------------------------- -- Code outside of the scene event functions below will only be executed ONCE unless -- the scene is removed entirely (not recycled) via "composer.removeScene()" -- ----------------------------------------------------------------------------------- -- ----------------------------------------------------------------------------------- -- Scene event functions -- ----------------------------------------------------------------------------------- -- create() function scene:create( event ) -- Code here runs when the scene is first created but has not yet appeared on screen local sceneGroup = self.view local bg = render.renderBackground() sceneGroup:insert ( bg ) local logo = render.renderLogo() sceneGroup:insert ( logo ) local trophy = render.renderTrophy() sceneGroup:insert ( trophy ) local gears = render.renderGears() sceneGroup:insert ( gears ) local levels = render.renderLevels() sceneGroup:insert ( levels ) print("CREATED: Main Menu scene!") end -- show() function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is still off screen (but is about to come on screen) elseif ( phase == "did" ) then -- Code here runs when the scene is entirely on screen end end -- hide() function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is on screen (but is about to go off screen) elseif ( phase == "did" ) then -- Code here runs immediately after the scene goes entirely off screen composer.removeScene( "main\_menu" ) print("REMOVED: Main Menu scene!") end end -- destroy() function scene:destroy( event ) local sceneGroup = self.view -- Code here runs prior to the removal of scene's view end -- ----------------------------------------------------------------------------------- -- Scene event function listeners -- ----------------------------------------------------------------------------------- scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) -- ----------------------------------------------------------------------------------- return scene

As you can see above, there is a line of code that says ​local levels = render.renderLevels() therefore I’ve also provided the code from that specific function in my render.lua ​ file (which has all kinds of functions to load UI):

function R.renderLevels() local level = {} local scrollView = widget.newScrollView { --width = 800, --height = 130, width = display.contentWidth, height = 73, scrollWidth = 1200, scrollHeight = 130, verticalScrollDisabled = true, hideBackground = true } scrollView.x = xCenter scrollView.y = yCenter + 50 local function loadLevel( event ) local id = event.target.id if (event.phase == "ended") then composer.gotoScene("level\_"..id, "crossFade", 1000) end return true end for i=1, 5 do level[i] = display.newImage( menuImageSheet , menuSheet:getFrameIndex("level\_"..i)) scrollView:insert( level[i] ) level[i].id = i level[i].y = 35 level[1]:addEventListener ( "touch", loadLevel ) -- Change back to "i" after development. if i \> 1 then level[i].x = level[(i - 1)].x + 100 else level[i].x = 50 end end return scrollView end

Any help understanding why this is happening would be great :slight_smile:

Thanks!

@adrianm

I think I figured it out, when I change the following:

level[1]:addEventListener ( “touch”, loadLevel ) 

and change it to:

level[i]:addEventListener ( “touch”, loadLevel )

Everything seems to work fine - I think what happened was the “for” loop runs 5 times and was adding 5 EventListeners to level[1]. For development purposes I had it set to “1” instead of “i” so only level 1 would be allowed “touches”.

Looks like it was my own mistake all along. Please clarify if there is anything else you’ve noticed.

That’s exactly what’s happening… At least it’s a simple bug with a simple solution! Sometimes just explaining the bug makes you see the bug.

Why do you have level_1.lua, level_2.lua, etc? Why not have just level.lua and pass in the level number? Then based on the param load the correct level. Much more extendable.