面向对象:面向对象的思路方法在游戏中的应用的一个例子



  接触过计算机高级语言人都听说过面向对象编程(即OOP),但究竟什么是面向对象编程呢?这个问题就不是刚刚接触计算机初学者所能完全理解了.为了帮助大家理解面向对象编程,我们这里用VB作为开发工具,以个相对较小例子来看看VB中怎样使用面向对象编程思路方法

什么是面向对象编程思路方法呢?

   总说来面向对象编程思路方法核心就是:封装性、继承性和多态性下面我们作个简单介绍:

   封装性也就是说要将对象信息(也就是对象属性)和对象所能执行动作(也就是对象思路方法)包装起来这样就可以让使用者可以用深入不了解内部运作原理就可以对其进行操作举个简单例子在v b中我们要使用个Control控件我们不必了解其中给个属性是怎样传给对象也不要知道当我们个对象Control控件思路方法时对象在其中为我们作了些什么我们所要作仅仅是要符合它使用规范标准对其进行简单使用即可

   继承性则是说个对象可以在个或多个已有对象基础上通过继承这些对象具有属性思路方法和事件并添加自己属性思路方法和事件使其功能更加强大而其中对已有对象属性思路方法和事件拥有就是继承性精髓所在比如在V B中有个TEXTBOXControl控件可以处理简单文本信息同时还有个RICH TEXTBOXControl控件很明显后个Control控件是在前基础上建立起来但在功能上后个除了具备前个Control控件功能外还具有远远强于前者更加丰富功能

   多态性简单说,就是在许多种区别对象中,可以使用相同思路方法,但是同样思路方法能为各个对象执行区别任务.也就是说能在运行时根据区别对象,运行是用于当前对象思路方法.例如,几乎所有Control控件都包含个move思路方法.当个Control控件这个思路方法时,能够准确知道是那个Control控件在使用这个思路方法,从而执行此Control控件特定任务.

   可以看到面向对象编程思路方法关键就是为了提高代码可重用性从而减少代码长度减轻负担

   那么在V B 中怎样进行面向对象编程呢?其实说出来也很简单在V B工具箱中个Control控件从实质上说都是个类其中包含了这个类所有属性事件和思路方法当你将个Control控件放置在V B工程窗体上时开发工具自动为你创建这个Control控件类个例子通过这个例子你可以方便使用它种属性事件和思路方法就是不使用可见Control控件而是从“工程”中“引用”个类并在代码中显示定义这个类个例子就可以想用Control控件样对其属性事件和思路方法进行灵活控制了

   我们这篇文章将用类思路方法完成个扫雷游戏编写,更主要是在其中我们可以看到怎样建立个自己以及在工程中使用这个类


、我们现来看看怎样建立个类.

   在VB中建立个类要使用Class Module(类模块).个类模块相当于个简单对象.当个类模块建立起来后,我们就可以在其他窗体中,定义个类例子,然后就像使用个对象样访问它属性,思路方法.下面是个简单例子.

   对于扫雷游戏来说,每个地雷都有个位置,这些地雷按位置紧密排列在窗体上,所以我们可以先建立个包含地雷x , y轴位置类.具体过程如下:

1、创建个类模块首先在v b[工程]菜单上选择[添加类模块]这样就在工作区中显示了个新类模块其默认名字为Class1

2、设定类属性和事件当建立个新类的后就需要设定类 3个属性值

属性:
   Name属性:也就是类名字可以在中区别于其他
   Public属性:用来确定该类在当前项目的外能否被其他使用只有两个值:True和False当该属性值为True时其他应用可以对该类进行某种类型访问其访问类型由下面Instancing决定当该属性值被设置为False时表示该类只能在定义类中使用
   Instancing属性:用来确定其他应用何时能够访问该类该属性只有在Public属性设置为真时才起作用(不是太重要我们省略)
   事件:类模块主要有两个事件:Initialize和Terminate在创建类时候我们可以在这些事件中添加代码这样就可以在类例子被创建或被析构时使采取些动作类似于c / c + +中构造和析构

   在这里我们主要修改了新类Name属性:设置为clsCoords这样我们就可以用这个名字来作为类名字定义它例子并访问它属性和事件了.

3、向类中添加属性

   在设定了类属性和事件的后,我们就需要为类添加用户自己定义属性了.要向类中添加属性有两种思路方法:种是在类中定义public变量.在这里Public关键字表明在类中创建变量是在任何模块中都可以使用公共变量,也正点,所以任何部分无须进行任何数据检查就能修改变量值,这样如果传给对象个坏数据,那么就可能产生问题(不过也使用简单,所以我们这里就使用这种思路方法);另种是使用Property过程.这种思路方法类似于使用个过程,通过过程时,将参数传递数据,经过验证後,赋给类中私有属性,(或者将私有属性传递给参数).通过使用这种思路方法,为访问和修改对象属性提供了接口,这样员就可以编写代码来验证向类传递数据是否正确,使类不会因传递了坏数据而崩溃.下面是对可以使用 3种属性过程及其作用整理总结:

   属性类别
   作 用

   Property Let
   从接收属性值,用来设置属性值

   Property Get
   向传送属性值,用来获取属性值

   Property Set
   特殊情况Let过程,用来设置对象变量

下面是具体个实现思路方法:

   要创建类属性过程,我们先要进到类代码窗口,然后从[工具] ( Tool )菜单中选择[添加过程] ( Add Procedure ).在显示”添加过程”对话框中,输入过程名字,以及”类型”中[属性]选项,单击[确定],这样就在类模块中创建了Property Let和Property Get过程

4、向类中添加思路方法

   任何都要实现动作才能体现它用处,因此,我们也要给类创建些思路方法.它实质上也就是通过在类中编写公共过程实现.这些过程和为其他部分编写过程类似.所有在类模块中过程都以声明语句开始.如果这语句使用Public关键字,那么该过程就是类思路方法,并且该过程可以被在任何模块中创建该类例子所;如果该过程用Private关键字开始,那么该过程只能在定义该类模块中.(思路方法同上)



5、向类中添加事件

为了在类中创建事件需要完成下面两个步骤:

(1)在类中声明事件若要声明事件只要提供事件名和在事件中要传递参数思路方法和定义属性类似只是关键字使用Event 并且开始关键字必须为Public这样才能使在区别模块中定义例子能触发该思路方法

(2)使用Raise Event语句触发事件声明了事件的后就可以使用Raise Event语句在类代码任何地方触发该事件

我们这里不使用对象事件所以省略


2、怎样在中使用定义类呢?

1、创建类对象

   要使用个已经定义首先要创建个该类对象有两种思路方法可以从用户开发类中创建对象:使用声明语句或使用Set语句旦用这两种思路方法创建了该类对象后就可以在中使用该对象并能通过给对象访问对象属性和思路方法

   当个类对象被创建后类模块中Initialize事件首先运行为了属性思路方法访问做准备相当于C语言中构造

(1)使用声明语句: 当定义了个类的后,我们就可以像使用类型样使用这个类.形式上唯区别的处就是,在dim . . . as ..的后,还要加个New关键字,形式变为 Dim . . . As New . . . 使用这种方式时,声明语句直接创建对象例子,我们可以通过这个对象例子在中访问对象属性和思路方法.

(2)使用Set语句:这是创建对象第 2种思路方法.使用这种思路方法时,首先要声明对象变量,然后用Set语句创建对象例子.如我们在游戏中使用了以下代码定义了个clsCoords类型例子objCoords

Dim objCoords As New clsCoords

   同样,在我们用Set语句创建了对象变量的后,我们可以通过这个对象例子在中访问对象属性和思路方法.和前种思路方法区别的处在于,虽然我们先创建了个对象类型,但是我们还是不能使用这个对象,直到使用Set语句时才实际创建了对象例子,这是我们才可以通过这个对象例子在中访问对象属性和思路方法.

2、设置和读取属性值:

   对象例子创建了,必然要对它属性进行访问.对于这步其实很简单,

例如我们在coords.cls类模块中定义两个变量

\'定义了个对象用来保存被标记地雷x , y轴坐标

Public mX As Integer

Public mY As Integer

当我们要访问它值时,我们只要使用像下面方式即可.

Dim objCoords As New clsCoords

\'在新建clsCoords类例子中存储被标记地雷X , Y坐标位置

objCoords.mX = X

objCoords.mY = Y

这里我们给clsCoords类例子objCoords两个属性mX和mY赋予了新值.

3、使用对象思路方法

   要使用用户自己创建对象思路方法,和在v b中使用内在对象和Control控件思路方法样.只要提供要执行对象名称和思路方法名,以及思路方法中要传递数据即可.下面是我们在代码中使用对象思路方法个例子.

Private objMine As New clsWinMine

Private Sub Form_MouseMove(Button As Integer, Sht As Integer, x As Single, y As Single)

\'当鼠标左键被按下时出发此事件clsWinMine类BeginHitTest过程来确定点击方格位置

objMine.TrackHitTest Button, x, y

End Sub

此段代码clsWinMine类BeginHitTest过程来确定点击方格位置


3、下面我们起来分析下在扫雷游戏中建立

   在这个例子中我们可以看到主要有两个类个是用来描述地雷位置coords类其中定义了两个属性mX , mY .代码如下:

\'在coords.cls类模块中定义两个变量
\'定义了个对象用来保存地雷x , y轴坐标
Public mX As Integer
Public mY As Integer

个类是用来控制扫雷游戏winmine类其中定义了主要属性思路方法代码如下:

\'定义鼠标左键,同VB中定义常数vbKeyLButton ,值都为1
Private Const LEFT_BUTTON As Byte = 1

\'标记个方格是否为空标志
Private Const NONE As Byte = 0

\'标记个方格是否为个带雷方格
Private Const MINE As Byte = 243

\'标记个方格是否被点开
Private Const BEEN As Byte = 244

\'标记个方格是否已经被标记为个带雷方格
Private Const FLAGGED As Byte = 2

\'标记个方格是否被标记为个问号,即个存有疑问,不能确定方格
Private Const QUESTION As Byte = 1

\'定义扫雷游戏中最大和最小地雷地图行数和列数及其地雷个数
Private Const MIN_MINES As Byte = 10

\'最小地雷数
Private Const MIN_ROWS As Integer = 8
Private Const MIN_COLS As Integer = 8

\'最小地图行数列数
Private Const MAX_MINES As Byte = 99

\'最大地雷数
Private Const MAX_ROWS As Integer = 24
Private Const MAX_COLS As Integer = 36

\'最大地图行数列数
\'设定每个方格宽度为16个象素
Private Const mButtonWidth As Byte = 16

\'设定每个方格宽度为16个像素
Private Const mButtonHeight As Byte = 16

\'记录玩家设定当前游戏水平中所包含地雷个数
Private mbytNumMines As Byte

\'记录在当前游戏中,被玩家正确标志出来地雷个数
Private mbytCorrectHits As Byte

\'记录在当前游戏中,被玩家标志出来地雷个数,包括被标记
Private mbytTotalHits As Byte

\'记录在当前游戏中,游戏被设定行数和列数
Private mRows As Integer
Private mCols As Integer

\' 记录在游戏中由玩家点击鼠标位置,而确定点击方块行数和列数
Private mRow As Integer
Private mCol As Integer

\'是否开始盘新游戏标志
Public mblnNewGame As Boolean

\'在正在进行游戏中鼠标点击事件标志
Private mblnHitTestBegun As Boolean

\'定义游戏显示主窗体
Private mfrmDisplay As Form

\'定义个动态 2维用来保存包含地雷方格位置以及那个位置周围有没有地雷有多少地雷以及那些方格被打开
Private mbytMineStatus As Byte



\'其中定义个动态 2维,用来保存被标记过方格位置,不管这个标记是否标记正确
Private mbytMarked As Byte

\'定义个动态 2维用来保存在分布地雷区域所有分布地雷总数y中坐标位置
Private mbytMineLocations As Byte

\'定义个集合用来存放clsCoords类对象x y轴坐标位置他们指示着游戏中被标记方格位置
Private mcolWrongLocations As New Collection

   有了这些类定义那么在游戏流程中我们只要用类就可以对游戏进行操作了可以想象到剩下工作就比较简单了这也就是VC中MFC使用广泛缘故


4、最后我们给出扫雷游戏源代码其中有详细注释大家可以参考

\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'
\' \'扫雷游戏总工程介绍 \'
\' \'
\' 这个游戏中我们主要通过类使用看看在VB中OOP使用思路方法其中主要文件及其主要作用如下所示: \'
\' \'
\' winmine.cls: 这是个类模块其中实现了游戏中主要功能 \'
\' \'
\' winmine.frm: 这是游戏显示得主窗口她是个和玩家进行互动娱乐主要界面接口并且它也显示了winmine . cls 类例子在游戏中运用思路方法 \'
\' \'
\' cords.cls: 这是另个类模块,这里主要是用来标记被标记地雷x , y坐标位置
\' \'
\' custdlg.frm: 这是个自定义游戏水平级别窗体,当点击游戏显示主窗体中自定义菜单时候,该对话框会以模式状态显示,所以必须做出选择,否则不能回到游戏现实主窗口进行游戏\'
\' \'
\' instruct.frm: 这是个窗体文件,当F1键被按下时,该窗口显示出来,用来显示游戏规则和对玩法
\' 进行指导, \'
\' \'
\' about.frm 这也是个窗体文件,用来显示些相关信息等等\' \'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

在coords.cls类模块中定义两个变量

\'定义了个对象用来保存被标记地雷x , y轴坐标
Public mX As Integer
Public mY As Integer

\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

在winmine.cls类模块中建立个类来方便对扫雷游戏控制:

Option Explicit

\'定义鼠标左键,同VB中定义常数vbKeyLButton ,值都为1
Private Const LEFT_BUTTON As Byte = 1

\'标记个方格是否为空标志
Private Const NONE As Byte = 0

\'标记个方格是否为个带雷方格
Private Const MINE As Byte = 243

\'标记个方格是否被点开
Private Const BEEN As Byte = 244

\'标记个方格是否已经被标记为个带雷方格
Private Const FLAGGED As Byte = 2

\'标记个方格是否被标记为个问号,即个存有疑问,不能确定方格
Private Const QUESTION As Byte = 1

\'定义扫雷游戏中最大和最小地雷地图行数和列数及其地雷个数
Private Const MIN_MINES As Byte = 10

\'最小地雷数
Private Const MIN_ROWS As Integer = 8
Private Const MIN_COLS As Integer = 8

\'最小地图行数列数
Private Const MAX_MINES As Byte = 99

\'最大地雷数
Private Const MAX_ROWS As Integer = 24
Private Const MAX_COLS As Integer = 36

\'最大地图行数列数
\'设定每个方格宽度为16个象素
Private Const mButtonWidth As Byte = 16

\'设定每个方格宽度为16个像素
Private Const mButtonHeight As Byte = 16

\'记录当前游戏玩家水平
Private mbytNumMines As Byte

\'记录在当前游戏中,被玩家正确标志出来地雷个数
Private mbytCorrectHits As Byte

\'记录在当前游戏中,被玩家标志出来地雷个数,包括被标记
Private mbytTotalHits As Byte

\'记录在当前游戏中,游戏被设定行数和列数
Private mRows As Integer
Private mCols As Integer

\' 记录在游戏中由玩家点击鼠标位置,而确定点击方块行数和列数
Private mRow As Integer
Private mCol As Integer

\'是否开始盘新游戏标志
Public mblnNewGame As Boolean

\'在正在进行游戏中鼠标点击事件标志
Private mblnHitTestBegun As Boolean

\'定义游戏显示主窗体
Private mfrmDisplay As Form

\'定义个动态 2维用来保存包含地雷方格位置以及那个位置周围有没有地雷有多少地雷
Private mbytMineStatus As Byte

\'其中定义个动态 2维,用来保存被标记过方格位置,不管这个标记是否标记正确
Private mbytMarked As Byte

\'定义个动态 2维用来保存在分布地雷区域所有分布地雷总数y中坐标位置
Private mbytMineLocations As Byte

