-
Notifications
You must be signed in to change notification settings - Fork 17
Arx 09 Basic Gamestates: Game and Menu
The core game mechanics is in place, and it is possible to think about some additional features. Most games start from the main menu screen. This chapter describes some preliminary preparations necessary to implement it.
Typically, at the menu screen some buttons are displayed.
While I have not yet described how to implement them, at this point it is not hard to guess
that buttons are going to be just another game objects.
It fact, Button
class is quite similar to the Brick
class.
That means, that we would need call the buttons' constructor in love.load
and place appropriate callbacks in the love.update
and love.draw
.
However, if we simply put them there, menu elements and game elements
will be updated and displayed simultaneously, overlapping, which we certainly do not want.
We need a some kind of if-else system to select which objects to display right now.
It can be put at the top level, directly into the love.draw
and love.update
, or we can
add some flags inside the objects.
There is a neater way to handle this — gamestates. Gamestate holds an information on what objects to update and display at the current moment of the game. It is possible to transition between them to select different sets of objects. The whole game therefore is viewed as a sequence of gamestates.
I'm going to start from two gamestates - the "menu", and the "game"; in the next part two more will be added - "gamepaused" and "gamefinished". The "menu" state is the first one to appear, displaying a welcome screen. The "game" is where the actual game happens, with bricks, ball, platform, and the rest. "Gamepaused" state is entered, when the player hits an 'Esc' button. And finally there is also the end-game state, displaying a congratulations on finishing the game.
To implement the gamestates, I'm going to use hump.gamestate library.
Its principle of operation is following: for each gamestate it is necessary to define
a number of callbacks similar to love.load
, love.update
and love.draw
,
which are called when the gamestate is active.
We use Gamestate.switch
function to switch between the gamestates.
The first gamestate is activated from the love.load()
callback.
Here is an example from the documentation:
local menu = {} --(*1)
local game = {}
function menu:draw() --(*2)
love.graphics.print("Press Enter to continue", 10, 10)
end
.....
function game:enter() --(*3)
Entities.clear()
-- setup entities here
end
function game:update(dt) --(*4)
Entities.update(dt)
end
function game:draw() --(*4)
Entities.draw()
end
function love.load()
Gamestate.registerEvents()
Gamestate.switch(menu) --(*5)
end
(*1) - Two gamestates are declared: menu
and game
.
(*2) - draw
callback for the menu
gamestate.
(*3) - enter
callback is called each time when gamestate is entered.
(*4) - draw
and update
callbacks for the game
gamestate.
(*5) - from the love.load
, we switch to the first gamestate (menu
).
Ok, let's create the "menu" gamestate according to the example suggestion.
In menu.lua
:
local Gamestate = require "gamestate" --(*1)
local game = require "game"
local love = love
local menu = menu or {}
if setfenv then
setfenv(1, menu)
else
_ENV = menu
end
state_name = "menu"
function menu:enter() --(*2)
end
function menu:update( dt ) --(*3)
end
function menu:draw() -- (*3)
love.graphics.print("Menu gamestate. Press Enter to continue", 10, 10)
end
function menu:keyreleased( key, code ) --(*4)
if key == 'return' then
Gamestate.switch( game ) --(*5)
elseif key == 'escape' then
love.event.quit()
end
end
function menu:leave() --(*2)
end
return menu
(*1) A menu
gamestate is just an ordinary Lua module; it is necessary to load other modules it depends
on, and "gamestate"
is one of them. It is also necessary to load the "game"
gamestate module,
since at (*5) transition to this gamestate is performed.
(*2) Right now we do not need any actions on entering or leaving "menu" gamestate, so
appropriate callbacks are empty.
(*3) The update
function is currently emtpy; draw
displays an introductory message.
(*4) The last defined callback is menu:keyreleased
, which switches to the
"game" gamestate if the "Enter" key is pressed (released, actually), or exits if "Esc" is pressed.
Next is the "game" gamestate.
Basically, it contains all the code that was in the main.lua
previously, except for different callback names.
Contents of love.load
are moved to game:enter
, love.update
to game:update
, and love.draw
to game:draw
.
The most noticeable difference is the game:leave()
callback.
It is a good practice to delete all unnecessary objects on leaving
a gamestate: call the objects' destructors and delete references to them:
function game:leave()
level_sequence = nil
level_counter = nil
level_filename = nil
level = nil
collider = nil
ball = nil
platform = nil
bricks_container = nil
walls_container = nil
end
The last thing is changes in the main.lua
.
It is necessary to load the menu
gamestate and initialize the library according to the example.
local Gamestate = require "gamestate"
local menu = require "menu" --(*1)
function love.load()
Gamestate.registerEvents() --(*2)
Gamestate.switch( menu ) --(*3)
end
function love.quit()
print("Thanks for playing! Come back soon!")
end
(*1): menu
gamestate is required.
(*2): All the gamestates are registered.
(*3): The game is switched into the "menu" gamestate.
Feedback is crucial to improve the tutorial!
Let me know if you have any questions, critique, suggestions or just any other ideas.
Chapter 1: Prototype
- The Ball, The Brick, The Platform
- Game Objects as Lua Tables
- Bricks and Walls
- Detecting Collisions
- Resolving Collisions
- Levels
Appendix A: Storing Levels as Strings
Appendix B: Optimized Collision Detection (draft)
Chapter 2: General Code Structure
- Splitting Code into Several Files
- Loading Levels from Files
- Straightforward Gamestates
- Advanced Gamestates
- Basic Tiles
- Different Brick Types
- Basic Sound
- Game Over
Appendix C: Stricter Modules (draft)
Appendix D-1: Intro to Classes (draft)
Appendix D-2: Chapter 2 Using Classes.
Chapter 3 (deprecated): Details
- Improved Ball Rebounds
- Ball Launch From Platform (Two Objects Moving Together)
- Mouse Controls
- Spawning Bonuses
- Bonus Effects
- Glue Bonus
- Add New Ball Bonus
- Life and Next Level Bonuses
- Random Bonuses
- Menu Buttons
- Wall Tiles
- Side Panel
- Score
- Fonts
- More Sounds
- Final Screen
- Packaging
Appendix D: GUI Layouts
Appendix E: Love-release and Love.js
Beyond Programming: