qt5base-lts/examples/widgets/doc/rogue.qdoc
Frederik Gladhorn e92c1976a6 Fix example includes for qdoc.
Change-Id: Ifa6a99db27ce51529489bf077a839a3107b524d2
Reviewed-by: Qt Doc Bot <qt_docbot@qt-project.org>
Reviewed-by: Paul Olav Tvete <paul.tvete@nokia.com>
2012-09-11 12:09:47 +02:00

209 lines
8.2 KiB
Plaintext

/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** GNU Free Documentation License
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms
** and conditions contained in a signed written agreement between you
** and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example statemachine/rogue
\title Rogue Example
The Rogue example shows how to use the Qt state machine for event
handling.
\image rogue-example.png
This example implements a simple text based game. Do you see the
\c{@} in the screenshot? That's you, the rogue. The \c{#}
characters are walls, and the dots represent floor. In a real
game, other ASCII characters would represent all kinds of objects
and creatures, for instance, ancient dragons (\c{D}s) or food
rations (\c{%}s). But let's not get carried away. In this game,
the rogue is simply running around in an empty room.
The rogue is moved with the keypad (2, 4, 8, 6). That aside, we
have implemented a \c quit command that triggers if the player
types \c {q}. The player is then asked if he/she really wants to
quit.
Most games have commands that need more than one key press (we
think of consecutive presses, i.e., not of several keys being
pressed at the same time). In this game, only the \c quit command
falls under this category, but for the sake of argument, let's
imagine a fully-fledged game with a rich set of commands. If we
were to implement these by catching key events in
\l{QWidget::}{keyPressEvent()}, we would have to keep a lot of
class member variables to track the sequence of keys already typed
(or find some other way of deducing the current state of a
command). This can easily lead to spaghetti, which is--as we all
well know, I'm sure--unpleasant. With a state machine, on the
other hand, separate states can wait for a single key press, and
that makes our lives a lot simpler.
The example consists of two classes:
\list
\li \c Window draws the text display of the game and sets
up the state machine. The window also has a status bar
above the area in which the rouge moves.
\li \c MovementTransition is a transition that carries out
a single move of the rogue.
\endlist
Before we embark on a code walkthrough, it is necessary to take a
closer look at the design of the machine. Here is a state chart
that shows what we want to achieve:
\image rogue-statechart.png
The input state waits for a key press to start a new command.
When receiving a key it recognizes, it transitions to one of the
two commands of the game; though, as we will see, movement is
handled by the transition itself. The quit state waits for the
player to answer yes or no (by typing \c y or \c n) when asked
whether he/she really wants to quit the game.
The chart demonstrates how we use one state to wait for a single
key press. The press received may trigger one of the transitions
connected to the state.
\section1 Window Class Definition
The \c Window class is a widget that draws the text display of the
game. It also sets up the state machine, i.e., creates and
connects the states in the machine. It is the key events from this
widget that are used by the machine.
\snippet statemachine/rogue/window.h 0
\c Direction specifies the direction in which the rogue is to
move. We use this in \c movePlayer(), which moves the rogue and
repaints the window. The game has a status line above the area in
which the rogue moves. The \c status property contains the text of
this line. We use a property because the QState class allows
setting any Qt \l{Qt's Property System}{property} when entered.
More on this later.
\snippet statemachine/rogue/window.h 1
The \c map is an array with the characters that are currently
displayed. We set up the array in \c setupMap(), and update it
when the rogue is moved. \c pX and \c pY is the current position
of the rogue. \c WIDTH and \c HEIGHT are macros specifying the
dimensions of the map.
The \c paintEvent() function is left out of this walkthrough. We
also do not discuss other code that does not concern the state
machine (the \c setupMap(), \c status(), \c setStatus(), \c
movePlayer(), and \c sizeHint() functions). If you wish to take a
look at the code, click on the link for the \c window.cpp file at
the top of this page.
\section1 Window Class Implementation
Here is the constructor of \c Window:
\snippet statemachine/rogue/window.cpp 0
\dots
\snippet statemachine/rogue/window.cpp 1
The player starts off at position (5, 5). We then set up the map
and statemachine. Let's proceed with the \c buildMachine()
function:
\snippet statemachine/rogue/window.cpp 2
We enter \c inputState when the machine is started and from the \c
quitState if the user wants to continue playing. We then set the
status to a helpful reminder of how to play the game.
First, the \c Movement transition is added to the input state.
This will enable the rogue to be moved with the keypad. Notice
that we don't set a target state for the movement transition. This
will cause the transition to be triggered (and the
\l{QAbstractTransition::}{onTransition()} function to be invoked),
but the machine will not leave the \c inputState. If we had set \c
inputState as the target state, we would first have left and then
entered the \c inputState again.
\snippet statemachine/rogue/window.cpp 3
When we enter \c quitState, we update the status bar of the
window.
\c QKeyEventTransition is a utility class that removes the hassle
of implementing transitions for \l{QKeyEvent}s. We simply need to
specify the key on which the transition should trigger and the
target state of the transition.
\snippet statemachine/rogue/window.cpp 4
The transition from \c inputState allows triggering the quit state
when the player types \c {q}.
\snippet statemachine/rogue/window.cpp 5
The machine is set up, so it's time to start it.
\section1 The MovementTransition Class
\c MovementTransition is triggered when the player request the
rogue to be moved (by typing 2, 4, 6, or 8) when the machine is in
the \c inputState.
\snippet statemachine/rogue/movementtransition.h 0
In the constructor, we tell QEventTransition to only send
\l{QEvent::}{KeyPress} events to the
\l{QAbstractTransition::}{eventTest()} function:
\snippet statemachine/rogue/movementtransition.h 1
The KeyPress events come wrapped in \l{QStateMachine::WrappedEvent}s. \c event
must be confirmed to be a wrapped event because Qt uses other
events internally. After that, it is simply a matter of checking
which key has been pressed.
Let's move on to the \c onTransition() function:
\snippet statemachine/rogue/movementtransition.h 2
When \c onTransition() is invoked, we know that we have a
\l{QEvent::}{KeyPress} event with 2, 4, 6, or 8, and can ask \c
Window to move the player.
\section1 The Roguelike Tradition
You might have been wondering why the game features a rogue. Well,
these kinds of text based dungeon exploration games date back to a
game called, yes, "Rogue". Although outflanked by the technology
of modern 3D computer games, roguelikes have a solid community of
hard-core, devoted followers.
Playing these games can be surprisingly addictive (despite the
lack of graphics). Angband, the perhaps most well-known rougelike,
is found here: \l{http://rephial.org/}.
*/