\'定义个集合用来存放clsCoords类对象x y轴坐标位置他们指示着游戏中被标记方格位置
Private mcolWrongLocations As New Collection


* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
\' \'
\' 作用: 判定那个鼠标键被点击,以及在窗体中点击位置,从而判断游戏玩家行为再主窗体显示区中鼠标按下事件中被
\' 输入参数: Button: 表示哪个鼠标键被点击(左键或者右键以及中键)
\' inX: 记录鼠标键被点击位置在X轴上坐标 \'
\' inY: 记录鼠标键被点击位置在Y轴上坐标
\' 返回值: 无
\'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Public Sub BeginHitTest ( Button As Integer , X As Single , Y As Single )

\'如果mblnNewGame值为真,表示新局游戏开始标志,所以当前游戏被结束,并且开始局新游戏, mblnNewGame 变量在前面有定义



If mblnNewGame Then
NewGame \' 开始局新游戏
End If

\' 如果游戏正在进行,那么设置mblnHitTestBegun值为真,表示鼠标点击事件开始
mblnHitTestBegun = True

\'判定鼠标点击位置, mButtonWidth和mButtonHeight在前面定义中,定义为每个方格宽度和高度,用得到鼠标点击位置除以方格宽高,取整後就可以得到鼠标点击了哪个方格,既第几行第几列中方格
X = Int(X / mButtonWidth)
Y = Int(Y / mButtonHeight)

\'如果点击位置超出了设定游戏窗口范围,那么退出此过程,也就是不做任何动作

If X >= mCols Or Y >= mRows Or X < 0 Or Y < 0 Then
\'如果鼠标点击位置X轴大于游戏有效窗口行数,
\'或者鼠标点击位置Y轴大于游戏有效窗口列数,
\'或者鼠标点击位置X轴小于游戏有效窗口最小位置,
\'或者鼠标点击位置Y轴小于游戏有效窗口最小位置,
\'可以断定鼠标点击位置已经超出了游戏有效窗口
\'所以退出此过程,也就是什么动作都不进行

Exit Sub
End If



\' X * mButtonWidth 从新到鼠标在窗口中位置坐标X轴,并赋值给mCol变量
\' Y * mButtonHeight 从新到鼠标在窗口中位置坐标X轴,并赋值给mCol变量
mCol = X * mButtonWidth
mRow = Y * mButtonHeight

\' mbytMineStatus ( ) ,判断鼠标点击位置X , Y 状态,如果这个方格已被点开,
\' 那么退出该过程, 即什么动作都不发生
If mbytMineStatus(Y, X) >= BEEN Then Exit Sub

\'定义个此过程中变量blnLeftDown,从而记录鼠标左键是否按下
Dim blnLeftDown As Boolean

\'用得到鼠标点击键和定义常数相和,如果大于0,那么将blnLeftDown 赋值为真,
\'介绍说明按下是鼠标左键,当然也可以用数值判断,将下面语句改为
\'blnLeftDown = (Button - LEFT_BUTTON) > 0

\'或者再和后面
\'blnLeftDown = (Button And LEFT_BUTTON) > 0
\'If blnLeftDown Then 这两句合并为
\'If Button = 1 then

blnLeftDown = (Button And LEFT_BUTTON) > 0

\'如果鼠标左键被点击
If blnLeftDown Then

\'mbytMarked(Y, X) 判断鼠标是否被标记为有雷
\'如果返回值大于等于 2 (即 FLAGGED ),介绍说明已经被标志,不做任何动作,退出此过程
If mbytMarked(Y, X) >= FLAGGED Then Exit Sub

\'mbytMarked(Y, X) 判断鼠标是否被标记为问号,即不能确定
\'如果返回值等于 1 (即 QUESTION ),介绍说明已经被标志为问号,
\'那么在原来位置上显示 方块被按下图片
If mbytMarked(Y, X) = QUESTION Then
mfrmDisplay.imgPressed.Visible = False
mfrmDisplay.imgQsPressed.Visible = False
mfrmDisplay.imgQsPressed.Left = mCol
mfrmDisplay.imgQsPressed.Top = mRow
mfrmDisplay.imgQsPressed.Visible = True
Else

\'mbytMarked(Y, X) 判断鼠标是否被标记为问号,即不能确定
\'如果返回值不等于 1 (即 QUESTION ),介绍说明没有被标志,
\'那么在原来位置上显示 方块被按下图片
mfrmDisplay.imgQsPressed.Visible = False
mfrmDisplay.imgPressed.Visible = False
mfrmDisplay.imgPressed.Left = mCol
mfrmDisplay.imgPressed.Top = mRow
mfrmDisplay.imgPressed.Visible = True
End If
Else

\' 如果按下是鼠标右键
Dim Msg As String
Dim CRLF As String

CRLF = Chr$(13) & Chr$(10)
Select Case mbytMarked(Y, X)

\'mbytMarked(Y, X) 判断鼠标是否被标记
Case NONE:

\'如果返回值大于等于 0 (即 NONE ),那么介绍说明这里为个空标志位
If mbytTotalHits = mbytNumMines Then

\'如果该游戏中所有雷数等于所标记为有雷标记数
\'那么对话框提示玩家不能再标记更多有雷标志了
Msg = \"不能再标记更多有雷标志了\" & CRLF
Msg = Msg & \"有个或更多位置被标志为有雷\" & CRLF
Msg = Msg & \"不能再用右键标志更多雷了.\"
MsgBox Msg , vbCritical , \"扫雷:\"
Exit Sub \' 退出该过程

End If

\'如果还可以标志雷,那么在鼠标点击位置显示相应有雷标志
mfrmDisplay.PaPicture mfrmDisplay.imgFlag, mCol, mRow

\'的后,将记录所标记地雷数量个数加1
mbytTotalHits = mbytTotalHits + 1

\' mbytNumMines – mbytTotalHits表示总地雷数量减去已经标志
\'为有地雷个数,从而得到未使用标记个数
mfrmDisplay.lblMinesLeft = \"剩余地雷数 : \" & mbytNumMines - mbytTotalHits

\'如果鼠标点击当前位置状态为有雷,那么标记为有雷正确个数加1.并且将此位置设置为已经标记过有雷位置
If mbytMineStatus(Y, X) = MINE Then


mbytCorrectHits = mbytCorrectHits + 1
mbytMarked(Y, X) = FLAGGED
Else

\'如果鼠标点击当前位置状态为无雷,即该位置被标记,那么定义个用来存储所有被标记地雷位置clsCoords类例子
Dim objCoords As New clsCoords

\'在新建clsCoords类例子中存储被标记地雷X , Y坐标位置
objCoords.mX = X
objCoords.mY = Y

\'并且在集合mcolWrongLocations中新添加个clsCoords类例子
mcolWrongLocations.Add objCoords

\'并且在mbytMarked中存储被标记方格索引
mbytMarked(Y, X) = mbytTotalHits - mbytCorrectHits + 2

End If

\' 如果所有地雷都被正确标记出来那么对话框提示”恭喜你!,你以经赢了!”

If mbytCorrectHits = mbytNumMines Then
Msg = \"恭喜你!\" & CRLF
Msg = Msg & \"你已经赢了!\" & CRLF
MsgBox Msg , vbInformation , \"扫雷\"

\' 准备开始盘新游戏
mblnNewGame = True
End If

Case QUESTION:

\'如果返回值等于 1 (即 QUESTION ),那么介绍说明这里为个被标志为问号标志位,所以要将这个位置状态设为NONE ,即设置为个空标志位
mbytMarked(Y, X) = NONE

\'在这个位置上显示正常按钮图形
mfrmDisplay.PaPicture mfrmDisplay.imgButton, mCol, mRow

Case Else:

\'如果返回值为别数值, 也就是为个标记为地雷状态,那么将其改为问号标志
mfrmDisplay.PaPicture mfrmDisplay.imgQuestion, mCol, mRow

\'并且将标记地雷总数减1
mbytTotalHits = mbytTotalHits - 1

