Building a Tetris Game using VB.NET (part 2)

This is a continuation of part 1 of the series. If you are looking for the source code and/or the executable file, you can find it in part 1.

In Part 1, we got the basic idea about the design of our tetris game. Here I will discuss how the classes are implemented and how they work in our game.

Creating the Game Board

This is the easiest part to do. We just instantiate a 2 dimensional array of TetrisCells and then arrange them into rows and columns.(like a spreadsheet). We call this by the name GameBoard in our program.

This part of the program creates the game board.

GameBoard = New TetrisBoard(GameBox)

With GameBoard

    .Rows = 20

    .Columns = 10

    .CellSize = New Size(20, 20)

    .Style = BorderStyle.FixedSingle

    .SetupBoard()

End With

And what does that SetupBoard method do? It creates an array of TetrisCells and arranges them into rows and columns (like a grid).

Creating the Blocks

At anyone time one block would be active in the game. (This may be more in the future at higher game levels, but for now assume it to be one). This is represented by the name FallingBlock in our game.

FallingBlock = New TetrisBlock(GameBoard)

We create a new falling block only once. For the 2nd and subsequent block we want to make fall, we just reposition the center cell of our existing block and assign it the shape the PreviewBlock had. And the PreviewBlock is assigned a new random shape.

Private Sub DropNextFallingBlock()
    FallingBlock.CenterCell = GameBoard.Cells(2, GameBoard.Columns \ 2)
    FallingBlock.Shape = PreviewBlock.Shape
    PreviewBlock.Shape = GetRandomShape()
    PreviewBlock.RefreshBackGround()
    PreviewBlock.Refresh()
End Sub

Rotating the Block

Rotating the falling block is interesting as well as tricky. We never actually rotate it. Instead, all we do is just set the shape to the next block in sequence, which is in fact the rotated version of the previous block. And when it is any of those with ‘4 suffix’ (e.g. I4, J4, L4 etc.) we set it back to 1. (e.g. I1, J1, L1 etc.), since the shape rotated 4 time will be back to its first position.

Public Enum Shapes

    I1 = 1

    I2

    I3

    I4

    J1

    J2

    T4

    Z1

    Z2

    Z3

    Z4

End Enum

Public Sub Rotate()

    RefreshBackGround()

    Shape = GetRotatedShape() ” This calls UpdateShape automatically.

End Sub

 

Private Sub UpdateShape()

    Cells = GetShapeCells(Shape, CenterCell)

    Refresh()

End Sub

 

Private Function GetRotatedShape() As Shapes

    Dim rotatedShape As Integer = If(Shape Mod 4 = 0, Shape – 3, Shape + 1)

    Return CType(rotatedShape, Shapes)

End Function

Private Function GetShapeCells(ByVal theShape As Shapes, ByVal referenceCell As TetrisCell) As TetrisCell()

    Dim theCells() As TetrisCell

    Select Case theShape

        Case Shapes.I1, Shapes.I3

            theCells = GetCells(referenceCell, -1, 0, 0, 0, 1, 0, 2, 0)

        Case Shapes.I2, Shapes.I4

            theCells = GetCells(referenceCell, 0, -1, 0, 0, 0, 1, 0, 2)

        Case Shapes.J1

            theCells = GetCells(referenceCell, -1, 0, 0, 0, 1, 0, 1, -1)

        Case Shapes.J2

            theCells = GetCells(referenceCell, 0, 1, 0, 0, 0, -1, -1, -1)

        Case Shapes.J3

            theCells = GetCells(referenceCell, 1, 0, 0, 0, -1, 0, -1, 1)

        Case Shapes.J4

            theCells = GetCells(referenceCell, 0, -1, 0, 0, 0, 1, 1, 1)

        Case Shapes.L1

            theCells = GetCells(referenceCell, -1, 0, 0, 0, 1, 0, 1, 1)

        Case Shapes.L2

            theCells = GetCells(referenceCell, 0, 1, 0, 0, 0, -1, 1, -1)

    End Select

    Return theCells

End Function

 

Private Function GetCells(ByVal referenceCell As TetrisCell, ByVal ParamArray relativeCoordinates() As Integer) As TetrisCell()

    Dim theCells((relativeCoordinates.Count \ 2) – 1) As TetrisCell

    Dim refRow As Integer = referenceCell.Row

    Dim refCol As Integer = referenceCell.Column

    For i As Integer = 0 To theCells.Count – 1

        theCells(i) = ParentBoard.Cells(refRow + relativeCoordinates(i * 2), refCol + relativeCoordinates(i * 2 + 1))

    Next

    Return theCells

End Function

To get those coordinates of the cells in shape, we just take the reference cell (center cell) as  point 0,0 and mark the others relative to that cell.

The figure below shows an example taking the J-shaped blocks:

J-shape-rotation

Making the Blocks Fall

In part 1 I had discussed about the center cell of the block. Here is where it is used. We bring down the center cell one line down (in a timer) and let it redraw itself. Since the TetricBlock class knows its shape and also knows how to draw it, its easy.

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick

    If FallingBlock.CanMove(MoveDirection.Down) Then

        FallingBlock.Move(MoveDirection.Down)

    Else

    ” Do other stuff like …

    ” Fix block to base

    ” Remove completed rows

    ” Update Statistics

    ” Make the next block fall

    ” Check if game has ended

    End If

End Sub

Controlling the Falling Block with our Keyboard

All we need to do for this is to handle the form keydown event. If user presses the Left/Right/Down keys, we move the block in that direction. Up key is used to rotate the block.

With FallingBlock

    Select Case e.KeyCode

        Case Keys.Left

            If .CanMove(MoveDirection.Left) Then .Move(MoveDirection.Left)

        Case Keys.Right

            If .CanMove(MoveDirection.Right) Then .Move(MoveDirection.Right)

        Case Keys.Down

            If .CanMove(MoveDirection.Down) Then .Move(MoveDirection.Down)

        Case Keys.Up

            If .CanRotate Then .Rotate()

    End Select

End With

Fixing the Blocks and Removing Completed Rows

When a block has reached the point where it can’t go down any further we need to fix it there. All we need to do for those cells is to set its IsEmpty property to false, so that it can’t be moved. To remove the completed rows we check if the row is complete (for each row the falling block occupied), and if it is, we remove it.

” Fix block to base

For Each cell As TetrisCell In FallingBlock.Cells

    cell.IsEmpty = False

Next

 

” Remove completed rows

Dim checkRows = From cell In FallingBlock.Cells _

                Order By cell.Row _

                Select cell.Row Distinct

Dim rowsRemoved As Integer = 0

For Each row In checkRows

    If GameBoard.IsRowComplete(row) Then

        GameBoard.RemoveRow(row)

        rowsRemoved += 1

    End If

Next

Scoring

We apply a simple concept here. We award more points for removing more rows in a single go. So the easiest way is square of rows removed. (why show misery for points Wink)

So in a nutshell, this is how the score works:
Award 100 for a single, 400 for a double, 900 for a triplet and 1600 for a quadruple.
For every 10 lines removed, increase speed by 1.
For every 10 speeds, increase level by 1. (the game at present has only 1 level, so this won’t work.)

” Update Statistics

Score += Math.Pow(rowsRemoved, 2) * 100

Lines += rowsRemoved

Speed = 1 + Lines \ 10

If Speed Mod 10 = 0 Then Level += 1 : Speed = 1

Timer1.Interval = (10 – Speed) * 100

UpdateStatistics()

In the future I plan to award points for sweeping the board, bonus levels, handicapped levels etc.

Ending the Game

This is simple. As soon as we make the block appear on the board and we find that it is not possible to move it, we know that game has ended.

” Check if game has ended

If Not FallingBlock.CanMove(FallingBlock.CenterCell) Then EndGame()

Pausing the Game

To pause the game we simply stop the timer that is falling the blocks.

We also disable the keyboard navigation too. (I know you will cheat if I don’t do that Tongue out)

Private Sub TogglePauseGame()

    If Status = GameStatus.Paused Then

        Status = GameStatus.Running

        MessageLabel.Visible = False

        Timer1.Enabled = True

    Else

        Status = GameStatus.Paused

        ShowMessage(String.Format(“{0}{0}GAME PAUSED{0}{0}{0}{0}Click here to resume.”, vbCrLf))

    End If

End Sub

 

Private Sub ShowMessage(ByVal message As String)

    MessageLabel.Text = message

    MessageLabel.Visible = True

    Timer1.Enabled = False

End Sub

Future Enhancements

In the future I plan to enhance the following things in this game:

  1. Upgrade the TetrisCell class to have better graphics. At present it is just a colored label.
  2. Add more levels. New levels will have more complex blocks. (At present each block is of 4 cells only). And there will be bonus levels, handicapped levels etc.
  3. Improve the scoring mechanism. Add bonus points for sweeping the board etc.
  4. Add facility to add some windows controls to form without disrupting the game. At present it is not possible to add any control that can take focus. e.g. ComboBox etc.

 

Advertisements

23 Responses to “Building a Tetris Game using VB.NET (part 2)”

  1. JasonawRider Says:

    Hello. And Bye.

  2. patPeterzyzaz Says:

    good explanation of the concept. Thanks

  3. Oswaldo Manhart Says:

    Hello. And Bye.
    Luciano Kretsinger

  4. Pantelis Says:

    Very Nice project!! and great explanation, really helped me a lot to understand it. But can u suggest how to upgrade graphics plz? what else can we use except label

  5. pradeep1210 Says:

    Thanks Pantelis.
    You could use a picturebox etc. to replace the label. But I think the best replacement would be a self made user control. You can customize it whatever way you need it to be. So it will have only that what you want and no more.
    You can build your own user control instead of the label, with appropriate properties and whatever advanced graphics you need. From the code it should be evident what properties and methods you would require.

  6. Mike Says:

    Excellent Source Code, you have really helped me to understand this!

  7. ethan Says:

    Hey how do u set up the design of the board like were the labels go and stuff please respond and great code

  8. Shaun Says:

    Firstly thanks for this useful source code
    but i need to upgrade this further as
    making a new user
    storing the user highest score in database and some more functionality like

    a block with different shape and color if falled should vanished dat particular color all blocks how to do that(like a joker of card game)
    kindly help me i will be very thankful to u

    • pradeep1210 Says:

      Thanks for your interest and appreciation.
      While I had planned the next version of this game, I couldn’t spare time from my busy schedule for this yet. However the link to source code is in the part 1 of this article and should be easy to modify according to your needs. Let me know if you have any difficulty doing so and I’ll be glad to help you out.

  9. Shaun Says:

    Kindly mail me the modified one
    thanks in advance

  10. Shaun Says:

    Thanks for frequent reply
    can u post something about a snake game in vb.net conceptually like dis game.
    eager to know it, good work done by u
    hope u continue like dis … Thanks

    • pradeep1210 Says:

      sure I’ll post that game too, though I can’t commit you any time frame for that…
      stay subscribed… you’ll soon see good news 🙂

    • Thomas Baumann Says:

      You can have a look at my older project 🙂
      I have ported Nibbles (the old QBasic MS-DOS game) to VB.Net.

      Source code is as much as possible as the original (so it’s not really good VB.Net). Only a few changes had to made (PLAY isn’t supported anymore, the “Timer” was replaced because the computers today are “a bit” faster than that before). It’s still a console application 😉

      You can download it here (source and binaries):
      http://dotnetbase.de/topic/895-nibblesnet

      It’s a german forum, but no registration is needed for the download. You may want to use Google Translator (or such) if you want to read the post content.

  11. Sayuko Fujiwara Says:

    really a great stuff . i think this can help me for my project :3

  12. Han Says:

    This sourcecode is very helpful for my project! i’m really confusing!:d

  13. Building a Tetris Game using VB.NET (part 1) | Pradeep1210's Blog Says:

    […] part 2, we will see how we make use of these 3 classes in our […]

  14. Vicki Says:

    Hi, great explanation, but I’m still a bit confused, I’m trying to learn how you made your classes in more detail than what you explained in part 1 (part 2 started off with the code, so I got a bit confused as to how you built the classes)

    • pradeep1210 Says:

      What part of the program are you having trouble understanding?

      • Vicki Says:

        How did you build your classes? could you go into more details as to how you coded your classes?

      • pradeep1210 Says:

        All the code is self-explanatory. If you have not yet downloaded the source-code, please download it and have a look. The link to source-code is in part 1 of the article.
        If you are talking about the strategy of how to come up with the class members, that’s easy. In any OOP language, the objects mimic real-world entities. So to design a class, visualize it as a real object; note down what characteristics (properties) it has, what actions (methods) it can perform, and what events it can raise for others to inform about something. Once you have a list of these three things, you already have a starting point for your class design, and you can build further on that.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: