source: trunk/doc/html/tutorial1-13.html

Last change on this file was 190, checked in by rudi, 14 years ago

reference documentation added

File size: 18.2 KB
RevLine 
[190]1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
2<!-- /home/espenr/tmp/qt-3.3.8-espenr-2499/qt-x11-free-3.3.8/doc/tutorial.doc:2032 -->
3<html>
4<head>
5<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
6<title>Qt Tutorial - Chapter 13: Game Over</title>
7<style type="text/css"><!--
8fn { margin-left: 1cm; text-indent: -1cm; }
9a:link { color: #004faf; text-decoration: none }
10a:visited { color: #672967; text-decoration: none }
11body { background: #ffffff; color: black; }
12--></style>
13</head>
14<body>
15
16<table border="0" cellpadding="0" cellspacing="0" width="100%">
17<tr bgcolor="#E5E5E5">
18<td valign=center>
19 <a href="index.html">
20<font color="#004faf">Home</font></a>
21 | <a href="classes.html">
22<font color="#004faf">All&nbsp;Classes</font></a>
23 | <a href="mainclasses.html">
24<font color="#004faf">Main&nbsp;Classes</font></a>
25 | <a href="annotated.html">
26<font color="#004faf">Annotated</font></a>
27 | <a href="groups.html">
28<font color="#004faf">Grouped&nbsp;Classes</font></a>
29 | <a href="functions.html">
30<font color="#004faf">Functions</font></a>
31</td>
32<td align="right" valign="center"><img src="logo32.png" align="right" width="64" height="32" border="0"></td></tr></table><h1 align=center>Qt Tutorial - Chapter 13: Game Over</h1>
33
34
35<p> <center><img src="t13.png" alt="Screenshot of tutorial thirteen"></center>
36<p> In this example we start to approach a real playable game with a
37score. We give MyWidget a new name (GameBoard) and add some slots.
38<p> We put the definition in gamebrd.h and the implementation in gamebrd.cpp.
39<p> The CannonField now has a game over state.
40<p> The layout problems in LCDRange are fixed.
41<p> <ul>
42<li> <a href="t13-lcdrange-h.html">t13/lcdrange.h</a> contains the LCDRange
43class definition.
44<li> <a href="t13-lcdrange-cpp.html">t13/lcdrange.cpp</a> contains the LCDRange
45implementation.
46<li> <a href="t13-cannon-h.html">t13/cannon.h</a> contains the CannonField class
47definition
48<li> <a href="t13-cannon-cpp.html">t13/cannon.cpp</a> contains the CannonField
49implementation.
50<li> <a href="t13-gamebrd-h.html">t13/gamebrd.h</a> contains the GameBoard
51class definition.
52<li> <a href="t13-gamebrd-cpp.html">t13/gamebrd.cpp</a> contains the GameBoard
53implementation.
54<li> <a href="t13-main-cpp.html">t13/main.cpp</a> contains MyWidget and main.
55</ul>
56<p> <h2> Line-by-line Walkthrough
57</h2>
58<a name="1"></a><p> <h3> <a href="t13-lcdrange-h.html">t13/lcdrange.h</a>
59</h3>
60<a name="1-1"></a><p>
61
62<p> <pre> #include &lt;<a href="qwidget-h.html">qwidget.h</a>&gt;
63
64 class QSlider;
65 class QLabel;
66
67 class LCDRange : public <a href="qwidget.html">QWidget</a>
68</pre>
69<p> We inherit <a href="qwidget.html">QWidget</a> rather than <a href="qvbox.html">QVBox</a>. QVBox is very easy to use, but
70again it showed its limitations so we switch to the more powerful and
71slightly harder to use <a href="qvboxlayout.html">QVBoxLayout</a>. (As you remember, QVBoxLayout is
72not a widget, it manages one.)
73<p> <h3> <a href="t13-lcdrange-cpp.html">t13/lcdrange.cpp</a>
74</h3>
75<a name="1-2"></a><p>
76
77<p> <pre> #include &lt;<a href="qlayout-h.html">qlayout.h</a>&gt;
78</pre>
79<p> We need to include qlayout.h now to get the other layout management
80API.
81<p> <pre> LCDRange::LCDRange( <a href="qwidget.html">QWidget</a> *parent, const char *name )
82 : <a href="qwidget.html">QWidget</a>( parent, name )
83</pre>
84<p> We inherit QWidget in the usual way.
85<p> The other constructor has the same change. init() is unchanged,
86except that we've added some lines at the end:
87<p> <pre> <a href="qvboxlayout.html">QVBoxLayout</a> * l = new <a href="qvboxlayout.html">QVBoxLayout</a>( this );
88</pre>
89<p> We create a QVBoxLayout with all the default values, managing this
90widget's children.
91<p> <pre> <a name="x2401"></a> l-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( lcd, 1 );
92</pre>
93<p> At the top we add the <a href="qlcdnumber.html">QLCDNumber</a> with a non-zero stretch.
94<p> <pre> l-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( slider );
95 l-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( label );
96</pre>
97<p> Then we add the other two, both with the default zero stretch.
98<p> This stretch control is something <a href="qvboxlayout.html">QVBoxLayout</a> (and <a href="qhboxlayout.html">QHBoxLayout</a>, and
99<a href="qgridlayout.html">QGridLayout</a>) offers but classes like <a href="qvbox.html">QVBox</a> do not. In this case
100we're saying that the QLCDNumber should stretch and the others should
101not.
102<p> <h3> <a href="t13-cannon-h.html">t13/cannon.h</a>
103</h3>
104<a name="1-3"></a><p> The CannonField now has a game over state and a few new functions.
105<p>
106
107<p> <pre> bool gameOver() const { return gameEnded; }
108</pre>
109<p> This function returns TRUE if the game is over or FALSE if a game
110is going on.
111<p> <pre> void setGameOver();
112 void restartGame();
113</pre>
114<p> Here are two new slots: setGameOver() and restartGame().
115<p> <pre> void canShoot( bool );
116</pre>
117<p> This new signal indicates that the CannonField is in a state where the
118shoot() slot makes sense. We'll use it below to enable/disable the
119Shoot button.
120<p> <pre> bool gameEnded;
121</pre>
122<p> This private variable contains the game state. TRUE means that the
123game is over, and FALSE means that a game is going on.
124<p> <h3> <a href="t13-cannon-cpp.html">t13/cannon.cpp</a>
125</h3>
126<a name="1-4"></a><p>
127
128<p> <pre> gameEnded = FALSE;
129</pre>
130<p> This line has been added to the constructor. Initially, the game is not
131over (luckily for the player :-).
132<p> <pre> void CannonField::shoot()
133 {
134 if ( isShooting() )
135 return;
136 timerCount = 0;
137 shoot_ang = ang;
138 shoot_f = f;
139 <a name="x2407"></a> autoShootTimer-&gt;<a href="qtimer.html#start">start</a>( 50 );
140 emit canShoot( FALSE );
141 }
142</pre>
143<p> We added a new isShooting() function, so shoot() uses it instead of
144testing directly. Also, shoot tells the world that the CannonField
145cannot shoot now.
146<p> <pre> void CannonField::setGameOver()
147 {
148 if ( gameEnded )
149 return;
150 if ( isShooting() )
151 autoShootTimer-&gt;<a href="qtimer.html#stop">stop</a>();
152 gameEnded = TRUE;
153 <a href="qwidget.html#repaint">repaint</a>();
154 }
155</pre>
156<p> This slot ends the game. It must be called from outside CannonField,
157because this widget does not know when to end the game. This is an
158important design principle in component programming. We choose to
159make the component as flexible as possible to make it usable with
160different rules (for example, a multi-player version of this in which the
161first player to hit ten times wins could use the CannonField unchanged).
162<p> If the game has already been ended we return immediately. If a game is
163going on we stop the shot, set the game over flag, and repaint the entire
164widget.
165<p> <pre> void CannonField::restartGame()
166 {
167 if ( isShooting() )
168 <a name="x2408"></a> autoShootTimer-&gt;<a href="qtimer.html#stop">stop</a>();
169 gameEnded = FALSE;
170 <a href="qwidget.html#repaint">repaint</a>();
171 emit canShoot( TRUE );
172 }
173</pre>
174<p> This slot starts a new game. If a shot is in the air, we stop shooting.
175We then reset the <tt>gameEnded</tt> variable and repaint the widget.
176<p> moveShot() too emits the new canShoot(TRUE) signal at the same time as
177either hit() or miss().
178<p> Modifications in CannonField::paintEvent():
179<p> <pre> void CannonField::<a href="qwidget.html#paintEvent">paintEvent</a>( <a href="qpaintevent.html">QPaintEvent</a> *e )
180 {
181 <a name="x2405"></a> <a href="qrect.html">QRect</a> updateR = e-&gt;<a href="qpaintevent.html#rect">rect</a>();
182 <a href="qpainter.html">QPainter</a> p( this );
183
184 if ( gameEnded ) {
185 p.<a href="qpainter.html#setPen">setPen</a>( black );
186 <a name="x2403"></a> p.<a href="qpainter.html#setFont">setFont</a>( QFont( "Courier", 48, QFont::Bold ) );
187 p.<a href="qpainter.html#drawText">drawText</a>( <a href="qwidget.html#rect">rect</a>(), AlignCenter, "Game Over" );
188 }
189</pre>
190<p> The paint event has been enhanced to display the text "Game Over" if
191the game is over, i.e., <tt>gameEnded</tt> is TRUE. We don't bother to
192check the update rectangle here because speed is not critical when
193the game is over.
194<p> To draw the text we first set a black pen; the pen color is used
195when drawing text. Next we choose a 48 point bold font from the
196Courier family. Finally we draw the text centered in the widget's
197rectangle. Unfortunately, on some systems (especially X servers with
198Unicode fonts) it can take a while to load such a large font. Because
199Qt caches fonts, you will notice this only the first time the font is
200used.
201<p> <pre> <a name="x2406"></a> if ( updateR.<a href="qrect.html#intersects">intersects</a>( cannonRect() ) )
202 paintCannon( &amp;p );
203 if ( isShooting() &amp;&amp; updateR.<a href="qrect.html#intersects">intersects</a>( shotRect() ) )
204 paintShot( &amp;p );
205 if ( !gameEnded &amp;&amp; updateR.<a href="qrect.html#intersects">intersects</a>( targetRect() ) )
206 paintTarget( &amp;p );
207 }
208</pre>
209<p> We draw the shot only when shooting and the target only when playing
210(that is, when the game is not ended).
211<p> <h3> <a href="t13-gamebrd-h.html">t13/gamebrd.h</a>
212</h3>
213<a name="1-5"></a><p> This file is new. It contains the definition of the GameBoard class,
214which was last seen as MyWidget.
215<p>
216
217<p> <pre> class QPushButton;
218 class LCDRange;
219 class QLCDNumber;
220 class CannonField;
221
222 #include "lcdrange.h"
223 #include "cannon.h"
224
225 class GameBoard : public <a href="qwidget.html">QWidget</a>
226 {
227 <a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>
228 public:
229 GameBoard( <a href="qwidget.html">QWidget</a> *parent=0, const char *name=0 );
230
231 protected slots:
232 void fire();
233 void hit();
234 void missed();
235 void newGame();
236
237 private:
238 <a href="qlcdnumber.html">QLCDNumber</a> *hits;
239 <a href="qlcdnumber.html">QLCDNumber</a> *shotsLeft;
240 CannonField *cannonField;
241 };
242</pre>
243<p> We have now added four slots. These are protected and are used internally.
244We have also added two QLCDNumbers (<tt>hits</tt> and <tt>shotsLeft</tt>) which display
245the game status.
246<p> <h3> <a href="t13-gamebrd-cpp.html">t13/gamebrd.cpp</a>
247</h3>
248<a name="1-6"></a><p> This file is new. It contains the implementation of the GameBoard
249class, which was last seen as MyWidget.
250<p>
251
252<p> We have made some changes in the GameBoard constructor.
253<p> <pre> cannonField = new CannonField( this, "cannonField" );
254</pre>
255<p> <tt>cannonField</tt> is now a member variable, so we carefully change the
256constructor to use it. (The <em>good</em> programmers at Trolltech never
257forget this, but I do. Caveat programmor - if "programmor" is Latin,
258at least. Anyway, back to the code.)
259<p> <pre> <a href="qobject.html#connect">connect</a>( cannonField, SIGNAL(hit()),
260 this, SLOT(hit()) );
261 <a href="qobject.html#connect">connect</a>( cannonField, SIGNAL(missed()),
262 this, SLOT(missed()) );
263</pre>
264<p> This time we want to do something when the shot has hit or missed the
265target. Thus we connect the hit() and missed() signals of the
266CannonField to two protected slots with the same names in this class.
267<p> <pre> <a href="qobject.html#connect">connect</a>( shoot, SIGNAL(<a href="qbutton.html#clicked">clicked</a>()), SLOT(fire()) );
268</pre>
269<p> Previously we connected the Shoot button's clicked() signal directly
270to the CannonField's shoot() slot. This time we want to keep track of
271the number of shots fired, so we connect it to a protected slot in
272this class instead.
273<p> Notice how easy it is to change the behavior of a program when you are
274working with self-contained components.
275<p> <pre> <a href="qobject.html#connect">connect</a>( cannonField, SIGNAL(canShoot(bool)),
276 <a name="x2416"></a> shoot, SLOT(<a href="qwidget.html#setEnabled">setEnabled</a>(bool)) );
277</pre>
278<p> We also use the cannonField's canShoot() signal to enable or disable
279the Shoot button appropriately.
280<p> <pre> QPushButton *restart
281 = new <a href="qpushbutton.html">QPushButton</a>( "&amp;New Game", this, "newgame" );
282 restart-&gt;setFont( QFont( "Times", 18, QFont::Bold ) );
283
284 <a href="qobject.html#connect">connect</a>( restart, SIGNAL(clicked()), this, SLOT(newGame()) );
285</pre>
286<p> We create, set up, and connect the New Game button as we have done
287with the other buttons. Clicking this button will activate the
288newGame() slot in this widget.
289<p> <pre> hits = new <a href="qlcdnumber.html">QLCDNumber</a>( 2, this, "hits" );
290 shotsLeft = new <a href="qlcdnumber.html">QLCDNumber</a>( 2, this, "shotsleft" );
291 <a href="qlabel.html">QLabel</a> *hitsL = new <a href="qlabel.html">QLabel</a>( "HITS", this, "hitsLabel" );
292 QLabel *shotsLeftL
293 = new <a href="qlabel.html">QLabel</a>( "SHOTS LEFT", this, "shotsleftLabel" );
294</pre>
295<p> We create four new widgets. Note that we don't bother to keep the
296pointers to the <a href="qlabel.html">QLabel</a> widgets in the GameBoard class because there's
297nothing much we want to do with them. Qt will delete them when the
298GameBoard widget is destroyed, and the layout classes will resize them
299appropriately.
300<p> <pre> <a href="qhboxlayout.html">QHBoxLayout</a> *topBox = new <a href="qhboxlayout.html">QHBoxLayout</a>;
301 <a name="x2413"></a> grid-&gt;<a href="qgridlayout.html#addLayout">addLayout</a>( topBox, 0, 1 );
302 topBox-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( shoot );
303 topBox-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( hits );
304 topBox-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( hitsL );
305 topBox-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( shotsLeft );
306 topBox-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( shotsLeftL );
307 <a name="x2410"></a> topBox-&gt;<a href="qboxlayout.html#addStretch">addStretch</a>( 1 );
308 <a name="x2411"></a> topBox-&gt;<a href="qboxlayout.html#addWidget">addWidget</a>( restart );
309</pre>
310<p> The number of widgets in the top-right cell is getting large. Once it
311was empty; now it's full enough that we group together the layout
312setting for better overview.
313<p> Notice that we let all the widgets have their preferred sizes, instead
314putting the stretch just to the left of the New Game button.
315<p> <pre> newGame();
316 }
317</pre>
318<p> We're all done constructing the GameBoard, so we start it all using
319newGame(). (NewGame() is a slot, but as we said, slots can be used as
320ordinary functions, too.)
321<p> <pre> void GameBoard::fire()
322 {
323 if ( cannonField-&gt;gameOver() || cannonField-&gt;isShooting() )
324 return;
325 shotsLeft-&gt;<a href="qlcdnumber.html#display">display</a>( shotsLeft-&gt;<a href="qlcdnumber.html#intValue">intValue</a>() - 1 );
326 cannonField-&gt;shoot();
327 }
328</pre>
329<p> This function fires a shot. If the game is over or if there is a shot in the
330air, we return immediately. We decrement the number of shots left and tell
331the cannon to shoot.
332<p> <pre> void GameBoard::hit()
333 {
334 hits-&gt;<a href="qlcdnumber.html#display">display</a>( hits-&gt;<a href="qlcdnumber.html#intValue">intValue</a>() + 1 );
335 if ( shotsLeft-&gt;<a href="qlcdnumber.html#intValue">intValue</a>() == 0 )
336 cannonField-&gt;setGameOver();
337 else
338 cannonField-&gt;newTarget();
339 }
340</pre>
341<p> This slot is activated when a shot has hit the target. We increment the
342number of hits. If there are no shots left, the game is over. Otherwise,
343we make the CannonField generate a new target.
344<p> <pre> void GameBoard::missed()
345 {
346 <a name="x2415"></a> if ( shotsLeft-&gt;<a href="qlcdnumber.html#intValue">intValue</a>() == 0 )
347 cannonField-&gt;setGameOver();
348 }
349</pre>
350<p> This slot is activated when a shot has missed the target. If there are no
351shots left, the game is over.
352<p> <pre> void GameBoard::newGame()
353 {
354 <a name="x2414"></a> shotsLeft-&gt;<a href="qlcdnumber.html#display">display</a>( 15 );
355 hits-&gt;<a href="qlcdnumber.html#display">display</a>( 0 );
356 cannonField-&gt;restartGame();
357 cannonField-&gt;newTarget();
358 }
359</pre>
360<p> This slot is activated when the user clicks the Restart button. It is
361also called from the constructor. First it sets the number of shots
362to 15. Note that this is the only place in the program where we set
363the number of shots. Change it to whatever you like to change the
364game rules. Next we reset the number of hits, restart the game, and
365generate a new target.
366<p> <h3> <a href="t13-main-cpp.html">t13/main.cpp</a>
367</h3>
368<a name="1-7"></a><p> This file has just been on a diet. MyWidget is gone, and the only
369thing left is the main() function, unchanged except for the name
370change.
371<p> <h2> Behavior
372</h2>
373<a name="2"></a><p> The cannon can shoot at a target; a new target is automatically created
374when one has been hit.
375<p> Hits and shots left are displayed and the program keeps track of them.
376The game can end, and there's a button to start a new game.
377<p> (See <a href="tutorial1-07.html#compiling">Compiling</a> for how to create a
378makefile and build the application.)
379<p> <h2> Exercises
380</h2>
381<a name="3"></a><p> Add a random wind factor and show it to the user.
382<p> Make some splatter effects when the shot hits the target.
383<p> Implement multiple targets.
384<p> You're now ready for <a href="tutorial1-14.html">Chapter 14.</a>
385<p> [<a href="tutorial1-12.html">Previous tutorial</a>]
386[<a href="tutorial1-14.html">Next tutorial</a>]
387[<a href="tutorial.html">Main tutorial page</a>]
388<p>
389<!-- eof -->
390<p><address><hr><div align=center>
391<table width=100% cellspacing=0 border=0><tr>
392<td>Copyright &copy; 2007
393<a href="troll.html">Trolltech</a><td align=center><a href="trademarks.html">Trademarks</a>
394<td align=right><div align=right>Qt 3.3.8</div>
395</table></div></address></body>
396</html>
Note: See TracBrowser for help on using the repository browser.