Straightforward Gamestates - noooway/love2d_arkanoid_tutorial GitHub Wiki

A typical game has a menu screen, a congratulations screen and some kind of a pause regime. These different game modes are called gamestates. In this part, a simple implementation for such functionality is discussed.

I'm going to define 4 gamestates: "menu", "game", "gamepaused" and "gamefinished". In the straightforward implementation it is enough to maintain a simple variable gamestate to switch between them. It is declared in the main.lua and initialized to the first game state - "menu".

 local gamestate = "menu"

After that, in each love callback an if-else structure is necessary to check this variable:

function love.update( dt )
   if gamestate == "menu" then
   .....
   elseif gamestate == "game" then
   .....
   elseif gamestate == "gamepaused" then
   .....
   elseif gamestate == "gamefinished" then
   .....
   end
end

In the "menu" state, a message is shown on the screen. When the Enter is pressed, the game starts (the state is changed to the "game"). On Esc the game is exited.

function love.draw()
   if gamestate == "menu" then
      love.graphics.print("Menu gamestate. Press Enter to continue.",   --(*1)
                           280, 250)
   elseif gamestate == "game" then
   .....
end

function love.keyreleased( key, code )
   if gamestate == "menu" then
      if key == "return" then
         gamestate = "game"                                             --(*2)
      elseif key == 'escape' then
         love.event.quit()                                              --(*3)
      end    
   elseif gamestate == "game" then
   ..... 
end

(*1): display a message on the screen.
(*2): on Enter, start the game
(*3): on Esc - quit.

There is nothing to update in this state, so the corresponding section of the love.update is empty:

function love.update( dt )
   if gamestate == "menu" then
   elseif gamestate == "game" then
   .....
end

For the "game" gamestate, the contents of the love.draw and love.update are transferred from the previous part. In love.keyreleased there is a minor difference: on Esc instead of quitting, the game switches to "gamepaused" state.

In the "gamepaused" state I want to freeze the ball and platform and display a message on the screen. This can be implemented if the game objects are drawn but not updated.

function love.draw()
   .....
   elseif gamestate == "gamepaused" then
      ball.draw()
      platform.draw()
      bricks.draw()
      walls.draw()
      love.graphics.print(
         "Game is paused. Press Enter to continue or Esc to quit",
         50, 50)
   elseif gamestate == "gamefinished" then
   .....
end

function love.update( dt )
   .....
   elseif gamestate == "gamepaused" then     --(*1)
   elseif gamestate == "gamefinished" then
   .....
end

(*1): the "gamepaused" update part is empty.

The love.keyreleased callback is similar to the "menu".

The "gamefinished" state is entered when there are no more levels.

function switch_to_next_level( bricks, ball, levels )          --(*1)
   if bricks.no_more_bricks then
      .....    
      elseif levels.current_level >= #levels.sequence then     
         gamestate = "gamefinished"                            --(*2)
      end
   end
end

(*1): This function is moved from levels table. An explanation is below.
(*2): When there are no more levels, gamestate is switched to "gamefinished".

"Gamefinished" is similar to the "menu" except that on Enter it resets the level counter and restarts the game from the first level.

function love.keyreleased( key, code )
   ..... 
   elseif gamestate == "gamefinished" then
      if key == "return" then
         levels.current_level = 1
         level = levels.require_current_level()
         bricks.construct_level( level )
         ball.reposition()               
         gamestate = "game"
      elseif key == 'escape' then
         love.event.quit()
      end    
   end
end

There is also one minor catch with levels.switch_to_next_level. The gamestate variable is defined in the scope of main.lua. If it is simply passed into this function, it will be passed by value and it won't be possible to change it from "game" to "gamefinished". There are several solutions to this problem; I've simply moved this function from levels table inside the levels.lua to main.lua.

function switch_to_next_level( bricks, ball, levels )
   if bricks.no_more_bricks then
      if levels.current_level < #levels.sequence then
         levels.current_level = levels.current_level + 1
         level = levels.require_current_level()
         bricks.construct_level( level )
         ball.reposition()               
      elseif levels.current_level >= #levels.sequence then
         gamestate = "gamefinished"
      end
   end
end