Click here to Skip to main content
13,513,296 members
Click here to Skip to main content
Add your own
alternative version


28 bookmarked
Posted 8 Jan 2004

Build a tile based game

, 8 Jan 2004
Rate this:
Please Sign up or sign in to vote.
Tile based game with GDI+.

Sample Image - tile_picture.png

This is "The Time Waster", a game I originally saw on the Mac that up till now has been missing a .NET port of the game. I believe this is the only game Mac's were designed to play, so really a PC should have no problem with cycles to spare :-)

Step 1

Modifying your form's startup

First thing is, we need to change the way your form starts so we can control the loading procedure. This is fairly simple to accomplish. What you need to do is create a Shared Sub Main in your main program and set that as the startup. Don't forget to name your form frmSlider.

Shared Sub Main() 
    Application.Run(New frmSlider()) 
End Sub

This will start up the form named frmClock. Then when it's done, exit the application. Now we need to modify the new procedure for the form and insert a command and an initGame command.

Public Sub New()
End Sub

All that's left to do now is change the project's startup properties to make it use our Shared sub Main and not the standard startup procedure. Go to Solution Explorer and right click on your project's properties and change the Startup Object.

Step 2

Creating the class

Public Class cPieces
    ' Cell this number is in
    Public position As Integer = 0
    ' What number is this
    Public number As Integer
    ' Image for the tile
    Private image As New Bitmap(32, 32, Imaging.PixelFormat.Format32bppRgb)

    ' The X and Y of where its located
    Private chords As New Point

    ' This sub takes a cell location (0-24) and determins the chords
    ' and sets the position
    Public Sub setLocation(ByVal loc As Integer)
        Dim row = Int(loc / 5)
        Dim col = loc - (row * 5)
        row = row * 32
        col = col * 32

        chords = New Point(col, row)
        position = loc
    End Sub

    ' This creates the tile and sets its number
    Public Sub New(ByVal thisnumber As Integer)
        Dim g As Graphics = Graphics.FromImage(image)
        Dim f As New Font("Impact", 15, FontStyle.Regular)
        Dim sze As SizeF = g.MeasureString(thisnumber, f)

        g.FillRectangle(Brushes.White, 0, 0, image.Width, image.Height)
        g.DrawRectangle(Pens.Black, 0, 0, image.Width, image.Height)

        g.DrawString(thisnumber, f, Brushes.Black, _
                     (image.Width - sze.Width) / 2, _
                     (image.Height - sze.Height) / 2)

        number = thisnumber
    End Sub

    ' This draws the tile
    Public Sub draw(ByVal g As Graphics)
        If Not number = 24 Then
            g.DrawImage(image, chords)
        End If
    End Sub
End Class

There really shouldn't be anything you haven't seen in here before, it's a standard class that stores the information for each tile and creates a bitmap.

SetLocationThis Sub takes a cell location (0-24) and determines the chords and sets the position
NewThis creates the tile and sets its number
drawThis draws the tile

Step 3


Go to your form, add a PictureBox control. Set the PictureBox to the name game with a background color of black and set it as docked with the entire form.

Step 4

Part of the code

You always have to start out initializing your variables. See the var pieces(24), that's an array of 24 items just like the game board has 24 tiles. A habit I got into a while back always has me set a debug flag. This way in your code you can say if debug then console.writeline("x = 56"), then when you are ready to distribute your code you can just turn the var to False and poof...all the debug stuff goes away.

Public debug = True
Public pieces(24) As cPieces
Public moves As Integer = 0
Public start As Date

Next the initialization sub.

' Initalize pieces and draw the board
Private Sub initgame()
    Dim counter As Integer
    Dim nextRnd As Integer
    Dim rndPos(24) As Boolean
    Dim randomNum As New Random

    Me.ClientSize = New Size(32 * 5, 32 * 5)

    moves = 0

    start = Now

    For counter = 0 To 24
        rndPos(counter) = False

    For counter = 0 To 24
            nextRnd = randomNum.Next(0, 25)
        Loop Until rndPos(nextRnd) = False

        pieces(counter) = New cPieces(counter)
        rndPos(nextRnd) = True

End Sub

Pretty straight forward, it initializes your values as well as determines random locations for the tiles. This part:

      nextRnd = randomNum.Next(0, 25)
Loop Until rndPos(nextRnd) = False

Generates a random number then checks to see that it hasn't used that number before. Very handy. Then the final command is game.invalidate(). This tells the Paint event to fire and redraw the box.

Now the game_paint Sub.

' Redraw the cells
Private Sub game_Paint(ByVal sender As Object, _
       ByVal e As System.Windows.Forms.PaintEventArgs) Handles game.Paint
    Dim counter As Integer

    For counter = 0 To 24
End Sub

Again very straight forward, asks each tile to redraw itself. Why not run your game now and marvel at the output?

Step 5

More interesting game

Your game is not very interesting now, try as you might clicking on tiles seems to do nothing! Well, perhaps we should take care of that.

' Converts mouse clicks into tile cells
Private Sub game_MouseUp(ByVal sender As Object, ByVal e As _
              System.Windows.Forms.MouseEventArgs) Handles game.MouseUp

    Dim posX As Integer = Int((e.X * 1.06) / 32)
    Dim posy As Integer = Int((e.Y * 1.06) / 32)

    If debug Then
        Console.WriteLine("Mouse: " & posX & ", " & posy)
    End If

    moveTiles(posX, posy)
End Sub

This is a standard mouse event tied to the game PictureBox. It sends its messages to a Sub called moveTiles with the current tile selected. So, I guess we need a movetiles Sub eh?

