How to Build Your Own Tetris 101 Presented by: Luke Arntson E-mail : arntsonl@cwu.edu 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 wayExamples: 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 BlockCode: 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.
© Copyright 2024