小游戏编程起步
软件世界
编者按:对广大初学者而言,学习编程往往从一门语言开始,而练习编程实例又往往是排序、查找等枯燥甚至艰深的数学问题,这往往导致很多初学者最终放弃学习。有什么办法能改变这一状况呢?编者认为,寓教于乐是最好的方法。所以,我们在暑期之际,策划了这个玩转小游戏编程系列选题,希望能对广大编程初学者有大的帮助。
玩惯了帝国、星际,在火光中拼杀过后,打开俄罗斯方块之类的经典小游戏,是不是有一种亲切的感觉呢?那么,就让我们探究它们的内部,打造自己钟爱的小游戏吧……
先回忆一下玩游戏的时候自己和机器都做了些什么。和大多数应用程序一样,无非是这样的循环:下达指令→机器处理→输出→判断→再下达指令。
其中机器处理和输出这两步涉及计算机编程,下达指令是输入的过程,因此计算机必须正确地响应,这就是要选择正确的控件事件。要考虑到如下几个因素:是否符合事件本身的定义,是否方便用户操作,是否便于程序处理。机器接收到数据后,传递给相应的函数和过程进行处理,这个环节是编程水平的主要体现。在这里AI(人工智能)的水平就显得很重要。因此,一定的数学知识是必需的。输出是游戏编程的另一重头戏,在小规模的游戏中一般指显示输出和音效输出。显示输出是主要的输出,关键是动画技术,一定的美工水平更可以为游戏增添光彩;音效输出包括背景音乐、事件触发音乐等,这些技术在以后会专门讨论。
掌握具体技术是一方面,对游戏的整体构思和全局把握也是很重要的。这些都需要多观察分析来不断积累经验。
今天我们介绍的游戏是“数字拼图”,目的是通过键盘上的方向键移动方块,方块只能横向或直行移动进入唯一的一个空位,最终把方块排列成以下形状即可:((图1))

在窗体上设置一组标签控件数组Label1充当方块,做成大小相同的正方形,Label(1)-(9)的Caption属性就是其索引号,BoderStyle属性设为1;Label1(0)用来占一个空格位,Caption为空,BoderStyle为0。一个标签Label2显示已走步数,两个命令按钮Command1和Command2,还有一个文本框Text1接收按键信息。为美观起见,把Text1拖到命令按钮后面藏起来。布置好的控件如(图2)所示。

编程的第一个问题是如何创造一个各个项都不同的随机数列用以确定各方块的位置。如果直接用RND函数产生,然后检验是否有重复,有则重新产生,直到没有重复为止,这样效率是非常低的。本例中,采用这样的方法:先定义一个数组Square代表各方块,Square的序号对应Label1控件数组的序号,对应的Square的值按照上表决定Label的位置。然后组建一个顺序数列(0至8,存储在数组变量SelectedPos中),再从中随机抽取,已经被取走位置号的SelectedPos变量赋以标记值-1,这样在下一个Square变量抽取时予值是否为-1判断可否取该位置值。比如Square(1)的抽取到的位置值是5,那么Label1(1)就会被放在上表的5号格。
第二个问题是方块移动时的位置问题。这里巧妙地利用了Label1(0)这个看不见的标签作为“空位”。游戏者在按动方向键的时候,假定按动了“↑”键,就说明要求方块向上移入“空位”,这时寻找是否有方块在“空位”下方,即该方块的Left属性与占位标签Label1(0)相同,Top属性等于Label1(0)的“Top+Height”。如果“空位”在最下方,按键无效。其他键以此类推。
Option Explicit
Dim I, Temp, TempLeft, TempTop, Counter As Long
Dim Square(9), SelectedPos(9) As Long
Dim Finished As Boolean
Private Sub Initialize()
Randomize '初始化随机种子数
For I = 0 To 8
SelectedPos(I) = I '给位置存储变量赋值
Next I
For I = 0 To 8
10 Temp = Int(Rnd * 10)'随机产生待取位置存储变量序号
If Temp < 0 Or Temp > 8 Then GoTo 10'如果超过数组边界则重新产生
If SelectedPos(Temp) = -1 Then GoTo 10'如果第Temp个位置存储变量所存储的位置已经被取走则重新选定位置
Square(I) = SelectedPos(Temp)'如果没有取走则取位置值
SelectedPos(Temp) = -1'给已经取走的位置存储变量作标记
Next I
For I = 0 To 8'按顺序给label1的各单元分配屏幕上的位置
Label1(I).Left = (Square(I) Mod 3) * Label1(I).Width'取模(余数)运算决定其所在列
Label1(I).Top = Int(Square(I) \ 3) * Label1(I).Height'整除运算决定其所在行
Next I
For I = 0 To 8
Label1(I).Visible = True
Next I
Label2.Caption = ""
Counter = 0
End Sub
Private Sub Command1_Click()
Initialize
Text1.SetFocus
End Sub
Private Sub Command2_Click()
End
End Sub
Private Sub Form_Load()
Label2.Caption = ""
For I = 0 To 8
Label1(I).Visible = False
Next I
End Sub
Private Sub Text1_KeyDown(KeyCode As Integer,Shift As Integer)
TempLeft = Label1(0).Left
TempTop = Label1(0).Top
Select Case KeyCode
Case vbKeyUp
If TempTop = 2 * Label1(0).Height Then Exit Sub
For I = 1 To 8
If Label1(I).Left = TempLeft And Label1(I).Top = TempTop + Label1(0).Height Then
Label1(0).Top = Label1(I).Top
Label1(I).Top = TempTop
Exit For
End If
Next I
Case vbKeyDown
If TempTop = 0 Then Exit Sub
For I = 1 To 8
If Label1(I).Left = TempLeft And Label1(I).Top = TempTop - Label1(I).Height Then
Label1(0).Top = Label1(I).Top
Label1(I).Top = TempTop
Exit For
End If
Next I
Case vbKeyLeft
If TempLeft = 2 * Label1(0).Width Then Exit Sub
For I = 1 To 8
If Label1(I).Top = TempTop And Label1(I).Left = TempLeft + Label1(0).Width Then
Label1(0).Left = Label1(I).Left
Label1(I).Left = TempLeft
Exit For
End If
Next I
Case vbKeyRight
If TempLeft = 0 Then Exit Sub
For I = 1 To 8
If Label1(I).Top = TempTop And Label1(I).Left = TempLeft - Label1(I).Width Then
Label1(0).Left = Label1(I).Left
Label1(I).Left = TempLeft
Exit For
End If
Next I
Case Else
Exit Sub
End Select
Counter = Counter + 1
Label2.Caption = "已用步数:" & Str(Counter)
Finished = False
For I = 1 To 8'逐个检查是否已经到达正确位置
If Label1(I).Left <> ((I - 1) Mod 3) * Label1(0).Width Or Label1(I).Top <> ((I - 1) \ 3) * Label1(0).Height Then Exit For
If I = 8 Then Finished = True'如果到最后一个都归位了则游戏结束
Next I
If Finished = True Then
MsgBox "恭喜您过关了", , "数字拼图"
Initialize
Command1.SetFocus
End If
End Sub
运行时界面如(图3)所示。
