📄 t13.html
字号:
used. <pre> if ( updateR.<a href="qrect.html#5b3d2b">intersects</a>( cannonRect() ) ) paintCannon( &p ); if ( isShooting() && updateR.<a href="qrect.html#5b3d2b">intersects</a>( shotRect() ) ) paintShot( &p ); if ( !gameEnded && updateR.<a href="qrect.html#5b3d2b">intersects</a>( targetRect() ) ) paintTarget( &p ); }</pre><p>We only draw the shot when shooting and the target only when playing(that is, when the game is not ended).<p><h3><a href="t13-gamebrd-h.html">gamebrd.h</a></h3><p>This file is new. It contains the definition of the GameBoard class,which was last seen as MyWidget. <pre> class QPushButton; class LCDRange; class QLCDNumber; class CannonField; #include "lcdrange.h" #include "cannon.h" class GameBoard : public QWidget { Q_OBJECT 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: <code>hits</code> and <code>shotsLeft,</code> which displaythe game status.<p><h3><a href="t13-gamebrd-cpp.html">gamebrd.cpp</a></h3><p>This file is new. It contains the implementation of the GameBoardclass, which was last seen as MyWidget. <p>We have made some changes in the GameBoard constructor. <pre> cannonField = new CannonField( this, "cannonField" );</pre><p><code>cannonField</code> is now a member variable, so we carefully change theconstructor to use it. (The <em>good</em> programmers at Trolltech neverforget this, but I do. Caveat programmor. If "programmor" is latin,at least. Anyway, back to the code.) <pre> <a href="qobject.html#fbde73">connect</a>( cannonField, SIGNAL(hit()), this, SLOT(hit()) ); <a href="qobject.html#fbde73">connect</a>( cannonField, SIGNAL(missed()), this, SLOT(missed()) );</pre><p>This time we want to do something when the shot has hit or missed thetarget. Thus we connect the hit() and missed() signals of theCannonField to two protected slots with the same names in this class. <pre> <a href="qobject.html#fbde73">connect</a>( shoot, SIGNAL(clicked()), SLOT(fire()) );</pre><p>Previously, we connected the shoot button's clicked() signal directlyto the CannonField's shoot() slot. This time we want to keep track ofthe number of shots fired, so we connect it to a protected slot inthis class instead.<p>Notice how easy it is to change the behavior of a program when you areworking with self-contained components. <pre> <a href="qobject.html#fbde73">connect</a>( cannonField, SIGNAL(canShoot(bool)), shoot, SLOT(<a href="qwidget.html#4b103c">setEnabled</a>(bool)) );</pre><p>We also use the cannonField's canShoot() signal to enable or disablethe Shoot button appropriately. <pre> <a href="qpushbutton.html">QPushButton</a> *restart = new <a href="qpushbutton.html">QPushButton</a>( "&New Game", this, "newgame" ); restart-><a href="qwidget.html#090d60">setFont</a>( <a href="qfont.html">QFont</a>( "Times", 18, QFont::Bold ) ); <a href="qobject.html#fbde73">connect</a>( restart, SIGNAL(clicked()), this, SLOT(newGame()) );</pre><p>We create, set up and connect the New Game button like we have donewith the other buttons. Clicking this button will activate thenewGame() slot in this widget. <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" ); <a href="qlabel.html">QLabel</a> *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 thepointers to the QLabel widgets in the GameBoard class since there'snothing much we want to do with them. Qt will delete them when theGameBoard widget is destroyed, and the layout classes will resize themappropriately. <pre> <a href="qhboxlayout.html">QHBoxLayout</a> *topBox = new <a href="qhboxlayout.html">QHBoxLayout</a>; grid-><a href="qgridlayout.html#a409bc">addLayout</a>( topBox, 0, 1 ); topBox-><a href="qboxlayout.html#ebba99">addWidget</a>( shoot ); topBox-><a href="qboxlayout.html#ebba99">addWidget</a>( hits ); topBox-><a href="qboxlayout.html#ebba99">addWidget</a>( hitsL ); topBox-><a href="qboxlayout.html#ebba99">addWidget</a>( shotsLeft ); topBox-><a href="qboxlayout.html#ebba99">addWidget</a>( shotsLeftL ); topBox-><a href="qboxlayout.html#0226eb">addStretch</a>( 1 ); topBox-><a href="qboxlayout.html#ebba99">addWidget</a>( restart );</pre><p>The number of widgets in the top-right cell is getting large. Once itwas empty, now it's full enough that we group together the layoutsetting for better overview.<p>Notice how we let all the widgets have their preferred sizes, insteadputting the stretch just to the left of the New Game button. <pre> newGame(); }</pre><p>We're all done constructing the GameBoard, so we start it all usingnewGame(). (newGame() is a slot, but as we said, slots can be used asordinary functions too.) <pre> void GameBoard::fire() { if ( cannonField->gameOver() || cannonField->isShooting() ) return; shotsLeft->display( shotsLeft->intValue() - 1 ); cannonField->shoot(); }</pre><p>This function fires a shot. If the game is over or there is a shot in theair, we return immediately. We decrement number of shots left and tellthe cannon to shoot. <pre> void GameBoard::hit() { hits->display( hits->intValue() + 1 ); if ( shotsLeft->intValue() == 0 ) cannonField->setGameOver(); else cannonField->newTarget(); }</pre><p>This slot is activated when a shot has hit the target. We increment thenumber of hits. If there are no shots left, the game is over. Otherwise,we make the CannonField generate a new target. <pre> void GameBoard::missed() { if ( shotsLeft->intValue() == 0 ) cannonField->setGameOver(); }</pre><p>This slot is activated when a shot has missed the target. If there are noshots left, the game is over. <pre> void GameBoard::newGame() { shotsLeft->display( 15 ); hits->display( 0 ); cannonField->restartGame(); cannonField->newTarget(); }</pre><p>This slot is activated when the user clicks the restart button. It isalso called from the constructor. First, it sets the number of shotsto 15. Note that this is the only place in the program that we setthe number of shots. Change it to whatever you like to change thegame rules. Next, we reset the number of hits, restart the game andgenerate a new target.<p><h3><a href="t13-main-cpp.html">main.cpp</a></h3><p>This file has just been on a diet. MyWidget is gone and the onlything left is the main() function, unchanged except for the namechange.<p><h2>Behavior</h2><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><h2>Exercises</h2><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 may now go on to <a href="t14.html">chapter fourteen.</a><p>[<a href="t12.html">Previous tutorial</a>][<a href="t14.html">Next tutorial</a>][<a href="tutorial.html">Main tutorial page</a>]<p><address><hr><div align="center"><table width="100%" cellspacing="0" border="0"><tr><td>Copyright
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -