How to Build Your Own Tetris 101 Presented by: E-mail

How to Build Your Own
Tetris 101
Presented by:
Luke Arntson
E-mail : [email protected]
Fall Quarter ’05
What Makes a Good Tetris?
► Emulates
► Feels
the real thing
like a solid game
► Visually
appealing
Tetris the Grand Master © Capcom\Arika
► Sound
effects are high quality
► Controls
are responsive
Simple Design Concepts
Puzzle Games
Timed Puzzle Games
Tetris Plus 2 © Jaleco
Action Puzzle Games
Etc…
Super Puzzle Fighter II Turbo © Capcom
Tetris Design Concepts
► Move
and rotate Tetris pieces
► Make
lines of blocks to remove a line
► Score
higher points with more lines removed at
once (four lines is called scoring a Tetris)
► Game
► The
gets faster as the levels gets higher
only goal is to get the highest score
Lets Talk Programming
► What
->
->
->
->
->
->
->
do we need for Tetris?
Puzzle Block Data Structures
Puzzle Block Rotation Algorithm
Way to Represent the Playing Field
Ways to Find Collisions in the PF
Easy Game Physics (timers, etc.)
Smooth Movement & Dropping
Levels, Lines and Hi-Scores Oh My
What is That Puzzle Block Made of??
How Can We Define Our Block?
►
-Each shape has 4 blocks, defined in some wayExamples:
philhassey – FTetris @ http://www.imitationpickles.org/ftetris/
Code:
1. Box shape
2. Z shape
3. T shape
1. [(0,0,0,0),
(0,1,1,0),
(0,1,1,0),
(0,0,0,0),]
2. [(0,0,0,0),
(1,1,0,0),
(0,1,1,0),
(0,0,0,0),]
3. [(0,0,0,0),
(1,1,1,0),
(0,1,0,0),
(0,0,0,0),]
my Tetris example
- Numbers represent distance from top left corner - Numbers are also in order: Left, Top, Right, and Bottom BlockCode:
1. Box shape
2. Z shape
3. T shape
1. [(0,1), (0,0), (1,0), (1,1)]
2. [(0,0), (1,0), (2,1), (1,1)]
3. [(0,0), (1,0), (2,0), (1,1)]
Ok, Lets Examine Example 1 Again
I personally am a visual person, and I know as a visual person there is no way to
get code like that unless we visualize what is going on.
 Let us pretend we are building a Tetris piece, and it has four blocks to play with
 Each block is represented as a 1 or a 0 in a 4x4 array
 We can now observe the array and watch how the 1s and 0s fill the grid
Y X
[(0,0,0,0),
(1,1,1,0),
(0,1,0,0),
(0,0,0,0),]
0
1
2
3
0
1
2
3
Ok, Lets Examine Example 2 Again
Now to examine the code I wrote, which is much more confusing, but can reduce
massive array listing to easy to manage coordinates.
 Let us pretend we are building a Tetris piece, and it has four blocks to play with
 Each block has (x,y) coordinates on a 4x4 grid
 We can now match the coordinates given with the code: [(0,0), (1,0), (2,0), (1,1)]
(0,0)
(1,0)
Y X
0
(2,0)
1
(1,1)
2
3
0
1
2
3
Now That We Understand Our Pieces,
How Do We Go About Rotating Them?
►
Lets step back to see how the pros do it. I’ll take the
classic example of Tetris © Atari Games.
How Do We Represent Rotations?
► First,
what is a set of rotations?
A set of rotations can be defined as a list of pieces. So using
(X,Y) coordinates, we can define the following:
Z Piece
1st.(0,0), (1,0), (2,1), (1,1)
2nd.(0,1), (1,0), (1,1), (0,2)
T Shape Piece
1st.(0,0), (1,0), (2,0), (1,1)
3rd.(0,1), (1,0), (2,1), (1,1)
2nd.(0,1), (1,0), (1,1), (1,2)
4th.(0,1), (0,0), (1,1), (0,2)
7 Shape Piece
1st.(0,0), (1,0), (1,1), (1,2)
3rd.(0,1), (0,0), (1,2), (0,2)
2nd.(0,1), (2,0), (2,1), (1,1)
4th.(0,0), (1,0), (2,0), (0,1)
So How Do We Add This In-Game?
►
Well one way of doing it is to keep track of the state of the current piece. For
Example, if you had a T shaped piece, it can rotate four times. So a rotation
involves increasing and decreasing your current state.
►
So we have two functions, one for rotation clockwise, and one for counterclockwise. Let’s make these simple and call them moveLeft and moveRight.
rotateLeft –
cycle from highest state to lowest state. Set to highest state if lowest state
reached.
rotateRight –
cycle from lowest state to highest state. Set to lowest state if highest state
reached.
Optimizing the Rotation
►
The first road to optimization is laying out our pieces and their number
of states.
Horizontal Bar Piece
Box Piece
T Shaped Piece
Z & S Shaped Pieces
F & 7 Shaped Pieces
►
- 2 States
- 1 State
- 4 States
- 2 States
- 4 States
Ok, now we know that our Box Piece has one state, our Horizontal Bar,
Z and S shaped pieces have two states, and our T, F & 7 shaped pieces
have four states. So lets rewrite our list of states.
1 state
2 states
4 states
- Box shaped piece.
- Horizontal Bar, Z, and S shaped pieces.
- T, F, and 7 shaped pieces.
Now We Can Finally Write Our Functions
rotateLeft
if (piece == horizontalbar or Z or S shape)
if ( state > 1 )
state -= 1;
else
state = 2;
if (piece == T or 7 or F shape)
if ( state > 1 )
state -= 1;
else
state = 4;
rotateRight
if (piece == horizontalbar or Z or S shape)
if ( state < 2 )
state += 1;
else
state = 1;
if (piece == T or 7 or F shape)
if ( state < 4 )
state += 1;
else
state = 1;
Creating the Playing Field
►
Using Tetris the Grand Master © Arika/Capcom as an example, we see
that our Tetris field can be broken into a 10x20 grid. This grid then
can be represented as a 2D array and used to display our game.
►
While a game piece is on the field, it is taking up four elements in the
2D array (four blocks to a piece.)
►
The values of the 2D array can also be used to determine what is in
the playing field, so essentially you can “add” a piece to the grid by
manipulating the 2D array.
►
Same goes for subtraction, if you want to “subtract” a line from the
grid when its filled, you can simply check the row and remove the row.
(more about this later.)
Visual Demonstration of Our Grid
Again, I am a visual person, so I’m going to give a visual representation of a
given Tetris grid.
► Given the following screenshot, we’ll visually represent what is in the grid with
1s for blocks, and 0s for empty spaces.
►
20x10 Grid
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
0
0
0
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
1
1
0
0
0
0
0
1
1
0
0
0
0
0
0
0
0
0
1
1
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
Color Representation
► Ok,
now that we understand how our grid
works, lets redo it with the following legend.
Legend
1 = Red 2 = Yellow 3 = Turquoise 4 = Purple 5 = Green 6 = Blue 7 = Orange
0
0
0
0
0
0
0
0
0
0
0
0
0
0
2
2
6
6
6
3
0
0
0
0
0
0
0
0
0
0
0
0
0
0
2
2
6
5
3
3
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
5
5
3
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
4
4
5
7
0
0
0
0
0
2
2
0
0
0
0
0
0
0
0
0
0
4
4
7
0
0
0
0
0
2
2
0
0
0
0
0
0
0
0
0
5
5
7
7
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
5
5
4
4
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
4
4
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
Lets Try Some Collision Detection
►
There is no right way to do this, as long as it succeeds 100%.
►
Optimization will depend on how the pieces are created, for my
example I have the blocks in their positional order: left, top, right, and
bottom. This allows for detection against borders to be quick.
►
However, when we are dealing with moving a piece against an already
created grid that has potentially one block obstacles, we should check
all four blocks. This is open to optimization, but for now this will
simplify things greatly.
Detection Within the Grid
►
A collision occurs when a piece attempts to move into an occupied
space on the grid.
►
Collisions can also occur when a piece attempts to move into the
boundaries of a grid.
►
Lets try some pseudo code for a collision function.
gridCollision
if the piece is trying to move into an X boundary
return a true grid collision
else if the piece is trying to move into a Y boundary
return a true grid collision
else for all blocks in piece
if piece[block] is not empty
return a true grid collision
else
return false, no grid collision
Writing the Collision Code
►
Ok, now for my grid collision function to optimize the
actual function I inserted two unique x difference and y
difference values into the function.
gridCollision( piece, grid, xDif, yDif )
if ( Furthest Right Block + xDif > 9 ) or ( Furthest Left Block + xDif < 0 )
return true
else if ( Furthest top piece + yDif < 0 ) or ( Furthest Bottom Piece + yDif > 19 )
return true
for ( I = 0 to I = 4 )
if Current Grid at [ Current Block’s X Value + xDif ] [ Current Block’s Y Value + yDif ] is
not empty
return true
Now to Add Some Basic Physics
►
The core essential to a Tetris game is simple, a drop counter. This
counter is used to determine when the piece will drop.
►
The drop counter is directly related to the level the player is on, so the
higher the level, the smaller the drop counter.
►
So in a 60fps game, if we set our drop counter to increment every
frame, and it resets after the drop counter hits 60, then our Tetris
piece would drop every 1 second.
Simple Code:
dropCounter = 0
dropMax = 60
gameLoop:
increase 1/60th of a second (to make 60fps)
dropCounter++
if (dropCounter > dropMax )
drop the piece one
dropCounter = 0
How to Detect A Tetris
►
A Tetris is simple, a line completely filled with blocks. So how can we
test this efficiently.
►
Simple, the only lines that could possibly be filled are the lines the last
piece was put into. In other words, the most lines we’d ever have to
test for a Tetris would be four lines if the straight block was put into
the grid.
Simplified Code:
hasTetris
for ( lowestPoint to highestPoint )
{
for ( row-left to row-right )
if ( current-row-spot is empty )
break, then continue to next point
mark current line as a Tetris
}
if no line was marked, return false
else return true
Tetris © Tengen
Player Movement
►
This goes along the lines of the Tetris world physics. One thing you
will notice when playing a homebrew Tetris game as apposed to a
professional Tetris game is how clunky the controls feel.
►
The real trick to smooth player movement is to allow a delay between
quick movement and slow movement. For example, a player holds
left, the piece in a clunky game will move left once per half second. In
a well done Tetris game, there will be about a half second delay, where
the piece moves once, then begins the slide in the direction.
►
This can be achieved by having a movement timer. So when left or
right is detected, begin the movement timer, move the piece once,
then when the timer has reached its limit, begin slide.
►
Down does not need such complicated methods, just shoot down.
Some Tetris games use an instant drop to the bottom when down is
pressed.
Levels, Lines, and Points
►
So now that Tetris has been created and pieces are moving, dropping,
and locking correctly, we can finally add our line and level variables.
►
We have two variables: Line and Level. Level is simply a function of
line, and the rate at which the levels increase is up to the programmer.
Lines are increased when a line is cleared, so adding the lines is easy.
►
Points can be given to the user in two ways: when a piece is dropped
onto the grid, and when lines are cleared. Multipliers can also be
added to the number of lines cleared to allow massive points for a full
four line Tetris.
Hi-Scores and Menus
►
Ok, now we have a fully working Tetris, and everything
runs smoothly. So we can start adding some creative parts
of the game.
►
The Hi-Score can be done one of two ways: first way is to
have a static variable Hi-Score that is assigned every time
the program starts. The second way is to have a Hi-Score
sheet of some kind the program can use to display and
save to.
►
Menus are up to the programmer. This part is all show, so
nice looking menus and options are all optional. In
professional Tetris games they are essential, but for a
homebrew game Menus can be an added bonus.
Modifying Tetris
►
There have been a lot of attempts since Tetris originally
came out to modify and create a new spin on the old
classic.
►
To give you some ideas of what’s been done: Tetris Plus 2
uses a non-player character to determine if you’re Tetris is
too high, mTetris is a purely sound based Tetris designed
for the blind, and Tetris Worlds uses a very large number
of effects to enhance game play.
►
Tetris has also been on almost every single console and pc
system, ranging from the Commodore 64, to the TI-83
calculator, to the latest Cell Phone.
Thanks goes to the following people
Phil Hassey – for helping me w/ my code, helping me layout my project, and
giving me some great advice.
Dr. Schwing – for giving me a place and time to present.
Zak Arntson – for helping me optimize my code.
my girlfriend Jill – for not killing me after a two day programming binge
making this game in the first place.