I may be running out of options, but running out isn’t an option.
– Mark Lawrence, Prince of Thorns
To further showcase our new state system, let us create an options menu to allow the user to change the full-screen
state, to set the game resolution and to pick their own keybindings.
Boost Preprocessor
Since getting the strings of enum variables is a bit tedious when done using a switch, I used the boost preprocessor
to create enums with an automatictoString function:
This might look weird, and it obviously is a bit more work than just defining an enum, but the ease of use easily beats
the added implementation time.
The first macro ENUM_TO_STRING is internally used by the second one. The second macro ENUM_WITH_STRING first
generates the enumeration, then generates a toString function called getGameCommandAsString that takes a
GameCommand as input and returns the enumerator name as a string, using the first macro to stringify enumeration
elements into wide chars. Note that this implementation obviously requires the enumerators to map to unique values.
Defining the actual enumerations is easy enough, as you can see, but the real power comes from the ease of use:
First notice that I like to add a count at the end of each enum, for example nGameCommands to be able to easily loop
through the enum:
And retrieving the string values of an enum is now as easy as it gets:
Just have fun with the new enums! Thanks, Boost!
Updated Buttons
First, please note that the game buttons have been updated. The most important updates are that the buttons can now hold
four animation cycles, one for each state, deselected, selected, clicked and locked, and that a button is now only
clickable if it is not locked:
Options Menu State
To show the options menu, we will create a new state, the options menu state, where the user can set the full-screen
state of the game as well as specify its resolution:
I won’t show the details for each little function in those states, but I will briefly elaborate on the lambda functions
of each button as well as the functions that provide the main functionality.
Creating the main navigation Buttons
The Back Button
This is an easy button to implement. We simply change the game state to the main menu, meaning that we delete all other
states. Note that a return value of false notifies the observer stack of the input handler that the stack of states
was changed.
The Save Button
When the user clicks on the save button, we write the current options to the LUA file on the hard drive and, if so
desired, change the resolution and/or toggle the full-screen state. Please note that in the above screenshot, the
resolution buttons are greyed out because the application does not have resizable graphics yet.
The Gamepad Button
When the user clicks on the gamepad button, we push another state to the stack, the key map menu state, allowing the
user to change the key bindings. Please note that pushing a state to the stack does not delete the previous states; they
are simply paused and will be resumed later on.
Toggle Fullscreen
There is nothing here, the main work is done when the save button is clicked.
Browse Supported Resolutions
To browse through all supported resolutions, we simply implement a left and right button, choosing the next or
previous resolution:
To grey out the buttons once we are at one of the ends of the list, we update their state during the game update as
follows:
Key Bindings Menu
This menu gives an overview of the currently active key maps. Clicking on the gamepad symbol next to a key binding
allows the user to select their own mappings:
The Main Navigation Buttons
As you can see, the save button is gone. The new key bindings are saved when chosen during the next state. The
gamepad button remains red, as it was clicked before the main menu state went into pause mode. The only button that is
left is the
Back Button
This is easy. We simply pop the current state, the key binding menu, and resume the previous state, the main game
options menu.
Pagination
We only show five game commands at once. To navigate through the game commands, we draw two pagination buttons with the
following lambda functions:
The New Gamepad Buttons
When a small gamepad button next to a key binding is clicked, we enter a new state, the NewKeyBindingMenuState where
the user can select a new chord for the specified action:
What is happening here is that we chose the appropriate game command, based on which gamepad button was clicked. We then
enter the new state with the appropriate game command from the key map to change.
New Key Bindings Menu
In this menu, the user can choose their own key bindings for the game commands:
The Back Button
The back button is, as always, easy to implement:
We simply pop the current state from the stack and resume the previous state, the key bindings menu. Note that any new
chords the user may have selected are discarded.
The Save Button
This button is as easy as the back button. We simply save the key bindings using boost serialization as described
in a previous tutorial on marshalling game commands and then pop the state
and resume the key bindings menu.
Listening for User Input
Once the NewKeyBindingState starts, we tell the input handler to start listening for user input.
The input handler then captures any user input that was not already mapped to a (different) game command and notifies us
to save the new chord:
Associating the new chord with the game command is done as follows:
And that’s it, we now have a fully functional game menu.
In the next tutorial, although I previously said that we won’t elaborate on HUDs yet, we will at least modify our state
stack to allow HUDs, as the HUD is drawn over the game state without pausing the game.