summaryrefslogtreecommitdiffstats
path: root/doc/src/examples/rogue.qdoc
blob: 3eb9249690720d42a5a6002a7361a386c2ce4993 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $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
        \o \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.
        \o \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 examples/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 examples/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 examples/statemachine/rogue/window.cpp 0
    \dots
    \snippet examples/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 examples/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 examples/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 examples/statemachine/rogue/window.cpp 4

    The transition from \c inputState allows triggering the quit state
    when the player types \c {q}.
    
    \snippet examples/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 examples/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 examples/statemachine/rogue/movementtransition.h 1

    The KeyPress events come wrapped in \l{QWrappedEvent}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 examples/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, i.e., the event
    is already unwrapped.

    \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/}.
*/