|
Show Index |
|---|
This page demonstrates a bare-bones tetris clone. In other words, the minimum code necessary in order to make a tetris game.
Start with setting up the graphics:
Graphics 800, 600 ;2D graphics at a resolution of 800x600 SetBuffer BackBuffer() ;do all drawing to the back drawing buffer
Seed the random number generator:
SeedRnd MilliSecs() ;needed to get random numbers
Dimension the arrays that you will need:
Dim Board(9, 22) ;10x20 game board (+3 rows above the board) Dim BlockColor(7, 2) ;stores r,g,b colors (0-2) for each of the 7 pieces Dim Piece(28, 3, 3) ;28 (4x4) piece descriptions (7 pieces in each of 4 rotations)
Read in the color data for the pieces:
For iter = 1 To 7 ;loop through seven pieces Read BlockColor(iter, 0) ;read the red amount Read BlockColor(iter, 1) ;read the green amount Read BlockColor(iter, 2) ;read the blue amount Next Data 255, 255, 0;yellow ;color of square Data 0, 128, 255;turquoise ;color of line Data 0, 0, 255 ;blue ;color of left L Data 255, 128, 0;orange ;color of right L Data 255, 0, 0 ;red ;color of left s Data 0, 255, 0 ;green ;color of right s Data 128, 0, 255;purple ;color of tee
Read the piece descriptions into the description array:
For iter = 1 To 28 ;loop through 28 descriptions (7x4)
For yiter = 0 To 3 ;loop through the rows of the description
For xiter = 0 To 3 ;loop through the columns of the description
Read Piece(iter, xiter, yiter) ;read in the data (0=blank, 1=filled)
Next
Next ;example of right L 0100
Next ;as a 4x4 matrix 0100
;in its 90deg rotation 0110
;piece descriptions ; (right L rot 1) 0000
Data 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;square
Data 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ;line
Data 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;left L
Data 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;right L
Data 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;left s
Data 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;right s
Data 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;tee up
;rotated 90 degrees cw
Data 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;square
Data 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0 ;line rot 1
Data 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;left L rot 1
Data 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 ;right L rot 1
Data 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ;left s sideways
Data 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;right s sideways
Data 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;tee right
;rotated 180 degrees cw
Data 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;square
Data 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 ;line rot 2
Data 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 ;left L rot 2
Data 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0 ;right L rot 2
Data 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;left s
Data 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;right s
Data 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;tee down
;rotated 270 degrees cw
Data 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;square
Data 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ;line rot 3
Data 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 ;left L rot 3
Data 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;right L rot 3
Data 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ;left s sideways
Data 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;right s sideways
Data 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ;tee left
We'll set up a screen origin just so that we don't have to offset all of our drawing commands.
Origin 200, 10 ;set screen origin (for convenience)
Set up some global variables (so that they can be modified from within the Functions) and initialize the ones that need to be:
Global Level = 1 ;Start the game at level one Global Lines = 5 ;Set the number of lines to clear to go to level two Global EndOfGame ;This is a flag that is True if the game is over Global CurrentPiece = Rand(1, 7) ;select a random starting piece Global CurPieceX = 3 ;set the pieces position to the center of the board Global CurPieceY ;set the pieces position (zero) to the top of the board Global CurPieceRot ;set the pieces rotation to the first rotation (zero) Global RepeatSpeed ;stores the amount of time before the piece can move left/right Global RepeatDelay = True ;makes the piece pause momentarily before repeating left/right Global NowTime ;current time returned from the Millisecs() command Global LastDropTime = MilliSecs() ;stores the time of the last drop Global DropSpeed = 735 ;time before piece can drop down normally (decreases each level) Global DropDelay ;amount of time before the piece can drop (normally or fast)
And here is the main game loop:
Repeat ;start of the main loop
Cls ;clears the screen (backbuffer)
DrawBoard() ;calls the DrawBoard function (even when the game is over)
If Not EndOfGame ;while the game is running...
DrawCurrentPiece() ;draws the current piece at its location
DropDelay = DropSpeed ;sets DropDelay to equal delay time for current level
ProcessInputs() ;move left, right, down or rotate if necessary
NowTime = MilliSecs() ;get the current time
If NowTime > LastDropTime + DropDelay ;if we've delayed long enough to drop another line...
LastDropTime = NowTime ;store the current time as the last drop time
If Not DropPiece() ;attempt to drop the piece
If Not PlacePiece() ;if can't drop piece, then attempt to add the piece to board
EndOfGame = True ;if can't add the piece to the board (because the piece
EndIf ; is too high) then end the game
EndIf
EndIf
EndIf
Flip ;flip backbuffer to the front
Until KeyHit(1) ;Esc key ;repeats main loop until the user presses the Esc key
End ;ends the program
Add a function to gather the player keypresses and deal with them:
Function ProcessInputs() ;collect user input
keyRotate = KeyHit(57) ;spacebar ;It's a good practice to collect these inputs only once
keyLeft = KeyDown(203) ;left arrow ;per loop. This will prevent odd behaviors from happening,
keyRight = KeyDown(205) ;right arrow ;for instance if the state of a key changes between multiple
keyDwn = KeyDown(208) ;down arrow ;checks of that key while still in the same loop.
If keyRotate Then RotatePiece() ;attempt to rotate the piece
If (Not keyLeft) And (Not keyRight) ;if neither the left key nor the right key is pressed then...
RepeatDelay = True ;reset RepeatDelay (so delay will occur when next key is pressed)
Else ;if one of the left/right keys is pressed
If RepeatSpeed < NowTime ;if enough time has passed since the last repeat
If RepeatDelay ;if RepeatDelay is true...
RepeatSpeed = NowTime + 150 ;make next repeat time longer than normal
RepeatDelay = False ;set to false now that we're done with it
Else ;if RepeatDelay is false...
RepeatSpeed = NowTime + 35 ;make next repeat time short (so repeats fast)
EndIf
If keyLeft Then MoveLeft() ;attempt to move left
If keyRight Then MoveRight() ;attempt to move right
EndIf
EndIf
If keyDwn Then DropDelay = 35 ;if down key is pressed then set DropDelay to short time
End Function
This function draws the game board and the level and lines remaining text:
Function DrawBoard()
Local block ;temp variable (used only for better readability of code)
;draw border
Color 255, 0, 0 ;set color to red
Rect -3, 72, 255, 505, 0 ;draw board outside border
;draw blocks
For yiter = 0 To 22 ;loop from (very) top to bottom of board
For xiter = 0 To 9 ;loop from left to right
block = Board(xiter, yiter) ;set block to color of board at xiter, yiter location
If block <> 0 ;if location isn't empty then set color to color of
Color BlockColor(block, 0), BlockColor(block, 1), BlockColor(block, 2) ;that type of block
Rect xiter*25, yiter*25, 24, 24 ;draw the block
EndIf
Next
Next
;draw level and lines remaining
Color 255, 255, 255 ;set color to white
Text 300, 200, "Level:" + RSet$(Str$(Level), 3) ;prints current level (right justified)
Text 300, 240, "Lines:" + RSet$(Str$(Lines), 3) ;prints lines remaining (right justified)
End Function
Add a function to draw the current piece:
Function DrawCurrentPiece() ;set color to color of the current piece
Color BlockColor(CurrentPiece, 0), BlockColor(CurrentPiece, 1), BlockColor(CurrentPiece, 2)
For yiter = 0 To 3 ;loop from top to bottom of piece
For xiter = 0 To 3 ;loop from left to right of piece
If Piece(CurrentPiece + (CurPieceRot * 7), xiter, yiter) ;if location on piece isn't empty
Rect CurPieceX*25+xiter*25, CurPieceY*25+yiter*25, 24, 24 ;draw this block of the piece
EndIf
Next
Next
End Function
Add a function to determine if the current piece can be dropped down a line:
Function DropPiece()
For yiter = 3 To 0 Step -1 ;loop from bottom to top of piece
For xiter = 0 To 3 ;loop from left to right of piece
If Piece(CurrentPiece + (CurPieceRot * 7), xiter, yiter) <> 0 ;if location on piece isn't empty
If CurPieceY+yiter+1 < 23 ;make sure block is still within bottom board limit
If Board(CurPieceX+xiter, CurPieceY+yiter+1) <> 0 Then Return False ;if board location not empty then fail
Else ;...block would be below board limit if dropped
Return False ;so return a False to show a failure
EndIf
EndIf
Next
Next
CurPieceY = CurPieceY + 1 ;successful - drop down one line
Return True ;show that drop was a success by returning True
End Function
Here's a function to add the piece to the board (once it can drop no furthur):
Function PlacePiece()
Local outofbounds ;keeps track of whether this function fails or not
For yiter = 0 To 3 ;loop from top to bottom
For xiter = 0 To 3 ;loop from left to right
If Piece(CurrentPiece + (CurPieceRot * 7), xiter, yiter) <> 0 ;if location on piece isn't empty
Board(CurPieceX+xiter, CurPieceY+yiter) = CurrentPiece ;set board location = piece block type
If CurPieceY+yiter < 3 Then outofbounds = True ;above the 10x20 playing area - game over
EndIf
Next
Next
If outofbounds Then Return False ;piece was too high so return a False to show failure
;check if any lines were created
ClearLines() ;clear any lines that may have been created
;reset piece to top center
CurPieceX = 3 ;set back to center
CurPieceY = 0 ;set back to top
CurPieceRot = 0 ;set back to first rotation
CurrentPiece = Rand(1, 7) ;get a new random piece
Return True ;return a True to show a success at placing the piece
End Function
Now you need a function to handle clearing any lines that may have been created.
Function ClearLines()
Local blockcount ;counts the number of blocks on each line
Local numLines ;stores the number of lines cleared (0-4)
For yiter = 22 To 3 Step -1 ;loop from bottom to top of board
For xiter = 0 To 9 ;loop from left to right
If Board(xiter, yiter) <> 0 Then blockcount = blockcount + 1 ;if location isn't empty then increment blockcount
Next
If blockcount = 10 ;if 10 blocks found then...
numLines = numLines + 1 ;increment number of lines
;shift blocks down ;shift all of the upper blocks downward
For lineIter = yiter To 3 Step -1 ;loop from current line iteration up to top
For blockIter = 0 To 9 ;loop from left to right
Board(blockIter, lineIter) = Board(blockIter, lineIter - 1) ;block below = block above
Next
Next
yiter = yiter + 1 ;adjust for line that was just removed
EndIf
blockcount = 0 ;reset the block count
Next
;1 line = 1 point
;2 lines = 3 points
;3 lines = 5 points ;subtract number of lines cleared from the lines remaining
;4 lines = 8 points
If numLines > 0 Then Lines = Lines - (numLines + (numLines - 1)) - (1 * (numLines = 4))
;if cleared 5 lines (per level) then level up
If Lines < 1 ;if lines remaining are cleared...
DropSpeed = DropSpeed - 50 + (50 * (DropSpeed < 50));decrease drop time (but not less than 35 millisecs)
Level = Level + 1 ;increment level
Lines = Level * 5 ;set new lines goal
EndIf
End Function
This function will move the piece left if it's clear:
Function MoveLeft()
For xiter = 0 To 3 ;loop from left to right
For yiter = 0 To 3 ;loop from top to bottom
If Piece(CurrentPiece + (CurPieceRot * 7), xiter, yiter) <> 0 ;if location on piece isn't empty
If CurPieceX+xiter-1 > -1 ;make sure block is still within left board limit
If Board(CurPieceX+xiter-1, CurPieceY+yiter) <> 0 Then Return ;if board location not empty then fail
Else ;...block would be left of board limit if moved
Return ;so return because attempted move failed
EndIf
EndIf
Next
Next
CurPieceX = CurPieceX - 1 ;successful - move piece left one block
End Function
And this function will move the piece right if it's clear:
Function MoveRight()
For xiter = 3 To 0 Step -1 ;loop from right to left
For yiter = 0 To 3 ;loop from top to bottom
If Piece(CurrentPiece + (CurPieceRot * 7), xiter, yiter) <> 0 ;if location on piece isn't empty
If CurPieceX+xiter+1 < 10 ;make sure block is still within right board limit
If Board(CurPieceX+xiter+1, CurPieceY+yiter) <> 0 Then Return ;if board location not empty then fail
Else ;...block would be right of board limit if moved
Return ;so return because attempted move failed
EndIf
EndIf
Next
Next
CurPieceX = CurPieceX + 1 ;successful - move piece right one block
End Function
And finally, a function to handle rotating the piece:
Function RotatePiece()
For yiter = 0 To 3 ;loop from top to bottom
For xiter = 0 To 3 ;loop from left to right
If Piece(CurrentPiece + (((CurPieceRot + 1) * (CurPieceRot < 3)) * 7), xiter, yiter) <> 0 ;if location isn't empty
If CurPieceX+xiter > -1 And CurPieceX+xiter < 10 And CurPieceY+yiter < 23;make sure block is within board limits
If Board(CurPieceX+xiter, CurPieceY+yiter) <> 0 Then Return ;if board location not empty then fail
Else ;...block would be outside of board if rotated
Return ;so return because attempted rotation failed
EndIf
EndIf
Next
Next
CurPieceRot = (CurPieceRot + 1) * (CurPieceRot < 3) ;successful - rotate the piece
End Function
And here is the entire code pieced together so that you can copy&paste it and immediately run it to see how it works.
| Keys used in this example: | |||
|
| If you've reached this page and there's no index on the left, Click here to show the Index Page | |