\'显示剩余标志个数
mfrmDisplay.lblMinesLeft = \"剩余地雷数 : \" & mbytNumMines - mbytTotalHits

\' 如果鼠标点击位置状态是个地雷,那么
If mbytMineStatus(Y, X) = MINE Then

\'将正确地雷标志,换为了问号标志,所以正确标志数减1
mbytCorrectHits = mbytCorrectHits - 1

Else .

\' 如果鼠标点击位置状态不是个地雷,也就是说开始标记是,那么修改后,为正确,所以要从标记表中删除这标记
mcolWrongLocations.Remove mbytMarked(Y, X) - 2

Dim Xwm As Integer \' 标记方格x轴坐标位置
Dim Ywm As Integer \'标记方格y轴坐标位置
Dim i As Integer \' 循环数

\'在mbytMarked中删除被标记方格索引

For i = mbytMarked(Y, X) - 2 To mcolWrongLocations.Count
Xwm = mcolWrongLocations(i).mX
Ywm = mcolWrongLocations(i).mY
mbytMarked(Ywm, Xwm) = mbytMarked(Ywm, Xwm) - 1
Next

End If

\' 最後将鼠标点击位置状态改为问号
mbytMarked(Y, X) = QUESTION

End Select
End If
End Sub


\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \' \'
\' 介绍说明: 当鼠标被按下时,用来测定鼠标光标是在那个方格位置上经过,从而决定产生什么动作,这个过程在游戏显示主窗口中产生鼠标弹起事件时被
\'
\' 输入参数: Button: 表示哪个鼠标键被点击(左键或者右键以及中键)
\' inX: 记录鼠标键被点击位置在X轴上坐标 \'
\' inY: 记录鼠标键被点击位置在Y轴上坐标
\'
\' 返回值: 无
\'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Public Sub EndHitTest(Button As Integer, X As Single, Y As Single)

\' 如果当前正在进行鼠标单击事件标志mblnHitTestBegun为真
If mblnHitTestBegun Then

\' 那么从新设置这个标志为假
mblnHitTestBegun = False

Else

\'如果当前正在进行鼠标单击事件标志mblnHitTestBegun为假,那么可以断定鼠标按下位置不是在游戏主显示窗口合法位置,因此不做任何动作,退出该过程
Exit Sub
End If

Dim blnLeftDown As Boolean
blnLeftDown = (Button And LEFT_BUTTON) > 0

\' 如果鼠标左键被按下
If blnLeftDown Then

\'判定鼠标点击位置, mButtonWidth和mButtonHeight在前面定义中,定义为每个方格宽度和高度,用得到鼠标点击位置除以方格宽高,取整後就可以得到鼠标点击了哪个方格,既第几行第几列中方格
X = Int(X / mButtonWidth)
Y = Int(Y / mButtonHeight)

\'如果点击位置超出了设定游戏窗口范围,那么退出此过程,也就是不做任何动作
If X >= mCols Or Y >= mRows Or X < 0 Or Y < 0 Then

\'如果鼠标点击位置X轴大于游戏有效窗口行数,
\'或者鼠标点击位置Y轴大于游戏有效窗口列数,
\'或者鼠标点击位置X轴小于游戏有效窗口最小位置,
\'或者鼠标点击位置Y轴小于游戏有效窗口最小位置,
\'可以断定鼠标点击位置已经超出了游戏有效窗口
\'所以退出此过程,也就是什么动作都不进行
Exit Sub
End If

\' 如果鼠标安键动作被释放位置上方格已经被标记,那么什么动作都不做,退出该过程


If mbytMarked(Y, X) >= FLAGGED Then Exit Sub

\'如果鼠标安键动作被释放位置上方格没有被标记,那么计算鼠标光标最后有效位置坐标
X = mCol \\ mButtonWidth
Y = mRow \\ mButtonHeight

\'如果该坐标位置被标记为问号,那么不显示问号图标
\'否则不显示鼠标按下图标
If mbytMarked(Y, X) = QUESTION Then
mfrmDisplay.imgQsPressed.Visible = False
Else
mfrmDisplay.imgPressed.Visible = False
End If

\'判断鼠标弹起位置,方格状态
Select Case mbytMineStatus(Y, X)

Case Is >= BEEN:

\' 如果当前位置鼠标方格被打开,那么什么都不做,退出该过程
Exit Sub

Case NONE:

\'如果当前方格状态为空,那么打开它周围所有空方格
OpenBlanks X, Y

Case MINE:

\' 如果当前方格中包含地雷,那么你踩到地雷了
Dim Xm As Integer \' 地雷分布区X坐标
Dim Ym As Integer \'地雷分布区Y坐标
Dim vntCoord As Variant \' 循环计数值
Dim i As Integer \' 循环计数值

\'显示所有包含地雷方格
For i = 0 To mbytNumMines - 1

\' 在mbytMineLocations中取得所有包含地雷方格坐标
Ym = mbytMineLocations(i, 0)
Xm = mbytMineLocations(i, 1)

\' 如果这个坐标位置方格已经被标记,那么显示小旗图标
If mbytMarked(Ym, Xm) < FLAGGED Then
mfrmDisplay.PaPicture mfrmDisplay.imgMine, Xm * mButtonWidth, Ym * mButtonHeight
End If
Next

\' 在当前方格中显示被踩中地雷图标
mfrmDisplay.PaPicture mfrmDisplay.imgBlown, mCol, mRow

\' 显示所有被标记地雷图标(用差号)
For Each vntCoord In mcolWrongLocations

\' 在mcolWrongLocations中取得被标记地雷图标位置
Ym = vntCoord.mY
Xm = vntCoord.mX

\' 显示所有被标记地雷图标
mfrmDisplay.PaPicture mfrmDisplay.imgWrongMine, Xm * mButtonWidth, Ym * mButtonHeight
Next

\' 准备开始盘新游戏
mblnNewGame = True

Dim CRLF As String
CRLF = Chr$(13) & Chr$(10)
\' 对话框提示\"你输了!\"
MsgBox \"你输了!\", vbExclamation, \"扫雷\"

Case Else:

\' 如果这个方格周围有个或更多方格中包含地雷,那么显示它周围包含地理数
mfrmDisplay.PaPicture mfrmDisplay.imgPressed, mCol, mRow
mfrmDisplay.CurrentX = mCol
mfrmDisplay.CurrentY = mRow
mfrmDisplay.ForeColor = QBColor(mbytMineStatus(Y, X))
mfrmDisplay.Pr mbytMineStatus(Y, X)

\' 并且标记这个位置已经被打开
mbytMineStatus(Y, X) = mbytMineStatus(Y, X) + BEEN
End Select
End If
End Sub


\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \' \'
\' 介绍说明: 当这个窗体旧对象显示尺寸被赋予新属性值时,过程被该过程在主显示窗体被载入时被
\'
\' 输入参数 : frmDisplay: 旧主显示窗体对象 \'
\' \'
\' 输出参数: 无 \'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Public Property Set frmDisplay(frmDisplay As Form)
\' Property 表示为个类属性,属性名为frmDisplay

Set mfrmDisplay = frmDisplay
mfrmDisplay.FontBold = True

\' 按游戏中设置尺度和雷数,来从新确定主窗体大小
ResizeDisplay

End Property
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
\' \'
\' 介绍说明: 将当前游戏中设定游戏级别地雷分布行数 、列数以及地雷数显示在自定义对话框文本框中
\'
\' 输入参数 : frmDisplay: 旧主显示窗体对象 \'
\' \'
\' 输出参数: 无 \'
\' \'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Public Sub GetMineFieldDimensions(frmDialog As Form)



\' 得到当前游戏中设定游戏级别地雷分布行数 、列数以及地雷数
frmDialog.txtRows = mRows
frmDialog.txtColumns = mCols
frmDialog.txtMines = mbytNumMines

\' 将其高亮显示在自定义对话框文本框中
frmDialog.txtRows.SelLength = Len(frmDialog.txtRows)
frmDialog.txtColumns.SelLength = Len(frmDialog.txtColumns)
frmDialog.txtMines.SelLength = Len(frmDialog.txtMines)

End Sub

