废话不多说,先放成果。在GitHub上查看源代码 。
开始编写
阅读此教程,你需要了解
什么是VBA,对VBA初步了解
如何在Excel中编辑VBA,并启用它
否则请另行百度。
最重要的两个函数
Range
和Cells
是整个游戏程序中的核心,它们都能返回一个 表示一个单元格、一行、一列、一个包含单个或若干连续单元格区域的选定单元格范围,或者一个三维区域。(摘自https://docs.microsoft.com/zh-cn/office/vba/api/excel.range(object))
简单来说就是能帮助我们获取到Excel中的每一个格子,以便我们操作他们的属性。
如以下代码:
Cells(2, 22) = "贪吃蛇撞墙过猛,游戏结束"
获取了y坐标为2,x坐标为22的单元格,并设置它的文本。(这里可能和我们平时的认知不太一样,Cells
函数是y在前x在后的)
初始化
游戏初始化过程的代码如下:
Option Explicit
'定义贪吃蛇坐标变量
Dim snackX(400) As Integer
Dim snackY(400) As Integer
'定义贪吃蛇坐标引索
Dim snackIndex As Integer
'定义贪吃蛇移动变量
Dim snackMoveX As Integer
Dim snackMoveY As Integer
'苹果坐标
Dim appleX As Integer
Dim appleY As Integer
'游戏是否运行
Dim isGameRunning As Integer
'导入win32API模块
#If VBA7 And Win64 Then
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
#Else
Private Declare Function GetTickCount Lib "kernel32" () As Long
#End If
'工具类函数
Private Sub Sleep(numa As Double)
Dim num1 As Double
Dim num2 As Double
Dim numb As Double
numb = 0
num1 = GetTickCount
Do While numa - numb > 0
num2 = GetTickCount
numb = num2 - num1
DoEvents
Loop
End Sub
其中,第一行规定全局的变量必须定义后才可以使用,当然取消后代码也能正确运行,但会存在许多潜在的漏洞。
之后,是贪吃蛇和苹果位置信息的定义,以便我们可以全局使用它。需要注意的是,贪吃蛇坐标变量
定义了两个长度为400的整形数组,这是经过了计算的,即贪吃蛇沾满格子的长度。
最后,我们导入了kernel32
模块。又利用其中的GetTickCount
定义了一个Sleep
函数,用于延时,以控制游戏帧数。
游戏基础——画布类
'画布类
Public Sub CanvasClean()
With Range("B2:S19").Interior
.Pattern = xlNone
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End Sub
Public Sub CanvasReLoad()
With Range("A1:T20").Interior
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorLight1
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End Sub
CanvasClean
函数,顾名思义,我们用其清理画布的区域。其中,使用Range("B2:S19")
获取了Excel中的格子区域(图中框选区域),并将它清空。
CanvasReLoad
中,我们将整个黑框区域恢复原样,及在CanvasClean
的基础上,将黑框也重新绘制。
为什么要这么做,给你看个图就知道了:
主角——贪吃蛇类
'贪吃蛇类
Private Sub TextBox1_Change()
Select Case TextBox1.Text
Case Is = "w"
If snackMoveY <> 1 Then
snackMoveY = -1
snackMoveX = 0
End If
Case Is = "s"
If snackMoveY <> -1 Then
snackMoveY = 1
snackMoveX = 0
End If
Case Is = "a"
If snackMoveX <> 1 Then
snackMoveX = -1
snackMoveY = 0
End If
Case Is = "d"
If snackMoveX <> -1 Then
snackMoveX = 1
snackMoveY = 0
End If
End Select
TextBox1.Text = ""
End Sub
Public Sub snackCreate()
snackIndex = 3
Dim i As Integer
Dim x As Integer
Dim y As Integer
x = Int(Rnd * 13) + 3
y = Int(Rnd * 13) + 3
For i = 0 To snackIndex
snackX(i) = x
snackY(i) = y + i
Next
End Sub
Public Sub snackMove()
Dim i As Integer
For i = snackIndex To 1 Step -1
snackX(i) = snackX(i - 1)
snackY(i) = snackY(i - 1)
Next
snackX(0) = snackX(0) + snackMoveX
snackY(0) = snackY(0) + snackMoveY
End Sub
Public Sub snackDraw()
Dim i As Integer
For i = 0 To snackIndex
Cells(snackY(i), snackX(i)).Interior.Color = 255
Next
End Sub
Public Sub snackHitWall()
If snackX(0) = 1 Or snackX(0) = 20 Or snackY(0) = 1 Or snackY(0) = 20 Then
Cells(2, 22) = "贪吃蛇撞墙过猛,游戏结束"
isGameRunning = 0
End If
End Sub
Public Sub snackEatApple()
If snackX(0) = appleX And snackY(0) = appleY Then
appleCreate
snackIndex = snackIndex + 1
Cells(4, 22) = Int(Cells(4, 22).Value) + 1
End If
End Sub
Public Sub snackHitHimself()
Dim i As Integer
For i = 1 To snackIndex
If snackX(0) = snackX(i) And snackY(0) = snackY(i) Then
Cells(2, 22) = "贪吃蛇把自己吃了,游戏结束"
isGameRunning = 0
End If
Next
End Sub
如果你熟悉VB,那你一定看出来了,TextBox1_Change
是TextBox1中的文本改变时自动调用的一个过程。我们在这里进行判断,已根据WASD的方向键调整贪吃蛇的移动量,最后再将其内容清空,以便下一次检测 。
其次,就是故名思意了:
- snackCreate——创建贪吃蛇
- snackMove——根据贪吃蛇的移动量移动贪吃蛇
- snackDraw——绘制贪吃蛇
- snackHitWall——检测贪吃蛇是否撞到墙
- snackEatApple——检测贪吃蛇是否吃到苹果
- snackHitHimself——检测贪吃蛇是否把自己吃了
食物——苹果类
'苹果类
Public Sub appleCreate()
appleX = Int(Rnd * 15) + 3
appleY = Int(Rnd * 15) + 3
End Sub
Public Sub appleDraw()
Cells(appleY, appleX).Interior.Color = 100
End Sub
与贪吃蛇基本相同,但简单许多:
- appleCreate——创建苹果
- appleDraw——绘制苹果
游戏中的Main函数——游戏类
'游戏类
Public Sub restGame()
snackMoveY = 0
snackMoveX = 1
Cells(2, 22) = "游戏中"
Cells(4, 22) = 0
End Sub
Public Sub beginGame()
isGameRunning = 1
restGame
CanvasReLoad
snackCreate
appleCreate
Do While isGameRunning = 1
CanvasClean
appleDraw
snackMove
snackDraw
snackHitWall
snackEatApple
snackHitHimself
Sleep (300)
Loop
End Sub
Public Sub stopGame()
isGameRunning = 0
Cells(2, 22) = "游戏结束"
End Sub
restGame
重置游戏,stopGame
停止游戏。
beginGame
函数的逻辑为。
start=>start: 开始游戏
is_gameRunning_true=>operation: 将游戏状态设置为Ture
restGame=>operation: 重置游戏
CanvasReLoad=>operation: 重置画布
snackCreate=>operation: 创建贪食蛇
appleCreate=>operation: 创建苹果
is_gameRunning=>condition: 游戏状态是否为True
CanvasClean=>operation: 清空画布
appleDraw=>operation: 绘制苹果
snackMove=>operation: 移动贪吃蛇
snackDraw=>operation: 绘制贪吃蛇
snackHitWall=>operation: 检测贪吃蛇是否撞到墙
snackEatApple=>operation: 检测贪吃蛇是否吃到苹果
snackHitHimself=>operation: 检测贪吃蛇是否把自己吃了
sleep=>operation: 延时300秒
end=>end: 游戏结束
start->is_gameRunning_true->restGame->CanvasReLoad->snackCreate->appleCreate->is_gameRunning
is_gameRunning(yes)->CanvasClean->appleDraw->snackMove->snackDraw->snackHitWall->snackEatApple->snackHitHimself->sleep(left)->is_gameRunning
is_gameRunning(no)->end