 # Creating your own Tetris game with VB.NET - Part 3

## Introduction

If you have been following this series up to this point, I know what you are thinking: When are we going to start with the physical shapes and game play? Well, wait no more :)  In this part of my series, I am going to cover the whole creation of all the shapes, and the game playing methods. if you haven't yet read the previous 2 parts of this article series, I suggest going through them now, as this part is where everything happens!

## Logic / Explanation

As mentioned in the first part, Tetris makes use of tetrominoes. A tetromino, also spelled tetramino or tetrimino, is a geometric shape composed of four squares, connected orthogonally. This is a particular type of polyomino, just like dominoes and pentominoes. Sometimes the term is generalized to apply to configurations of four orthogonally connected cubes.  There are seven possible shapes :

 Shape Picture I O T L J S Z We are going to create these shapes now.  We will also make each shape move and change, so that each shape behaves as it should, in any other Tetris game.  With the help of pressing some keys during game play, the shapes must be flipped over, or turned, but more on that a little bit later, we should create the shapes first, so let us now start.

We do not need to change or add anything on our Form's design, so we can start with the code directly.

## Coding

The first thing we have to do is to create our Shapes class.  Add a new class to your project, and name it clsTetShapes

### clsTetShapes

```    Public rctShape() As Rectangle 'Shape

Public blnMoving As Boolean 'Is The Shape Moving
Private blnIsNextShape As Boolean = False 'Image In The Next Shape Window

Private intStartX As Integer 'Starting Horz Loc
Private intCurrShape As Integer = 1 'Current Shape
Private intCurrShapePos As Integer = 1 'Shape Location
Private intBlockX(4) As Integer 'Each Part Of Tetri, Horz
Private intBlockY(4) As Integer 'Each part Of Tetri, Vert
Private intXPos(4) As Integer 'Starting Horz Loc
Private intPanelWidth As Integer 'Panel Width
Private intPanelHeight As Integer 'Panel height```

These variables are used throughout the class. The width & height of the drawing area(s), the size of the shapes are all stored in the above variables

```    Public Sub New(ByVal intShapeType As Integer, ByVal intScreenW As Integer, ByVal intScreenH As Integer, ByVal blnNextShape As Boolean)

intStartX = CInt((intScreenW - 1) / 2) 'Start In Center Of X Axis

intPanelWidth = intScreenW 'Canvas Width
intPanelHeight = intScreenH 'Canvas Height

blnIsNextShape = blnNextShape ' Is The Next Shape Shown

intCurrShape = intShapeType 'Set Current Shape

ShapeStart() 'Call To ShapeStart Gives Initial values To All Blocks Of All Shapes

End Sub```

In the constructor, we obtain the width of our game area, get a random shape, and calls the ShapeStart sub procedure which basically creates our shapes

The next sub procedure, Build is used to build the four blocks of each shape, depending on which shape we are busy with, enter the following:

```    Private Sub Build(ByVal intShapeType As Integer)

rctShape = New Rectangle(4) {} 'Each Block Of Shape

'Create 4 Blocks 10 x 10
rctShape(0) = New Rectangle(intBlockX(0), intBlockY(0), 10, 10)
rctShape(1) = New Rectangle(intBlockX(1), intBlockY(1), 10, 10)
rctShape(2) = New Rectangle(intBlockX(2), intBlockY(2), 10, 10)
rctShape(3) = New Rectangle(intBlockX(3), intBlockY(3), 10, 10)

End Sub```

We utilise the rctShape array, and give each of the four blocks a starting value

# Creating your own Tetris game with VB.NET - Part 3

The following three (3) methods handles the shape's movement, left, right, and down. Let us add all three methods now :

```    Public Function MoveDown(ByVal intMovePixels As Integer, ByVal rctGameGrid()() As Rectangle) As Rectangle()
Dim blnMovable As Boolean = True 'Is The Shape Movable?

Dim j As Integer 'Loop Counter

' Check To See If Any X Positions Will Go Over The Edges
For j = 0 To 3
If intBlockY(j) + 10 + intMovePixels > intPanelHeight - 1 Then
blnMovable = False
blnMoving = False
Exit For
End If
Next j

If blnMovable Then

' Shape Hasn't Reached The Bottom Yet, See If It Will Hit A
' Stationary Block
Dim k As Integer
For k = 0 To 3
If [Decimal].Remainder(intBlockY(k), 10) = 0 And intBlockY(k) >= 0 Then
If Not rctGameGrid(CInt((intBlockY(k) / 10 + 1)))(CInt((intBlockX(k) / 10))).IsEmpty Then

' Shape Is About To Enter A Blocked Area
blnMovable = False
blnMoving = False
Exit For
End If
End If
Next k
End If

If blnMovable Then
Dim i As Integer
For i = 0 To 3
intBlockY(i) += intMovePixels
Next i
End If
Build(intCurrShape)
Return rctShape
End Function

Public Function MoveLeft(ByVal intMovePixels As Integer, ByVal rctGameGrid()() As Rectangle) As Rectangle()
Dim blnMovable As Boolean = True
Dim intYPos(4) As Integer

' Set X Pos To Be Out Of The Range In Our Panel
Dim intFurthestX As Integer = intPanelWidth

Dim j As Integer

' Check To See If Any X Positions Will Go Over The Edge Or Try And Enter A
' Blocked Area We Need The Leftmost Block(s) To Test Could Be 1,2,3, or 4
For j = 0 To 3
If intBlockX(j) <= intFurthestX Then
intFurthestX = intBlockX(j)
intYPos(j) = intBlockY(j)
If [Decimal].Remainder(intBlockY(j), 10) <> 0 Then
intYPos(j) += 5
End If
End If
If intBlockX(j) - intMovePixels < 0 Then
blnMovable = False
Exit For
End If
Next j

If blnMovable Then
Dim i As Integer
For i = 0 To 3
If intYPos(i) >= 0 Then
If Not rctGameGrid(CInt((intYPos(i) / 10)))(CInt(((intFurthestX - 10) / 10))).IsEmpty Then
' Shape Is About To Enter A Blocked Area
blnMovable = False
Exit For
End If
End If
Next i
End If

If blnMovable Then
Dim i As Integer
For i = 0 To 3
intBlockX(i) -= intMovePixels
Next i
End If
Build(intCurrShape)
Return rctShape
End Function

Public Function MoveRight(ByVal intMovePixels As Integer, ByVal rctGameGrid()() As Rectangle) As Rectangle()
Dim blnMovable As Boolean = True

Dim intYPos(4) As Integer

' Set X Pos To Be Out Of The Range In Our Panel
Dim intFurthestX As Integer = 0

' Check To See If Any X Positions Will Go Over The Edge
Dim j As Integer

For j = 0 To 3
If intBlockX(j) >= intFurthestX Then
intFurthestX = intBlockX(j)
intYPos(j) = intBlockY(j)
If [Decimal].Remainder(intBlockY(j), 10) <> 0 Then
intYPos(j) += 5
End If
End If

If intBlockX(j) + intMovePixels + 10 >= intPanelWidth Then
blnMovable = False
Exit For
End If

Next j

If blnMovable Then
Dim i As Integer

For i = 0 To 3
If intYPos(i) >= 0 Then
If Not rctGameGrid(CInt(intYPos(i) / 10))(CInt(((intFurthestX + 10) / 10))).IsEmpty Then

' Shape Is About To Enter A Blocked Area
blnMovable = False
Exit For
End If
End If
Next i
End If

If blnMovable Then
Dim i As Integer
For i = 0 To 3
intBlockX(i) += intMovePixels
Next i
End If

Build(intCurrShape)

Return rctShape

End Function
```

Whichever tetri is displaying, must act as a unit. Yes, all four blocks of each shape is separate, it is our job to make use of the For loops to move each part along with the other 3 blocks. The trick is in the For Loops, as I said. Because we loop through each element of the rctBlock X or Y array, we can move each block separately the specified amount of pixels

# Creating your own Tetris game with VB.NET - Part 3

The next Sub procedure Flips the shapes, let us add it:

```    Public Function Flip(ByVal strDirection As String, ByVal rctGameGrid()() As Rectangle) As Rectangle()

Dim blnMovable As Boolean = True

If strDirection = "right" Then
intCurrShapePos += 1
If intCurrShapePos > 4 Then
intCurrShapePos = 1
End If
End If

If strDirection = "left" Then
intCurrShapePos -= 1
If intCurrShapePos < 1 Then
intCurrShapePos = 4 'Was 4
End If
End If

SetShapePos()
Build(intCurrShape)

' Before Returning The Shape, See If It Is Within The Bounds Of The Panel
Dim recGameArea As New Rectangle(0, 0, intPanelWidth, intPanelHeight)

Dim intYPositions(4) As Integer
Dim i As Integer

For i = 0 To 3
If Not recGameArea.Contains(rctShape(i)) Then
blnMovable = False
Exit For
End If

' See If The Shape Is Going To Collide With Any Other
' Stationary Objects

intYPositions(i) = intBlockY(i)
If [Decimal].Remainder(intBlockY(i), 10) <> 0 Then
intYPositions(i) += 5
End If

If Not rctGameGrid(CInt((intYPositions(i) / 10)))(CInt((intBlockX(i) / 10))).IsEmpty Then

' Shape Is About To Enter A Blocked Area
blnMovable = False
Exit For
End If
Next i

If Not blnMovable Then
' Rollback
If strDirection = "right" Then
intCurrShapePos -= 1
If intCurrShapePos < 1 Then
intCurrShapePos = 4
End If

SetShapePos()
Build(intCurrShape)
End If

If strDirection = "left" Then
intCurrShapePos += 1
If intCurrShapePos > 4 Then
intCurrShapePos = 1
End If

SetShapePos()
Build(intCurrShape)
End If
End If

Return rctShape

End Function
```

This sub gets called whenever K or A is pressed during game play. This sub also makes use of the SetShapePos method to turn each shape, according to whichever key was pressed. When Flipping a graphic object, it means that you are actually creating a mirror image from it, and it returns the mirror image. We can flip the shape left or right

Next up we have the ShapeStart method. This method, although long, sets the start-up positions for whichever shape in the main game window, or in the Preview window

