Problem with overlapping key event listener

Hi, currently I’m working on pause menu in my snake-like game. FYI: It’s Windows only and for control it uses arrow keys to move in game and menu, and Enter for selection/pause.

Here is simplyfied piece of code (tell me if you need more details)


-- Above that are other game functions not related to the topic

local function resumeGame(button)

	if button == "resume" then
	
	pauseButtonSelect = nil
	backGroup.alpha, mainGroup.alpha, topGroup.alpha = 1, 1, 1
	timer.resume( gameLoopTimer )
	timer.resume( snakeMoveTimer )
	
	elseif button == "exit" then
	
	pauseButtonSelect = nil
	
	endGame()
	end

end


local function pauseGame()

	timer.pause(snakeMoveTimer)
	timer.pause(gameLoopTimer)
	
	backGroup.alpha, mainGroup.alpha, topGroup.alpha = 0.75, 0.75, 0.75

	local pauseButtonSelect = "resume"
	
	local pauseMenu = display.newImageRect (uiGroup, "img/pause.png", 552, 574 )
	pauseMenu.x, pauseMenu.y = 512, 400
	
	local resumeButton = display.newText (uiGroup, "> Resume <", display.contentCenterX, 400, "fonts/BRLNSDB.ttf", 45)
	resumeButton:setFillColor (1, 1, 0)
	
	local exitButton = display.newText (uiGroup, "Exit", display.contentCenterX, 470, "fonts/BRLNSDB.ttf", 45)
	exitButton:setFillColor (1, 1, 1 )
	


	function pauseControls(event)
	
		if ((event.keyName == "up" or event.keyName == "down") and event.phase == "down" and pauseButtonSelect == "resume") then
			resumeButton.text = "Resume"
			exitButton.text = "> Exit <"
			resumeButton:setFillColor(1,1,1)
			exitButton:setFillColor(1,1,0)
			pauseButtonSelect = "exit"
		elseif ((event.keyName == "up" or event.keyName == "down") and event.phase == "down" and pauseButtonSelect == "exit") then
			resumeButton.text = "> Resume <"
			exitButton.text = "Exit"
			resumeButton:setFillColor(1,1,0)
			exitButton:setFillColor(1,1,1)
			pauseButtonSelect = "resume"
		end
		
		if (event.keyName == "enter" and event.phase == "down" and pauseButtonSelect == "resume") then
		
	
		display.remove(resumeButton)
		resumeButton = nil
		display.remove(exitButton)
		exitButton = nil
		display.remove(pauseMenu)
		pauseMenu = nil
		Runtime:removeEventListener( "key", pauseControls )
			resumeGame(pauseButtonSelect)
		
		elseif (event.keyName == "enter" and event.phase == "down" and pauseButtonSelect == "exit") then
			
			display.remove(resumeButton)
			resumeButton = nil
			display.remove(exitButton)
			exitButton = nil
			display.remove(pauseMenu)
			pauseMenu = nil
			Runtime:removeEventListener( "key", pauseControls )
	
			resumeGame(pauseButtonSelect)
		end
		return false
	end
	
	Runtime:addEventListener( "key", pauseControls)
end


local function snakeControls (event)

	if (event.keyName == "up" and event.phase == "down" and headDir ~= "down") then
		headDir = "up"
	elseif (event.keyName == "down" and event.phase == "down" and headDir ~= "up") then
		headDir = "down"
	elseif (event.keyName == "left" and event.phase == "down" and headDir ~= "right") then
		headDir = "left"
	elseif (event.keyName == "right" and event.phase == "down" and headDir ~= "left") then
		headDir = "right"
	elseif (event.keyName == "enter" and event.phase == "down") then
		pauseGame()
	end
	return false
end


function scene:create( event )

	local sceneGroup = self.view

-- Rest of the code that draws objects etc.

	Runtime:addEventListener( "key", snakeControls )
end

Problem is that when I press Enter to pause game and then press again to resume, code draws background, and text from pauseGame() again or never remove it from screen, although the timers are resumed resumeGame() and I can play, but only without pressing up and down arrow keys.

If I choose Exit instead, it works by exit to main menu but again if I press Enter or up and down at the Main menu scene, shows various errors, like listener for pause wasn’t removed.

So I’m guessing that it’s problem with Runtime:addEventListener("key") placement and fact that in both situations I’m using same key (Enter). If so, what should I do with it to avoid that happen?

Only way I found was to make get to pause menu by Enter key and use other key (eg. Space) for select. But I want to do it by same key.

I tried to implement pause menu by using composer.showOverlay but it makes same effect or even worse.

You declare local pauseButtonSelect = "resume" inside pauseGame(), but then try to use it in resumeGame() where it’s nil.

You don’t always need to remove and recreate buttons, you can set alpha = 0 and 1.

Similar to eventKey, you can set return true at the beginning of the event to ignore it.

I always prefer to have a single key listener / touch listener / etc and to control what it will do at a given moment by setting some kind of “state” for the scene:

local gameState = "active"

local function resumeGame(button)
	
	if gameState == "active" then
		--already active, nothing to do here
		return
	end

	gameState = "active"

	--do your resume game stuff now

end


local function pauseGame()
	
	if gameState == "paused" then
		--already paused, nothing to do here
		return
	end

	gameState = "paused"

	--do your pause stuff now
end


local function snakeControls (event)

	if gameState == "active" then

		if (event.keyName == "up" and event.phase == "down" and headDir ~= "down") then
			headDir = "up"
		elseif (event.keyName == "down" and event.phase == "down" and headDir ~= "up") then
			headDir = "down"
		elseif (event.keyName == "left" and event.phase == "down" and headDir ~= "right") then
			headDir = "left"
		elseif (event.keyName == "right" and event.phase == "down" and headDir ~= "left") then
			headDir = "right"
		elseif (event.keyName == "enter" and event.phase == "down") then
			pauseGame()
		end
	elseif gameState == "paused" then
		if ((event.keyName == "up" or event.keyName == "down") and event.phase == "down" and pauseButtonSelect == "resume") then
			pauseButtonSelect = "exit"
		elseif ((event.keyName == "up" or event.keyName == "down") and event.phase == "down" and pauseButtonSelect == "exit") then
			pauseButtonSelect = "resume"
		end
		
		if (event.keyName == "enter" and event.phase == "down" and pauseButtonSelect == "resume") then
			resumeGame(pauseButtonSelect)
		elseif (event.keyName == "enter" and event.phase == "down" and pauseButtonSelect == "exit") then
			resumeGame(pauseButtonSelect)
		end
	end

	return false
end


function scene:create( event )

	local sceneGroup = self.view

	Runtime:addEventListener( "key", snakeControls )
end

I’ve removed some of your code just to simplify the example, but hopefully you get the idea. It means you never have 2 functions attempting to do the same thing at the same time.

1 Like

Actually I was thinking about something like that but I had enough for yesterday.

So now I did it, but in addition I had to adjust code by declare variables for pause menu ui outside functions. I think just that could repair my previous code but since it’s already working fine I won’t touch it anymore lol

Thank you!