Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
517 lines (373 sloc) 13.932 kb

Pong is an important milestone in gaming history. If you can write it, you understand the basics of game programming. The next step in mastery comes from writing something like Tetris, with better animation and more complex scoring.

To follow along, download the sample code from https://github.com/PerlGameDev/SDL_Manual/raw/master/games/tetris.zip. To start the game, extract this Zip file and run:

The Game Window

The game starts out as you should expect by now:

This game requires several pieces of artwork, and so the program must manage and store them somehow. The SDLx::Surface module handles the conversion of files from their storage on disk into a format SDL can use, and an array will hold them:

The $back variable holds one special surface: the background image. Everything else is in the @piece array.

Managing Blocks

Blocks are critical to the success of a Tetris game. The program must represent them in a sensible way: they must be easy to access and they must be easy to manipulate and calculate. A hash fulfills the ease of access:

Each hash entry holds a four-element array reference which represents a grid of the piece. Each item in the array corresponds to an image in the @piece array. Drawing a piece means blitting one element of @piece for each non-zero entry in the piece's array.

Selecting pieces needs some randomness. The core List::Util module can help:

This code randomly chooses a $next_tile, then sets the piece data for the first piece in $curr_tile.

Piece Collisions

Collision detection is both easier (because only one piece at a time moves) and more difficult (because the screen continues to fill up with pieces). One solution is to treat the screen as two overlapping grids. The first grid represents the moving piece. The second grid represents the pieces already in place. When a moving piece collides with a piece in the fixed grid, the moving piece becomes stationary and joins the fixed grid. When that action clears one or more lines, the stationary grid changes.

Start by defining these grids:

Rotating a piece means transforming each of its elements:

This math needs some explanation for everyone who hasn't done linear algebra in a while.

Collision detection requires checking both grids for a piece overlap in the direction the user wants to move the piece:

The math concern applies here too. A diagram might help.

All of the pieces are in place to move the piece: make the collision check, then place the piece into the appropriate grid for its next position:

Of course this all needs an event handler to attempt to move the pieces appropriately:

Score and Game State

First we keep hold a variable to keep the game score and set the SDLx::Text font and options to draw the text on the screen later on.

The game state in Tetris is the combination of the fixed placement grid, the current piece, and the current score. The move handler can update all of these:

Start by updating the current piece's state as movable or fixed:

Then update the state of the grid and check for lines to remove:

Why count backwards? This seems like it could be for my $y (0 .. 22). Maybe the question is whether to remove rows from the bottom up or the top down.

Deleting a line should increment the user's score:

... and should clear that line off of the fixed grid:

These loops should merge.

... and the game should launch a new tile.

Drawing the Game

Those are the mechanics. How about displaying the game? The show handler needs to iterate through all of the elements in both grids and draw the appropriate tile:

... and should draw the score:

Author

Code for this chapter was provided by Tobias Leich "FROGGS".

POD ERRORS

Hey! The above document had some coding errors, which are explained below:

Around line 1:

Unknown directive: =head0

Around line 10:

Deleting unknown formatting code U<>

Around line 179:

=end for without matching =begin. (Stack: [empty])

Around line 206:

=end for without matching =begin. (Stack: [empty])

Around line 379:

=end for without matching =begin. (Stack: [empty])

Around line 415:

=end for without matching =begin. (Stack: [empty])

Jump to Line
Something went wrong with that request. Please try again.