```    Private Sub ShapeStart()

If Not blnIsNextShape Then 'If Shape Shown In Main Game Window
Select Case intCurrShape

Case 1 'T Shape - Blue
intBlockX(0) = intStartX
intBlockY(0) = -10
intBlockX(1) = intStartX - 10
intBlockY(1) = -10
intBlockX(2) = intStartX + 10
intBlockY(2) = -10
intBlockX(3) = intStartX
intBlockY(3) = -20

Case 2 'L Shape - Red
intBlockX(0) = intStartX
intBlockY(0) = -10
intBlockX(1) = intStartX - 10
intBlockY(1) = -10
intBlockX(2) = intStartX + 10
intBlockY(2) = -10
intBlockX(3) = intStartX + 10
intBlockY(3) = -20

Case 3 'J Shape - Green
intBlockX(0) = intStartX
intBlockY(0) = -10
intBlockX(1) = intStartX - 10
intBlockY(1) = -10
intBlockX(2) = intStartX + 10
intBlockY(2) = -10
intBlockX(3) = intStartX - 10
intBlockY(3) = -20

Case 4 'O Shape - Yellow
intBlockX(0) = intStartX
intBlockY(0) = -10
intBlockX(1) = intStartX + 10
intBlockY(1) = -10
intBlockX(2) = intStartX
intBlockY(2) = -20
intBlockX(3) = intStartX + 10
intBlockY(3) = -20

Case 5 'I Shape - Brown
intBlockX(0) = intStartX
intBlockY(0) = -10
intBlockX(1) = intStartX
intBlockY(1) = -20
intBlockX(2) = intStartX
intBlockY(2) = -30
intBlockX(3) = intStartX
intBlockY(3) = -40

Case 6 'S Shape - Orange
intBlockX(0) = intStartX - 10
intBlockY(0) = -10
intBlockX(1) = intStartX
intBlockY(1) = -10
intBlockX(2) = intStartX
intBlockY(2) = -20
intBlockX(3) = intStartX + 10
intBlockY(3) = -20

Case 7 'Z Shape - Purple
intBlockX(0) = intStartX + 10
intBlockY(0) = -10
intBlockX(1) = intStartX
intBlockY(1) = -10
intBlockX(2) = intStartX
intBlockY(2) = -20
intBlockX(3) = intStartX - 10
intBlockY(3) = -20

Case Else
End Select

Else

Select Case intCurrShape 'If Shape Shown In preview Window

Case 1 'T
intBlockX(0) = intStartX - 5
intBlockY(0) = 35
intBlockX(1) = intStartX - 15
intBlockY(1) = 35
intBlockX(2) = intStartX + 5
intBlockY(2) = 35
intBlockX(3) = intStartX - 5
intBlockY(3) = 25

Case 2 'L
intBlockX(0) = intStartX - 5
intBlockY(0) = 35
intBlockX(1) = intStartX - 15
intBlockY(1) = 35
intBlockX(2) = intStartX + 5
intBlockY(2) = 35
intBlockX(3) = intStartX + 5
intBlockY(3) = 25

Case 3 'J
intBlockX(0) = intStartX - 5
intBlockY(0) = 35
intBlockX(1) = intStartX - 15
intBlockY(1) = 35
intBlockX(2) = intStartX + 5
intBlockY(2) = 35
intBlockX(3) = intStartX - 15
intBlockY(3) = 25

Case 4 'O
intBlockX(0) = intStartX - 10
intBlockY(0) = 35
intBlockX(1) = intStartX
intBlockY(1) = 35
intBlockX(2) = intStartX - 10
intBlockY(2) = 25
intBlockX(3) = intStartX
intBlockY(3) = 25

Case 5 'I
intBlockX(0) = intStartX - 5
intBlockY(0) = 45
intBlockX(1) = intStartX - 5
intBlockY(1) = 35
intBlockX(2) = intStartX - 5
intBlockY(2) = 25
intBlockX(3) = intStartX - 5
intBlockY(3) = 15

Case 6 'S
intBlockX(0) = intStartX - 10
intBlockY(0) = 45
intBlockX(1) = intStartX
intBlockY(1) = 45
intBlockX(2) = intStartX
intBlockY(2) = 35
intBlockX(3) = intStartX + 10
intBlockY(3) = 35

Case 7 'Z
intBlockX(0) = intStartX + 10
intBlockY(0) = 45
intBlockX(1) = intStartX
intBlockY(1) = 45
intBlockX(2) = intStartX
intBlockY(2) = 35
intBlockX(3) = intStartX - 10
intBlockY(3) = 35

Case Else
End Select

Build(intCurrShape)

End If
End Sub
```

Actually quite simple when you look at it, once you have the basic location for the starting block, you can easily arrange all other blocks into the shape you want, because all blocks are the same size

# Creating your own Tetris game with VB.NET - Part 3

The next simple function, simply gets the next shape in the queue, to display it in the Preview window

```    Public Function GetShape() As Rectangle()
Return rctShape ' Return Next Shape
End Function```

The last sub we are going to do for the clsTetShapes class is SetShapePos sub, let us add it quickly :

```    Private Sub SetShapePos()
Select Case intCurrShape
Case 1 'T

Select Case intCurrShapePos

Case 1
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) - 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0) + 10
intBlockY(2) = intBlockY(0)
intBlockX(3) = intBlockX(0)
intBlockY(3) = intBlockY(0) - 10

Case 2
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) - 10
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) + 10
intBlockX(3) = intBlockX(0) + 10
intBlockY(3) = intBlockY(0)

Case 3
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) + 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0) - 10
intBlockY(2) = intBlockY(0)
intBlockX(3) = intBlockX(0)
intBlockY(3) = intBlockY(0) + 10

Case 4
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) + 10
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) - 10
intBlockX(3) = intBlockX(0) - 10
intBlockY(3) = intBlockY(0)

Case Else
End Select

Case 2 'L
Select Case intCurrShapePos

Case 1
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) - 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0) + 10
intBlockY(2) = intBlockY(0)
intBlockX(3) = intBlockX(0) + 10
intBlockY(3) = intBlockY(0) - 10

Case 2
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) - 10
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) + 10
intBlockX(3) = intBlockX(0) + 10
intBlockY(3) = intBlockY(0) + 10

Case 3
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) + 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0) - 10
intBlockY(2) = intBlockY(0)
intBlockX(3) = intBlockX(0) - 10
intBlockY(3) = intBlockY(0) + 10

Case 4
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) + 10
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) - 10
intBlockX(3) = intBlockX(0) - 10
intBlockY(3) = intBlockY(0) - 10
Case Else
End Select

Case 3 'J
Select Case intCurrShapePos

Case 1
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) - 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0) + 10
intBlockY(2) = intBlockY(0)
intBlockX(3) = intBlockX(0) - 10
intBlockY(3) = intBlockY(0) - 10

Case 2
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) - 10
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) + 10
intBlockX(3) = intBlockX(0) + 10
intBlockY(3) = intBlockY(0) - 10

Case 3
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) + 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0) - 10
intBlockY(2) = intBlockY(0)
intBlockX(3) = intBlockX(0) + 10
intBlockY(3) = intBlockY(0) + 10

Case 4
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) + 10
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) - 10
intBlockX(3) = intBlockX(0) - 10
intBlockY(3) = intBlockY(0) + 10

Case Else
End Select

'4 Is O Shape - Already Square Won't See Anything

Case 5  'I
Select Case intCurrShapePos

Case 1
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) + 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0) + 20
intBlockY(2) = intBlockY(0)
intBlockX(3) = intBlockX(0) + 30
intBlockY(3) = intBlockY(0)

Case 2
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) - 10
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) - 20
intBlockX(3) = intBlockX(0)
intBlockY(3) = intBlockY(0) - 30

Case 3
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) + 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0) + 20
intBlockY(2) = intBlockY(0)
intBlockX(3) = intBlockX(0) + 30
intBlockY(3) = intBlockY(0)

Case 4
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) - 10
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) - 20
intBlockX(3) = intBlockX(0)
intBlockY(3) = intBlockY(0) - 30

Case Else
End Select

Case 6 'S
Select Case intCurrShapePos

Case 1
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) + 10
intBlockX(2) = intBlockX(0) + 10
intBlockY(2) = intBlockY(0) + 10
intBlockX(3) = intBlockX(0) + 10
intBlockY(3) = intBlockY(0) + 20

Case 2
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) - 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) - 10
intBlockX(3) = intBlockX(0) + 10
intBlockY(3) = intBlockY(0) - 10

Case 3
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) + 10
intBlockX(2) = intBlockX(0) + 10
intBlockY(2) = intBlockY(0) + 10
intBlockX(3) = intBlockX(0) + 10
intBlockY(3) = intBlockY(0) + 20

Case 4
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) - 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) - 10
intBlockX(3) = intBlockX(0) + 10
intBlockY(3) = intBlockY(0) - 10

Case Else
End Select

Case 7 'Z
Select Case intCurrShapePos

Case 1
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) - 10
intBlockX(2) = intBlockX(0) - 10
intBlockY(2) = intBlockY(0) - 10
intBlockX(3) = intBlockX(0) - 10
intBlockY(3) = intBlockY(0) - 20

Case 2
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) + 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) + 10
intBlockX(3) = intBlockX(0) - 10
intBlockY(3) = intBlockY(0) + 10

Case 3
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0)
intBlockY(1) = intBlockY(0) - 10
intBlockX(2) = intBlockX(0) - 10
intBlockY(2) = intBlockY(0) - 10
intBlockX(3) = intBlockX(0) - 10
intBlockY(3) = intBlockY(0) - 20

Case 4
intBlockX(0) = intBlockX(0)
intBlockY(0) = intBlockY(0)
intBlockX(1) = intBlockX(0) + 10
intBlockY(1) = intBlockY(0)
intBlockX(2) = intBlockX(0)
intBlockY(2) = intBlockY(0) + 10
intBlockX(3) = intBlockX(0) - 10
intBlockY(3) = intBlockY(0) + 10

Case Else
End Select

Case Else
End Select

End Sub```

Stop crying, please! It is not that bad! :) We followed the same principle as we did in the ShapeStart sub. The only difference now is that because we have 4 possible variations of the current shape, we must now set each variation's shape's coordinates properly. If you do not understand what I mean about the four possible variations, I mean that you can turn the shape clockwise or counter clockwise ( depending on whether A or K was pressed), and to cater for that shape change, we must set each block's location in each shape accordingly.

# Creating your own Tetris game with VB.NET - Part 3

Phew! That was some coding wasn't it? Bad news, we're still not done. Let us now proceed to all the necessary methods and objects we need to add to frmTet's code. Open the Code for frmTet, and add the following variables into the General Declarations area

```    Private shpShape As clsTetShapes 'Current Shape
Private shpNextShape As clsTetShapes 'Next Shape

Private rndShapeType As New Random 'Random Shape

Private rctGame()() As Rectangle 'Game Rectangle
Private rctShape As Rectangle() 'Shape Rectangle

Private blnGamePaused As Boolean 'Game Paused?
Private blnRowFull As Boolean 'Row Full?

Private intNextShapeType As Integer 'Next Shape Type
Private intShapeType As Integer 'Current Shape Type```

Here, we just created all the necessary variables in order to make use of our clsTetShapes class efficiently

Add the Following sub procedure to frmTet:

```    Private Sub MainGame()

Dim g As Graphics = scrGame.GetGraphics() 'Allow Drawing

arrGridBrushes = grdGame.GetGridBrushes() 'Get All Brush Colours

arrColours = grdGame.GetColours() 'Get All Block Colours

rctGame = grdGame.GetGrid() 'Set Up Grid

scrGame.ClearScreen() 'Clear Current Screen - To Redraw

' Draw The Stationary Shapes First
Dim i As Integer
For i = 0 To intNoOfRows - 1
Dim k As Integer

For k = 0 To intNoOfCols - 1

If Not grdGame.IsLocEmpty(i, k) Then 'Is Row Full?
g.FillRectangle(arrGridBrushes(i)(k), rctGame(i)(k))
g.DrawRectangle(New Pen(Color.White, 1), rctGame(i)(k))
End If

Next k

Next i

' Draw Moving Shape
Dim j As Integer

For j = 0 To rctShape.Length - 1
g.FillRectangle(arrColours((intShapeType - 1)), rctShape(j))
g.DrawRectangle(New Pen(Color.White, 1), rctShape(j))
Next j

scrGame.BufferImage() 'Double Buffer

End Sub```

Aptly named, this sub controls the whole flow of our game in progress. Most importantly, it draws all the stationary shapes ( the shapes standing still ), and then the moving shape

The next function, named NextShape is next

```    Private Sub NextShape()

Dim g As Graphics = scrPreview.GetGraphics() 'Allow Drawing In Preview

Dim rctNextShape() As Rectangle 'Set Up Next Shape

rctNextShape = shpNextShape.GetShape() 'Get Next Shape

arrColours = grdGame.GetColours() 'Get Next Shape Colours

scrPreview.ClearScreen() ' Clear Screen

Dim j As Integer

For j = 0 To 3 'Draw Shape
g.FillRectangle(arrColours((intNextShapeType - 1)), rctNextShape(j))
g.DrawRectangle(New Pen(Color.White, 1), rctNextShape(j))
Next j

scrPreview.BufferImage()

End Sub```

The above sub simply gets the next shape in the queue and draws it in the Preview window

```    Private Function GetShapeType() As Integer
Dim intShapeType As Integer 'Shape Type

Do 'Randomly Pick Shape type
intShapeType = rndShapeType.Next(8)
Loop While intShapeType = 0

Return intShapeType

End Function```

The GetShapeType function randomly picks a shape out of the seven possible shapes, and then return it to the calling function

Game Over is next:

```    Private Sub GameOver()

Dim gOver As Graphics = scrSplash.GetGraphics() 'Make Use Of Splash Graphic Area

scrSplash.ClearScreen() 'Clear Whatever Was Drawn

'Draw GAME OVER
gOver.DrawString("GAME OVER", New Font("Courier", 18), New SolidBrush(Color.Red), 5, 100)

scrSplash.BufferImage()
End Sub```

This sub simply draws the string GAME OVER when your game is over

# Creating your own Tetris game with VB.NET - Part 3

```    Private Sub UpdateScore(ByVal intRowNum As Integer)
intLevelRows += 1 'Increment Level Rows
intTotalRows += 1 'Increment Total Rows

Dim intReverseRow As Integer = 30 - intRowNum 'Get "UnUsed Rows"

'UnUsed Rows * Level Rows * Level * 10
lngScore = lngScore + intReverseRow * intLevelRows * intLevel * 10

lblTetScore.Text = lngScore.ToString() 'Update Score

If intLevelRows = 10 Then 'If 10 Rows Done In Level, Increment Level
UpdateLevel()
intLevelRows = 0
End If

lblTetRows.Text = intTotalRows.ToString() 'Show How Many Rows "Won" So Far

End Sub

Private Sub UpdateLevel()
intLevel += 1 'Increment Level

If intGameSpeed > 10 Then 'Increase Game Speed
intGameSpeed -= 10
End If

lblTetLevel.Text = intLevel.ToString() 'Show Current Level

End Sub
```

The previous 2 subs simply updates the score and the level. Once 10 rows have been completed, a new level starts, and obviously, the timer's speed increases

```    Private Sub frmTet_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown

Dim strKeyPress As String = Nothing
strKeyPress = e.KeyCode.ToString() 'Gets Key Pressed

If Not blnGameOver Then 'Still Playing?
Select Case strKeyPress.ToUpper()

Case "Z" 'Move Left
If shpShape.blnMoving Then
rctShape = shpShape.MoveLeft(10, grdGame.GetGrid())
End If

Case "M" 'Move Right
If shpShape.blnMoving Then
rctShape = shpShape.MoveRight(10, grdGame.GetGrid())
End If

Case "K" 'Flip Right
If shpShape.blnMoving Then
rctShape = shpShape.Flip("right", grdGame.GetGrid())
End If

Case "A" 'Flip Left
If shpShape.blnMoving Then
rctShape = shpShape.Flip("left", grdGame.GetGrid())
End If

Case "Q" 'Stop, Reset Everything
tmrTet.Stop()
SetUp()
Splash()

Case "P" 'Pause
If Not blnGamePaused Then
tmrTet.Stop()
blnGamePaused = True
Else
tmrTet.Start()
blnGamePaused = False
End If

Case "SPACE" 'Drop Shape
If shpShape.blnMoving Then
tmrTet.Interval = intDropRate
blnDropped = True
End If
Case Else

End Select

Else

Select Case strKeyPress.ToUpper()

Case "RETURN" 'Start When Enter Pressed

' Setup Game And Set Default Properties
SetUp()
intShapeType = GetShapeType()

shpShape = New clsTetShapes(intShapeType, scrGame.ScreenWidth, scrGame.ScreenHeight, False)

intNextShapeType = GetShapeType()

shpNextShape = New clsTetShapes(intNextShapeType, scrPreview.ScreenWidth, scrPreview.ScreenHeight, True)

shpShape.blnMoving = True
blnGameOver = False

tmrTet.Interval = intGameSpeed

tmrTet.Enabled = True
tmrTet.Start()
NextShape()

Case Else
End Select
End If
End Sub```

The KeyDown event determines which key has been pressed and acts accordingly

The last event we are adding to our form is the Timer's tick event, which makes everything happen, let us have a look:

```    Private Sub tmrTet_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tmrTet.Tick

If shpShape.blnMoving Then 'IF Shape Is Still Moving, Move Down
rctShape = shpShape.MoveDown(intDropRate, grdGame.GetGrid())
MainGame()
Else

'Current Shape Stopped Moving
Dim intXCoordinate As Integer
Dim intYCoordinate As Integer
Dim i As Integer

'Is Shape Within Game Area? If Not, Game Is Over
For i = 0 To 3
If Not rctScreen.Contains(rctShape(i)) Then
blnGameOver = True
Exit For
End If
Next i

If Not blnGameOver Then

Dim intYCoordinates(4) As Integer
'Paint Shape's Final Position

For i = 0 To 3 'Gets Coordinates
intXCoordinate = rctShape(i).X
intYCoordinate = rctShape(i).Y
intYCoordinates(i) = intYCoordinate / 10

'Copy Shape's Position Into Game Grid
grdGame.SetLoc(intYCoordinate / 10, intXCoordinate / 10, rctShape(i), intShapeType)
Next i

'Sort Array Of Y Coordinates So That We Can Go From Small To Large
'Ensures That We Drop Rows Sequentially
Array.Sort(intYCoordinates)

For i = 0 To 3
blnRowFull = True

'Check If Shape Causes An Entire Row To Fill
'If It Does Then We Need To Eliminate The Row And Drop The Rest Down
Dim j As Integer

For j = 0 To intNoOfCols - 1
If grdGame.IsLocEmpty(intYCoordinates(i), j) Then
blnRowFull = False
Exit For
Else
blnRowFull = True
End If
Next j

If blnRowFull Then
'Drop Row And Fill From Next Row Down

Dim k As Integer
For k = intYCoordinates(i) To -1 Step -1
'Update All Coordinates Of Our Shapes

Dim l As Integer
For l = 0 To intNoOfCols - 1

'Set Value Into Row Below
grdGame.DropDown(k, l)
Next l
Next k

'Do Row 0
grdGame.FirstRow()

'Update Score
End If
Next i

intShapeType = intNextShapeType
shpShape = New clsTetShapes(intShapeType, scrGame.ScreenWidth, scrGame.ScreenHeight, False)
intNextShapeType = GetShapeType()
shpNextShape = New clsTetShapes(intNextShapeType, scrPreview.ScreenWidth, scrPreview.ScreenHeight, True)
shpShape.blnMoving = True
NextShape()

'Reset Game Speed
tmrTet.Interval = intGameSpeed
blnDropped = False
Else
tmrTet.Stop()
GameOver()
End If
End If
End Sub
```

You may be thinking that we are done. Not yet. We just have to edit an already existing sub in clsTetGrid. Let us open up the code for clsTetGrid and edit the DropDown sub to look like the following:

```    Public Sub DropDown(ByVal intRowNo As Integer, ByVal intColNo As Integer)

'CHANGED HERE IN PART 3 - ADDED OR NOT INTROWNO <= 0
If Not IsLocEmpty(intRowNo - 1, intColNo) Or Not intRowNo <= 0 Then 'Check If Next Row Is Empty

'Set Current Location Equal To The Same Location Horizontally, But
'Next Row ( Vertically )
'Size Is 10
arrGrids(intRowNo)(intColNo) = New Rectangle(arrGrids((intRowNo - 1))(intColNo).X, arrGrids((intRowNo - 1))(intColNo).Y + 10, 10, 10)

'Make Sure Gridlines Move Too
arrGridBrushes(intRowNo)(intColNo) = arrGridBrushes((intRowNo - 1))(intColNo)

Else 'Next Row Is Not Empty
arrGrids(intRowNo)(intColNo) = arrGrids((intRowNo - 1))(intColNo)
End If

End Sub```

It is mostly the same as it was before, I just added the Or Not statement into the equation. Why? because I noticed it threw an exception when the rows are supposed to be cleared. It doesn't now

All the coding is done, for this part. I was planning to do only three parts for this article series, but as luck would have it, I have noticed several minor issues and a major issue, which can be classified as a bug, with this current Beta version. You have to understand that I want to share with you all my experiences while creating this project, and if an error or two creeps in, it is normal, and we'll get it fixed in the next part

## Conclusion

Well, this one was hard work, go out and have a beer, it's on me :) We have covered the whole game play experience with this article, and covered the creation of the shapes in this section. What we will do in the next part, is to fix all the known issues ( that I have discovered so far ), and if you find any others, please let me know. Until next time, have fun - but not too much!

#### Hannes du Preez

Hannes du Preez is a Microsoft MVP for Visual Basic. He is a trainer at a South African-based company. He is the co-founder of hmsmp.co.za, a community for South African developers.