diff options
Diffstat (limited to 'doc/html/tutorial1-13.html')
-rw-r--r-- | doc/html/tutorial1-13.html | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/doc/html/tutorial1-13.html b/doc/html/tutorial1-13.html new file mode 100644 index 000000000..3153d87f9 --- /dev/null +++ b/doc/html/tutorial1-13.html @@ -0,0 +1,396 @@ +<!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>TQt 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>TQt 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 TQSlider; + class TQLabel; + + class LCDRange : public <a href="qwidget.html">TQWidget</a> +</pre> +<p> We inherit <a href="qwidget.html">TQWidget</a> rather than <a href="qvbox.html">TQVBox</a>. TQVBox 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">TQVBoxLayout</a>. (As you remember, TQVBoxLayout 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">TQWidget</a> *parent, const char *name ) + : <a href="qwidget.html">TQWidget</a>( parent, name ) +</pre> +<p> We inherit TQWidget 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">TQVBoxLayout</a> * l = new <a href="qvboxlayout.html">TQVBoxLayout</a>( this ); +</pre> +<p> We create a TQVBoxLayout 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">TQLCDNumber</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">TQVBoxLayout</a> (and <a href="qhboxlayout.html">TQHBoxLayout</a>, and +<a href="qgridlayout.html">TQGridLayout</a>) offers but classes like <a href="qvbox.html">TQVBox</a> do not. In this case +we're saying that the TQLCDNumber 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">TQPaintEvent</a> *e ) + { + <a name="x2405"></a> <a href="qrect.html">TQRect</a> updateR = e-><a href="qpaintevent.html#rect">rect</a>(); + <a href="qpainter.html">TQPainter</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>( TQFont( "Courier", 48, TQFont::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 +TQt 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 TQPushButton; + class LCDRange; + class TQLCDNumber; + class CannonField; + + #include "lcdrange.h" + #include "cannon.h" + + class GameBoard : public <a href="qwidget.html">TQWidget</a> + { + <a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a> + public: + GameBoard( <a href="qwidget.html">TQWidget</a> *parent=0, const char *name=0 ); + + protected slots: + void fire(); + void hit(); + void missed(); + void newGame(); + + private: + <a href="qlcdnumber.html">TQLCDNumber</a> *hits; + <a href="qlcdnumber.html">TQLCDNumber</a> *shotsLeft; + CannonField *cannonField; + }; +</pre> +<p> We have now added four slots. These are protected and are used internally. +We have also added two TQLCDNumbers (<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> TQPushButton *restart + = new <a href="qpushbutton.html">TQPushButton</a>( "&New Game", this, "newgame" ); + restart->setFont( TQFont( "Times", 18, TQFont::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">TQLCDNumber</a>( 2, this, "hits" ); + shotsLeft = new <a href="qlcdnumber.html">TQLCDNumber</a>( 2, this, "shotsleft" ); + <a href="qlabel.html">TQLabel</a> *hitsL = new <a href="qlabel.html">TQLabel</a>( "HITS", this, "hitsLabel" ); + TQLabel *shotsLeftL + = new <a href="qlabel.html">TQLabel</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">TQLabel</a> widgets in the GameBoard class because there's +nothing much we want to do with them. TQt will delete them when the +GameBoard widget is destroyed, and the layout classes will resize them +appropriately. +<p> <pre> <a href="qhboxlayout.html">TQHBoxLayout</a> *topBox = new <a href="qhboxlayout.html">TQHBoxLayout</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>TQt 3.3.8</div> +</table></div></address></body> +</html> |