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






4.71/5 (7投票s)
2004年1月9日
4分钟阅读

90236

423
使用 GDI+ 构建的基于图块的游戏。
这是“时间杀手”,一款我最初在 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
相关联。它将消息发送到一个名为 moveTiles
的 Sub
,并附带当前选定的图块。所以,我想我们需要一个 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
好了,就是这样。你应该有一个完全可以运行的图块游戏了。