~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to doc/html/tutorial-t13.html

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?xml version="1.0" encoding="iso-8859-1"?>
 
2
<!DOCTYPE html
 
3
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
 
4
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 
5
<!-- /tmp/qt-4.0.0-espenr-1119621036935/qt-x11-opensource-desktop-4.0.0/doc/src/examples/tutorial.qdoc -->
 
6
<head>
 
7
    <title>Qt 4.0: Qt Tutorial 13 - Game Over</title>
 
8
    <style>h3.fn,span.fn { margin-left: 1cm; text-indent: -1cm; }
 
9
a:link { color: #004faf; text-decoration: none }
 
10
a:visited { color: #672967; text-decoration: none }
 
11
td.postheader { font-family: sans-serif }
 
12
tr.address { font-family: sans-serif }
 
13
body { background: #ffffff; color: black; }</style>
 
14
    <link rel="prev" href="tutorial-t12.html" />
 
15
    <link rel="contents" href="tutorial.html" />
 
16
    <link rel="next" href="tutorial-t14.html" />
 
17
</head>
 
18
<body>
 
19
<table border="0" cellpadding="0" cellspacing="0" width="100%">
 
20
<tr>
 
21
<td align="left" valign="top" width="32"><img src="images/qt-logo.png" align="left" width="32" height="32" border="0" /></td>
 
22
<td width="1">&nbsp;&nbsp;</td><td class="postheader" valign="center"><a href="index.html"><font color="#004faf">Home</font></a>&nbsp;&middot; <a href="classes.html"><font color="#004faf">All&nbsp;Classes</font></a>&nbsp;&middot; <a href="mainclasses.html"><font color="#004faf">Main&nbsp;Classes</font></a>&nbsp;&middot; <a href="annotated.html"><font color="#004faf">Annotated</font></a>&nbsp;&middot; <a href="groups.html"><font color="#004faf">Grouped&nbsp;Classes</font></a>&nbsp;&middot; <a href="functions.html"><font color="#004faf">Functions</font></a></td>
 
23
<td align="right" valign="top" width="230"><img src="images/trolltech-logo.png" align="right" width="203" height="32" border="0" /></td></tr></table><p>
 
24
[Previous: <a href="tutorial-t12.html">Chapter 12</a>]
 
25
[<a href="tutorial.html">Qt Tutorial</a>]
 
26
[Next: <a href="tutorial-t14.html">Chapter 14</a>]
 
27
</p>
 
28
<h1 align="center">Qt Tutorial 13 - Game Over</h1>
 
29
<p>Files:</p>
 
30
<ul>
 
31
<li><a href="tutorial-t13-cannonfield-cpp.html">tutorial/t13/cannonfield.cpp</a></li>
 
32
<li><a href="tutorial-t13-cannonfield-h.html">tutorial/t13/cannonfield.h</a></li>
 
33
<li><a href="tutorial-t13-gameboard-cpp.html">tutorial/t13/gameboard.cpp</a></li>
 
34
<li><a href="tutorial-t13-gameboard-h.html">tutorial/t13/gameboard.h</a></li>
 
35
<li><a href="tutorial-t13-lcdrange-cpp.html">tutorial/t13/lcdrange.cpp</a></li>
 
36
<li><a href="tutorial-t13-lcdrange-h.html">tutorial/t13/lcdrange.h</a></li>
 
37
<li><a href="tutorial-t13-main-cpp.html">tutorial/t13/main.cpp</a></li>
 
38
</ul>
 
39
<center><img src="images/t13.png" alt="Screenshot of tutorial thirteen" /></center><p>In this example we start to approach a real playable game with a score. We give <tt>MyWidget</tt> a new name (<tt>GameBoard</tt>) and add some slots.</p>
 
40
<p>We put the definition in <tt>gameboard.h</tt> and the implementation in <tt>gameboard.cpp</tt>.</p>
 
41
<p>The <tt>CannonField</tt> now has a game over state.</p>
 
42
<p>The layout problems in <tt>LCDRange</tt> are fixed.</p>
 
43
<a name="line-by-line-walkthrough"></a>
 
44
<h2>Line by Line Walkthrough</h2>
 
45
<a name="t13-lcdrange-cpp"></a>
 
46
<h3><a href="tutorial-t13-lcdrange-cpp.html">t13/lcdrange.cpp</a></h3>
 
47
<pre>&nbsp;       label-&gt;setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);</pre>
 
48
<p>We set the size policy of the <a href="qlabel.html">QLabel</a> to (<a href="qsizepolicy.html#Policy-enum">Preferred</a>, <a href="qsizepolicy.html#Policy-enum">Fixed</a>). The vertical component ensures that the label won't stretch or shrink vertically; it will stay at its optimal size (its <a href="qwidget.html#sizeHint-prop">sizeHint()</a>). This solves the layout problems observed in Chapter 12.</p>
 
49
<a name="t13-cannonfield-h"></a>
 
50
<h3><a href="tutorial-t13-cannonfield-h.html">t13/cannonfield.h</a></h3>
 
51
<p>The <tt>CannonField</tt> now has a game over state and a few new functions.</p>
 
52
<pre>&nbsp;       bool gameOver() const { return gameEnded; }</pre>
 
53
<p>This function returns <tt>true</tt> if the game is over, <tt>false</tt> if a game is going on.</p>
 
54
<pre>&nbsp;       void setGameOver();
 
55
        void restartGame();</pre>
 
56
<p>Here are two new slots: <tt>setGameOver()</tt> and <tt>restartGame()</tt>.</p>
 
57
<pre>&nbsp;       void canShoot(bool can);</pre>
 
58
<p>This new signal indicates that the <tt>CannonField</tt> is in a state where the <tt>shoot()</tt> slot makes sense. We'll use it below to enable or disable the <b>Shoot</b> button.</p>
 
59
<pre>&nbsp;       bool gameEnded;</pre>
 
60
<p>This private variable contains the game state; <tt>true</tt> means that the game is over, and <tt>false</tt> means that a game is going on.</p>
 
61
<a name="t13-cannonfield-cpp"></a>
 
62
<h3><a href="tutorial-t13-cannonfield-cpp.html">t13/cannonfield.cpp</a></h3>
 
63
<pre>&nbsp;       gameEnded = false;</pre>
 
64
<p>This line has been added to the constructor. Initially, the game is not over (luckily for the player :-).</p>
 
65
<pre>&nbsp;   void CannonField::shoot()
 
66
    {
 
67
        if (isShooting())
 
68
            return;
 
69
        timerCount = 0;
 
70
        shootAngle = currentAngle;
 
71
        shootForce = currentForce;
 
72
        autoShootTimer-&gt;start(5);
 
73
        emit canShoot(false);
 
74
    }</pre>
 
75
<p>We added a new <tt>isShooting()</tt> function, so <tt>shoot()</tt> uses it instead of testing directly. Also, shoot tells the world that the <tt>CannonField</tt> cannot shoot now.</p>
 
76
<pre>&nbsp;   void CannonField::setGameOver()
 
77
    {
 
78
        if (gameEnded)
 
79
            return;
 
80
        if (isShooting())
 
81
            autoShootTimer-&gt;stop();
 
82
        gameEnded = true;
 
83
        update();
 
84
    }</pre>
 
85
<p>This slot ends the game. It must be called from outside <tt>CannonField</tt>, because this widget does not know when to end the game. This is an important design principle in component programming. We choose to make the component as flexible as possible to make it usable with different rules (for example, a multi-player version of this in which the first player to hit ten times wins could use the <tt>CannonField</tt> unchanged).</p>
 
86
<p>If the game has already been ended we return immediately. If a game is going on we stop the shot, set the game over flag, and repaint the entire widget.</p>
 
87
<pre>&nbsp;   void CannonField::restartGame()
 
88
    {
 
89
        if (isShooting())
 
90
            autoShootTimer-&gt;stop();
 
91
        gameEnded = false;
 
92
        update();
 
93
        emit canShoot(true);
 
94
    }</pre>
 
95
<p>This slot starts a new game. If a shot is in the air, we stop shooting. We then reset the <tt>gameEnded</tt> variable and repaint the widget.</p>
 
96
<p><tt>moveShot()</tt> too emits the new <tt>canShoot(true)</tt> signal at the same time as either <tt>hit()</tt> or <tt>miss()</tt>.</p>
 
97
<p>Modifications in CannonField::paintEvent():</p>
 
98
<pre>&nbsp;   void CannonField::paintEvent(QPaintEvent * /* event */)
 
99
    {
 
100
        QPainter painter(this);
 
101
 
 
102
        if (gameEnded) {
 
103
            painter.setPen(Qt::black);
 
104
            painter.setFont(QFont(&quot;Courier&quot;, 48, QFont::Bold));
 
105
            painter.drawText(rect(), Qt::AlignCenter, &quot;Game Over&quot;);
 
106
        }</pre>
 
107
<p>The paint event has been enhanced to display the text &quot;Game Over&quot; if the game is over, i.e., <tt>gameEnded</tt> is <tt>true</tt>. We don't bother to check the update rectangle here because speed is not critical when the game is over.</p>
 
108
<p>To draw the text we first set a black pen; the pen color is used when drawing text. Next we choose a 48 point bold font from the Courier family. Finally we draw the text centered in the widget's rectangle. Unfortunately, on some systems (especially X servers with Unicode fonts) it can take a while to load such a large font. Because Qt caches fonts, you will notice this only the first time the font is used.</p>
 
109
<pre>&nbsp;       paintCannon(painter);
 
110
        if (isShooting())
 
111
            paintShot(painter);
 
112
        if (!gameEnded)
 
113
            paintTarget(painter);
 
114
    }</pre>
 
115
<p>We draw the shot only when shooting and the target only when playing (that is, when the game is not ended).</p>
 
116
<a name="t13-gameboard-h"></a>
 
117
<h3><a href="tutorial-t13-gameboard-h.html">t13/gameboard.h</a></h3>
 
118
<p>This file is new. It contains the definition of the <tt>GameBoard</tt> class, which was last seen as <tt>MyWidget</tt>.</p>
 
119
<pre>&nbsp;   class QLCDNumber;
 
120
    class CannonField;
 
121
 
 
122
    class GameBoard : public QWidget
 
123
    {
 
124
        Q_OBJECT
 
125
 
 
126
    public:
 
127
        GameBoard(QWidget *parent = 0);
 
128
 
 
129
    protected slots:
 
130
        void fire();
 
131
        void hit();
 
132
        void missed();
 
133
        void newGame();
 
134
 
 
135
    private:
 
136
        QLCDNumber *hits;
 
137
        QLCDNumber *shotsLeft;
 
138
        CannonField *cannonField;
 
139
    };</pre>
 
140
<p>We have now added four slots. These are protected and are used internally. We have also added two <a href="qlcdnumber.html">QLCDNumber</a>s (<tt>hits</tt> and <tt>shotsLeft</tt>) that display the game status.</p>
 
141
<a name="t13-gameboard-cpp"></a>
 
142
<h3><a href="tutorial-t13-gameboard-cpp.html">t13/gameboard.cpp</a></h3>
 
143
<p>This file is new. It contains the implementation of the <tt>GameBoard</tt> class, which was last seen as <tt>MyWidget</tt>.</p>
 
144
<p>We have made some changes in the <tt>GameBoard</tt> constructor.</p>
 
145
<pre>&nbsp;       cannonField = new CannonField;</pre>
 
146
<p><tt>cannonField</tt> is now a member variable, so we carefully change the constructor to use it.</p>
 
147
<pre>&nbsp;       connect(cannonField, SIGNAL(hit()),
 
148
                this, SLOT(hit()));
 
149
        connect(cannonField, SIGNAL(missed()),
 
150
                this, SLOT(missed()));</pre>
 
151
<p>This time we want to do something when the shot has hit or missed the target. Thus we connect the <tt>hit()</tt> and <tt>missed()</tt> signals of the <tt>CannonField</tt> to two protected slots with the same names in this class.</p>
 
152
<pre>&nbsp;       connect(shoot, SIGNAL(clicked()),
 
153
                this, SLOT(fire()));</pre>
 
154
<p>Previously we connected the <b>Shoot</b> button's <tt>clicked()</tt> signal directly to the <tt>CannonField</tt>'s <tt>shoot()</tt> slot. This time we want to keep track of the number of shots fired, so we connect it to a protected slot in this class instead.</p>
 
155
<p>Notice how easy it is to change the behavior of a program when you are working with self-contained components.</p>
 
156
<pre>&nbsp;       connect(cannonField, SIGNAL(canShoot(bool)),
 
157
                shoot, SLOT(setEnabled(bool)));</pre>
 
158
<p>We also use the <tt>cannonField</tt>'s <tt>canShoot()</tt> signal to enable or disable the <b>Shoot</b> button appropriately.</p>
 
159
<pre>&nbsp;       QPushButton *restart = new QPushButton(&quot;&amp;New Game&quot;);
 
160
        restart-&gt;setFont(QFont(&quot;Times&quot;, 18, QFont::Bold));
 
161
 
 
162
        connect(restart, SIGNAL(clicked()), this, SLOT(newGame()));</pre>
 
163
<p>We create, set up, and connect the <b>New Game</b> button as we have done with the other buttons. Clicking this button will activate the <tt>newGame()</tt> slot in this widget.</p>
 
164
<pre>&nbsp;       hits = new QLCDNumber(2);
 
165
        shotsLeft = new QLCDNumber(2);
 
166
        QLabel *hitsLabel = new QLabel(&quot;HITS&quot;);
 
167
        QLabel *shotsLeftLabel = new QLabel(&quot;SHOTS LEFT&quot;);</pre>
 
168
<p>We create four new widgets. Note that we don't bother to keep the pointers to the <a href="qlabel.html">QLabel</a> widgets in the <tt>GameBoard</tt> class because there's nothing much we want to do with them. Qt will delete them when the <tt>GameBoard</tt> widget is destroyed, and the layout classes will resize them appropriately.</p>
 
169
<pre>&nbsp;       QHBoxLayout *topLayout = new QHBoxLayout;
 
170
        topLayout-&gt;addWidget(shoot);
 
171
        topLayout-&gt;addWidget(hits);
 
172
        topLayout-&gt;addWidget(hitsLabel);
 
173
        topLayout-&gt;addWidget(shotsLeft);
 
174
        topLayout-&gt;addWidget(shotsLeftLabel);
 
175
        topLayout-&gt;addStretch(1);
 
176
        topLayout-&gt;addWidget(restart);</pre>
 
177
<p>The number of widgets in the top-right cell is getting large. Once it was empty; now it's full enough that we group together the layout setting for better overview.</p>
 
178
<p>Notice that we let all the widgets have their preferred sizes, instead putting the stretch just to the left of the <b>New Game</b> button.</p>
 
179
<pre>&nbsp;       newGame();
 
180
    }</pre>
 
181
<p>We're all done constructing the <tt>GameBoard</tt>, so we start it all using <tt>newGame()</tt>. Although <tt>newGame()</tt> is a slot, it can also be used as an ordinary function.</p>
 
182
<pre>&nbsp;   void GameBoard::fire()
 
183
    {
 
184
        if (cannonField-&gt;gameOver() || cannonField-&gt;isShooting())
 
185
            return;
 
186
        shotsLeft-&gt;display(shotsLeft-&gt;intValue() - 1);
 
187
        cannonField-&gt;shoot();
 
188
    }</pre>
 
189
<p>This function fires a shot. If the game is over or if there is a shot in the air, we return immediately. We decrement the number of shots left and tell the cannon to shoot.</p>
 
190
<pre>&nbsp;   void GameBoard::hit()
 
191
    {
 
192
        hits-&gt;display(hits-&gt;intValue() + 1);
 
193
        if (shotsLeft-&gt;intValue() == 0)
 
194
            cannonField-&gt;setGameOver();
 
195
        else
 
196
            cannonField-&gt;newTarget();
 
197
    }</pre>
 
198
<p>This slot is activated when a shot has hit the target. We increment the number of hits. If there are no shots left, the game is over. Otherwise, we make the <tt>CannonField</tt> generate a new target.</p>
 
199
<pre>&nbsp;   void GameBoard::missed()
 
200
    {
 
201
        if (shotsLeft-&gt;intValue() == 0)
 
202
            cannonField-&gt;setGameOver();
 
203
    }</pre>
 
204
<p>This slot is activated when a shot has missed the target. If there are no shots left, the game is over.</p>
 
205
<pre>&nbsp;   void GameBoard::newGame()
 
206
    {
 
207
        shotsLeft-&gt;display(15);
 
208
        hits-&gt;display(0);
 
209
        cannonField-&gt;restartGame();
 
210
        cannonField-&gt;newTarget();
 
211
    }</pre>
 
212
<p>This slot is activated when the user clicks the Restart button. It is also called from the constructor. First it sets the number of shots to 15. Note that this is the only place in the program where we set the number of shots. Change it to whatever you like to change the game rules. Next we reset the number of hits, restart the game, and generate a new target.</p>
 
213
<a name="t13-main-cpp"></a>
 
214
<h3><a href="tutorial-t13-main-cpp.html">t13/main.cpp</a></h3>
 
215
<p>This file has just been on a diet. <tt>MyWidget</tt> is gone, and the only thing left is the <tt>main()</tt> function, unchanged except for the name change.</p>
 
216
<a name="running-the-application"></a>
 
217
<h2>Running the Application</h2>
 
218
<p>The cannon can shoot at a target; a new target is automatically created when one has been hit.</p>
 
219
<p>Hits and shots left are displayed and the program keeps track of them. The game can end, and there's a button to start a new game.</p>
 
220
<a name="exercises"></a>
 
221
<h2>Exercises</h2>
 
222
<p>Add a random wind factor and show it to the user.</p>
 
223
<p>Make some splatter effects when the shot hits the target.</p>
 
224
<p>Implement multiple targets.</p>
 
225
<p>
 
226
[Previous: <a href="tutorial-t12.html">Chapter 12</a>]
 
227
[<a href="tutorial.html">Qt Tutorial</a>]
 
228
[Next: <a href="tutorial-t14.html">Chapter 14</a>]
 
229
</p>
 
230
<p /><address><hr /><div align="center">
 
231
<table width="100%" cellspacing="0" border="0"><tr class="address">
 
232
<td width="30%">Copyright &copy; 2005 <a href="trolltech.html">Trolltech</a></td>
 
233
<td width="40%" align="center"><a href="trademarks.html">Trademarks</a></td>
 
234
<td width="30%" align="right"><div align="right">Qt 4.0.0</div></td>
 
235
</tr></table></div></address></body>
 
236
</html>