\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
\' \'
\' 介绍说明: 按当前游戏中设定地雷游戏尺寸,动态分配大小,并且随机分配地雷分布区域
\' 输入参数: 无 \'
\' 输出参数: 无
\'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Private Sub InitializeMineField

\' 按设置行列数及雷数,设置 2维动态大小
ReDim mbytMineStatus(mRows - 1, mCols - 1)
ReDim mbytMarked(mRows - 1, mCols - 1)
ReDim mbytMineLocations(mbytNumMines - 1, 1)

\'在地雷分布区中产生随机地雷位置,并将其存放在mbytMineLocations
\'并且用包含地雷位置及其周围包含地雷数填充mbytMineStatus
Randomize

Dim i As Integer \'循环数
Dim r As Integer \'循环数
Dim c As Integer \'循环数

For i = 0 To mbytNumMines - 1
Dim X As Integer
Dim Y As Integer
X = Int(Rnd * mCols)
Y = Int(Rnd * mRows)

\'如果得到位置状态为有雷,那么从新分配
While mbytMineStatus(Y, X) = MINE
X = Int(Rnd * mCols)
Y = Int(Rnd * mRows)
Wend

\'将得到位置状态标记为有地雷
mbytMineStatus(Y, X) = MINE

\'将这个位置存放在 2维
mbytMineLocations(i, 0) = Y
mbytMineLocations(i, 1) = X

\'找到当前位置周围8个位置,并判断在没有出地雷分布区时,这8个位置状态,只要每有地雷分布,就将他们状态加1,也就是将它标记为无雷
For r = -1 To 1
For c = -1 To 1

Dim blnDx As Boolean
Dim blnDy As Boolean

\'找它周围8个位置,看是否出了有效地雷分布区
blnDy = Y + r >= 0 And Y + r < mRows
blnDx = X + c >= 0 And X + c < mCols

\'如果没有出有效地雷分布区
If blnDy And blnDx Then

\'判断他们状态是否有地雷分布
If mbytMineStatus(Y + r, X + c) <> MINE Then

\'如果没有地雷分布,那么将它状态加1 ( 即设为无雷),并存放在mbytMineStatus中
mbytMineStatus(Y + r, X + c) = mbytMineStatus(Y + r, X + c) + 1

End If
End If

Next
Next

Next

End Sub

\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
\' \'
\' 介绍说明: 开始盘新游戏
\'
\' 输入参数: 无 \'
\'
\' 输出参数: 无 \'
\' \'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Public Sub NewGame

\' 清除再主窗体中显示
mfrmDisplay.Cls

\' 从新设置游戏中变量和标志位
mbytCorrectHits = 0
mbytTotalHits = 0
mRow = -1
mCol = -1
mblnNewGame = False
mblnHitTestBegun = False

Dim i As Integer \'循环数

\' 清空标记地雷mcolWrongLocations集合
For i = 1 To mcolWrongLocations.Count
mcolWrongLocations.Remove 1
Next

\'从新计算新地雷分布区域
InitializeMineField

\' 从新设置主窗体中最下面剩余地雷数
mfrmDisplay.lblMinesLeft = \"剩余地雷数 : \" & mbytNumMines

End Sub

\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
\' \'
\' 介绍说明:如果这个方格被点击并且其中不含有地雷那么这个过程将打开所有它周围方格直到遇到包含地雷方格为止这里使用了种算法有兴趣可以研究首先从点击方格位置开始直向左查找直到遇到个不为空包含地雷方格为止此时以前个扫描方格位置为中心顺时针查找它周围方格是否含有地雷从而勾画出没有地雷方格边缘并存储边缘地雷位置x周坐标
\'
\' 输入参数: inX: 记录鼠标键被点击位置在X轴上坐标 \'
\' inY: 记录鼠标键被点击位置在Y轴上坐标
\' \'
\' 返回值: 无
\' \'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Private Sub OpenBlanks(ByVal X As Single, ByVal Y As Single)

\' 定义 4个布尔型变量用来保存查找动作移动方向
Dim blnGoUp As Boolean
Dim blnGoRight As Boolean
Dim blnGoDown As Boolean
Dim blnGoLeft As Boolean

\' the border starts
\' 用来保存查找动作移动位置X Y轴坐标
Dim XStart As Integer
Dim YStart As Integer

\' 集合队列中位置索引
Dim Pos As Integer

\' 循环计数值
Dim element As Variant

\' 循环计数值
Dim y As Integer
Dim x As Integer
Dim i As Integer

\'个动态整型集合.其中每个元素存放扫描行起始和终止方格x轴坐标位置通过这个数值可以得到没有包含地雷位置边缘
Dim colX As New Collection

\'设定这个大小和地雷分布区域行数相同
ReDim colX(mRows - 1)



\'直向左搜索,直到找到个空不包含地雷位置
While mbytMineStatus(Y, X) = NONE
X = X - 1
If X < 0 Then
X = 0
XStart = X
YStart = Y
GoTo LFT
End If
Wend

\' first direction to go is up
\' 首先是向上搜索
blnGoUp = True

\' store this first non-empty mine location as the starting po.
\'将搜索到不包含地雷位置作为个新开始位置保存起来,以进行次新搜索
XStart = X
YStart = Y

\'勾画出边界直到又回到开始位置
Do
If mbytMineStatus(Y, X) = NONE Then
If blnGoUp Then
X = X - 1
Y = Y + 1
colX(Y).Remove (colX(Y).Count)
blnGoUp = False
blnGoLeft = True
ElseIf blnGoRight Then
X = X - 1
Y = Y - 1
blnGoRight = False
blnGoUp = True
ElseIf blnGoDown Then
X = X + 1
Y = Y - 1
colX(Y).Remove (colX(Y).Count)
blnGoDown = False
blnGoRight = True
ElseIf blnGoLeft Then
X = X + 1
Y = Y + 1
blnGoLeft = False
blnGoDown = True
End If

If (XStart = X And YStart = Y) Then Exit Do
Else
If blnGoUp Then
colX(Y).Add X
If mbytMineStatus(Y, X + 1) = NONE Then
If Y = 0 Then
blnGoUp = False
UP: X = X + 1
If (XStart = X And YStart = Y) Then Exit Do

While mbytMineStatus(Y, X) = NONE
If X = mCols - 1 Then GoTo RIGHT
X = X + 1
If (XStart = X And YStart = Y) Then Exit Do
Wend

blnGoDown = True
Else
Y = Y - 1
If (XStart = X And YStart = Y) Then Exit Do
End If
Else
blnGoUp = False
blnGoRight = True
X = X + 1
If (XStart = X And YStart = Y) Then
If colX(Y).Count Mod 2 <> 0 Then
Pos = 1
For Each element In colX(Y)
If element = XStart Then
colX(Y).Remove (Pos)
Exit Do
End If

Pos = Pos + 1
Next

End If
Exit Do
End If
End If

ElseIf blnGoRight Then
If mbytMineStatus(Y + 1, X) = NONE Then
If X = mCols - 1 Then
blnGoRight = False
RIGHT: colX(Y).Add X
Y = Y + 1
If (XStart = X And YStart = Y) Then Exit Do
While mbytMineStatus(Y, X) = NONE
colX(Y).Add X
If Y = mRows - 1 Then GoTo DOWN
Y = Y + 1

If (XStart = X And YStart = Y) Then Exit Do
Wend
colX(Y).Add X
blnGoLeft = True
Else
X = X + 1
If (XStart = X And YStart = Y) Then
If colX(Y).Count Mod 2 <> 0 Then
Pos = 1
For Each element In colX(Y)
If element = XStart Then
colX(Y).Remove (Pos)
Exit Do
End If

Pos = Pos + 1
Next

End If
Exit Do
End If
End If

Else

blnGoRight = False
blnGoDown = True
colX(Y).Add X


Y = Y + 1
If (XStart = X And YStart = Y) Then Exit Do
End If

ElseIf blnGoDown Then
colX(Y).Add X
If mbytMineStatus(Y, X - 1) = NONE Then
If Y = mRows - 1 Then
blnGoDown = False

DOWN: X = X - 1

If (XStart = X And YStart = Y) Then Exit Do

While mbytMineStatus(Y, X) = NONE
If X = 0 Then GoTo LFT
X = X - 1

If (XStart = X And YStart = Y) Then Exit Do
Wend

blnGoUp = True
Else
Y = Y + 1
If (XStart = X And YStart = Y) Then Exit Do
End If

Else
blnGoDown = False
blnGoLeft = True
X = X - 1
If (XStart = X And YStart = Y) Then Exit Do
End If

ElseIf blnGoLeft Then

If mbytMineStatus(Y - 1, X) = NONE Then
If X = 0 Then
blnGoLeft = False
LFT: colX(Y).Add X
If Y = 0 Then GoTo UP
Y = Y - 1
If (XStart = X And YStart = Y) Then Exit Do
While mbytMineStatus(Y, X) = NONE
colX(Y).Add X

If Y = 0 Then GoTo UP
Y = Y - 1
If (XStart = X And YStart = Y) Then Exit Do
Wend
colX(Y).Add X
blnGoRight = True

Else
X = X - 1
If (XStart = X And YStart = Y) Then Exit Do
End If

Else
blnGoLeft = False
blnGoUp = True
colX(Y).Add X
Y = Y - 1
If (XStart = X And YStart = Y) Then Exit Do
End If

End If

End If

Loop

\'从新遍历集合中个扫描行位置,并且打开曾经记录被点开方格
For y = 0 To mRows - 1
If colX(y).Count > 0 Then
\' Sort the X co-ord pairs in ascending order, by using
\' a standard Listbox control
For x = 1 To colX(y).Count
Dim Xvalue As Integer
Xvalue = colX(y)(x)

If Xvalue < 10 Then
Xvalue = Xvalue + 48
ElseIf Xvalue >= 10 Then
Xvalue = Xvalue + 55
End If
mfrmDisplay.lstSortedX.AddItem Chr$(Xvalue)
Next

\'显示在集合中保存扫描起始和终止位置X坐标的间方格为打开状态

For x = 0 To mfrmDisplay.lstSortedX.ListCount - 1 Step 2
Dim R1 As Integer
Dim C1 As Integer
Dim ColStart As Integer
Dim ColEnd As Integer
Dim Dx As Integer
Dim Width As Integer

R1 = y * mButtonHeight
ColStart = Asc(mfrmDisplay.lstSortedX.List(x))
If ColStart <= 57 Then
ColStart = ColStart - 48
ElseIf ColStart >= 65 Then

ColStart = ColStart - 55
End If

ColEnd = Asc(mfrmDisplay.lstSortedX.List(x + 1))
If ColEnd <= 57 Then
ColEnd = ColEnd - 48
ElseIf ColEnd >= 65 Then
ColEnd = ColEnd - 55
End If

C1 = ColStart * mButtonWidth
Dx = ColEnd - ColStart + 1
Width = Dx * mButtonWidth

mfrmDisplay.PaPicture mfrmDisplay.imgOpenBlocks, C1, R1, , , 0, 0, Width, mButtonHeight

For i = 0 To Dx - 1
If mbytMarked(y, ColStart + i) > NONE Then
If mbytMarked(y, ColStart + i) = QUESTION Then


mfrmDisplay.PaPicture mfrmDisplay.imgQuestion, C1 + i * mButtonWidth, R1
Else
mfrmDisplay.PaPicture mfrmDisplay.imgFlag, C1 + i * mButtonWidth, R1
End If
ElseIf mbytMineStatus(y, ColStart + i) > NONE Then
mfrmDisplay.CurrentX = C1 + i * mButtonWidth
mfrmDisplay.CurrentY = R1
If mbytMineStatus(y, ColStart + i) >= BEEN Then
mfrmDisplay.ForeColor = QBColor(mbytMineStatus(y, ColStart + i) - BEEN)
mfrmDisplay.Pr mbytMineStatus(y, ColStart + i) - BEEN
ElseIf mbytMineStatus(y, ColStart + i) = MINE Then
mfrmDisplay.PaPicture mfrmDisplay.imgButton, C1 + i * mButtonWidth, R1
Else
mfrmDisplay.ForeColor = QBColor(mbytMineStatus(y, ColStart + i))
mfrmDisplay.Pr mbytMineStatus(y, ColStart + i)
mbytMineStatus(y, ColStart + i) = mbytMineStatus(y, ColStart + i) + BEEN
End If
End If
Next
Next

mfrmDisplay.lstSortedX.Clear
End If

Next
End Sub

\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \' \'
\' 介绍说明:按照游戏中设置窗体大小,从新设置游戏主显示窗体尺寸
\'
\' 输入参数:无

\'输出参数:无
\'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Private Sub ResizeDisplay

\'设置窗体尺寸
mfrmDisplay.ScaleMode = 1
mfrmDisplay.Width = mfrmDisplay.Width - mfrmDisplay.ScaleWidth + mCols * mButtonWidth * Screen.TwipsPerPixelX
mfrmDisplay.Height = mfrmDisplay.Height - mfrmDisplay.ScaleHeight + mRows * mButtonHeight * Screen.TwipsPerPixelY + mfrmDisplay.lblMinesLeft.Height

\'设置用来显示剩余地雷个数labelControl控件尺寸
mfrmDisplay.lblMinesLeft.Left = 0
mfrmDisplay.lblMinesLeft.Top = mfrmDisplay.ScaleHeight - mfrmDisplay.lblMinesLeft.Height
mfrmDisplay.lblMinesLeft.Width = mfrmDisplay.ScaleWidth
mfrmDisplay.lblMinesLeft = \"剩余地雷数 : \" & mbytNumMines
mfrmDisplay.ScaleMode = 3

End Sub

\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
\' \'
\'介绍说明: 只要鼠标左键被按下,即触发此动作,并测定鼠标光标在那个方格上经过.
在游戏主显示窗口鼠标移动事件中被
\'
\'输入参数: Button: 表示哪个鼠标键被点击(左键或者右键以及中键)
\' inX: 记录鼠标键被点击位置在X轴上坐标 \'
\' inY: 记录鼠标键被点击位置在Y轴上坐标
\'
\' 返回值: 空 \'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Public Sub TrackHitTest(Button As Integer, X As Single, Y As Single)

Dim blnLeftDown As Boolean

\'定义个布尔变量blnLeftDown ,用来标记鼠标左键是否被按下
blnLeftDown = (Button And LEFT_BUTTON) > 0

\'判断按下是否为鼠标左键
\'如果按下是鼠标左键
If blnLeftDown Then

\' 如果不是在运行中游戏中点击左键,那么什么都不做,退出此过程
If Not mblnHitTestBegun Then Exit Sub

\'判定鼠标点击位置, mButtonWidth和mButtonHeight在前面定义中,定义为每个方格宽度和高度,用得到鼠标点击位置除以方格宽高,取整後就可以得到鼠标点击了哪个方格,既第几行第几列中方格
X = Int(X / mButtonWidth)
Y = Int(Y / mButtonHeight)

\'如果鼠标点击位置X轴大于游戏有效窗口行数,
\'或者鼠标点击位置Y轴大于游戏有效窗口列数,
\'或者鼠标点击位置X轴小于游戏有效窗口最小位置,
\'或者鼠标点击位置Y轴小于游戏有效窗口最小位置,
\'可以断定鼠标点击位置已经超出了游戏有效窗口
\'所以退出此过程,也就是什么动作都不进行
If X >= mCols Or Y >= mRows Or X < 0 Or Y < 0 Then
mfrmDisplay.imgQsPressed.Visible = False
mfrmDisplay.imgPressed.Visible = False
Exit Sub
End If

\' 如果鼠标点击方格已经被标记为个有地雷方格
\' 那么什么都不做,并退出此过程
If mbytMarked(Y, X) >= FLAGGED Then
mfrmDisplay.imgQsPressed.Visible = False
mfrmDisplay.imgPressed.Visible = False
Exit Sub

End If


Dim RowOld As Integer
Dim ColOld As Integer

\'定义两个变量RowOld和 ColOld ,用来记录前次鼠标点击位置
RowOld = mRow
ColOld = mCol

\'得到鼠标点击方格坐标
mCol = X * mButtonWidth
mRow = Y * mButtonHeight

\'如果鼠标当前点击位置,和前次点击位置相同,那么什么都不做并退出此过程
\'除非鼠标当前点击位置,和前次点击位置不相同,继续向下执行
If RowOld = mRow And ColOld = mCol Then


If mfrmDisplay.imgPressed.Visible Or mfrmDisplay.imgQsPressed.Visible Then
Exit Sub
End If
End If

\' 如果鼠标点击当前位置已被点开,那么什么都不做,退出此过程
If mbytMineStatus(Y, X) >= BEEN Then
mfrmDisplay.imgPressed.Visible = False
mfrmDisplay.imgQsPressed.Visible = False
Exit Sub
End If

\' 如果鼠标点下位置上方格被标记为问号,那么显示鼠标按下问号图标
If mbytMarked(Y, X) = QUESTION Then
mfrmDisplay.imgPressed.Visible = False
mfrmDisplay.imgQsPressed.Visible = False
mfrmDisplay.imgQsPressed.Left = mCol
mfrmDisplay.imgQsPressed.Top = mRow
mfrmDisplay.imgQsPressed.Visible = True
Else

\' 如果鼠标点下位置上方格没被标记,那么显示鼠标按下图标
mfrmDisplay.imgQsPressed.Visible = False
mfrmDisplay.imgPressed.Visible = False
mfrmDisplay.imgPressed.Left = mCol
mfrmDisplay.imgPressed.Top = mRow
mfrmDisplay.imgPressed.Visible = True

End If
End If
End Sub

\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
\'介绍说明: 当个clsWinMine类型对象被化时,此.从而化游戏中变量和各个标志位以及从新布雷区
\'\' 输入参数: 无
\'
\' 输出参数 : 无
\' \'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Private Sub Class _ Initialize ( )

\'设定当前级别游戏总地雷数
mbytNumMines = 10

\'化被正确标记为有地雷方块个数
mbytCorrectHits = 0

\'化所做标记数(包括标记)
mbytTotalHits = 0

\'化地雷区域总行数
mRows = 8

\'化地雷区域总列数
mCols = 8

\'化被正确标记出来地雷区域行数
mRow = -1

\'化被正确标记出来地雷区域列数
mCol = -1

\'化开始个新游戏标记
mblnNewGame = False

\'化被当鼠标点下时该标记是否正确
mblnHitTestBegun = False

\'化游戏显示主窗体
Set mfrmDisplay = Nothing

\'随机分布地雷位置
InitializeMineField

End Sub


\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
\' \'
\' 介绍说明: 阻止玩家设置不适当地雷行数、列数以及地雷数并将地雷行数、列数以及地雷数设置在适当范围最后将地雷行数、列数以及地雷数存储在游戏clsWinMine类相关属性中 \'
\' 输入参数: Rows: 设定地雷分布区行数 \'
\' Cols: 设定地雷分布区列数 \'
\' bytMines: 设定地雷分布区所包含地雷数 \'
\' blnLevelCustom: 如果是玩家自定义地雷行数、列数以及地雷数那么该值被设为True否则该值被设为假
\'
\' 输出参数 : 无 \'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Public Sub SetMineFieldDimension(Rows As Integer, Cols As Integer, bytMines As Byte, blnLevelCustom As Boolean)

\'取得游戏中设置行列数,并进行比较,使它设置被局限在合适范围的内

mRows = Rows
If Rows < MIN_ROWS Then mRows = MIN_ROWS
If Rows > MAX_ROWS Then mRows = MAX_ROWS
mCols = Cols
If Cols < MIN_COLS Then mCols = MIN_COLS
If Cols > MAX_COLS Then mCols = MAX_COLS

\'并且保证玩家设置地雷数量也合适, (当然具体数量可以自己确定)
mbytNumMines = bytMines
If blnLevelCustom Then
Dim Mines As Integer
Mines = (mRows * mCols) \\ 5
If bytMines < Mines Then
mbytNumMines = Mines
bytMines = Mines
ElseIf bytMines > (Mines * 4) \\ 3 Then
mbytNumMines = (Mines * 4) \\ 3
bytMines = mbytNumMines
End If
End If

If bytMines < MIN_MINES Then mbytNumMines = MIN_MINES
If bytMines > MAX_MINES Then mbytNumMines = MAX_MINES

\' 清除当前窗口显示,开始盘新游戏
mfrmDisplay.Cls

\'根据游戏中设置地雷地图尺寸,调整显示主窗口大小
ResizeDisplay

End Sub

\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \' \'
\' 介绍说明:当游戏clsWinMine类型例子对象被设置为空时候也就是类析构 \'   用来释放游戏中所用到动态内存空间并且腾空存储标记地雷位置内存空间
\' \'
\' 输入参数: 无
\' \'
\' 输出参数: 无 \'
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'

Private Sub Class_Terminate

\' 在类型clsWinMine被析构时释放 3个内存空间
Erase mbytMineStatus
Erase mbytMarked
Erase mbytMineLocations



Dim i As Integer \' 定义循环数

\'腾空存储标记地雷位置内存空间
For i = 1 To mcolWrongLocations.Count
mcolWrongLocations.Remove 1
Next

End Sub


\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'
\' winmine.frm: 这是游戏显示得主窗口她是个和玩家进行互动娱乐主要界面接口并且它\'也显示了winmine.cls 类例子在游戏中运用思路方法
\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'

\'在通用模块中声明个clsWinMine类对象,并且将其命名为objMine.并且objMine对象拥有了
\'clsWinMine类所有属性(也就是变量),思路方法(也就是)

Private objMine As New clsWinMine

\'主窗体被载入时相应以下事件:
Private Sub Form _ Load ( )

\' 通过objMine对象,赋予它所属clsWinMine类frmDisplay属性值,从而设置游戏主窗
\'口为当前窗口,这样当前窗口就可以随着游戏进行而改变窗口显示了
Set objMine.frmDisplay = Me

End Sub


\'菜单新游戏中代码:
Private Sub mnuNew _ Click ( )

\' 准备开始局新游戏.
objMine.NewGame \' objMine对象NewGame思路方法,开始局新游戏.

End Sub


\'选择主窗体中游戏级别为初级水平时,触发此事件
Private Sub mnuBeginner _ Click ( )

\' 将游戏级别中初级水平前画上对勾(即将其选中)
mnuBeginner . Checked = True

\'将游戏级别中其余 3种水平前取消对勾(即不将其选中)
mnuIntermediate . Checked = False
mnuExpert . Checked = False
mnuCustom . Checked = False


\' 设置主窗体中埋雷位置为8 * 8 正方形,其中藏有10个雷,初级水平
objMine.SetMineFieldDimension 8, 8, 10, False

\'并且开始局所设定水平新游戏
objMine.mblnNewGame = True

End Sub


\'选择主窗体中游戏级别为中级水平时,触发此事件
Private Sub mnuIntermediate_Click

mnuBeginner.Checked = False
mnuIntermediate.Checked = True
mnuExpert.Checked = False
mnuCustom.Checked = False

\'设定游戏中地雷分布区域尺寸为中级水平,并且准备开始盘新游戏
objMine.SetMineFieldDimension 16, 16, 40, False
objMine.mblnNewGame = True

End Sub


\'选择主窗体中游戏级别为高级水平时,触发此事件
Private Sub mnuExpert_Click

mnuBeginner.Checked = False
mnuIntermediate.Checked = False
mnuExpert.Checked = True
mnuCustom.Checked = False


\'设定游戏中地雷分布区域尺寸为专家水平,并且准备开始盘新游戏
objMine.SetMineFieldDimension 16, 30, 100, False
objMine.mblnNewGame = True

End Sub


\'选择主窗体中游戏级别为自定义水平时,触发此事件,此事件可以使用户自己决定要玩多大藏雷地图并设定藏有多少颗雷.
Private Sub mnuCustom _ Click ( )

\' 将游戏级别中自定义水平前画上对勾(即将其选中)
\'将游戏级别中其余 3种水平前取消对勾(即不将其选中)
mnuBeginner.Checked = False
mnuIntermediate.Checked = False
mnuExpert.Checked = False
mnuCustom.Checked = True

\'得到前次进行游戏时设定藏雷位置大小,以及藏雷数量
\'并将所得到次进行游戏时设定藏雷位置大小,以及藏雷数量值作为自定义窗体中相应输入框默认值.
objMine.GetMineFieldDimensions frmCustomDlg

frmCustomDlg.Show 1 \' 显示自定义大小及雷数自定义窗体

\' 如果在自定义窗体中,按下键盘左上角Escape键,那么退出自定义窗体
If frmCustomDlg.mblnEscape Then Exit Sub

\' 如果点击自定义窗体中确定按钮,那么将以在自定义窗体中设定藏雷地图大小和所藏雷个数重新建立新扫雷游戏
objMine.SetMineFieldDimension Val(frmCustomDlg.txtRows), Val(frmCustomDlg.txtColumns), Val(frmCustomDlg.txtMines), True

\' 并且释放自定义窗体
Unload frmCustomDlg

\' 按设定,重新开始局新游戏
objMine.mblnNewGame = True

End Sub


\' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'
\' 下面是自定义窗体中中添加相关代码: custdlg . frm: 这是个自定义游戏水平级别窗体,当点击游戏显示主窗体中自定义菜单时候,该对话框会以模式状态显示,所以必须做出选择,否则不能回到游戏现实主窗口进行游戏\'
\'\'* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'

Option Explicit

\'定义了个布尔型变量用来标记在自定义窗口中是否按下了ESC键如果按下了ESC键那么什么都不做直接退出对话框

Public mblnEscape As Boolean
Private Sub cmdEscape_Click

\'当ESC键被按下表示这个对话框中设置将不被保存放弃所以退出对话窗口
\'并且设置变量mblnEscape为真
mblnEscape = True
Unload Me

End Sub


Private Sub cmdOK_Click

\'当对话框上确定按钮被按下那么退出对话窗口但其中设置数值将被保存到相应变量中
Me.Hide

End Sub


Private Sub Form_Load

\'在窗口载入时化变量mblnEscape为假
mblnEscape = False

End Sub


Private Sub Form_Unload(Cancel As Integer)

\'在窗口内存被释放时设置变量mblnEscape为真
mblnEscape = True

End Sub


Private Sub txtColumns_GotFocus



\'当设置对话框中行数文本框得到焦点时那么选中其中文字使其被高亮显示
txtColumns.SelStart = 0
txtColumns.SelLength = Len(txtColumns)

End Sub


Private Sub txtMines_GotFocus

\'当设置对话框中地雷数量文本框得到焦点时那么选中其中文字使其被高亮显示
txtMines.SelStart = 0
txtMines.SelLength = Len(txtMines)

End Sub


Private Sub txtRows_GotFocus

\'当设置对话框中列数文本框得到焦点时那么选中其中文字使其被高亮显示
txtRows.SelStart = 0
txtRows.SelLength = Len(txtRows)

End Sub


\'操作思路方法:
Private Sub Form_MouseDown(Button As Integer, Sht As Integer, x As Single, y As Single)

\'当鼠标左键被按下时出发此事件clsWinMine类BeginHitTest过程来确定点击方格位置
objMine.BeginHitTest Button, x, y

End Sub


Private Sub Form_MouseMove(Button As Integer, Sht As Integer, x As Single, y As Single)

\'当鼠标左键被按下并且经过某个位置时出发此事件clsWinMine类TrackHitTest过程来确定经过方格位置
objMine.TrackHitTest Button, x, y

End Sub


Private Sub Form_MouseUp(Button As Integer, Sht As Integer, x As Single, y As Single)

\'当鼠标左键弹起时出发此事件clsWinMine类TrackHitTest过程来确定鼠标弹起方格位置
objMine.EndHitTest Button, x, y

End Sub


\'* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'
\'添加about对话框
下图是我们添加对话框运行结果,其中我们加入了个安钮(设置它caption属性为cmdok ) , 和个标签Control控件(设置它caption属性为空, 我们在代码中进行了动态设置).下面是主要代码:
图画 About
\'* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'

‘= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
‘cmdOK _ Click ( ) 事件是点击按钮时发生, 语句Unload Me 时释放窗体内存意思
‘= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

Private Sub cmdOK _ Click ( )
Unload Me
End Sub

‘= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
‘Form _ Load ( ) 事件是点击菜单中”有关”时发生, 作用是将窗体载入内存.
‘= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

Private Sub Form_Load

Dim hchh As String ‘定义
hchh = Chr$(13) & Chr$(10) ‘并且将它值设置为回车换行符
Dim AboutMessage As String ‘定义个消息串,用来显示相关有关信息
AboutMessage = hchh & hchh & \"制作人:潇潇\" & hchh
AboutMessage = AboutMessage & \" 2零零 4年 4月末\"
lblAbout.Caption = AboutMessage ‘在标签中显示有关信息

End Sub


\'* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'
\'在主窗体中添加”有关”菜单,并且在主窗体代码窗中添加对有关窗体代码:
\'* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'

Private Sub mnuAboutWinMine _ Click ( )

\'显示”有关”对话框
frmAboutBox.Show 1

End Sub


\'* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'
\'在主窗体中添加”游戏规则介绍说明”菜单,并且在主窗体代码窗中添加对游戏规则介绍说明窗体代码:
\'* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'

Private Sub mnuPlayingInstructions_Click

\' 显示游戏规则介绍说明窗体
frmInstructBox.Show 1

End Sub


\'* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'
\'下面是我们在游戏规则介绍说明窗体中添加代码:
\'当点击游戏规则介绍说明窗体中确定按钮时,释放当前游戏规则介绍说明窗体
\'* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'

Private Sub cmdOK _ Click ( )

Unload Me

End Sub

\'* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'
\'当游戏规则介绍说明窗体载入时显示相关介绍说明,这些介绍说明被定义在youxiguize变量中.
\'* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \'

Private Sub Form_Load

Dim hhhc As String
hhhc = Chr$(13) & Chr$(10)



Dim youxiguize As String
youxiguize = CRLF & \"按下 F2 去开始盘新游戏.\" & CRLF & CRLF
youxiguize = youxiguize & \"这个游戏目标就是要想方设法标记出游戏中包含地雷方格. \"
youxiguize = youxiguize & \"在游戏中你可以通过察看,已经被打开方格中显示得周围8个方格中所包含地雷数,来判断其余地雷随机分布情况. \"
youxiguize = youxiguize & \"如果你在游戏中点开了个包含有地雷方格,那么你就失败了,并且游戏也就随的结束了. \"
youxiguize = youxiguize & \"如果你在游戏中带开个显示数字方格,那么你可以通过这个数字判断周围地雷数,这个数字就是表明了周围8个方格中包含地雷数 \"
youxiguize = youxiguize & \"你可以在个方格上点击鼠标右键,这时会在这个方格位置上显示个小旗标志,它表示这里被你确定为有地雷. \"
youxiguize = youxiguize & \"如果在个被标记为个有地雷方格上再次点击鼠标右键,那么就会再此方格位置上显示个问号图标,表示这个地方你不能确定是否有地雷;如果你在次在此位置上点击鼠标右键,那么将显示个正常方格按钮,恢复最初状态. \"
youxiguize = youxiguize & \"当你不能确定个方格位置是否有地雷,那么这个问号是个有益帮助,你可以在以再返回来研究这个地方. \"
youxiguize = youxiguize & \"你也可以直接在个方格上点击鼠标右键两次,那么它也会在此位置上显示个问号图标. \"
youxiguize = youxiguize & \"当然要想进行游戏,我们必须点击鼠标左键,这样如果点击位置上没有地雷,就会打开这个位置,并且在这个上显示个周围8个方格中所包含地雷个数.\"

txtInstruct . text = youxiguize

End Sub
 

Tags:  面向对象技术 什么是面向对象 面向对象程序设计 面向对象

延伸阅读

最新评论

发表评论