diff options
Diffstat (limited to 'doc/src/examples/tetrix.qdoc')
-rw-r--r-- | doc/src/examples/tetrix.qdoc | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/doc/src/examples/tetrix.qdoc b/doc/src/examples/tetrix.qdoc new file mode 100644 index 0000000..b9adb98 --- /dev/null +++ b/doc/src/examples/tetrix.qdoc @@ -0,0 +1,445 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example widgets/tetrix + \title Tetrix Example + + The Tetrix example is a Qt version of the classic Tetrix game. + + \image tetrix-example.png + + The object of the game is to stack pieces dropped from the top of the + playing area so that they fill entire rows at the bottom of the playing area. + + When a row is filled, all the blocks on that row are removed, the player earns + a number of points, and the pieces above are moved down to occupy that row. + If more than one row is filled, the blocks on each row are removed, and the + player earns extra points. + + The \gui{Left} cursor key moves the current piece one space to the left, the + \gui{Right} cursor key moves it one space to the right, the \gui{Up} cursor + key rotates the piece counter-clockwise by 90 degrees, and the \gui{Down} + cursor key rotates the piece clockwise by 90 degrees. + + To avoid waiting for a piece to fall to the bottom of the board, press \gui{D} + to immediately move the piece down by one row, or press the \gui{Space} key to + drop it as close to the bottom of the board as possible. + + This example shows how a simple game can be created using only three classes: + + \list + \o The \c TetrixWindow class is used to display the player's score, number of + lives, and information about the next piece to appear. + \o The \c TetrixBoard class contains the game logic, handles keyboard input, and + displays the pieces on the playing area. + \o The \c TetrixPiece class contains information about each piece. + \endlist + + In this approach, the \c TetrixBoard class is the most complex class, since it + handles the game logic and rendering. One benefit of this is that the + \c TetrixWindow and \c TetrixPiece classes are very simple and contain only a + minimum of code. + + \section1 TetrixWindow Class Definition + + The \c TetrixWindow class is used to display the game information and contains + the playing area: + + \snippet examples/widgets/tetrix/tetrixwindow.h 0 + + We use private member variables for the board, various display widgets, and + buttons to allow the user to start a new game, pause the current game, and quit. + + Although the window inherits QWidget, the constructor does not provide an + argument to allow a parent widget to be specified. This is because the window + will always be used as a top-level widget. + + \section1 TetrixWindow Class Implementation + + The constructor sets up the user interface elements for the game: + + \snippet examples/widgets/tetrix/tetrixwindow.cpp 0 + + We begin by constructing a \c TetrixBoard instance for the playing area and a + label that shows the next piece to be dropped into the playing area; the label + is initially empty. + + Three QLCDNumber objects are used to display the score, number of lives, and + lines removed. These initially show default values, and will be filled in + when a game begins: + + \snippet examples/widgets/tetrix/tetrixwindow.cpp 1 + + Three buttons with shortcuts are constructed so that the user can start a + new game, pause the current game, and quit the application: + + \snippet examples/widgets/tetrix/tetrixwindow.cpp 2 + \snippet examples/widgets/tetrix/tetrixwindow.cpp 3 + + These buttons are configured so that they never receive the keyboard focus; + we want the keyboard focus to remain with the \c TetrixBoard instance so that + it receives all the keyboard events. Nonetheless, the buttons will still respond + to \key{Alt} key shortcuts. + + We connect \l{QAbstractButton::}{clicked()} signals from the \gui{Start} + and \gui{Pause} buttons to the board, and from the \gui{Quit} button to the + application's \l{QApplication::}{quit()} slot. + + \snippet examples/widgets/tetrix/tetrixwindow.cpp 4 + \snippet examples/widgets/tetrix/tetrixwindow.cpp 5 + + Signals from the board are also connected to the LCD widgets for the purpose of + updating the score, number of lives, and lines removed from the playing area. + + We place the label, LCD widgets, and the board into a QGridLayout + along with some labels that we create with the \c createLabel() convenience + function: + + \snippet examples/widgets/tetrix/tetrixwindow.cpp 6 + + Finally, we set the grid layout on the widget, give the window a title, and + resize it to an appropriate size. + + The \c createLabel() convenience function simply creates a new label on the + heap, gives it an appropriate alignment, and returns it to the caller: + + \snippet examples/widgets/tetrix/tetrixwindow.cpp 7 + + Since each label will be used in the widget's layout, it will become a child + of the \c TetrixWindow widget and, as a result, it will be deleted when the + window is deleted. + + \section1 TetrixPiece Class Definition + + The \c TetrixPiece class holds information about a piece in the game's + playing area, including its shape, position, and the range of positions it can + occupy on the board: + + \snippet examples/widgets/tetrix/tetrixpiece.h 0 + + Each shape contains four blocks, and these are defined by the \c coords private + member variable. Additionally, each piece has a high-level description that is + stored internally in the \c pieceShape variable. + + The constructor is written inline in the definition, and simply ensures that + each piece is initially created with no shape. The \c shape() function simply + returns the contents of the \c pieceShape variable, and the \c x() and \c y() + functions return the x and y-coordinates of any given block in the shape. + + \section1 TetrixPiece Class Implementation + + The \c setRandomShape() function is used to select a random shape for a piece: + + \snippet examples/widgets/tetrix/tetrixpiece.cpp 0 + + For convenience, it simply chooses a random shape from the \c TetrixShape enum + and calls the \c setShape() function to perform the task of positioning the + blocks. + + The \c setShape() function uses a look-up table of pieces to associate each + shape with an array of block positions: + + \snippet examples/widgets/tetrix/tetrixpiece.cpp 1 + \snippet examples/widgets/tetrix/tetrixpiece.cpp 2 + + These positions are read from the table into the piece's own array of positions, + and the piece's internal shape information is updated to use the new shape. + + The \c x() and \c y() functions are implemented inline in the class definition, + returning positions defined on a grid that extends horizontally and vertically + with coordinates from -2 to 2. Although the predefined coordinates for each + piece only vary horizontally from -1 to 1 and vertically from -1 to 2, each + piece can be rotated by 90, 180, and 270 degrees. + + The \c minX() and \c maxX() functions return the minimum and maximum horizontal + coordinates occupied by the blocks that make up the piece: + + \snippet examples/widgets/tetrix/tetrixpiece.cpp 3 + \snippet examples/widgets/tetrix/tetrixpiece.cpp 4 + + Similarly, the \c minY() and \c maxY() functions return the minimum and maximum + vertical coordinates occupied by the blocks: + + \snippet examples/widgets/tetrix/tetrixpiece.cpp 5 + \snippet examples/widgets/tetrix/tetrixpiece.cpp 6 + + The \c rotatedLeft() function returns a new piece with the same shape as an + existing piece, but rotated counter-clockwise by 90 degrees: + + \snippet examples/widgets/tetrix/tetrixpiece.cpp 7 + + Similarly, the \c rotatedRight() function returns a new piece with the same + shape as an existing piece, but rotated clockwise by 90 degrees: + + \snippet examples/widgets/tetrix/tetrixpiece.cpp 9 + + These last two functions enable each piece to create rotated copies of itself. + + \section1 TetrixBoard Class Definition + + The \c TetrixBoard class inherits from QFrame and contains the game logic and display features: + + \snippet examples/widgets/tetrix/tetrixboard.h 0 + + Apart from the \c setNextPieceLabel() function and the \c start() and \c pause() + public slots, we only provide public functions to reimplement QWidget::sizeHint() + and QWidget::minimumSizeHint(). The signals are used to communicate changes to + the player's information to the \c TetrixWindow instance. + + The rest of the functionality is provided by reimplementations of protected event + handlers and private functions: + + \snippet examples/widgets/tetrix/tetrixboard.h 1 + + The board is composed of a fixed-size array whose elements correspond to + spaces for individual blocks. Each element in the array contains a \c TetrixShape + value corresponding to the type of shape that occupies that element. + + Each shape on the board will occupy four elements in the array, and these will + all contain the enum value that corresponds to the type of the shape. + + We use a QBasicTimer to control the rate at which pieces fall toward the bottom + of the playing area. This allows us to provide an implementation of + \l{QObject::}{timerEvent()} that we can use to update the widget. + + \section1 TetrixBoard Class Implementation + + In the constructor, we customize the frame style of the widget, ensure that + keyboard input will be received by the widget by using Qt::StrongFocus for the + focus policy, and initialize the game state: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 0 + + The first (next) piece is also set up with a random shape. + + The \c setNextPieceLabel() function is used to pass in an externally-constructed + label to the board, so that it can be shown alongside the playing area: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 1 + + We provide a reasonable size hint and minimum size hint for the board, based on + the size of the space for each block in the playing area: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 2 + \snippet examples/widgets/tetrix/tetrixboard.cpp 3 + + By using a minimum size hint, we indicate to the layout in the parent widget + that the board should not shrink below a minimum size. + + A new game is started when the \c start() slot is called. This resets the + game's state, the player's score and level, and the contents of the board: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 4 + + We also emit signals to inform other components of these changes before creating + a new piece that is ready to be dropped into the playing area. We start the + timer that determines how often the piece drops down one row on the board. + + The \c pause() slot is used to temporarily stop the current game by stopping the + internal timer: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 5 + \snippet examples/widgets/tetrix/tetrixboard.cpp 6 + + We perform checks to ensure that the game can only be paused if it is already + running and not already paused. + + The \c paintEvent() function is straightforward to implement. We begin by + calling the base class's implementation of \l{QWidget::}{paintEvent()} before + constructing a QPainter for use on the board: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 7 + + Since the board is a subclass of QFrame, we obtain a QRect that covers the area + \e inside the frame decoration before drawing our own content. + + If the game is paused, we want to hide the existing state of the board and + show some text. We achieve this by painting text onto the widget and returning + early from the function. The rest of the painting is performed after this point. + + The position of the top of the board is found by subtracting the total height + of each space on the board from the bottom of the frame's internal rectangle. + For each space on the board that is occupied by a piece, we call the + \c drawSquare() function to draw a block at that position. + + \snippet examples/widgets/tetrix/tetrixboard.cpp 8 + \snippet examples/widgets/tetrix/tetrixboard.cpp 9 + + Spaces that are not occupied by blocks are left blank. + + Unlike the existing pieces on the board, the current piece is drawn + block-by-block at its current position: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 10 + \snippet examples/widgets/tetrix/tetrixboard.cpp 11 + \snippet examples/widgets/tetrix/tetrixboard.cpp 12 + + The \c keyPressEvent() handler is called whenever the player presses a key while + the \c TetrixBoard widget has the keyboard focus. + + \snippet examples/widgets/tetrix/tetrixboard.cpp 13 + + If there is no current game, the game is running but paused, or if there is no + current shape to control, we simply pass on the event to the base class. + + We check whether the event is about any of the keys that the player uses to + control the current piece and, if so, we call the relevant function to handle + the input: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 14 + + In the case where the player presses a key that we are not interested in, we + again pass on the event to the base class's implementation of + \l{QWidget::}{keyPressEvent()}. + + The \c timerEvent() handler is called every time the class's QBasicTimer + instance times out. We need to check that the event we receive corresponds to + our timer. If it does, we can update the board: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 15 + \snippet examples/widgets/tetrix/tetrixboard.cpp 16 + \snippet examples/widgets/tetrix/tetrixboard.cpp 17 + + If a row (or line) has just been filled, we create a new piece and reset the + timer; otherwise we move the current piece down by one row. We let the base + class handle other timer events that we receive. + + The \c clearBoard() function simply fills the board with the + \c TetrixShape::NoShape value: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 18 + + The \c dropDown() function moves the current piece down as far as possible on + the board, either until it is touching the bottom of the playing area or it is + stacked on top of another piece: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 19 + \snippet examples/widgets/tetrix/tetrixboard.cpp 20 + + The number of rows the piece has dropped is recorded and passed to the + \c pieceDropped() function so that the player's score can be updated. + + The \c oneLineDown() function is used to move the current piece down by one row + (line), either when the user presses the \gui{D} key or when the piece is + scheduled to move: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 21 + + If the piece cannot drop down by one line, we call the \c pieceDropped() function + with zero as the argument to indicate that it cannot fall any further, and that + the player should receive no extra points for the fall. + + The \c pieceDropped() function itself is responsible for awarding points to the + player for positioning the current piece, checking for full rows on the board + and, if no lines have been removed, creating a new piece to replace the current + one: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 22 + \snippet examples/widgets/tetrix/tetrixboard.cpp 23 + + We call \c removeFullLines() each time a piece has been dropped. This scans + the board from bottom to top, looking for blank spaces on each row. + + \snippet examples/widgets/tetrix/tetrixboard.cpp 24 + \snippet examples/widgets/tetrix/tetrixboard.cpp 25 + \snippet examples/widgets/tetrix/tetrixboard.cpp 26 + \snippet examples/widgets/tetrix/tetrixboard.cpp 27 + + If a row contains no blank spaces, the rows above it are copied down by one row + to compress the stack of pieces, the top row on the board is cleared, and the + number of full lines found is incremented. + + \snippet examples/widgets/tetrix/tetrixboard.cpp 28 + \snippet examples/widgets/tetrix/tetrixboard.cpp 29 + + If some lines have been removed, the player's score and the total number of lines + removed are updated. The \c linesRemoved() and \c scoreChanged() signals are + emitted to send these new values to other widgets in the window. + + Additionally, we set the timer to elapse after half a second, set the + \c isWaitingAfterLine flag to indicate that lines have been removed, unset + the piece's shape to ensure that it is not drawn, and update the widget. + The next time that the \c timerEvent() handler is called, a new piece will be + created and the game will continue. + + The \c newPiece() function places the next available piece at the top of the + board, and creates a new piece with a random shape: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 30 + \snippet examples/widgets/tetrix/tetrixboard.cpp 31 + + We place a new piece in the middle of the board at the top. The game is over if + the piece can't move, so we unset its shape to prevent it from being drawn, stop + the timer, and unset the \c isStarted flag. + + The \c showNextPiece() function updates the label that shows the next piece to + be dropped: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 32 + \snippet examples/widgets/tetrix/tetrixboard.cpp 33 + + We draw the piece's component blocks onto a pixmap that is then set on the label. + + The \c tryMove() function is used to determine whether a piece can be positioned + at the specified coordinates: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 34 + + We examine the spaces on the board that the piece needs to occupy and, if they + are already occupied by other pieces, we return \c false to indicate that the + move has failed. + + \snippet examples/widgets/tetrix/tetrixboard.cpp 35 + + If the piece could be placed on the board at the desired location, we update the + current piece and its position, update the widget, and return \c true to indicate + success. + + The \c drawSquare() function draws the blocks (normally squares) that make up + each piece using different colors for pieces with different shapes: + + \snippet examples/widgets/tetrix/tetrixboard.cpp 36 + + We obtain the color to use from a look-up table that relates each shape to an + RGB value, and use the painter provided to draw the block at the specified + coordinates. +*/ |