Struggling to get a pause screen working with HUMP

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
orange_god
Prole
Posts: 2
Joined: Thu Dec 02, 2021 5:04 pm

Struggling to get a pause screen working with HUMP

Post by orange_god »

Hi Everyone,

I'm trying to get a Pause screen working, using HUMP for gamestates. I have a main level that I load into initially, and then when I hit P it switches to a Pause screen. The issue is when I hit P again to pop the pause gamestate off, I get a bunch of errors,

Code: Select all

Error

stack overflow


Traceback

[C]: in function 'getWidth'
gamestates/pause.lua:10: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
...
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
gamestates/pause.lua:12: in function 'draw'
[C]: in function 'xpcall'

I can't for the life of me figure out whats happening.

gameLevel1.lua

Code: Select all

-- importing gamestate lib
Gamestate = require 'libs.hump.gamestate'

-- importing entities
local Entities = require 'entities.Entities'
local Entity = require 'entities.Entity'

-- Creating gamestate for level1
local gameLevel1 = Gamestate.new()

-- bringing in entities that we need [players, enemies, background, bullets]
local Player = require 'entities.player'
local BoxEnemy = require 'entities.boxEnemy'
local Map = require 'entities.level1map'
local Bullet = require 'entities.bullet'

-- switch to pause screen


function gameLevel1:enter()
    current_state = gameLevel1

    --initializing entities
    Entities:enter()
    Player:init()
    Bullet:init()
    BoxEnemy:init()
    Map:init()

    -- updating Entity list
    Entities:addMany({Map, Player, BoxEnemy, Bullet})
end

-- update for each entity
function gameLevel1:update(dt)
    Entities:update(dt)
    function love.keypressed(key)
        if Gamestate.current() ~= mgameLevel1 and key == 'p' then
            Gamestate.push(pause)
        end
    end
    
end

function love.keypressed(key)
    if Gamestate.current() ~= gameLevel1 and key == 'p' then
        Gamestate.push(pause)
    end
end

-- draws each entitiy
function gameLevel1:draw()
    Entities:draw()
end

return gameLevel1

Pause.lua

Code: Select all

pause = Gamestate.new()

function pause:enter(from)
  self.from = from -- record previous state
end

function pause:draw()
  local w, h = love.graphics.getWidth(), love.graphics.getHeight()
  -- draw previous screen
  self.from:draw()

  -- overlay with pause message
  love.graphics.setColor(0,0,0, 100)
  love.graphics.rectangle('fill', 0,0, w, h)
  love.graphics.setColor(255,255,255)
  love.graphics.printf('PAUSE', 0, h/2, w, 'center')
end



-- extending the example of Gamestate.push() above
function love.keypressed(key)
  if key == 'p' then
      return Gamestate.pop() -- return to previous state
  end
end

return pause
As I understand it pushing the pause gamestate to the stop of the stack causes the other non-top gamestates to go into limbo, and then popping it off should let gameLevel1 go back to the top of the stack and keep doing its thing without having reset anything.
MrFariator
Party member
Posts: 509
Joined: Wed Oct 05, 2016 11:53 am

Re: Struggling to get a pause screen working with HUMP

Post by MrFariator »

I believe the issues lies with how you're defining the love.keypressed callback. You've defined it twice in two (or more) places, and only the one that is read last will be used, because redefining a löve callback will rewrite over the old. So, here it seems that gameLevel1.lua is the file that executed last for this purpose. This creates a situation that when you're pressing P once, the game switches to the pause state, and when you try to press it again, it switch to the pause state again, setting the pause state's last state the pause state itself. This then leads to the pause state calling its own draw() function repeatedly recursively, until löve gives up and gives a stack overflow error.

So, in order to fix this you'll have to rethink your approach to how you use love.keypressed. You could define it only in one place, like main.lua or even some controls.lua file that's dedicated for handling controls. From there you can do something like:

Code: Select all

-- it's better to use scancodes
-- read more here: https://love2d.org/wiki/love.keypressed (Under "notes")
local keysHeld = {}
function love.keypressed(key, scancode) 
    keysHeld[scancode] = true
end

function love.keyreleased(key,scancode)
  keysHeld[scancode] = false
end
And from there you could write a function to check what buttons are held and what are not, and use them to detect the P presses wherever you need them. Of course, keep in mind that the above code would need a way to register a button only once (as opposed to being considered 'down' until released), which you can do with simple timers or similar.

Alternatively, you can also use functions like love.keyboard.isDown or love.keyboard.isScancodeDown for any quicker testing or fixing. Or even swap the function love.keypressed is pointing to in whenever state changes, though that can become bothersome to maintain if complexity and amount of states increases.
orange_god
Prole
Posts: 2
Joined: Thu Dec 02, 2021 5:04 pm

Re: Struggling to get a pause screen working with HUMP

Post by orange_god »

Ah, that makes a lot of sense! I should have realized that was the issue. I went with defining love.kepyressed in main like this

Code: Select all

function love.keypressed(key)
    if key == "escape" then
        love.event.push("quit")
    end

    if Gamestate.current() ~= pause and key == 'p' then
        Gamestate.push(pause)
    end
end
and adding this to my pause.lua file:

Code: Select all

function pause:update(dt)
  -- pauseTimer starts ticking
  pauseTimer = pauseTimer + dt

  -- wait a moment before checking agian if 'p' is pressed
  if love.keyboard.isScancodeDown('p') and pauseTimer > 0.3 then

    -- reset timer
    pauseTimer = 0
    -- remove pause gamestate from the stack
    return Gamestate.pop()
  end
end
As far as I can tell it's working even if it's not ideal,a nd thanks for the info about scancodes!
grump
Party member
Posts: 947
Joined: Sat Jul 22, 2017 7:43 pm

Re: Struggling to get a pause screen working with HUMP

Post by grump »

Using scancodes instead of keycodes is generally a good idea in most situations; movement with WASD, or actions with Z and X, etc. But it's not the best idea to use them when the key is a mnemonic, as in 'p' for 'pause'. You probably want this to always be the 'p' key regardless of the physical location of the key. Scancode is no good for that.

And a word of warning against the isDown functions: there's a possibility that the game will miss key presses because you're sampling the keyboard state at a low (and unknown) sample rate. Quick key presses on a laggy computer can go unnoticed, making your game unplayable in worst case.

Code: Select all

-- an extreme example
-- this will only sporadically detect a quickly pressed and released 'a' key
function love.draw()
	if love.keyboard.isDown('a') then
		love.graphics.print("a is down")
	end
	love.timer.sleep(.5)
end
Post Reply

Who is online

Users browsing this forum: No registered users and 18 guests