Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Falling Blocks

3.80/5 (2 votes)
19 Oct 2015CPOL5 min read 14.9K   288  
Another way to implement Falling Blocks Game

Introduction

After downloading Visual Studio 2015, I wanted to do something fun with it, so following a time-honored tradition I built yet another version of the famous Falling Blocks game (I've also included a Visual Studio 2010 solution file). Since I am not a Falling Blocks expert, there may be some features I have inadvertently left out. If you are an expert, this may be a good framework with which to build your own full-featured Falling Blocks game.

Background

If you have no experience playing Falling Blocks, a number of Falling Blocks tutorials can be found online.

Using the Code

After downloading and extracting the project, open it with either Visual Studio 2010 or 2015 and build it by pressing F6. Then click the Start button and the Tetrominos will appear and begin falling on the game surface (often called the "well"). I shall henceforth use the word "piece" rather than "Tetromino" since it rolls off the tongue a little easier. The right and left arrow keys move the piece left or right, and the up and down arrow keys rotate the piece 90°. Pressing the space bar causes the current piece to "drop" from its current position to the lowest position it can occupy. The TrackBar control (or "slider" as it is commonly called) in the upper left controls the speed at which the pieces fall; moving it to the left decreases the speed, and moving it to the right increases the speed. Clicking the "New Game" button sets the score to zero and restarts the game. Pressing P or Esc toggles the pausing of the game after starting the game.

The 7 pieces, known as I, J, L, O, S, T, and Z are derived from the FallingBlocksPiece class. The derived classes are IPiece, JPiece, and so on. Each piece has a two-dimensional array of 4 rectangles, with the first index representing the 0°, 90°, 180° and 270° orientations of the piece for the T, L and J pieces. Due to their symmetry, the I, S, and Z pieces have 2 orientations, and O piece has one orientation. The second index of the two-dimensional array ranges from 0 to 3 since each piece is comprised of four rectangles. Here is the declaration of the array:

C#
public Rectangle[][] Rectangles { get; set; }

The Draw method in the FallingBlocksPiece class is where the piece is rendered. The .NET Framework Matrix.Translate method is used to translate the pieces, either when the timer fires (described below) or you press the space bar or left or right arrow keys. I talk a little bit about rotation and translation matrices in my articles here and here. I had originally used the .NET Framework Matrix.Rotate method to rotate the pieces when you press the up and down arrow keys, but since the rotations are just multiples of 90°, I decided to simply store the rotated pieces in the two-dimensional array in the Piece classes described above. Pressing the up and down arrow keys determines the orientation and thus the first index.

Since the choices for the colors of the pieces varies considerably between various versions of Falling Blocks games, I decided to make them configurable via the app.config file. For example, to set the "S" piece to the color green, the following is in the app.config file:

C#
<setting name="ColorS" serializeAs="String">
    <value>Green</value>
</setting>

Whether or not row and column labels are displayed is also configurable; this was useful during development.

XML
<setting name="DrawLinesAndLabels" serializeAs="String">
    <value>True</value>
</setting>

The colors of the game surface are also configurable; see app.config for details.

A key component of my Falling Blocks game is a timer called timerPieceDrop and its callback method timerPieceDrop_Tick which moves the piece down when the timer fires (every second 2 seconds, unless changed using the TrackBar control as described above):

C#
private void timerPieceDrop_Tick(object sender, EventArgs e)
{
    if (!FallingBlocksGame.CanStillFall(currentPiece))
    {
        NextPiece();
    }

    this.pbFallingBlocks.Invalidate();
}

In order to keep track of where the pieces are on the game surface, I implemented a two-dimensional array of bool in the class OccupiedMap with false and true corresponding to not occupied and occupied, respectively.

I have implemented the scoring such that if you "clear" a line, that is fill in an entire line with pieces, that line will essentially "fall" to the row below it (overwriting what was there), and all the pieces above the cleared line will fall one row, and you earn 100 points. This is illustrated below: as it falls, the violet "S Tetromino" occupies rows 19 and 20 (the bottom row is 22).

Image 1

The next time the timer fires (or if you "drop" the piece by pressing the space bar, the piece will fall one more row and occupy rows 20 and 21 as illustrated below.

Image 2

In the Score class there is a method called RowsToDrop() that determines if all columns in line 21 are occupied. If so, the 4 pieces occupying row 21 are "dropped" to row 22 (overwriting what was on row 22), and the score is updated from 0 to 100 as illustrated below:

Image 3

Clearing four rows simultaneously earns you 100 points for each row, and an extra 400 points for a total of 800.

As the pieces are dropped (that is, moved to the next row), a simple algorithm to check for collisions is used: the Axis Aligned Bounding Box algorithm, or as it commonly referred to online, just "AABB". The method IsCollision returns true if any rectangles that comprise the current piece intersects with any of the rectangles in the other pieces. A method called UpdateTransformedPoints updates the current coordinates of the piece as it is moved. The game is over when no more pieces can be placed on the game surface. Or to stop playing, you can click the Pause button, or you can use File->Exit to exit the program.

As with my NXT Bluetooth Monitor project, I have attached a console to the Windows Forms project for debugging purposes using AllocConsole and .NET Interop. It is enabled by default; to disable it, simply comment out the first 7 lines of Main:

C#
IntPtr ptr = GetForegroundWindow();
int pid;
GetWindowThreadProcessId(ptr, out pid);
Process process = Process.GetProcessById(pid);
AllocConsole();
AttachConsole(process.Id);
Console.WriteLine("{0} FallingBlocksCEP started", DateTime.Now);

History

  • Version 1.0.5.0 (Click Help->About to see the version)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)