65.9K
CodeProject 正在变化。 阅读更多。
Home

构建一个基于瓦片的瓷砖游戏

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (7投票s)

2004年1月9日

4分钟阅读

viewsIcon

90236

downloadIcon

423

使用 GDI+ 构建的基于图块的游戏。

Sample Image - tile_picture.png

这是“时间杀手”,一款我最初在 Mac 上看到的游戏,但到目前为止还没有 .NET 版本。我认为这是 Mac 唯一设计来玩的游戏,所以 PC 应该有绰绰有余的计算能力 :-)

步骤 1

修改窗体的启动方式

首先,我们需要更改窗体的启动方式,以便能够控制加载过程。这很容易实现。你需要做的就是在主程序中创建一个 Shared Sub Main 并将其设为启动项。别忘了将你的窗体命名为 frmSlider

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

这将启动名为 frmClock 的窗体。然后,完成时,退出应用程序。现在我们需要修改窗体的 new 过程,并插入一个 me.show 命令和一个 initGame 命令。

Public Sub New()
    MyBase.New()
    InitializeComponent()
    initGame()
End Sub

现在唯一要做的就是更改项目的启动属性,使其使用我们的 Shared sub Main 而不是标准的启动过程。转到解决方案资源管理器,右键单击项目的属性,然后更改启动对象。

第二步

创建类

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
        f.Dispose()
        g.Dispose()
    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

这里应该没有什么你没见过的,它是一个标准的类,用于存储每个图块的信息并创建位图。

SetLocation 这个 Sub 接受一个单元格位置(0-24),然后确定坐标并设置位置。
新建 这会创建图块并设置其编号。
绘制。 这会绘制图块。

步骤 3

控件

转到你的窗体,添加一个 PictureBox 控件。将 PictureBox 设置为名为 game,背景色设置为黑色,并将其设置为停靠整个窗体。

步骤 4

部分代码

你总是需要先初始化你的变量。看到 var pieces(24),这是一个包含 24 个元素的数组,就像游戏板有 24 个图块一样。我很久以前养成的一个习惯是总是设置一个 debug 标志。这样,在你的代码中,你可以说 if debug then console.writeline("x = 56"),然后在准备分发代码时,你可以将 var 设置为 False,然后“嘭”——所有 debug 内容就消失了。

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

接下来是初始化 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
        Next

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

            pieces(counter) = New cPieces(counter)
            pieces(counter).setLocation(nextRnd)
            rndPos(nextRnd) = True
        Next

        game.Invalidate()
    End Sub

非常直接,它会初始化你的值并确定图块的随机位置。这部分

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

生成一个随机数,然后检查它是否之前没有使用过这个数字。非常有用。然后最后一个命令是 game.invalidate()。这会告诉 Paint 事件触发并重绘框。

现在是 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
            pieces(counter).draw(e.Graphics)
        Next
    End Sub

同样非常直接,要求每个图块重绘自身。为什么不现在就运行你的游戏,惊叹于输出呢?

步骤 5

更有趣的游戏

你的游戏现在一点也不有趣,你尽量点击图块似乎什么都没发生!好吧,也许我们应该处理一下。

    ' 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

这是一个标准的鼠标事件,与游戏 PictureBox 相关联。它将消息发送到一个名为 moveTilesSub,并附带当前选定的图块。所以,我想我们需要一个 movetiles Sub,对吧?

    ' 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("")
            Console.WriteLine("Blank Cell X: " & blankCellX & ", Y: " & _
                               blankCellY)
            Console.WriteLine(" This Cell X: " & thisCellX & ", Y: " & _
                                thisCelly)
        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)
                Next
            End If

            If blankCell < thisCell Then       ' Move to Left
                For counter = blankCell + 1 To thisCell
                    swaptiles(counter, counter - 1)
                Next
            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)
                Next
            End If

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

        End If

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

        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

我不会解释这个,这只是基本的数学。如果单元格和空白单元格在同一行,那么等等等等。但是,在 Sub 的末尾,我有一个获胜消息,只有当所有单元格都对齐时才会触发。但是等等!这里提到了 swaptiles,所以我们也需要处理一下。

    ' 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
        Next

        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
        '
        pieces(sTileNumber).setLocation(destination)
        pieces(dTileNumber).setLocation(source)

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

        game.Invalidate()
    End Sub

好吧,我撒谎了,你需要另外两个 Sub(但它们是组合使用的)。swaptiles 发送有关你正在点击的单元格的信息,但它不知道那个单元格里有什么。例如,假设你点击了单元格 11 并想将其移动到单元格 10,你不能仅仅说 cell10=cell11。你需要知道这些单元格代表什么,以便相应地更改值。这时 findcell 就派上用场了。findcell 遍历棋盘上的所有单元格,并创建一个数组,其中单元格按照它们在游戏板上的出现顺序排列,然后返回你点击位置的单元格编号。(呼——这真是一大串话。)

步骤 6

创建更好的界面

转到你的窗体,拖出一个老套的菜单控件,创建三个菜单项。将根菜单命名为“游戏控制”,其菜单选项为“新游戏”和“退出”。双击“新游戏”,将其设置为重新生成棋盘。双击“退出”,告诉它 me.close

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


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

好了,就是这样。你应该有一个完全可以运行的图块游戏了。

© . All rights reserved.