VERSION 5.00
Begin VB.Form FrmMain
BorderStyle = 1 'Fixed Single
Caption = "俄罗斯方块"
ClientHeight = 6255
ClientLeft = 150
ClientTop = 840
ClientWidth = 5190
HasDC = 0 'False
Icon = "FrmMain.frx":0000
KeyPreview = -1 'True
LinkTopic = "Form1"
LockControls = -1 'True
MaxButton = 0 'False
ScaleHeight = 6255
ScaleWidth = 5190
StartUpPosition = 3 '窗口缺省
Begin VB.Timer TmrGame
Enabled = 0 'False
Interval = 1000
Left = 2010
Top = 2880
End
Begin VB.CommandButton CmdRun
Caption = "开始"
Default = -1 'True
Height = 540
Left = 3630
TabIndex = 9
Top = 5250
Width = 1200
End
Begin VB.Frame FraValue
Caption = "得分"
Height = 795
Left = 3330
TabIndex = 7
Top = 4020
Width = 1800
Begin VB.TextBox TxtValue
Alignment = 1 'Right Justify
BackColor = &H8000000F&
BeginProperty Font
Name = "Fixedsys"
Size = 12
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 360
Left = 150
Locked = -1 'True
TabIndex = 8
Text = "0"
Top = 300
Width = 1500
End
End
Begin VB.Frame FraSpeed
Caption = "当前速度"
Height = 795
Left = 3330
TabIndex = 5
Top = 3060
Width = 1800
Begin VB.TextBox TxtSpeed
Alignment = 1 'Right Justify
BackColor = &H8000000F&
BeginProperty Font
Name = "Fixedsys"
Size = 12
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 360
Left = 150
Locked = -1 'True
TabIndex = 6
Text = "0"
Top = 300
Width = 1500
End
End
Begin VB.Frame FraMax
Caption = "最高分"
Height = 795
Left = 3300
TabIndex = 3
Top = 2100
Width = 1800
Begin VB.TextBox TxtMax
Alignment = 1 'Right Justify
BackColor = &H8000000F&
BeginProperty Font
Name = "Fixedsys"
Size = 12
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 360
Left = 150
Locked = -1 'True
TabIndex = 4
Text = "0"
Top = 300
Width = 1500
End
End
Begin VB.Frame FraNext
Caption = "下一个"
Height = 1800
Left = 3300
TabIndex = 1
Top = 150
Width = 1800
Begin VB.PictureBox PicNext
AutoRedraw = -1 'True
BackColor = &H00FFFFFF&
Height = 1260
Left = 240
ScaleHeight = 1200
ScaleWidth = 1200
TabIndex = 2
Top = 300
Width = 1260
End
End
Begin VB.PictureBox PicGame
AutoRedraw = -1 'True
BackColor = &H00FFFFFF&
Height = 6060
Left = 120
ScaleHeight = 6000
ScaleWidth = 3000
TabIndex = 0
Top = 120
Width = 3060
End
Begin VB.Menu mnuGame
Caption = "游戏(&G)"
Begin VB.Menu mnuOption
Caption = "选项(&O)..."
End
Begin VB.Menu mnuAbout
Caption = "关于(&A)..."
End
Begin VB.Menu mnuSep0_0
Caption = "-"
End
Begin VB.Menu mnuExit
Caption = "退出(&X)"
End
End
End
Attribute VB_Name = "FrmMain"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Private Declare Sub InitCommonControls Lib "comctl32.dll" ()
'== 图格信息
Private Const m_Line As Long = 20 '行数
Private Const m_Col As Long = 10 '列数
'游戏网格
Private m_Grid(0 To m_Col - 1, 0 To m_Line - 1) As Byte
Private m_BoxWidth As Long '格子的宽度
Private m_BoxHeight As Long '格子的高度
'== 游戏状态
Private m_Playing As Boolean '是否正在运行游戏
Private m_Speed As Long '游戏速度
Private m_Value As Long '当前分数
Private m_Max As Long '最高分数
Private m_ClipTop As Boolean '用于pvHitTest,表示是否超过上边缘
'当前方块的信息
Private m_CurIndex As Long '方块类型
Private m_CurStatus As Long '方块旋转状态
Private m_CurColor As Long '颜色(QBColor索引)
Private m_CurX As Long, m_CurY As Long '当前位置。单位:图格
'下一个方块的信息
Private m_NextIndex As Long '方块类型
Private m_NextStatus As Long '方块旋转状态
Private m_NextColor As Long '颜色(QBColor索引)
'== 设置信息
Public FastDown As Boolean '快速下降。False:按一次“下”只下降一行;True:按一次“下”直接落到底
Public RotMode As Boolean '旋转模式。为假表示顺时针,为真表示逆时针
Public ShowNext As Boolean '是否显示下一个方块
'键盘定义(按键的KeyDown编码)
Public KeyLeft As Integer '左移
Public KeyRight As Integer '右移
Public KeyRot As Integer '旋转
Public KeyDown As Integer '落下
'计算得分
Private Function pvValueFormLine(ByVal nLine As Long) As Long
Debug.Assert nLine >= 0 And nLine <= m_Line
'-- 得分计算方法
'计算过程:
' 100 + 200
' 300 + 400
' 700 + 800
'1500 +1600
'......
'正好是(2^n-1)*100的形式
pvValueFormLine = (2 ^ nLine - 1) * 100
End Function
'绘制单个格子
'oOut:目的图片框
'nIndex:颜色编号。0表示没有,色彩为QBColor(nIndex)
Private Sub pvDrawBox(ByRef oOut As PictureBox, _
ByVal nIndex As Long, _
ByVal X As Single, ByVal Y As Long, _
ByVal Width As Single, ByVal Height As Single)
Dim PixelX As Single, PixelY As Single '1像素所占空间
'利用断言检查参数
Debug.Assert Not (oOut Is Nothing) '对象不能为空
Debug.Assert oOut.ScaleMode <> vbUser '不能是自定义坐标系
Debug.Assert nIndex >= 0 And nIndex <= 15 '索引必须在规定的范围内
Debug.Assert Width > 0 And Height > 0 '大小判断
With oOut
'计算1像素所占空间
PixelX = .ScaleX(1, vbPixels, .ScaleMode)
PixelY = .ScaleY(1, vbPixels, .ScaleMode)
If nIndex = 0 Then
'绘制白色背景
oOut.Line (X, Y)-Step(Width, Height), vbWhite, BF
'绘制边线
oOut.Line (X + PixelX, Y + PixelY)-Step(Width - PixelX * 2, Height - PixelY * 2), QBColor(nIndex), B
Else
'绘制白色边线
oOut.Line (X, Y)-Step(Width, Height), vbWhite, B
'绘制填充区域
oOut.Line (X + PixelX, Y + PixelY)-Step(Width - PixelX * 2, Height - PixelY * 2), QBColor(nIndex), BF
'绘制白色内边线
oOut.Line (X + PixelX * 2, Y + PixelY * 2)-Step(Width - PixelX * 4, Height - PixelY * 4), vbWhite, B
End If
End With
End Sub
'绘制游戏画面
Private Sub pvPaint(ByVal hDC As Long)
Dim I As Long
Dim J As Long
Dim X As Single
Dim Y As Single
Y = 0
For I = 0 To m_Line - 1
X = 0
For J = 0 To m_Col - 1
'绘制格子
Call pvDrawBox(PicGame, m_Grid(J, I), X, Y, m_BoxWidth, m_BoxHeight)
'下一个格子
X = X + m_BoxWidth
Next J
'下一行格子
Y = Y + m_BoxHeight
Next I
End Sub
'刷新游戏画面
Private Sub pvRefresh()
With PicGame
If .AutoRedraw Or .HasDC Then
Call pvPaint(.hDC)
End If
If .AutoRedraw Or .HasDC = False Then
Call .Refresh
End If
End With
End Sub
'更新PicNext的图像
Private Sub pvRefreshNext()
Dim I As Long, J As Long
Dim X As Single, Y As Single
Dim Idx As Long
Debug.Assert m_NextIndex >= -1 And m_NextIndex < BlockCount
Debug.Assert m_NextStatus >= 0 And m_NextStatus < RotateStatusCount
Debug.Assert m_NextColor >= 0 And m_NextColor <= 15
Debug.Assert PicNext.AutoRedraw '自动重画必须为真
If ShowNext And m_NextIndex >= 0 Then '有下一个项目
With Blocks(m_NextStatus, m_NextIndex)
Y = 0
For I = 0 To BlockSize - 1
X = 0
For J = 0 To BlockSize - 1
'计算颜色
If .Box(J, I) Then
Idx = m_NextColor
Else
Idx = 0
End If
'绘制格子
Call pvDrawBox(PicNext, Idx, X, Y, m_BoxWidth, m_BoxHeight)
'下一个格子
X = X + m_BoxWidth
Next J
'下一行格子
Y = Y + m_BoxHeight
Next I
End With
Else '没有下一个项目
Idx = 0
Y = 0
For I = 0 To BlockSize - 1
X = 0
For J = 0 To BlockSize - 1
'绘制格子
Call pvDrawBox(PicNext, Idx, X, Y, m_BoxWidth, m_BoxHeight)
'下一个格子
X = X + m_BoxWidth
Next J
'下一行格子
Y = Y + m_BoxHeight
Next I
End If
End Sub
'更新状态显示
Private Sub pvUpdataStatus()
TxtValue.Text = CStr(m_Value)
TxtMax.Text = CStr(m_Max)
If m_Playing Then
If TmrGame.Enabled Then
CmdRun.Caption = "暂停"
Else
CmdRun.Caption = "继续"
End If
Else
CmdRun.Caption = "开始"
End If
End Sub
'生成下一个方块(只是设置数据)
Private Sub pvCreateNextBlock()
m_NextIndex = Int(Rnd() * BlockCount)
m_NextStatus = Int(Rnd() * RotateStatusCount)
m_NextColor = Int(Rnd() * 7) + 1 '在1~7的范围内
End Sub
'更新当前方块
Private Sub pvUpdataCurBlock()
'类型信息
m_CurIndex = m_NextIndex
m_CurStatus = m_NextStatus
m_CurColor = m_NextColor
m_CurX = (m_Col - BlockSize) / 2 '居中
m_CurY = 1 - BlockSize
'生成下一个方块
Call pvCreateNextBlock
Call pvRefreshNext
End Sub
'将方块加入网格
Private Sub pvFillBlock(ByVal nColor As Long)
Dim I As Long, J As Long
Dim X As Long, Y As Long
Debug.Assert m_CurIndex >= 0 And m_CurIndex < BlockCount
Debug.Assert m_CurStatus >= 0 And m_CurStatus < RotateStatusCount
Debug.Assert nColor >= 0 And nColor <= 15 '索引必须在规定的范围内
With Blocks(m_CurStatus, m_CurIndex)
Y = m_CurY
For I = 0 To BlockSize - 1 'Y循环
If Y >= 0 And Y < m_Line Then 'Y在范围内
X = m_CurX
For J = 0 To BlockSize - 1 'X循环
If X >= 0 And X < m_Col Then 'X在范围内
If .Box(J, I) Then
'设置
m_Grid(X, Y) = nColor
End If
End If
X = X + 1
Next J
End If
Y = Y + 1
Next I
End With
End Sub
'测试是否能放置
Public Function pvHitTest(ByVal X0 As Long, ByVal Y0 As Long, ByVal Status As Long) As Boolean
Dim I As Long, J As Long
Dim X As Long, Y As Long
Debug.Assert m_CurIndex >= 0 And m_CurIndex < BlockCount
Debug.Assert Status >= 0 And Status < RotateStatusCount
m_ClipTop = False
With Blocks(Status, m_CurIndex)
Y = Y0
For I = 0 To BlockSize - 1 'Y循环
X = X0
For J = 0 To BlockSize - 1 'X循环
If .Box(J, I) Then
'判断范围
If Y < m_Line And X >= 0 And X < m_Col Then '下、左、右边界判断
If Y < 0 Then '超过上边缘
m_ClipTop = True
Else
If m_Grid(X, Y) Then '已占据
pvHitTest = False
Exit Function
End If
End If
Else '在范围外
pvHitTest = False
Exit Function
End If
End If
X = X + 1
Next J
Y = Y + 1
Next I
End With
pvHitTest = True
End Function
'开始游戏
Private Sub pvStartGame()
Dim I As Long, J As Long
Debug.Assert m_Playing = False
'清空网格
For I = 0 To m_Line - 1
For J = 0 To m_Col - 1
m_Grid(J, I) = 0
Next J
Next I
'计算当前方块
Call pvCreateNextBlock
Call pvUpdataCurBlock
Call pvFillBlock(m_CurColor) '将方块加入网格
'开始游戏
m_Playing = True
Speed = 1
m_Value = 0
TmrGame.Enabled = m_Playing
Call pvUpdataStatus
'更新游戏画面
Call pvRefresh
End Sub
'结束游戏
Private Sub pvEndGame()
'结束游戏
m_Playing = False
Speed = 1
m_Value = 0
TmrGame.Enabled = m_Playing
Call pvUpdataStatus
'更新“下一个”
m_NextIndex = -1
Call pvRefreshNext
End Sub
'尝试消行
Private Sub pvFindLine()
Dim I As Long, J As Long
Dim bDel(0 To m_Line - 1) As Boolean
Dim Count As Long
Dim Idx As Long
'得到消行的个数
Count = 0
For I = 0 To m_Line - 1 '逐行
'判断满行
bDel(I) = True
For J = 0 To m_Col - 1 'X
If m_Grid(J, I) = 0 Then '存在空格
bDel(I) = False
Exit For
End If
Next J
If bDel(I) Then
Count = Count + 1
End If
Next I
If Count > 0 Then
'消行
For I = 0 To m_Line - 1 'y
If bDel(I) Then
For J = 0 To m_Col - 1 'X
m_Grid(J, I) = 0
Next J
End If
Next I
'更新分数
m_Value = m_Value + pvValueFormLine(Count)
If m_Value > m_Max Then m_Max = m_Value
Me.Speed = m_Value / 2000 + 1 '得分每增加2000分,程序自动将速度调高一档
Call pvUpdataStatus
'更新游戏画面
Call pvRefresh
'下移
Idx = m_Line - 1
I = Idx
Do While I >= 0 '逐行
If bDel(I) Then
Else
'复制一行
If I <> Idx Then
For J = 0 To m_Col - 1 'X
m_Grid(J, Idx) = m_Grid(J, I)
Next J
End If
'指向下一行
Idx = Idx - 1
End If
I = I - 1
Loop
'清除多余的行
For I = 0 To Idx 'Y
For J = 0 To m_Col - 1 'X
m_Grid(J, I) = 0
Next J
Next I
End If
End Sub
'下移一格
'返回值:是否成功
Private Function pvDoMoveDown() As Boolean
'清除原方块
Call pvFillBlock(0)
'是否能够下移
If pvHitTest(m_CurX, m_CurY + 1, m_CurStatus) Then '能够下移
'更新位置
m_CurY = m_CurY + 1 '修改坐标
Call pvFillBlock(m_CurColor) '将方块加入网格
'更新游戏画面
Call pvRefresh
pvDoMoveDown = True
Else '不能够下移
'将方块加入网格
Call pvFillBlock(m_CurColor)
'判断是否堆满
If m_ClipTop Then
Call pvEndGame
'Call VBA.Beep '报警
MsgBox "GameOver!", vbExclamation Or vbOKOnly
Else
'消去方块
Call pvFindLine
'创建新方块
Call pvUpdataCurBlock
Call pvFillBlock(m_CurColor) '将方块加入网格
'更新游戏画面
Call pvRefresh
End If
pvDoMoveDown = False
End If
End Function
'水平移动
'返回值:是否成功
Private Function pvDoMoveH(ByVal StepX As Long) As Boolean
Dim Rc As Boolean
'清除原方块
Call pvFillBlock(0)
'是否能够移动
Rc = pvHitTest(m_CurX + StepX, m_CurY, m_CurStatus)
If Rc Then '能够移动
'更新位置
m_CurX = m_CurX + StepX '修改坐标
Call pvFillBlock(m_CurColor) '将方块加入网格
'更新游戏画面
Call pvRefresh
pvDoMoveH = True
Else '不能够移动
'将方块加入网格
Call pvFillBlock(m_CurColor)
Call VBA.Beep
pvDoMoveH = False
End If
End Function
'旋转
'返回值:是否成功
Private Function pvDoRotate() As Boolean
Dim Rc As Boolean
Dim nTemp As Long
'计算新的状态
If RotMode = False Then
nTemp = m_CurStatus + 1
Else
nTemp = m_CurStatus - 1
End If
nTemp = nTemp And 3
'清除原方块
Call pvFillBlock(0)
'是否能够旋转
Rc = pvHitTest(m_CurX, m_CurY, nTemp)
If Rc Then '能够旋转
'更新状态
m_CurStatus = nTemp '修改状态
Call pvFillBlock(m_CurColor) '将方块加入网格
'更新游戏画面
Call pvRefresh
pvDoRotate = True
Else '不能够旋转
'将方块加入网格
Call pvFillBlock(m_CurColor)
Call VBA.Beep
pvDoRotate = False
End If
End Function
Private Sub CmdRun_Click()
If m_Playing Then
'切换暂停状态
TmrGame.Enabled = Not TmrGame.Enabled
'更新状态显示
Call pvUpdataStatus
Else
Call pvStartGame
End If
End Sub
Private Sub Form_Initialize()
'初始化随机数
Call Randomize(Timer)
'初始化方块数据
Call InitBlock
'设置信息
FastDown = True
RotMode = False
ShowNext = True
'初始化按键
KeyLeft = vbKeyLeft
KeyRight = vbKeyRight
KeyRot = vbKeyUp
KeyDown = vbKeyDown
'初始化comctl32.dll,使应用程序支持WinXP界面风格
Call InitCommonControls
End Sub
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
If m_Playing Then
If TmrGame.Enabled Then
Select Case KeyCode
Case KeyLeft
Call pvDoMoveH(-1)
Case KeyRight
Call pvDoMoveH(1)
Case KeyRot
Call pvDoRotate
Case KeyDown
If FastDown Then
'直到不能落下为止
Do While pvDoMoveDown()
Loop
Else
Call pvDoMoveDown
End If
End Select
End If
End If
End Sub
Private Sub Form_Load()
'得到格子大小
With PicGame
m_BoxWidth = .ScaleWidth / m_Col
m_BoxHeight = .ScaleHeight / m_Line
End With
m_Playing = False
Speed = 1
m_NextIndex = -1 '没有下一个方块
'更新PicGame
Call pvRefresh
'更新PicNext
Call pvRefreshNext
'更新状态显示
Call pvUpdataStatus
End Sub
Private Sub mnuAbout_Click()
Dim TempStr As String
TempStr = TempStr & "产品:" & App.ProductName & vbCrLf
TempStr = TempStr & "版本:" & App.Major & "." & App.Minor & "." & App.Revision & vbCrLf
TempStr = TempStr & "作者:" & App.CompanyName & vbCrLf
TempStr = TempStr & "版权:" & App.LegalCopyright & vbCrLf
TempStr = TempStr & "说明:" & App.FileDescription & vbCrLf
MsgBox TempStr, vbInformation, "关于" & App.Title
End Sub
Private Sub mnuExit_Click()
Unload Me
End Sub
Private Sub mnuOption_Click()
Call FrmOption.DoModal(Me)
Call pvRefreshNext
End Sub
Private Sub PicGame_Paint()
Call pvPaint(PicGame.hDC)
End Sub
Private Sub TmrGame_Timer()
'若没有进行游戏
If m_Playing = False Then
TmrGame.Enabled = False
Exit Sub
End If
'下移一格
Call pvDoMoveDown
End Sub
'取得/设置 速度
Public Property Get Speed() As Long
Speed = m_Speed
End Property
Public Property Let Speed(ByVal RHS As Long)
Dim nItv As Long 'Timer控件的时间间隔
Debug.Assert RHS > 0
m_Speed = RHS
'计算间隔
nItv = 500 / m_Speed
If nItv < 1 Then nItv = 1
TmrGame.Interval = nItv
'更新速度文本框
TxtSpeed.Text = m_Speed
End Property
|