Application states are simply the states an application can be in. A rough definition would be: a state is the set of data and logic that is responsible for handling input, and displaying output at any given time. State examples are:
- Main menu
- Selecting a single target tile
- Selecting a single target entity ( several entities can lie on a tile, e.g. items on the floor, a creature and a trap)
Note: Another name would be “game stack”, “game states” and so on. Of course these can all have several other meanings depending on who you talk to, but that’s irrelevant)
- Active state stack
- Top of the stack is running, others are paused
- State hierarchy
- A state can be a state machine. For example, a state of “select 5 targets” can be a state machine which 5 transitions of type select_target
- Arbitrary data exchange between states
- States have a blackboard of sorts ( a flat collection of map< string , type>, one per type ) that can store data used to communicate to parent/child states
- Configurable via JSON
- Should be able to define states and state machines, complete with transfers
There are two main actors in this system: the Application State Manager and the Application State. The public interface related to transitions should be very simple.
The manager is responsible for creating states, providing the current active state and execute one of the three transition operations:
- Push(state, next_state, transfer_request) : Pauses the state, pushes next_state to the stack and starts next_state. When next_state finishes successfully, it will use the transfer_request to transfer data to state.
- Finish(state, success) : Stops the state, marking the state execution as successful or not
- SwitchTo( state, next_state) : A convenience function for finishing a state successfully and starting another state
States own a root widget and a blackboard. States can do their custom input handling and rendering in addition to the widgets (done before/after or completely ignoring the widget-related functionality)
I used the above concepts to create a simple example, which is demonstrated in the below video. We have 5 states:
- Menu: Start the application at this state. It has it’s own gui with two buttons: Play and Quit. Play button leads to “Loading” state. Quit quits the application
- Loading: This state shows a gui with a timer – after 1 second the state is switched to “Game”
- Game: This state has again its own gui, and shows a map with a playable lich character, which can be moved with the arrow keys. ‘L’ key changes the state to “TargetSingleTile”, while ‘T’ key changes state to “TargetMultiTile”
- TargetSingleTile: This state does not have its own gui – it uses whatever gui was there before. It adds a selection of highlighted tiles, centered at the character. When the user clicks on a tile that is within the selection, the state completes successfully and transfers the selected tile to the parent. At the tile, a skeleton appears.
- TargetMultiTile: This state doesn’t have its own gui either. It is a state machine, and calls TargetSingleTile successively. When the state machine is finished, the locations of all selected tiles are sent to the parent state (Game). At those tiles, wraiths appear
The system is still a bit rough around the edges, and it looks like it can be converted to a generic state machine (which I could reuse perhaps for some basic AI), but it’s ok for now.