Creation of a complete Flash Tetris game

If you did not already bought my book and want to see how in detail I am explaining every game, here it is the chapter covering Tetris, with some minor change to make it fit on the blog.

Tetris features shapes called tetrominoes, geometric shapes composed of four squared blocks connected orthogonally, that fall from the top of the playing field.

Once a tetromino touches the ground, it lands and cannot be moved anymore, being part of the ground itself, and a new tetromino falls from the top of the game field, usually a 10×20 tiles vertical rectangle.

The player can move the falling tetromino horizontally and rotate by 90 degrees to create a horizontal line of blocks.

When a line is created, it disappears and any block above the deleted line falls down. If the stacked tetrominoes reach the top of the game field, it’s game over.

DEFINING GAME DESIGN

There’s not that much to say about game design, since Tetris is a well known game and as you read this post you should be used to dealing with game design.

By the way, there is something really important about this game you need to know before you start reading this article: you won’t draw anything in the Flash IDE.

That is, you won’t manually draw tetrominoes, the game field, or any other graphic assets. Everything will be generated on the fly using AS3 drawing methods.

Tetris is the best game for starting learning how to draw with AS3 as it only features blocks, blocks, and only blocks.

Moreover, although the game won’t include advanced programming features, its principles make Tetris one of the hardest tile based puzzle games to code. Survive Tetris and you will have the skills to create more puzzle games focusing more on new features and techniques rather than on programming logic.

IMPORTING CLASSES AND DECLARING FIRST VARIABLES

The first thing we need to do, as usual, is set up the project and define the main class and function, as well as preparing the game field.

Create a new file (File | New) then from New Document window select Actionscript 3.0. Set its properties as width to 400 px, height to 480 px, background color to #333333 (a dark gray), and frame rate to 30 (quite useless anyway since there aren’t animations, but you can add an animated background on your own). Also, define the Document Class as Main and save the file as tetris.fla.

Without closing tetris.fla, create a new file and from New Document window select ActionScript 3.0 Class. Save this file as Main.as in the same path you saved tetris.fla. Then write:

We already know we have to interact with the keyboard to move, drop, and rotate tetrominoes and we have to deal with timers to manage falling delay, so I already imported all needed libraries.

Then, there are some declarations to do:

TS is the size, in pixels, of the tiles representing the game field. It’s a constant as it won’t change its value during the game, and its value is 24. With 20 rows of tiles, the height of the whole game field will be 24×20 = 480 pixels, as tall as the height of our movie.

fieldArray is the array that will numerically represent the game field.

fieldSprite is the DisplayObject that will graphically render the game field.

Let’s use it to add some graphics.

DRAWING GAME FIELD BACKGROUND

Nobody wants to see an empty black field, so we are going to add some graphics. As said, during the making of this game we won’t use any drawn Movie Clip, so every graphic asset will be generated by pure ActionScript.

The idea: Draw a set of squares to represent the game field.

The development: Add this line to Main function:

then write generateField function this way:

Test the movie and you will see:

The 20×10 game field has been rendered on the stage in a lighter gray. I could have used constants to define values like 20 and 10, but I am leaving it to you at the end of the post.

Let’s see what happened:

These lines just construct fieldArray array and fieldSprite DisplayObject, then add it to stage as you have already seen a million times.

fieldSprite.graphics.lineStyle(0,0x000000);

This line introduces a new world called Graphics class. This class contains a set of methods that will allow you to draw vector shapes on Sprites.

lineStyle method sets a line style that you will use for your drawings. It accepts a big list of arguments, but at the moment we’ll focus on the first two of them.

The first argument is the thickness of the line, in points. I set it to 0 because I wanted it as thin as a hairline, but valid values are 0 to 255.

The second argument is the hexadecimal color value of the line, in this case black.

Hexadecimal uses sixteen distinct symbols to represent numbers from 0 to 15. Numbers from zero to nine are represented with 0-9 just like the decimal numeral system, while values from ten to fifteen are represented by letters A-F. That’s the way it is used in most common paint software and in the web to represent colors.

You can create hexadecimal numbers by preceding them with 0x.

Also notice that lineStyle method, like all Graphics class methods, isn’t applied directly on the DisplayObject itself but as a method of the graphics property.

The remaining lines are made by the classical couple of for loops initializing fieldArray array in the same way you already initialized all other array-based games, and drawing the 200 (20×10) rectangles that will form the game field.

beginFill method is similar to lineStyle as it sets the fill color that you will use for your drawings. It accepts two arguments, the color of the fill (a dark gray in this case) and the opacity (alpha). Since I did not specify the alpha, it takes the default value of 1 (full opacity).

With a line and a fill style, we are ready to draw some squares with drawRect method, that draws a rectangle. The four arguments represent respectively the x and y position relative to the registration point of the parent DisplayObject (fieldSprite, that happens to be currently on 0,0 in this case), the width and the height of the rectangle. All the values are to be intended in pixels.

endFill method applies a fill to everything you drew after you called beginFill method.

This way we are drawing a square with a TS pixels side for each for iteration. At the end of both loops, we’ll have 200 squares on the stage, forming the game field.

DRAWING A BETTER GAME FIELD BACKGROUND

Tetris background game fields are often represented as a checkerboard, so let’s try to obtain the same result.

The idea: Once we defined two different colors, we will paint even squares with one color, and odd squares with the other color.

The development: We have to modify the way generateField function renders the background:

We can define an array of colors and play with modulo operator to fill the squares with alternate colors and make the game field look like a chessboard grid.

The core of the script lies in this line:

that plays with modulo to draw a checkerboard.

Test the movie and you will see:

Now the game field looks better.

CREATING THE TETROMINOES

The concept behind the creation of representable tetrominoes is the hardest part of the making of this game. Unlike most tile based games that features actors of the same size, in Tetris every tetromino has its own width and height. Moreover, every tetromino except the square one is not symmetrical, so its size is going to change when the player rotates it.

How can we manage a tile-based game with tiles of different width and height?

The idea: Since tetrominoes are made by four squares connected orthogonally (that is, forming a right angle), we can split tetrominoes into a set of tiles and include them into an array.

The easiest way is to include each tetromino into a 4×4 array, although most of them would fit in smaller arrays, it’s good to have a standard array.

Something like this:

Every tetromino has its own name based on the alphabet letter it reminds, and its own color, according to The Tetris Company (TTC), the company that currently owns the trademark of the game Tetris.

Just for your information, TTC sues every Tetris clone whose name somehow is similar to “Tetris”, so if you are going to create and market a Tetris clone, you should call it something like “Crazy Bricks” rather than “Tetriz”.

Anyway, following the previous picture, from left-to-right and from top-to-bottom, the “official” names and colors for tetrominoes are:

I – color: cyan (0x00FFFF)
T – color: purple (0xAA00FF)
L – color: orange (0xFFA500)
J – color: blue (0x0000FF)
Z – color: red (0xFF0000)
S – color: green (0x00FF00)
O – color: yellow (0xFFFF00)

The development: First, add two new class level variables:

tetrominoes array is the four-dimensional array containing all tetrominoes information, while colors array will store their colors.

Now add a new function call to Main function:

initTetrominoes function will initialize tetrominoes-related arrays.

colors array is easy to understand: it’s just an array with the hexadecimal value of each tetromino color.

tetrominoes is a four-dimensional array. It’s the first time you see such a complex array, but don’t worry. It’s no more difficult than the two-dimensional arrays you’ve been dealing with since the creation of Minesweeper. Tetrominoes are coded into the array this way:

tetrominoes[n] contains the arrays with all the information about the n-th tetromino. These arrays represent the various rotations, the four rows and the four columns.

tetrominoes[n][m] contains the arrays with all the information about the n-th tetromino in the m-th rotation. These arrays represent the four rows and the four columns.

tetrominoes[n][m][o] contains the array with the four elements of the n-th tetromino in the m-th rotation in the o-th row.

tetrominoes[n][m][o][p] is the p-th element of the array representing the o-th row in the m-th rotation of the n-th tetromino. Such element can be 0 if it’s an empty space or 1 if it’s part of the tetromino.

There isn’t much more to explain as it’s just a series of data entry. Let’s add our first tetromino to the field.

PLACING YOUR FIRST TETROMINO

Tetrominoes always fall from the top-center of the level field, so this will be its starting position.

The idea: We need a DisplayObject to render the tetromino itself, and some variables to store which tetromino we have on stage, as well as its rotation and horizontal and vertical position.

The development: Add some new class level variables:

tetromino is the DisplayObject representing the tetromino itself.

currentTetromino is the number of the tetromino currently in game, and will range from 0 to 6.

currentRotation is the rotation of the tetromino and will range from 0 to 3 since a tetromino can have four distinct rotations, but for some tetrominoes such as “I”, “S” and “Z” will range from 0 to 1 and it can be only 0 for the “O” one. It depends on how may distinct rotations a tetromino can have.

tRow and tCol will represent the current vertical and horizontal position of the tetromino in the game field.

Since the game starts with a tetromino in the game, let’s add a new function call to Main function:

generateTetromino function will generate a random tetromino to be placed on the game field:

The function is very easy to understand: it generates a random integer number between 0 and 6 (the possible tetrominoes) and assigns it to currentTetromino. There is no need to generate a random starting rotation as in all Tetris versions I played, tetrominoes always start in the same position, so I assigned 0 to currentRotation, but feel free to add a random rotation if you want.

tRow (the starting row) is set to 0 to place the tetromino at the very top of the game field, and tCol is always 3 because tetrominoes are included in a 4 elements wide array, so to center it in a 10 column wide field, its origin must be at (10-4)/2 = 3.

Once the tetromino has been generated, drawTetromino function renders it on the screen.

Actually the first line has no sense, I only needed a variable with a name shorter than currentTetromino or the script wouldn’t have fitted on the layout. That’s why I created ct variable.

The rest of the script is quite easy to understand: first tetromino DisplayObject is constructed and added to Display List, then lineStyle method is called to prepare us to draw the tetromino.

This is the main loop:

These two for loops scan through tetrominoes array elements relative to the current tetromino in the current rotation.

This is how we apply the concept explained during the creation of tetrominoes array.

We are looking for the j-th element in the i-th row of the currentRotation-th rotation of the ct-th tetromino. If it’s equal to 1, we must draw a tetromino tile.

These lines:

just draw a square in the same way we used to do with the field background. The combination of all squares we drew will form the tetromino.

Finally, the tetromino is placed calling placeTetromino function that works this way:

It just places the tetromino in the correct place according to tCol and tRow values. You already know these values are respectively 3 and 0 at the beginning, but this function will be useful every time you need to update a tetromino’s position.

Test the movie and you will see your first tetromino placed on the game field. Test it a few more times, to display all of your tetrominoes, and you should find a glitch.

While “O” tetromino is correctly placed on the top of the game field, “T” tetromino has shifted one row down.

This happens because some tetrominoes in some rotations have the first row empty. Since all tetrominoes are embedded in a 4×4 array, when the first row is empty it looks like the tetromino is starting from the second row of the game field rather than the first one.

We should scan for the first row of a newborn tetromino and set tRow to -1 rather than 0 if its first row is empty, to make it fall from the first game field row.

tRow cannot be an unsigned integer anymore as it can take a -1 value, so change the level class variables declarations:

Then in generateTetromino function we must look for a 1 in the first row of the first rotation to make sure the current tetromino has a piece in the first row. If not, we have to set tRow to -1. Change generateTetromino function this way:

Then test the movie and finally every tetromino will start at the very top of the game field.

Tetrominoes won’t float forever so it’s time to add some interaction to the game.

MOVING TETROMINOES HORIZONTALLY

Players should be able to move tetrominoes horizontally with arrow keys (and any other keys you want to enable, but in this article we’ll only cover arrow keys movement).

The idea: Pressing LEFT arrow key will make the current tetromino move to the left by one tile (if allowed) and pressing RIGHT arrow key will make the current tetromino move to the right by one tile (if allowed).

The development: The first thing which comes to mind is some tetrominoes in some rotations can have the leftmost column empty, just as it happened with the first row. For this reason, it’s better to declare tCol variable as an integer since it can assume negative values when you next move the tetromino to the left edge of the game field.

Now you can add the keyboard listener to make the player move the pieces. It will be added on Main function:

onKDown function will handle the keys pressed in the same old way you already know. The core of this process is the call to another function called canFit that will tell us if a tetromino can fit in its new position.

If we look at what happens when the player presses LEFT arrow key (case 37) we see tCol value is decreased by 1 and the tetromino is placed in its new position using placeTetromino function only if the value returned by canFit function is true.

Also, notice its arguments: the current row (tRow) and the current column decreased by 1 (tCol-1). It should be clear canFit function checks whether the tetromino can fit in a given position or not.

So when the player presses LEFT or RIGHT keys, we check if the tetromino would fit in the new given position, and if it fits we update its tCol value and draw it in the new position.

Now we are ready to write canFit function, that wants two integer arguments for the candidate row and column, and returns true if the current tetromino fits in these coordinates, or false if it does not fit.

As seen, ct variable exists for a layout purpose.

In this function we have the classical couple of for loops and the if statement to check for current tetromino’s pieces:

and then the core of the function: checking for the tetromino to be completely inside the game field:

and

Once we found a tetromino piece at tetrominoes[ct][currentRotation][i][j], we know j is the column value inside the tetromino and col is the candidate column for the tetromino.

If the sum of col and j is a number outside the boundaries of game field, then at least a piece of the tetromino is outside the game field, and the position is not legal (return false) and nothing is done.

If all current tetromino’s pieces are inside the game field, then the position is legal (return true) and the position of the tetromino is updated.

Look at this picture:

The “Z” tetromino is in an illegal position; let’s see how we can spot it. The red frame indicates the tetromino’s area, with black digits showing tetromino’s array indexes.

The green digit represents the origin column value of the tetromino in the game field, while the blue one represents the origin row value.

When we check the tetromino piece at 1,0, we have to sum its column value (0) to the origin column value (-1). Since the result is less than zero, we can say the piece is in an illegal spot, so the entire tetromino can’t be placed here.

All remaining tetromino’s pieces are in legal places, because when you sum tetromino’s pieces column values (1 or 2) with origin column value (-1), the result will always be greater than zero.

This concept will be applied to all game field sides.

Test the movie and you will be able to move tetrominoes horizontally.

Now, let’s move on to vertical movement.

MOVING TETROMINOES DOWN

Moving tetrominoes down obviously applies the same concept to vertical direction.

The idea: Once the DOWN arrow key has been pressed, we should call canFit function passing as arguments the candidate row value (tRow+1 as the tetromino is moving one row down) and the current column value.

The development: Modify onKDown function adding the new case:

We also need to update canFit function to check if the tetromino would go out of the bottom boundary.

Add this new if statement to canFit function:

As you can see it’s exactly the same concept applied to horizontal movement.

Test the movie and you will be able to move tetrominoes down.

Everything is fine and easy at the moment, but you know once a tetromino touches the ground, it must stay in its position and a new tetromino should fall from the top of the field.

MANAGING TETROMINOES LANDING

The first thing to determine is: when should a tetromino be considered as landed? When it should move down but it can’t. That’s it. Easier than you supposed, I guess.

The idea: When it’s time to move the tetromino down a row (case 40 in onKDown function), when you can’t move it down (canFit function returns false), it’s time to make it land and generate a new tetromino.

The development: Modify onKDown function this way:

When you can’t move down a tetromino, landTetromino function is called to manage its landing and a new tetromino is generated with generateTetromino function.

This is landTetromino function:

It works creating four new DisplayObjects, one for each tetromino’s piece, and adding them to the Display List. At the same time, fieldArray array is updated.

Let’s see this process in detail:

This is the variable I created for layout purpose.

landed is the DisplayObject we’ll use to render each tetromino piece.

This is the loop to scan for pieces into the tetromino. Once it finds a piece, here comes the core of the function:

landed DisplayObject is added to Display List.

Draws a square where the tetromino piece should lie. It’s very similar to what you’ve seen in drawTetromino function.

Updating fieldArray array setting the proper element to 1 (occupied).

At the end of the function, the old tetromino is removed. A new one is about to come from the upper side of the game.

Test the movie and move down a tetromino until it reaches, then try to move it down again to see it land on the ground and a new tetromino appear from the top.

Everything will work fine until you try to make a tetromino fall over another tetromino.

This happens because we haven’t already managed the collision between the active tetromino and the landed ones.

MANAGING TETROMINOES COLLISIONS

Do you remember once a tetromino touches the ground we updated fieldArray array? Now the array contains the mapping of all game field cells occupied by a tetromino piece.

The idea: To check for a collision between tetrominoes we just need to add another if statement to canFit function to see if in the candidate position of the current tetromino there is a cell of the game field already occupied by a previously landed tetromino, that is the fieldArray array element is equal to 1.

The development: It’s just necessary to add these three lines to canFit function:

Test the movie and see how tetrominoes stack correctly.

By the way, making lines is not easy if you can’t rotate tetrominoes.

ROTATING TETROMINOES

The concept behind a tetromino rotation is not that different than the one behind its movement.

The idea: We have to see if the tetromino in the candidate rotation fits in the game field, and eventually apply the rotation.

The development: The first thing to do is to change canFit function to let it accept a third argument, the candidate rotation. Change it this way:

As you can see there’s nothing difficult in it: I just added a third argument called side that will contain the candidate rotation of the tetromino.

Then obviously any call to class level variable currentRotation has to be replaced with side argument.

Every existing call to canFit function in onKDown function must be updated passing the new argument, usually currentRotation, except when the player tries to rotate the tetromino (case 38):

Now let’s see what happens when the player presses UP arrow key:

ct variable is used only for a layout purpose, to have currentRotation value in a variable with a shorter name.

rot variable will take the value of the candidate rotation. It’s determined by adding 1 to current rotation and applying a modulo with the number of possible rotations of the current tetromino, that’s determined by tetrominoes[currentTetromino].length.

Calls canFit function passing the current row, the current column, and the candidate rotation as parameters. If canFit returns true, then these lines are executed:

currentRotation variable takes the value of the candidate rotation.

The current tetromino is removed.

A new tetromino is created and placed on stage. You may wonder why I delete and redraw the tetromino rather than simply rotating the DisplayObject representing the current tetromino. That’s because tetrominoes’ rotations aren’t symmetrical to their centers, as you can see looking at their array values.

Test the movie and press UP arrow key to rotate the current tetromino.

You will notice you can’t rotate some tetrominoes when they are close to the first or last row or column. In some Tetris versions, when you try to rotate a tetromino next to game field edges, it’s automatically shifted horizontally by one position (if possible) to let it rotate anyway.

In this prototype, I did not add this feature because there’s nothing interesting from a programming point of view so I preferred to focus more in detail on other features rather than writing just a couple of lines about everything.

Anyway, if you want to try it by yourself, here’s how it should work:

When a tetromino can’t be rotated as one of its piece would go out of the game field, along with the rotation the tetromino is shifted in a safe area, if possible.

Finally, you can make lines! Let’s see how to manage them.

REMOVING COMPLETED LINES

According to game mechanics, a line can be completed only after a tetromino is landed.

The idea: Once the falling tetromino lands on the ground or over another tetromino, we’ll check if there is any completed line. A line is completed when it’s entirely filled by tetrominoes pieces.

The development: At the end of landTetromino function you should check for completed lines and eventually remove them. Change landTetromino this way:

As said, the last line calls checkForLines function that will check for completed lines. But before doing it, take a look at how I am giving a name to each piece of any landed tetromino. The name is meant to be easily recognizable by its row and column, so for instance the piece at the fifth column of the third row would be r3c5. Naming pieces this way will help us when it’s time to remove them. We will be able to find them easily with the getChildByName method you should have already mastered.

Add checkForLines function:

Test the movie and you will be able to remove complete lines.

Let’s see how checkForLines function works:

for loop iterating through all 20 lines in the game field

Since a line must be completely filled with tetrominoes pieces to be considered as completed, the array must be filled by 1, that is, there can’t be any 0. That’s what this if statement is checking on the i-th line.

If a line is completed, then we iterate through all its ten columns to remove it.

This clears the game field bringing back fieldArray[i][j] element at 0.

And this removes the corresponding DisplayObject, easily located by its name.

Now, we have to manage “floating” lines.

MANAGING REMAINING LINES

When a line is removed, probably there are some tetrominoes above it, just like in the previous picture. Obviously you can’t leave the game field as is, but you have to make the above pieces fall down to fill the removed lines.

The idea: Check all pieces above the removed line and move them down to fill the gap left by the removed line.

The development: We can do it by simply moving down one tile, all tetrominoes pieces above the line we just deleted, and updating fieldArray array consequently.

Change checkForLines function this way:

Let’s see what we are going to do:

This is the most important loop. It ranges from i (the row we just cleared) back to zero. In other words, we are scanning all rows above the row we just cleared, including it.

This for loop iterates trough all 10 elements in the j-th row.

Checks if there is a tetromino piece in the k-th column of the j-th row.

Sets the k-th column of the j-th row to 0.

Sets the k-th column of the (j+1)-th row to 1. This way we are shifting down an entire line.

Moves down the corresponding DisplayObject by TS pixels.

Changes the corresponding DisplayObject name according to its new position.

Test the game and try to remove one or more lines. Everything will work properly.

Now, to make the player’s life harder, we can make tetrominoes fall down by themselves.

MAKING TETROMINOES FALL

One major feature still lacking in this prototype is the gravity that makes tetrominoes fall down at a given interval of time. With the main engine already developed and working, it’s just a matter of adding a timer listener and doing the same thing as the player presses DOWN arrow key.

The idea: After a given amount of time, make the tetromino controlled by the player move down by one line.

The development: First, add a new class level variable.

timeCount is the variable that will trigger the event listener every 500 milliseconds.

The timer listener will be added once a new tetromino is generated.

Modify generateTetromino function this way:

You already know how this listener works so this was easy, and writing onTime function will be even easier as it’s just a copy/paste of the code to execute when the player presses DOWN arrow key (case 40).

The listener also needs to be removed once the tetromino lands, to let the script create a brand new one when a new tetromino is placed on the game field.

Remove it in landTetromino function this way:

Test the movie, and tetrominoes will fall down one row every 500 milliseconds.

Now you have to think quickly, or you’ll stack tetrominoes until you reach the top of the game field.

CHECKING FOR GAME OVER

Finally it’s time to tell the player the game is over.

The idea: If the tetromino that just appeared on the top of the game field collides with tetrominoes pieces, the game is over.

The development: First we need a new class level variable:

gameOver variable will tell us if the game is over (true) or not (false). At the beginning obviously, the game is not over.

What should happen when the game is over? First, the player shouldn’t be able to move the current tetromino, so change onKDown function this way:

Then, no more tetrominoes should be generated. Change generateTetromino function this way:

The first if statement:

executes the whole function only if gameOver variable is false.

Then the event listener is added only if canFit function applied to the tetromino in its starting position returns true. If not, this means the tetromino cannot fit even in its starting position, so the game is over, and gameOver variable is set to true.

Test the movie and try to stack tetrominoes until you reach the top of the game field, and the game will stop.

In the previous picture, when the “T” tetromino is added, it’s game over.

Last but not least, we must show which tetromino will appear when the player lands the current one.

SHOWING NEXT TETROMINO

To add strategy to the game, we need to show the next tetromino that will fall after the current one has landed.

The idea: Don’t random generate the current tetromino, but the next one. When the current tetromino lands, you already know which tetromino will fall from the top because the next tetromino becomes the current one, and you will generate a new random next tetromino.

The development: We need a new class level variable where the value of the next falling tetromino is stored.

At this point, the logic is to generate the random value of the next tetromino first, even before generating the current one. Moreover, forget completely the current tetromino generation. Change Main function to generate the next tetromino this way:

And the trick is done. Now when it’s time to generate the current tetromino, assign it the value of the next one and generate the next random tetromino this way:

As you can see, you are only randomly generating the next tetromino, while the current one only takes its value.

drawNext function just draws the next tetromino in the same way drawTetromino does, just in another place.

Test the movie, and here it is, your next tetromino.

Now you can play the fully functional Tetris prototype.

SUMMARY

You went through the creation of a complete Tetris game, and this alone would be enough. Moreover, you also managed to draw basic shapes with AS3.

Where to go now

To improve your skills, you could clean the code a bit, using constants where required. This is not mandatory, but using FIELD_WIDTH and FIELD_HEIGHT rather than 10 and 20 here and there could improve code readability. It would also be nice if you decrease the timer that controls tetrominoes’ falling speed every, let’s say, ten completed lines.

You can create two new class level variables called completedLines (starting at zero and increasing every time the player completes a line) and fallingTimer (to be set at 500 and used rather than new Timer(500)).

Then every time completedLines is a multiple of ten (use modulo), stop the timer using stop method just like you made it start with start method, remove the listener, decrease fallingTimer by, let’s say, 25, and create and start a new timer with a new listener.

And that’s it. I hope you enjoyed the making of Tetris and you will buy my book. It would be a great support for the blog.

  • Oh, that’s thought to buy or not buy your book … Now I have no doubt) thanks for the lesson!

  • Kevin

    I haven’t finish your book yet, but I have to say I really enjoy it.

    Please make a sequel of your book with more advance topic!! I will definitely buy it!

  • Jae

    There’s something wrong with your landTetromino function, it is drawing all the squares on the same location for each tetromino…I’m trying to debug it, I will post corrected code if someone doesn’t beat me to it.

  • Jae

    Found it, the line where you drawRect needs parenthesis to prevent the multiplication before addition:

    landed.graphics.drawRect(TS*(tCol+j),TS*(tRow+i),TS,TS);

  • MikeMnD

    and something small from me: the color for the “T” tetromino shoud be #0xAA00FF instead of the #0x767676

  • Oh tetris. It’s classic:)

  • Hey. In “big” listing you have some error. For example:

    getChildByName(“r”+j+”c”+k).name=”r”+j+1+”c”+k;

    must be:

    getChildByName(“r”+j+”c”+k).name=”r”+(j+1)+”c”+k;

    Whet this is wrong, then function checkForLines not working

  • Simon Gleizes

    Saw this in your book and I have to say, I found it very useful! Thanks for posting here as well.

  • ohhhh my head hurts!!

  • Giles

    I don’t know, but I may be the only one facing this problem. The function placeTetromino isn’t working well… Right where it first appears, if I put it right after addChild, it work. But if those “for”, my tetromino just stays as 0,0, and not at the midle.
    Ok, and than, when I try to move the tetromino around, that function doesn’t work at all, and I don’t know why! Can somebody tell me what am I doing wrong?
    Tnks.

  • I write about bug. When i fix it, all game working super well :)

    Read carefully lines UNDER the long listing (explanation) , because they are correct.

    I add CONST, level progress, points ect. When this game working, they all very easy to add.

    I only have one question.

    Do you have any idea for uses graphics for tetromino? Maybe drawt better graphic?

  • Thank you so much. Your post is amazing.
    Regards.

  • 2 years ago i had implemented a j2me tetris same way using 4×4 grid ,
    Where as my friend had implemented it using the matrix rotation and scaling .
    Liked that you implemented it in a similar way ,Now I can call my friend and tell him my way was not bad at all .

    here is a link to that code you can give a view published 14 months ago

    http://hubpages.com/hub/Game-Design-continued-Tetris-clone

  • Elem3nt

    So I am working on the rotation of the pieces but I keep getting this error:

    1137: Incorrect number of arguments. Expected no more than 2.

    It’s pointing me to the if statements for the key events. Anyone have an idea on how to get this fixed?

  • Arkshija

    was your book edited in spanish?

  • Counting down the seconds until the Tetris Holdings Company face-rapes Emanuele…

  • Emanuele Feronato

    @Arkshija: it’s only in english at the moment, but a very simple english, don’t worry

    @Ryan Henson Creighton: sorry to disappoint you but I own Tetris Holdings Company…

  • This remind me of my 1st semester project using C language. It have same algorithm in collision detection and the grid (that time i have no idea about matrix). However as it was built using C and C don’t have concept about movieclip so i was using 2 dimensional array to create the plane..

    I’m glad i was already cover all your technical consideration when building a tetris game. Thanks for remind me of a good old time.

  • Hey Elem3nt I had the same problem.

    There is a small typo/inconsistency in the tutorial.

    Inside onKDown, case 38:
    var rot = (ct + 1)% tetrominos…

    The the big block of code the brackets are left out, but when he breaks down each line you will see them there.

    Add the brackets to your code and it will fix the problem.

    O

  • Tabrel

    For those of you have an issue with the rotation, I went ahead and made all of the Arrays in initTetrominoes() have four rotations. So for I, Z and S tetrominoes I repeated the two rotations, and for O tetromino I repeated the one rotation four times. So my O looks like:
    // O
    tetrominoes[6]=[[[0,1,1,0],
    [0,1,1,0],
    [0,0,0,0],
    [0,0,0,0,]],
    [[0,1,1,0],
    [0,1,1,0],
    [0,0,0,0],
    [0,0,0,0,]],
    [[0,1,1,0],
    [0,1,1,0],
    [0,0,0,0],
    [0,0,0,0,]],
    [[0,1,1,0],
    [0,1,1,0],
    [0,0,0,0],
    [0,0,0,0,]]];
    colors[6]=0xFFFF00;

    Then in onKDown() I made a change to case 38 (UP arrow key) so that once it had gone thru all rotations it went back to the beginning of the array without throwing the error many of you have seen. So my case 38 starts off with:

    case 38 :
    var rot:uint=currentRotation+1%tetrominoes[currentTetromino].length;
    if (rot==4) {
    rot=0;
    }
    // …

  • Tabrel

    Oops, forgot to say “Grazie mille, Emanuele!” Next paycheck I am ordering your book.

  • Emanuele Feronato

    a big thank you to everybody who fixed the code :)

  • Didibal

    Very good lesson, congratuations!
    I need so help. There is something wrong with function: checkForLines(). The game works till the moment, when line is done, then I have a message:

    TypeError: Error #2007: Parameter child must be non-null.
    at flash.display::DisplayObjectContainer/removeChild()
    at Main/checkForLines()
    at Main/landTetromino()
    at Main/onKDown()
    ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
    at flash.display::DisplayObjectContainer/removeChild()
    at Main/landTetromino()
    at Main/onKDown()

    Could someone help me please?

  • Pingback: Tetris en AS3 « Curso Flash CS5 – UMH()

  • Pingback: Creation of a complete Flash Astro-PANIC! game - Emanuele Feronato()

  • gabor

    Dibidal,
    it seems that in landTetromino() the name is assigned wrong, that’s why checkForLines() throws the exception.
    Try changing the landed.name= assignment in landTetromino() to this:
    landed.name = “r” + (tRow + i) + “c” + (tCol + j);

  • brucef

    great tutorial, but i’ve found one major bug:

    when one clears more than one line at one time (like a pro tetris player), only one line is cleared and the game stops working.

    im not that great with actionscript, and don’t know how to fix this problem myself, has anyone found what causes this?

    thanks

  • Martin

    I have the same issue like brucef. I bought the book 4 month ago and cant find what causes this.

    Could someone help?

  • Martin

    ok i solved the problem.
    add an enter frame lstener at the end of landTetromino() function and change checkForLines() like this:

    .
    .
    .
    addEventListener(Event.ENTER_FRAME,checkForLines);
    }

    private function checkForLines(e:Event):void{

    i hope that i could help. (i am a really amateur in as3)

  • Donnelle Belanger-Taylor

    Not being able to remove multiple lines at a time is because:

    getChildByName(“r”+j+”c”+k).name=”r”+j+1+”c”+k;

    should be

    getChildByName(“r”+j+”c”+k).name=”r”+(j+1)+”c”+k;

  • Volkan

    Hi Emanuele,

    First of all you are amazing game developer and i learn lots of thing your book and tutorials. Thnk u.

    I create tetris clone game and use your way.
    But i sholud develop tetris ai. My game likes tetris battle player vs computer.
    And im lost..

    Pls help me..

  • MFT

    I also have same issue as Volcan. I really liked this tutorial and I hope that there could be some way to add AI feature to it. I’ve been googling it and can’t find any good resource.

  • Thank you Emanuele!

  • Pingback: need help - actionscript 3 - tetris game start and stop option()

  • Hello,

    Great tutorial..
    i used this tutorial te make a tetris game with a few exeptions:

    1. Creation of a gamefield from an Array ( external class )
    2. Tetrominoes arrays from external class to create more readability.
    2. I drawed my squares with a bevel in flash instead of letting actionscript do the job for me.
    4. Extra key control event to pause the game and restart.
    5. Game output of game over and pause.
    6. Extra key control to start a new game.
    7. level count. ( completedlines % 10 ) :)
    8. game count.

    I also wanted to add any sound to it, however i don’t have the skills to make my own sound and i dislike using sounds that are not mine or not created by me.

    anyone who wants to debug my tetris version? If you don’t understand the text in my swf…it’s in dutch.

    users.telenet.be/myflashgames

  • Pingback: 8bitrocket Daily Inter-Web Mash-Up – Tuesday, May 3, 2011 « 8bitrocket.com()

  • Rafa

    Hi Emanuele.
    I am very new to Flash but i decided to learn as much as i can of it.
    First, thank you. And second, i want to make my own tetris game and i will like to change the tetrominoes that you wrote by code for some tetrominoes that i have got already made in photoshop by importing them to the library.
    My question is:
    How can i change the code to use my symbol from the library instead of creating them?
    Ej: i have got a square symbol named cuadrado. I want to use it like your “o” for the square. And like this with every tetrominoes…

  • Toke

    Hi Emanuele,

    Great tutorial on creating Tetris. I am able to modified your code to my own project. Just one question, if I want the user to be able to remove a tetromino on the screen when click a mouse on it and press the Del key, how do I do it? I tried pushing the landed tetromino into an array but it only removes a block from that tetromino. I want the user to be able to remove the whole piece of the tetromino such as pressing the L tetromino, press Del key, then the L tetromino is removed from the screen. Thank you very much.

  • toke

    Great tutorial! Is there a way to delete a single tetromino instead of clearing out the completed line?

  • Toke

    Great Tutorial! Thank you Emanuele. If this is my third post, please apologize because I do not see my two previous posts. Basically, what I would like to accomplish is to be able to click on a single tetromino on the screen and press the delete key to delete it. I tried pushing the “landed” into an array and use for each but it only removes a block from that single tetromino. Thank you again.

  • Any way to have save result from user? like for example wp users?

  • Pingback: Complete Flash AS3 “Columns” game with source code - Emanuele Feronato()

  • Pinkesh

    I am trying to make game as same method but in bigining it’s showing an error that
    “”Main.as, Line 25 1013: The private attribute may be used only on class property definitions.””

  • Gor

    How to save a game?

  • Hello
    I need support
    How do I do this? So much hard
    What will I do to play this game?
    What is the use of tetris.flat and main.as
    How could I make my own game like this?

    Please reply quickly…

  • Rado

    Thank you for tutorial!

  • gstryjew

    Great tutorial, thanks!

  • Loren Helgeson

    It’s a lot to take in at once, but thanks for the excellent tutorial! I’ll be sure to check out your book.

  • warum

    tRow == -1 gives a lot of problem

  • Peter

    Hi.
    First of all, congrats for you tutorial. I loved it! It’s really good!

    Unfortunately i found a big bug that i cant resolve, even after read and correct all the bugs said in the coments bellow.

    After put the tetromino at one of the sides, i rotate him and the I, T, J and L tetrominoes stay with a piece out of the field, and then the function landTetromino() is executed and the tetromino stays there.

    Does anybody have notice the same problem?
    I am playing the game using microphone to rotate the tetrominoes, so I dont know if i made something wrong.
    Pls take a look and tell me if only I have this problem.
    Thanks ^^

    • Peter

      Sorry, my mistake

      I was using “currentRotation” instead “side” at the if statement. ^^^

      Problem solved :D

  • Pr

    One very good thing about this tutorial is
    it is completed in Main class itself and no additional creation of different classes for different objects of the game

    Its really easy to understand

  • Gabriel Reinhart Allard

    Hi Emanuele! I stumbled upon your site after searching how to make a Tetris game to improve my coding skills after making my own (and full of glitches) Tetris game in AS3. I completely LOVED the lesson and definitely want to buy your book, but my main concern is if the book is directed to people who are programing in FlashDevelop or is more focused on other programs.
    Thank you very much!

  • Dhan

    PLEASE HELP ME FIND THE ERROR. I’M A NEWBIE AND I CAN’T SOLVE THIS.
    ==================================================================
    package {
    import flash.display.Sprite;
    import flash.utils.Timer;
    import flash.events.TimerEvent;
    import flash.events.KeyboardEvent;
    public class Main extends Sprite {

    private const TS:uint=24;
    private var fieldArray:Array;
    private var fieldSprite:Sprite;
    private var tetrominoes:Array = new Array();
    private var colors:Array=new Array();
    private var tetromino:Sprite;
    private var currentTetromino:uint;
    private var currentRotation:uint;
    private var tRow:int;
    private var tCol:int;
    private var timeCount:Timer=new Timer(500);
    private var gameOver:Boolean=false;

    public function Main() {
    generateField();
    initTetrominoes();
    generateTetromino();
    stage.addEventListener(KeyboardEvent.KEY_DOWN,onKDown);
    }

    private function generateField():void {
    var colors:Array=new Array(“0x444444″,”0x555555”);
    fieldArray=new Array ;
    var fieldSprite:Sprite=new Sprite ;
    addChild(fieldSprite);
    fieldSprite.graphics.lineStyle(0,0×000000);
    for (var i:uint=0; i<20; i++) {
    fieldArray[i]=new Array ;
    for (var j:uint=0; j<10; j++) {
    fieldArray[i][j]=0;
    fieldSprite.graphics.beginFill(colors[(j%2+i%2)%2]);
    fieldSprite.graphics.drawRect(TS*j,TS*i,TS,TS);
    fieldSprite.graphics.endFill();
    }
    }
    }

    private function initTetrominoes():void {
    // I
    tetrominoes[0]=[[[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]],[[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]]];
    colors[0]=0x00FFFF;
    // T
    tetrominoes[1]=[[[0,0,0,0],[1,1,1,0],[0,1,0,0],[0,0,0,0]],[[0,1,0,0],[1,1,0,0],[0,1,0,0],[0,0,0,0]],[[0,1,0,0],[1,1,1,0],[0,0,0,0],[0,0,0,0]],[[0,1,0,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]]];
    colors[1]=0xAA00FF;
    // L
    tetrominoes[2]=[[[0,0,0,0],[1,1,1,0],[1,0,0,0],[0,0,0,0]],[[1,1,0,0],[0,1,0,0],[0,1,0,0],[0,0,0,0]],[[0,0,1,0],[1,1,1,0],[0,0,0,0],[0,0,0,0]],[[0,1,0,0],[0,1,0,0],[0,1,1,0],[0,0,0,0]]];
    colors[2]=0xFFA500;
    // J
    tetrominoes[3]=[[[1,0,0,0],[1,1,1,0],[0,0,0,0],[0,0,0,0]],[[0,1,1,0],[0,1,0,0],[0,1,0,0],[0,0,0,0]],[[0,0,0,0],[1,1,1,0],[0,0,1,0],[0,0,0,0]],[[0,1,0,0],[0,1,0,0],[1,1,0,0],[0,0,0,0]]];
    colors[3]=0x0000FF;
    // Z
    tetrominoes[4]=[[[0,0,0,0],[1,1,0,0],[0,1,1,0],[0,0,0,0]],[[0,0,1,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]]];
    colors[4]=0xFF0000;
    // S
    tetrominoes[5]=[[[0,0,0,0],[0,1,1,0],[1,1,0,0],[0,0,0,0]],[[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]]];
    colors[5]=0x00FF00;
    // O
    tetrominoes[6]=[[[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]]];
    colors[6]=0xFFFF00;
    }

    private function generateTetromino():void {
    if (! gameOver) {
    currentTetromino=Math.floor(Math.random()*7);
    currentRotation=0;
    tRow=0;
    if (tetrominoes[currentTetromino][0][0].indexOf(1)==-1) {
    tRow=-1;
    }
    tCol=3;
    drawTetromino();
    timeCount.addEventListener(TimerEvent.TIMER,onTime);
    timeCount.start();
    }
    }

    private function drawTetromino():void {
    var ct:uint=currentTetromino;
    tetromino=new Sprite ;
    addChild(tetromino);
    tetromino.graphics.lineStyle(0,0×000000);
    for (var i:int=0; i<tetrominoes[ct][currentRotation].length; i++) {
    for (var j:int=0; j<tetrominoes[ct][currentRotation][i].length; j++) {
    if (tetrominoes[ct][currentRotation][i][j]==1) {
    tetromino.graphics.beginFill(colors[ct]);
    tetromino.graphics.drawRect(TS*j,TS*i,TS,TS);
    tetromino.graphics.endFill();
    }
    }
    }
    placeTetromino();
    }

    private function placeTetromino():void {
    tetromino.x=tCol*TS;
    tetromino.y=tRow*TS;
    }

    private function onKDown(e:KeyboardEvent):void {
    if (! gameOver){
    switch (e.keyCode) {
    case 37 :
    if (canFit(tRow,tCol-1,currentRotation)) {
    tCol–;
    placeTetromino();
    }
    break;

    case 38 :

    var ct:uint=currentRotation;
    var rot:uint=(ct+1)%tetrominoes[currentTetromino].length;
    if (canFit(tRow,tCol,rot)) {
    currentRotation=rot;
    removeChild(tetromino);
    drawTetromino();
    placeTetromino();
    }
    break;

    case 39 :
    if (canFit(tRow,tCol+1,currentRotation)) {
    tCol++;
    placeTetromino();
    }
    break;

    case 40 :
    if (canFit(tRow+1,tCol,currentRotation)) {
    tRow++;
    placeTetromino();
    } else {
    landTetromino();
    generateTetromino();
    }
    break;
    }
    }
    }

    private function canFit(row:int,col:int,side:uint):Boolean {
    var ct:uint=currentTetromino;
    for (var i:int=0; i<tetrominoes[ct][side].length; i++) {
    for (var j:int=0; j<tetrominoes[ct][side][i].length; j++) {
    if (tetrominoes[ct][side][i][j]==1) {
    // out of left boundary
    if (col+j9) {
    return false;
    }
    // out of bottom boundary
    if (row+i>19) {
    return false;
    }
    // over another tetromino
    if (fieldArray[row+i][col+j]==1) {
    return false;
    }
    }
    }
    }
    return true;
    }

    private function landTetromino():void {
    var ct:uint=currentTetromino;
    var landed:Sprite;
    for (var i:int=0; i<tetrominoes[ct][currentRotation].length; i++) {
    for (var j:int=0; j<tetrominoes[ct][currentRotation][i].length; j++) {
    if (tetrominoes[ct][currentRotation][i][j]==1) {
    landed = new Sprite();
    addChild(landed);
    landed.graphics.lineStyle(0,0×000000);
    landed.graphics.beginFill(colors[currentTetromino]);
    landed.graphics.drawRect(TS*(tCol+j),TS*(tRow+i),TS,TS);
    landed.graphics.endFill();
    fieldArray[tRow+i][tCol+j]=1;
    }
    }
    }
    removeChild(tetromino);
    timeCount.removeEventListener(TimerEvent.TIMER,onTime);
    timeCount.stop();
    checkForLines();

    }

    private function checkForLines():void {
    for (var i:int=0; i<20; i++) {
    if (fieldArray[i].indexOf(0)==-1) {
    for (var j:int=0; j=0; j–) {
    for (var k:int=0; k<10; k++) {
    if (fieldArray[j][k]==1) {
    fieldArray[j][k]=0;
    fieldArray[j+1][k]=1;
    getChildByName("r"+j+"c"+k).y+=TS;
    getChildByName("r"+j+"c"+k).name="r"+(j+1)+"c"+k;
    }
    }
    }
    }
    }

    }

    private function onTime(e:TimerEvent):void {
    if (canFit(tRow+1,tCol,currentRotation)) {
    tRow++;
    placeTetromino();
    } else {
    landTetromino();
    generateTetromino();
    }
    }

    }
    }

  • tRow == -1 gives a lot of problem