' Can we move one or more tiles, lets see
Sub moveTiles(ByVal thisCellX As Integer, ByVal thisCelly As Integer)
    Dim counter As Integer
    Dim thisCell = (thisCelly * 5) + thisCellX
    Dim blankCellY = Int(pieces(24).position / 5)
    Dim blankCellX = pieces(24).position - (blankCellY * 5)
    Dim blankCell = pieces(24).position

    If debug Then
        Console.WriteLine("Blank Cell X: " & blankCellX & ", Y: " & _
        Console.WriteLine(" This Cell X: " & thisCellX & ", Y: " & _
    End If

    If blankCellY = thisCelly Then ' Move horizontally

        moves += 1

        If blankCell > thisCell Then         ' Move to right
            For counter = blankCell - 1 To thisCell Step -1
                swaptiles(counter, counter + 1)
        End If

        If blankCell < thisCell Then       ' Move to Left
            For counter = blankCell + 1 To thisCell
                swaptiles(counter, counter - 1)
        End If

    ElseIf blankCellX = thisCellX Then        ' Move Vertically

        moves += 1

        If blankCell > thisCell Then            ' Move Down
            For counter = blankCell To thisCell + 5 Step -5
                swaptiles(counter, counter - 5)
        End If

        If blankCell < thisCell Then             ' Move Up
            For counter = blankCell To thisCell - 5 Step 5
                swaptiles(counter, counter + 5)
        End If

    End If

    Dim Winner As Boolean = True
    For counter = 1 To 24
        If pieces(counter).position <> counter Then Winner = False

    If Winner Then
        MsgBox("You have won in " & moves & " moves and wasted " & _
               DateDiff(DateInterval.Minute, start, Now) & _
               " minutes, don't you feel productive.", &
               MsgBoxStyle.Exclamation, "Time Waster")
    End If
End Sub

I'm not going to explain this, it's just basic math. If the cell and the blank are in the same row then yadda yadda yadda. However, at the end of the Sub, I have a winners message that's triggered only if all the cells are in alignment. But wait! There is a mention of swaptiles here, so we need to address that as well.

' This function takes a cell position from the board and figures out
' what tile occupies that space
Private Function findtile(ByVal findme As Integer)
    Dim Counter As Integer

    Dim tiles(24)

    ' Create an array of tiles but based on position not value
    For Counter = 0 To 24
        tiles(pieces(Counter).position) = Counter

    Return tiles(findme)
End Function

' Swaps the cells on the board
Private Sub swaptiles(ByVal source As Integer, _
                      ByVal destination As Integer)

    ' Figures out what tile is at the cell
    Dim sTileNumber = findtile(source)
    Dim dTileNumber = findtile(destination)

    ' Changes them

    If debug Then Console.WriteLine("Swapping: Number: " & _
        findtile(source) & " (Position: " & source & ") and Number: " & _
        findtile(destination) & " (Position: " & destination & ")")

End Sub

Ok, I lied, you need two more Subs (but they are used together). swaptiles sends information on the cell you are clicking on but it doesn't know what occupies that cell. For instance, say you clicked on cell 11 and wanted to move it to cell 10, you can't merely say cell10=cell11. You need to know what those cells represent so you can change the values accordingly. This is where findcell comes in. findcell takes all the cells on the board and creates an array with the cells ordered as they appear on the playing board, then returns the number of the cell in the position you are clicking. (Phew...that was a mouthful.)

Step 6

Creating a better interface

Go to your form and drag out that good ol' menubar control and create three menu entries. Call the root menu "Game Controls" with "New" and "Exit" as its menu choices. Double click on "New" and set it to regenerate the board. Double click on "Exit" and tell it me.close.

' Menu Commands
Private Sub MenuItem2_Click(ByVal sender As System.Object, _
                    ByVal e As System.EventArgs) Handles MenuItem2.Click
End Sub

Private Sub MenuItem3_Click(ByVal sender As System.Object, _
                    ByVal e As System.EventArgs) Handles MenuItem3.Click
End Sub

Well, that's it. You should have a fully working tile game.


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Matthew Hazlett
Web Developer
United States United States
I started programming for fun when I was about 10 on an Franklin Ace 1000.

I still do it just for fun but it has gotten me a few jobs over the years. More then I can say for my Microsoft Certifications. Smile | :)

The way I learned was by example, now its time to give back to the next generation of coders.

You may also be interested in...


Comments and Discussions

Generalthere is a fatal bug Pin
CoDeR XxX27-Jun-06 22:18
memberCoDeR XxX27-Jun-06 22:18 
GeneralRe: there is a fatal bug Pin
CoDeR XxX6-Jul-06 3:39
memberCoDeR XxX6-Jul-06 3:39 
Generaluse double buffering Pin
Tweety14-Jan-04 18:00
memberTweety14-Jan-04 18:00 
QuestionSo what is the goal of the gaim? Pin
Uwe Keim10-Jan-04 17:54
sitebuilderUwe Keim10-Jan-04 17:54 
AnswerRe: So what is the goal of the gaim? Pin
hazlema10-Jan-04 17:57
memberhazlema10-Jan-04 17:57 
QuestionCan you wrap the code please Pin
Colin Angus Mackay10-Jan-04 4:35
memberColin Angus Mackay10-Jan-04 4:35 
AnswerRe: Can you wrap the code please Pin
hazlema10-Jan-04 6:03
memberhazlema10-Jan-04 6:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.180417.1 | Last Updated 9 Jan 2004
Article Copyright 2004 by Matthew Hazlett
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid