You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
397 lines
18 KiB
397 lines
18 KiB
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<!-- /home/espenr/tmp/qt-3.3.8-espenr-2499/qt-x11-free-3.3.8/doc/tutorial.doc:2032 -->
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
|
<title>Qt Tutorial - Chapter 13: Game Over</title>
|
|
<style type="text/css"><!--
|
|
fn { margin-left: 1cm; text-indent: -1cm; }
|
|
a:link { color: #004faf; text-decoration: none }
|
|
a:visited { color: #672967; text-decoration: none }
|
|
body { background: #ffffff; color: black; }
|
|
--></style>
|
|
</head>
|
|
<body>
|
|
|
|
<table border="0" cellpadding="0" cellspacing="0" width="100%">
|
|
<tr bgcolor="#E5E5E5">
|
|
<td valign=center>
|
|
<a href="index.html">
|
|
<font color="#004faf">Home</font></a>
|
|
| <a href="classes.html">
|
|
<font color="#004faf">All Classes</font></a>
|
|
| <a href="mainclasses.html">
|
|
<font color="#004faf">Main Classes</font></a>
|
|
| <a href="annotated.html">
|
|
<font color="#004faf">Annotated</font></a>
|
|
| <a href="groups.html">
|
|
<font color="#004faf">Grouped Classes</font></a>
|
|
| <a href="functions.html">
|
|
<font color="#004faf">Functions</font></a>
|
|
</td>
|
|
<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>
|
|
|
|
|
|
<p> <center><img src="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 MyWidget a new name (GameBoard) and add some slots.
|
|
<p> We put the definition in gamebrd.h and the implementation in gamebrd.cpp.
|
|
<p> The CannonField now has a game over state.
|
|
<p> The layout problems in LCDRange are fixed.
|
|
<p> <ul>
|
|
<li> <a href="t13-lcdrange-h.html">t13/lcdrange.h</a> contains the LCDRange
|
|
class definition.
|
|
<li> <a href="t13-lcdrange-cpp.html">t13/lcdrange.cpp</a> contains the LCDRange
|
|
implementation.
|
|
<li> <a href="t13-cannon-h.html">t13/cannon.h</a> contains the CannonField class
|
|
definition
|
|
<li> <a href="t13-cannon-cpp.html">t13/cannon.cpp</a> contains the CannonField
|
|
implementation.
|
|
<li> <a href="t13-gamebrd-h.html">t13/gamebrd.h</a> contains the GameBoard
|
|
class definition.
|
|
<li> <a href="t13-gamebrd-cpp.html">t13/gamebrd.cpp</a> contains the GameBoard
|
|
implementation.
|
|
<li> <a href="t13-main-cpp.html">t13/main.cpp</a> contains MyWidget and main.
|
|
</ul>
|
|
<p> <h2> Line-by-line Walkthrough
|
|
</h2>
|
|
<a name="1"></a><p> <h3> <a href="t13-lcdrange-h.html">t13/lcdrange.h</a>
|
|
</h3>
|
|
<a name="1-1"></a><p>
|
|
|
|
<p> <pre> #include <<a href="qwidget-h.html">qwidget.h</a>>
|
|
|
|
class QSlider;
|
|
class QLabel;
|
|
|
|
class LCDRange : public <a href="qwidget.html">QWidget</a>
|
|
</pre>
|
|
<p> We inherit <a href="qwidget.html">QWidget</a> rather than <a href="qvbox.html">QVBox</a>. QVBox is very easy to use, but
|
|
again it showed its limitations so we switch to the more powerful and
|
|
slightly harder to use <a href="qvboxlayout.html">QVBoxLayout</a>. (As you remember, QVBoxLayout is
|
|
not a widget, it manages one.)
|
|
<p> <h3> <a href="t13-lcdrange-cpp.html">t13/lcdrange.cpp</a>
|
|
</h3>
|
|
<a name="1-2"></a><p>
|
|
|
|
<p> <pre> #include <<a href="qlayout-h.html">qlayout.h</a>>
|
|
</pre>
|
|
<p> We need to include qlayout.h now to get the other layout management
|
|
API.
|
|
<p> <pre> LCDRange::LCDRange( <a href="qwidget.html">QWidget</a> *parent, const char *name )
|
|
: <a href="qwidget.html">QWidget</a>( parent, name )
|
|
</pre>
|
|
<p> We inherit QWidget in the usual way.
|
|
<p> The other constructor has the same change. init() is unchanged,
|
|
except that we've added some lines at the end:
|
|
<p> <pre> <a href="qvboxlayout.html">QVBoxLayout</a> * l = new <a href="qvboxlayout.html">QVBoxLayout</a>( this );
|
|
</pre>
|
|
<p> We create a QVBoxLayout with all the default values, managing this
|
|
widget's children.
|
|
<p> <pre> <a name="x2401"></a> l-><a href="qboxlayout.html#addWidget">addWidget</a>( lcd, 1 );
|
|
</pre>
|
|
<p> At the top we add the <a href="qlcdnumber.html">QLCDNumber</a> with a non-zero stretch.
|
|
<p> <pre> l-><a href="qboxlayout.html#addWidget">addWidget</a>( slider );
|
|
l-><a href="qboxlayout.html#addWidget">addWidget</a>( label );
|
|
</pre>
|
|
<p> Then we add the other two, both with the default zero stretch.
|
|
<p> This stretch control is something <a href="qvboxlayout.html">QVBoxLayout</a> (and <a href="qhboxlayout.html">QHBoxLayout</a>, and
|
|
<a href="qgridlayout.html">QGridLayout</a>) offers but classes like <a href="qvbox.html">QVBox</a> do not. In this case
|
|
we're saying that the QLCDNumber should stretch and the others should
|
|
not.
|
|
<p> <h3> <a href="t13-cannon-h.html">t13/cannon.h</a>
|
|
</h3>
|
|
<a name="1-3"></a><p> The CannonField now has a game over state and a few new functions.
|
|
<p>
|
|
|
|
<p> <pre> bool gameOver() const { return gameEnded; }
|
|
</pre>
|
|
<p> This function returns TRUE if the game is over or FALSE if a game
|
|
is going on.
|
|
<p> <pre> void setGameOver();
|
|
void restartGame();
|
|
</pre>
|
|
<p> Here are two new slots: setGameOver() and restartGame().
|
|
<p> <pre> void canShoot( bool );
|
|
</pre>
|
|
<p> This new signal indicates that the CannonField is in a state where the
|
|
shoot() slot makes sense. We'll use it below to enable/disable the
|
|
Shoot button.
|
|
<p> <pre> bool gameEnded;
|
|
</pre>
|
|
<p> This private variable contains the game state. TRUE means that the
|
|
game is over, and FALSE means that a game is going on.
|
|
<p> <h3> <a href="t13-cannon-cpp.html">t13/cannon.cpp</a>
|
|
</h3>
|
|
<a name="1-4"></a><p>
|
|
|
|
<p> <pre> gameEnded = FALSE;
|
|
</pre>
|
|
<p> This line has been added to the constructor. Initially, the game is not
|
|
over (luckily for the player :-).
|
|
<p> <pre> void CannonField::shoot()
|
|
{
|
|
if ( isShooting() )
|
|
return;
|
|
timerCount = 0;
|
|
shoot_ang = ang;
|
|
shoot_f = f;
|
|
<a name="x2407"></a> autoShootTimer-><a href="qtimer.html#start">start</a>( 50 );
|
|
emit canShoot( FALSE );
|
|
}
|
|
</pre>
|
|
<p> We added a new isShooting() function, so shoot() uses it instead of
|
|
testing directly. Also, shoot tells the world that the CannonField
|
|
cannot shoot now.
|
|
<p> <pre> void CannonField::setGameOver()
|
|
{
|
|
if ( gameEnded )
|
|
return;
|
|
if ( isShooting() )
|
|
autoShootTimer-><a href="qtimer.html#stop">stop</a>();
|
|
gameEnded = TRUE;
|
|
<a href="qwidget.html#repaint">repaint</a>();
|
|
}
|
|
</pre>
|
|
<p> This slot ends the game. It must be called from outside CannonField,
|
|
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 CannonField unchanged).
|
|
<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> <pre> void CannonField::restartGame()
|
|
{
|
|
if ( isShooting() )
|
|
<a name="x2408"></a> autoShootTimer-><a href="qtimer.html#stop">stop</a>();
|
|
gameEnded = FALSE;
|
|
<a href="qwidget.html#repaint">repaint</a>();
|
|
emit canShoot( TRUE );
|
|
}
|
|
</pre>
|
|
<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> moveShot() too emits the new canShoot(TRUE) signal at the same time as
|
|
either hit() or miss().
|
|
<p> Modifications in CannonField::paintEvent():
|
|
<p> <pre> void CannonField::<a href="qwidget.html#paintEvent">paintEvent</a>( <a href="qpaintevent.html">QPaintEvent</a> *e )
|
|
{
|
|
<a name="x2405"></a> <a href="qrect.html">QRect</a> updateR = e-><a href="qpaintevent.html#rect">rect</a>();
|
|
<a href="qpainter.html">QPainter</a> p( this );
|
|
|
|
if ( gameEnded ) {
|
|
p.<a href="qpainter.html#setPen">setPen</a>( black );
|
|
<a name="x2403"></a> p.<a href="qpainter.html#setFont">setFont</a>( QFont( "Courier", 48, QFont::Bold ) );
|
|
p.<a href="qpainter.html#drawText">drawText</a>( <a href="qwidget.html#rect">rect</a>(), AlignCenter, "Game Over" );
|
|
}
|
|
</pre>
|
|
<p> The paint event has been enhanced to display the text "Game Over" if
|
|
the game is over, i.e., <tt>gameEnded</tt> is TRUE. We don't bother to
|
|
check the update rectangle here because speed is not critical when
|
|
the game is over.
|
|
<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> <pre> <a name="x2406"></a> if ( updateR.<a href="qrect.html#intersects">intersects</a>( cannonRect() ) )
|
|
paintCannon( &p );
|
|
if ( isShooting() && updateR.<a href="qrect.html#intersects">intersects</a>( shotRect() ) )
|
|
paintShot( &p );
|
|
if ( !gameEnded && updateR.<a href="qrect.html#intersects">intersects</a>( targetRect() ) )
|
|
paintTarget( &p );
|
|
}
|
|
</pre>
|
|
<p> We draw the shot only when shooting and the target only when playing
|
|
(that is, when the game is not ended).
|
|
<p> <h3> <a href="t13-gamebrd-h.html">t13/gamebrd.h</a>
|
|
</h3>
|
|
<a name="1-5"></a><p> This file is new. It contains the definition of the GameBoard class,
|
|
which was last seen as MyWidget.
|
|
<p>
|
|
|
|
<p> <pre> class QPushButton;
|
|
class LCDRange;
|
|
class QLCDNumber;
|
|
class CannonField;
|
|
|
|
#include "lcdrange.h"
|
|
#include "cannon.h"
|
|
|
|
class GameBoard : public <a href="qwidget.html">QWidget</a>
|
|
{
|
|
<a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>
|
|
public:
|
|
GameBoard( <a href="qwidget.html">QWidget</a> *parent=0, const char *name=0 );
|
|
|
|
protected slots:
|
|
void fire();
|
|
void hit();
|
|
void missed();
|
|
void newGame();
|
|
|
|
private:
|
|
<a href="qlcdnumber.html">QLCDNumber</a> *hits;
|
|
<a href="qlcdnumber.html">QLCDNumber</a> *shotsLeft;
|
|
CannonField *cannonField;
|
|
};
|
|
</pre>
|
|
<p> We have now added four slots. These are protected and are used internally.
|
|
We have also added two QLCDNumbers (<tt>hits</tt> and <tt>shotsLeft</tt>) which display
|
|
the game status.
|
|
<p> <h3> <a href="t13-gamebrd-cpp.html">t13/gamebrd.cpp</a>
|
|
</h3>
|
|
<a name="1-6"></a><p> This file is new. It contains the implementation of the GameBoard
|
|
class, which was last seen as MyWidget.
|
|
<p>
|
|
|
|
<p> We have made some changes in the GameBoard constructor.
|
|
<p> <pre> cannonField = new CannonField( this, "cannonField" );
|
|
</pre>
|
|
<p> <tt>cannonField</tt> is now a member variable, so we carefully change the
|
|
constructor to use it. (The <em>good</em> programmers at Trolltech never
|
|
forget this, but I do. Caveat programmor - if "programmor" is Latin,
|
|
at least. Anyway, back to the code.)
|
|
<p> <pre> <a href="qobject.html#connect">connect</a>( cannonField, SIGNAL(hit()),
|
|
this, SLOT(hit()) );
|
|
<a href="qobject.html#connect">connect</a>( cannonField, SIGNAL(missed()),
|
|
this, SLOT(missed()) );
|
|
</pre>
|
|
<p> This time we want to do something when the shot has hit or missed the
|
|
target. Thus we connect the hit() and missed() signals of the
|
|
CannonField to two protected slots with the same names in this class.
|
|
<p> <pre> <a href="qobject.html#connect">connect</a>( shoot, SIGNAL(<a href="qbutton.html#clicked">clicked</a>()), SLOT(fire()) );
|
|
</pre>
|
|
<p> Previously we connected the Shoot button's clicked() signal directly
|
|
to the CannonField's shoot() 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> Notice how easy it is to change the behavior of a program when you are
|
|
working with self-contained components.
|
|
<p> <pre> <a href="qobject.html#connect">connect</a>( cannonField, SIGNAL(canShoot(bool)),
|
|
<a name="x2416"></a> shoot, SLOT(<a href="qwidget.html#setEnabled">setEnabled</a>(bool)) );
|
|
</pre>
|
|
<p> We also use the cannonField's canShoot() signal to enable or disable
|
|
the Shoot button appropriately.
|
|
<p> <pre> QPushButton *restart
|
|
= new <a href="qpushbutton.html">QPushButton</a>( "&New Game", this, "newgame" );
|
|
restart->setFont( QFont( "Times", 18, QFont::Bold ) );
|
|
|
|
<a href="qobject.html#connect">connect</a>( restart, SIGNAL(clicked()), this, SLOT(newGame()) );
|
|
</pre>
|
|
<p> We create, set up, and connect the New Game button as we have done
|
|
with the other buttons. Clicking this button will activate the
|
|
newGame() slot in this widget.
|
|
<p> <pre> hits = new <a href="qlcdnumber.html">QLCDNumber</a>( 2, this, "hits" );
|
|
shotsLeft = new <a href="qlcdnumber.html">QLCDNumber</a>( 2, this, "shotsleft" );
|
|
<a href="qlabel.html">QLabel</a> *hitsL = new <a href="qlabel.html">QLabel</a>( "HITS", this, "hitsLabel" );
|
|
QLabel *shotsLeftL
|
|
= new <a href="qlabel.html">QLabel</a>( "SHOTS LEFT", this, "shotsleftLabel" );
|
|
</pre>
|
|
<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 GameBoard class because there's
|
|
nothing much we want to do with them. Qt will delete them when the
|
|
GameBoard widget is destroyed, and the layout classes will resize them
|
|
appropriately.
|
|
<p> <pre> <a href="qhboxlayout.html">QHBoxLayout</a> *topBox = new <a href="qhboxlayout.html">QHBoxLayout</a>;
|
|
<a name="x2413"></a> grid-><a href="qgridlayout.html#addLayout">addLayout</a>( topBox, 0, 1 );
|
|
topBox-><a href="qboxlayout.html#addWidget">addWidget</a>( shoot );
|
|
topBox-><a href="qboxlayout.html#addWidget">addWidget</a>( hits );
|
|
topBox-><a href="qboxlayout.html#addWidget">addWidget</a>( hitsL );
|
|
topBox-><a href="qboxlayout.html#addWidget">addWidget</a>( shotsLeft );
|
|
topBox-><a href="qboxlayout.html#addWidget">addWidget</a>( shotsLeftL );
|
|
<a name="x2410"></a> topBox-><a href="qboxlayout.html#addStretch">addStretch</a>( 1 );
|
|
<a name="x2411"></a> topBox-><a href="qboxlayout.html#addWidget">addWidget</a>( restart );
|
|
</pre>
|
|
<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> Notice that we let all the widgets have their preferred sizes, instead
|
|
putting the stretch just to the left of the New Game button.
|
|
<p> <pre> newGame();
|
|
}
|
|
</pre>
|
|
<p> We're all done constructing the GameBoard, so we start it all using
|
|
newGame(). (NewGame() is a slot, but as we said, slots can be used as
|
|
ordinary functions, too.)
|
|
<p> <pre> void GameBoard::fire()
|
|
{
|
|
if ( cannonField->gameOver() || cannonField->isShooting() )
|
|
return;
|
|
shotsLeft-><a href="qlcdnumber.html#display">display</a>( shotsLeft-><a href="qlcdnumber.html#intValue">intValue</a>() - 1 );
|
|
cannonField->shoot();
|
|
}
|
|
</pre>
|
|
<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> <pre> void GameBoard::hit()
|
|
{
|
|
hits-><a href="qlcdnumber.html#display">display</a>( hits-><a href="qlcdnumber.html#intValue">intValue</a>() + 1 );
|
|
if ( shotsLeft-><a href="qlcdnumber.html#intValue">intValue</a>() == 0 )
|
|
cannonField->setGameOver();
|
|
else
|
|
cannonField->newTarget();
|
|
}
|
|
</pre>
|
|
<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 CannonField generate a new target.
|
|
<p> <pre> void GameBoard::missed()
|
|
{
|
|
<a name="x2415"></a> if ( shotsLeft-><a href="qlcdnumber.html#intValue">intValue</a>() == 0 )
|
|
cannonField->setGameOver();
|
|
}
|
|
</pre>
|
|
<p> This slot is activated when a shot has missed the target. If there are no
|
|
shots left, the game is over.
|
|
<p> <pre> void GameBoard::newGame()
|
|
{
|
|
<a name="x2414"></a> shotsLeft-><a href="qlcdnumber.html#display">display</a>( 15 );
|
|
hits-><a href="qlcdnumber.html#display">display</a>( 0 );
|
|
cannonField->restartGame();
|
|
cannonField->newTarget();
|
|
}
|
|
</pre>
|
|
<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> <h3> <a href="t13-main-cpp.html">t13/main.cpp</a>
|
|
</h3>
|
|
<a name="1-7"></a><p> This file has just been on a diet. MyWidget is gone, and the only
|
|
thing left is the main() function, unchanged except for the name
|
|
change.
|
|
<p> <h2> Behavior
|
|
</h2>
|
|
<a name="2"></a><p> The cannon can shoot at a target; a new target is automatically created
|
|
when one has been hit.
|
|
<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> (See <a href="tutorial1-07.html#compiling">Compiling</a> for how to create a
|
|
makefile and build the application.)
|
|
<p> <h2> Exercises
|
|
</h2>
|
|
<a name="3"></a><p> Add a random wind factor and show it to the user.
|
|
<p> Make some splatter effects when the shot hits the target.
|
|
<p> Implement multiple targets.
|
|
<p> You're now ready for <a href="tutorial1-14.html">Chapter 14.</a>
|
|
<p> [<a href="tutorial1-12.html">Previous tutorial</a>]
|
|
[<a href="tutorial1-14.html">Next tutorial</a>]
|
|
[<a href="tutorial.html">Main tutorial page</a>]
|
|
<p>
|
|
<!-- eof -->
|
|
<p><address><hr><div align=center>
|
|
<table width=100% cellspacing=0 border=0><tr>
|
|
<td>Copyright © 2007
|
|
<a href="troll.html">Trolltech</a><td align=center><a href="trademarks.html">Trademarks</a>
|
|
<td align=right><div align=right>Qt 3.3.8</div>
|
|
</table></div></address></body>
|
|
</html>
|