Tap of button on modal overlay propagates to scene below after hideOverlay()

This is driving me crazy… I have a game scene that is showing a pause menu using an overlay scene. On the overlay scene there are buttons including a resume game and restart level button (created using widget.newButton(). When showing the overlay, I use isModal = true which works fine in that the game scene does not receive touch events through the overlay scene.

When you hit the resume game button, the onRelease function fires a runtime event that is picked up by a listener on the game scene below, which hides the overlay and resumes the game… however, at that point the game receives a touch event right below where the resume button was!

It’s like the overlay scene is removed so quickly the touch event is still firing, even though hideOverlay() is not being triggered until the release phase. The same happens with the restart level button - the whole game resets and a touch event is immediately triggered below the button. I’ve tried moving the hideOverlay() function call around in my resume and restart functions and using transition time on hideOverlay() but I can’t fix this… any ideas much appreciated.

This is the pause and resume functions from game-scene.lua:
[lua]function scene:gamePaused(event)
print(“Pausing game”)

– Add event listeners for pause menu buttons
Runtime:addEventListener(“resumeGame”, scene)
Runtime:addEventListener(“restartGame”, scene)
Runtime:addEventListener(“quitGame”, scene)

local options = {
effect = “fade”,
time = 500,
isModal = true
}
storyboard.showOverlay(“scripts.menu.pause”, options)

return true
end

function scene:resumeGame(event)
print(“Resuming game”)
– Hide the pause menu
Runtime:removeEventListener(“resumeGame”, scene)
Runtime:removeEventListener(“restartGame”, scene)
Runtime:removeEventListener(“quitGame”, scene)
storyboard.hideOverlay(“fade”, 500)

– Resume game
game:resume()

return true
end

function scene:restartGame(event)
print(“Restarting level”)
– Reset game
game:quit()
local gameView = game:new(level.params)
self.view:insert(gameView)
game:start()

– Hide the menu
Runtime:removeEventListener(“resumeGame”, scene)
Runtime:removeEventListener(“restartGame”, scene)
Runtime:removeEventListener(“quitGame”, scene)
storyboard.hideOverlay(“fade”, 500)

return true
end[/lua]

And this is from menu/pause.lua:
[lua]-----------------------------------------------------------------------------------------
– Pause Menu (in game)

local storyboard = require(“storyboard”)
local scene = storyboard.newScene()
local widget = require(“widget”)

local resumeBtn
local restartBtn
local quitBtn


– EVENT HANDLERS

local function resumeBtnRelease(event)
if (event.phase == “release”) then
Runtime:dispatchEvent({name = “resumeGame”})
end
return true
end

local function restartBtnRelease(event)
Runtime:dispatchEvent({name = “restartGame”})
return true
end

local function quitBtnRelease(event)
Runtime:dispatchEvent({name = “quitGame”})
return true
end


– SCENE EVENTS

– Called when the scene’s view does not exist:
function scene:createScene(event)
print(“Showing pause menu”)

– background overlay
local background = display.newRect(0, 0, display.contentWidth, display.contentHeight)
background:setFillColor(0, 0, 0) – black background
background.alpha = 0.8

– “Game paused” title
local pausedText = display.newText(“Game paused”,
0,
0,
“Helvetica-Light”,
20
)
pausedText:setReferencePoint(display.CenterReferencePoint)
pausedText.x = display.contentCenterX
pausedText.y = display.contentWidth * 0.1

– Resume button
resumeBtn = widget.newButton{
label = “Resume game”,
labelColor = {
default = {0},
over = {128}
},
font = “Helvetica-Light”,
fontSize = 20,
width = 150,
emboss = true,
onRelease = resumeBtnRelease
}
resumeBtn.x = display.contentCenterX
resumeBtn.y = display.contentHeight * 0.3

– Restart button
restartBtn = widget.newButton{
label = “Restart level”,
labelColor = {
default = {0},
over = {128}
},
font = “Helvetica-Light”,
fontSize = 20,
width = 140,
emboss = true,
onRelease = restartBtnRelease
}
restartBtn.x = display.contentCenterX
restartBtn.y = display.contentHeight * 0.5

– Quit button
quitBtn = widget.newButton{
label = “Quit game”,
labelColor = {
default = {0},
over = {128}
},
font = “Helvetica-Light”,
fontSize = 20,
emboss = true,
onRelease = quitBtnRelease
}
quitBtn.x = display.contentCenterX
quitBtn.y = display.contentHeight * 0.7

self.view:insert(background)
self.view:insert(pausedText)
self.view:insert(resumeBtn)
self.view:insert(restartBtn)
self.view:insert(quitBtn)

end[/lua] [import]uid: 164840 topic_id: 33320 reply_id: 333320[/import]

Hello,
Before trying more “complex” possibilities, how about adding a short timer delay (50 milliseconds) before dispatching your events back to the main scene? This would in theory allow the touch to “resolve” before the event is actually dispatched. It might not work, but it’s worth a shot before you rework the code more severely.

Brent [import]uid: 9747 topic_id: 33320 reply_id: 132386[/import]

Good suggestion, works beautifully. Not sure whether this could be considered a hack or not but with the amount of time I spent scratching my head, I really don’t care.

Out of curiosity, I tried a few different values for the delay and it turns out, in the simulator at least, it works right down to 1 millisecond. Pretty strange behaviour really, I’ll be interested to see if it changes on different devices. Here’s my final code if anyone cares:

[lua]local function dispatchRuntimeEvent(event)
Runtime:dispatchEvent(event)
return true
end

local function resumeBtnRelease(event)
local event = {name = “resumeGame”}
timer.performWithDelay(1, function(e) return dispatchRuntimeEvent(event) end)
return true
end[/lua]

Out of interest, is there a better way to communicate this kind of thing from overlay scenes back to the main scene or are Runtime events the best way to do it?

Thanks! [import]uid: 164840 topic_id: 33320 reply_id: 132408[/import]

Hello again,
I’m not an expert with Storyboard, but there’s definitely a way to pass variables from scene to scene. I think you add them as parameters of the Storyboard module itself, or something like that. It’s definitely a better method than using Runtime dispatches, and it would solve this need for using timers.

Another method (one that I use) is to create a sort of global “hub” function in main.lua that accepts parameters from any external module and performs some action. Global functions are generally not good for memory management (global anything generally isn’t), but using just one as a hub is acceptable, I’ve found, because I use it for many purposes. That function performs an action and/or passes on its parameters to another local function above it.

Either of these cases should be preferable to the dispatching. :slight_smile:

Brent
[import]uid: 9747 topic_id: 33320 reply_id: 132467[/import]

Adding to Brent’s answer, storyboard parameters are easy:

In the sending module:

local options = {  
 effect="fade",  
 time=100,  
 params = {  
 whateveryouwant = "whatevervalueitneedstobe",  
 someotherparamenter = "someothervalue"  
 }  
}  
  
storyboard.gotoScene("mycoolscene",options)  

In the receiving module’s createScene() and/or enterScene() events (probably works in willEnterScene too)

function scene:createScene( event )  
 local params = event.params  
  
 print(params.whateveryouwant)  
 print(params.someotherparameter)  
  
...  

[import]uid: 199310 topic_id: 33320 reply_id: 132500[/import]

Hello,
Before trying more “complex” possibilities, how about adding a short timer delay (50 milliseconds) before dispatching your events back to the main scene? This would in theory allow the touch to “resolve” before the event is actually dispatched. It might not work, but it’s worth a shot before you rework the code more severely.

Brent [import]uid: 9747 topic_id: 33320 reply_id: 132386[/import]

Good suggestion, works beautifully. Not sure whether this could be considered a hack or not but with the amount of time I spent scratching my head, I really don’t care.

Out of curiosity, I tried a few different values for the delay and it turns out, in the simulator at least, it works right down to 1 millisecond. Pretty strange behaviour really, I’ll be interested to see if it changes on different devices. Here’s my final code if anyone cares:

[lua]local function dispatchRuntimeEvent(event)
Runtime:dispatchEvent(event)
return true
end

local function resumeBtnRelease(event)
local event = {name = “resumeGame”}
timer.performWithDelay(1, function(e) return dispatchRuntimeEvent(event) end)
return true
end[/lua]

Out of interest, is there a better way to communicate this kind of thing from overlay scenes back to the main scene or are Runtime events the best way to do it?

Thanks! [import]uid: 164840 topic_id: 33320 reply_id: 132408[/import]

Thanks Rob, although I’m not sure if storyboard parameters apply to this situation. Since I have a main scene and an overlay scene and I want to trigger an action on the main scene from within the overlay scene, I can’t use storyboard.gotoScene() can I? I saw here that you must close the overlay scene before switching scenes. Also, I don’t want to change scenes, just close the overlay and resume the game action taking place in the main scene.

This must be a common setup with pause screens - as I see it, the function that closes the overlay and resumes the game needs to be in the main scene. My question was really whether using runtime events is the best way to trigger this function from within the overlay scene. Does the storyboard provide access to a table of loaded scenes as another way of doing this… storyboard.mainScene:resumeGame() or something? This may also be another solution to the original issue I had of touch events hitting the main scene after the overlay was hidden (fixed with Brent’s workaround… I’ve decided it is a workaround rather than a hack by the way). [import]uid: 164840 topic_id: 33320 reply_id: 132591[/import]

Sorry, only just saw this one after I replied to Rob. The idea of a “hub” table in main.lua seems sensible. It seems like something the storyboard might be able to handle though, since I specifically want to call a method in one scene from another and storyboard is supposed to be for handling interaction between scenes. [import]uid: 164840 topic_id: 33320 reply_id: 132594[/import]

Hello again,
I’m not an expert with Storyboard, but there’s definitely a way to pass variables from scene to scene. I think you add them as parameters of the Storyboard module itself, or something like that. It’s definitely a better method than using Runtime dispatches, and it would solve this need for using timers.

Another method (one that I use) is to create a sort of global “hub” function in main.lua that accepts parameters from any external module and performs some action. Global functions are generally not good for memory management (global anything generally isn’t), but using just one as a hub is acceptable, I’ve found, because I use it for many purposes. That function performs an action and/or passes on its parameters to another local function above it.

Either of these cases should be preferable to the dispatching. :slight_smile:

Brent
[import]uid: 9747 topic_id: 33320 reply_id: 132467[/import]

Hi again,
I don’t use Storyboard myself (I use a sort of Director “Lite”), but as Rob pointed out, you should be able to send a simple flag variable like “overlayClosed=true” when you close the overlay and return to the main scene… and the main scene should recognize that.

The “hub” idea makes alot of sense for my latest project, but not for everybody. It’s more about my coding style in that project: my entire game engine is contained in “main.lua”, with various modules like a pause screen, results screen, and load-level screen branching off from main.lua. Some people, especially those using Storyboard, seem to use main.lua only as a “setup” that instantly jumps to a Storyboard scene like “game.lua”. It’s simply a matter of preference, nothing wrong with either approach.

Anyway, I use the hub as a method to call any local function within main.lua from one of the external modules . Case in point? I have a function that controls all updating of external data (files) to save high score, level progress, etc. This is a local function within main.lua, meaning I could never call it from an external module directly… and so I call the global hub function from the module, passing data to it, and then onward pass that data to the local data processing function. Now, one might wonder, why not just make the data function global and call it directly? Well, as I mentioned above, I don’t want to create global functions in main.lua unless necessary. Using the hub method allows me to create just ONE global function (the hub) which then calls local functions above it (and as I also mentioned, I’m not just using the hub to call the data function, I’m using it for many things like pausing/resuming, starting and stopping music, loading a new level, restarting the level, etc.).

Brent
[import]uid: 9747 topic_id: 33320 reply_id: 132619[/import]

Adding to Brent’s answer, storyboard parameters are easy:

In the sending module:

local options = {  
 effect="fade",  
 time=100,  
 params = {  
 whateveryouwant = "whatevervalueitneedstobe",  
 someotherparamenter = "someothervalue"  
 }  
}  
  
storyboard.gotoScene("mycoolscene",options)  

In the receiving module’s createScene() and/or enterScene() events (probably works in willEnterScene too)

function scene:createScene( event )  
 local params = event.params  
  
 print(params.whateveryouwant)  
 print(params.someotherparameter)  
  
...  

[import]uid: 199310 topic_id: 33320 reply_id: 132500[/import]

Thanks Rob, although I’m not sure if storyboard parameters apply to this situation. Since I have a main scene and an overlay scene and I want to trigger an action on the main scene from within the overlay scene, I can’t use storyboard.gotoScene() can I? I saw here that you must close the overlay scene before switching scenes. Also, I don’t want to change scenes, just close the overlay and resume the game action taking place in the main scene.

This must be a common setup with pause screens - as I see it, the function that closes the overlay and resumes the game needs to be in the main scene. My question was really whether using runtime events is the best way to trigger this function from within the overlay scene. Does the storyboard provide access to a table of loaded scenes as another way of doing this… storyboard.mainScene:resumeGame() or something? This may also be another solution to the original issue I had of touch events hitting the main scene after the overlay was hidden (fixed with Brent’s workaround… I’ve decided it is a workaround rather than a hack by the way). [import]uid: 164840 topic_id: 33320 reply_id: 132591[/import]

Sorry, only just saw this one after I replied to Rob. The idea of a “hub” table in main.lua seems sensible. It seems like something the storyboard might be able to handle though, since I specifically want to call a method in one scene from another and storyboard is supposed to be for handling interaction between scenes. [import]uid: 164840 topic_id: 33320 reply_id: 132594[/import]

Hi again,
I don’t use Storyboard myself (I use a sort of Director “Lite”), but as Rob pointed out, you should be able to send a simple flag variable like “overlayClosed=true” when you close the overlay and return to the main scene… and the main scene should recognize that.

The “hub” idea makes alot of sense for my latest project, but not for everybody. It’s more about my coding style in that project: my entire game engine is contained in “main.lua”, with various modules like a pause screen, results screen, and load-level screen branching off from main.lua. Some people, especially those using Storyboard, seem to use main.lua only as a “setup” that instantly jumps to a Storyboard scene like “game.lua”. It’s simply a matter of preference, nothing wrong with either approach.

Anyway, I use the hub as a method to call any local function within main.lua from one of the external modules . Case in point? I have a function that controls all updating of external data (files) to save high score, level progress, etc. This is a local function within main.lua, meaning I could never call it from an external module directly… and so I call the global hub function from the module, passing data to it, and then onward pass that data to the local data processing function. Now, one might wonder, why not just make the data function global and call it directly? Well, as I mentioned above, I don’t want to create global functions in main.lua unless necessary. Using the hub method allows me to create just ONE global function (the hub) which then calls local functions above it (and as I also mentioned, I’m not just using the hub to call the data function, I’m using it for many things like pausing/resuming, starting and stopping music, loading a new level, restarting the level, etc.).

Brent
[import]uid: 9747 topic_id: 33320 reply_id: 132619[/import]