Stécker vum Himmel (Items from the Sky) is a Tetris clone. Tetris is the perfect game to begin our journey on the path
to becoming a game developer, as Tetris contains all the elements found in every game (game loop, input processing,
updating of the game world, …), minus all the fancy graphic stuff — and Tetris is straightforward to implement nowadays.
There are five basic tetronimos, two of them have mirror shapes, thus there are seven different types of pieces in a
standard Tetris game: the square, the I-shape, the L-shape, the mirrored L-shape, the N-shape, the mirrored N-shape and
the T-shape. The following picture shows the five basic Tetronimos (without the mirrored shapes):
To define those, simple matrices are sufficient. Here is the square shape in matrix form, for example:
where an entry of means no block, means a normal block and stands for the pivot centre or the centre
of rotation of the Tetronimo. To store this information in C++, we simply use a two-dimensional array:
To be able to rotate the pieces, we could use some linear algebra and use rotation matrices, but in this case, that
would clearly be overkill. It is way easier to simply store each possible rotation of each piece in different matrices —
that is, each piece will have four matrices associated to it. This is also the reason we are using
matrices.
Here is the C++-code for the L-shape:
That was rather easy, wasn’t it? There is another important question to consider, however: The correct position of each
piece at the top of the screen, once it enters the game. The piece should start at the centre of the screen. As each
piece is different, each piece needs different offset variables, which we will also store in an array:
I suggest you take a piece of paper and draw each Tetronimo to really understand what just happened. ^^
To easily use the newly defined Tetronimos in our game, we combine all of the above information into the Tetronimo
class.
The Tetronimo Class
The TetronimoPiece structure simply holds the basic information about each Tetronimo, i.e. its shape, its current
rotation, its current position as well as for long it has been in its current position. The latter information is used
to make the Tetronimos fall down over time.
The three public methods in the Tetronimo class are the ones we actually will need later in the game; basically they
are just getters.
getBlockType
Taking the type of a Tetronimo, its rotation and its position as input, this method simply returns the block type of the
given tetronimo at the current position. The return value is the 0, if there is no block, 1 if it is a normal block and
2 if the requested block is the actual pivot center of the Tetronimo:
getStartingX
Based on the type of the Tetronimo and its rotation, this method returns the horizontal offset that must be applied for
the Tetronimo to enter the game in the middle of the screen:
getStartingY
Based on the type of the Tetronimo and its rotation, this method returns the vertical offset that must be applied to the
Tetrimo to make sure that only one row of the Tetronimo is visible once it enters the game:
As you can see, creating the Tetronimos really wasn’t that difficult. Now we need to create a playing field for them.
The Game Board
To create the game board we will need, as you can surely guess, yet another array. The array will be filled with 0s or
1s, depending on whether the particular spot on the game board is filled with a Tetronimo or not:
Filling the Board
Storing the Tetronimos on the game board (once they can no longer move) is very straightforward, for each block of the
Tetronimo, the array of the game board is set to “PositionFilled”:
Obviously, once a Tetronimo is “stored” in the upper row, the game is over:
Clearing Lines
In order for the player to survive as long as possible, they have to clear lines. To delete a single line, or row, we
simply shift all the blocks above that line down by one:
Good players can clear multiple rows at a time (up to four rows). We thus have to check for possibly completed lines,
and delete them, at each update. To check whether a line is complete, we simply loop through its array:
The function monitors how many lines are to be deleted and depending on whether the player managed to clear four lines
at the same time or not, different animations and sounds are tasked to be played. The method also updates the current
score and searched for the highest row that is occupied by a Tetronimo block (this information is used to animate the
game bucket).
Initialization
Initializing the game board is effortless, we simply chose random Tetronimos to appear:
Collision Detection
The player can translate and rotate a Tetronimo piece. Before actually moving or rotation a piece, it is important to
check for collisions with pieces already stores on the board, or, for example, when trying to rotate against the borders
of the bucket:
Basically this method simply compares all the blocks of a Tetronimo pieces against the blocks already stores in the game
board to see whether a desired movement, rotation or translation, is possible or not.
Imagine the player wanting to rotate a Tetronimo. We would simply call this function with the desired rotation and if
the function return false, we discard the player’s wish to rotate the piece!
Creating new Tetronimos once in a while is equally easy:
Movement
The following three straightforward methods allow the player to translate a Tetronimo piece:
And here are the methods to rotate a Tetronimo:
Notice how the isPossibleMovement function is used to check whether the desired action is possible or not.
The player can also decide to let the piece fall down immediately:
Game Logic
Most of the work needs to be done while updating the game:
Drawing
Now all that is left to do is to actually draw the game board. There are two methods, the first is responsible to draw a
Tetronimo piece by simply running through its defining matrix:
The second method is used to draw the entire board:
To bring those two methods together, a third draw methods calls them in the correct order:
Gameplay Features
Game Score and Levels
To make the game a bit more interesting, the player can accumulate points while playing the game. Each time he reaches a
certain number of points or clears a certain number of lines, the game level increases, that is, the game will become
faster, i.e. the Tetronimos will fall down more quickly. To reflect the increase in game difficulty, the player will be
awarded with more points at higher levels:
Oracle
One new feature is to allow the player to see where the current Tetronimo would land if left alone.
To do so, we not only keep track of the current and the next piece, but of a third piece called the previewPiece,
which basically is just a copy of the current piece in play:
During the game update, we will simply update the position of the preview piece based on the current Tetronimo and
then tell the game to let it fall down to get a preview of where it would land:
When drawing the preview piece, we only draw the outlines of the Tetronimo:
Switch / Shift
In many modern Tetris games, the player can switch the current piece with the next piece, but not twice in a row. The
game must does keep track whether the piece was already switched or not, which can easily be done with a boolean.
Performing the shift itself is conceptually easy, the game only needs to swap the two pieces - making sure there are no
collisions. The collision detection was already implemented in the “isPossibleMovement” method:
Highscore
The game now also keeps track of the highest scores achieved, this is done via marshalling (as seen in previous
tutorials).
While there are many other features, such as some graphical tweaks (changing background images, moving dots around the
bucket, the bucket colours changing with the fill of the bucket) and music (the music speeds up as the levels increase),
as well as an intro, menus and credits, I don’t want to elaborate on all of them, you can simply read the source code to
learn how they work. All of these features simply build upon the previous tutorials. Katy wrote about some of these
features on her blog.
Here is a gameplay video of an alpha version of the game: