用VC++设计与实现扫雷系统Word文档下载推荐.docx
- 文档编号:16788967
- 上传时间:2022-11-26
- 格式:DOCX
- 页数:21
- 大小:107.31KB
用VC++设计与实现扫雷系统Word文档下载推荐.docx
《用VC++设计与实现扫雷系统Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《用VC++设计与实现扫雷系统Word文档下载推荐.docx(21页珍藏版)》请在冰豆网上搜索。
雷区和提示区。
提示区包括两个计数器和一个按键操作结果图像提示。
游戏过程中,当玩家用鼠标点击相应的方块,程序就会作出相应的鼠标响应事件,并伴随着GDI绘图,而众多鼠标事件的处理,都是围绕着实现扫雷程序的算法而衍生的。
3总体设计
3.1游戏框架的搭建
3.1.1工程项目的创建
利用应用程序向导创建一个名称为Mine的工程项目。
由于不需要诸如工具栏、状态栏等功能,并且扫雷游戏的框架是不允许改变窗口大小的,所以在向导的第四步里面把所有的选项置空,然后点击“Advanced”按钮,在弹出的对话框中选中“WindowsStyles”选项卡,将“Maximizebox”项置空,其他均使用默认设置。
3.1.2框架的改造
通过类向导添加一个继承于CFrameWnd的类,命名为CMineWnd,删除CMineDoc、CMineView和CAboutDlg类,将CMineWnd类代替CFrameWnd,让程序启动的时候以此窗口为主窗口予以显示。
结果如图1.2。
图1.2
3.2菜单的制作
参考Windows自带的扫雷游戏,创建出“游戏”和“帮助”菜单,然后通过菜单资源编辑器设定菜单的功能选项,包括难度级别的选择、颜色和音效是否开启、扫雷英雄榜、使用手册、关于软件的信息等。
具体的菜单选项分别如图1.3。
3.2.1难度级别的选择
不同的难度级别有不同的雷区大小和不同的布雷数目,所以需要设置游戏的难度级别。
其宏定义如下所示,预定义了不同级别的横向方块数目、纵向方块数目和雷数。
并将该宏定义放入新建的头文件“MineDefs.h”中。
#definePRIMARY_XNUM9//初级x方向的方块区域数目
#definePRIMARY_YNUM9//初级y方向的方块区域数目
#definePRIMARY_MINENUM10//初级雷的数目
#defineSECONDRY_XNUM16
#defineSECONDRY_YNUM16
#defineSECONDRY_MINENUM40
#defineADVANCE_XNUM30
#defineADVANCE_YNUM16
#defineADVANCE_MINENUM99
窗口除了雷区外至少还包括蓝色窗口边缘Frame_wide、白色的视觉效果区line_wide、3D的外壳边框3D_line_wide、雷区mine_area_wide等。
于是还需要定义关于位置的宏变量,如下所示。
//窗口宽度相关定义
#defineDEFAULT_FRAME_X6//窗口X方向宽
#defineDEFAULT_FRAME_Y52//窗口Y方向宽
#defineLINE_WIDTH_03//线边0的宽度
#defineLINE_WIDTH_12//线边1的宽度
#defineSIDE_WIDTH_06//边0的宽度
#defineSIDE_WIDTH_15//边1的宽度
#defineSHELL_S_H37//小外壳的高度
#defineSHELL_S_START_X9//小外壳的X坐标始发点
#defineSHELL_S_START_Y9//小外壳的Y坐标始发点
#defineSHELL_L_START_X9//大外壳的X坐标始发点
#defineSHELL_L_START_Y52//大外壳的Y坐标始发点
#defineMINEAREA_FRAME_X12
#defineMINEAREA_FRAME_Y55
//雷方块定义
#defineMINE_WIDTH16//雷方块的大小(宽度为16的位图)
#defineMINE_HEIGHT16
#defineMINE_AREA_LEFT12
#defineMINE_AREA_TOP55
由于难度级别的不同,窗口大小也会随之改变,因此通过在CMineWnd类增加一个改变窗口大小的函数SizeWindow()去实现,其代码如下所示。
voidCMineWnd:
:
SizeWindow(void)
{
//宽度
UINTuWidth=DEFAULT_FRAME_X+m_uXNum*MINE_WIDTH+
LINE_WIDTH_0*3+SIDE_WIDTH_0+SIDE_WIDTH_1;
//高度
UINTuHeight=DEFAULT_FRAME_Y+m_uYNum*MINE_HEIGHT+
LINE_WIDTH_0*3+SIDE_WIDTH_0*2+SIDE_WIDTH_1+SHELL_S_H;
//改变窗口大小
SetWindowPos(&
wndTopMost,0,0,uWidth,uHeight,
SWP_NOZORDER|SWP_NOMOVE|SWP_NOCOPYBITS);
GetClientRect(&
m_rcClient);
//笑脸按钮位置
m_uBtnRect[0]=m_rcClient.right/2-12;
m_uBtnRect[1]=m_rcClient.right/2-13;
m_uBtnRect[2]=m_rcClient.right/2+12;
//计数器位置
m_uNumRect[0]=m_rcClient.right-55;
m_uNumRect[1]=m_rcClient.right-15;
m_uNumRect[2]=m_rcClient.right-54;
//3D效果外壳位置
m_uShellRcX[0]=m_rcClient.right;
m_uShellRcX[1]=m_rcClient.right-14;
m_uShellRcY[0]=m_rcClient.bottom;
m_uShellRcY[1]=m_rcClient.bottom-SHELL_L_START_Y-5;
}
通过ClassWizard分别选择“初级”、“中级”和“高级”菜单资源ID,为它们添加处理函数OnMenuPrimary()、OnMenuSecond()、OnMenuAdvance()。
OnMenuAdvance()的实现如下,另外两个类似。
OnMenuAdvance()
m_uLevel=LEVEL_ADVANCE;
m_uXNum=ADVANCE_XNUM;
m_uYNum=ADVANCE_YNUM;
m_uMineNum=ADVANCE_MINENUM;
SetCheckedLevel();
InitGame();
Invalidate();
SizeWindow();
为了实现玩家对雷区大小的自定义,首先新建一个自定义雷区对话框资源(IDD_DLG_CUSTOM),然后添加高度、宽度、雷数三个静态文本控件和三个对应的(IDC_HEIGHT)、(IDC_WIDTH)、(IDC_NUMBER)编辑框控件,最后将OK和Cancel按钮分别改名为“确定”和“取消”。
首先为该对话框创建CDlgCustom类,然后为三个编辑控件分别添加关联变量m_uHeight、m_uNumber、m_uWidth,最后为OK按钮创建命令消息处理函数OnOK(),代码如下所示。
voidCDlgCustom:
OnOK()
UpdateData();
if(m_uWidth<
9)m_uWidth=9;
if(m_uWidth>
30)m_uWidth=30;
if(m_uHeight<
9)m_uHeight=9;
if(m_uHeight>
24)m_uHeight=24;
if(m_uNumber<
10)m_uNumber=10;
if(m_uNumber>
m_uWidth*m_uHeight)m_uNumber=m_uWidth*m_uHeight-1;
CMineWnd*pMine=(CMineWnd*)AfxGetMainWnd();
pMine->
SetCustom(m_uWidth,m_uHeight,m_uNumber);
//TODO:
Addextravalidationhere
CDialog:
OnOK();
3.2.2使用帮助的实现
由于Windows自带有扫雷游戏,所以直接调用它的使用手。
为“使用帮助”菜单选项创建命令消息处理函数OnMemuHelpUse(),代码如下所示。
OnMemuHelpUse()
{
//在命令行调用HH.exe,并输入参数NTHelp.CHM,
//令其打开该文件,即Windows自带有扫雷游戏的是使用手册
:
WinExec("
HHNTHelp.CHM"
SW_SHOW);
3.2.3关于信息的实现
OnMemuAbout()
ShellAbout(this->
m_hWnd,"
扫雷"
"
yixiaoqianjin@"
NULL);
3.3布雷,扫雷核心算法的设计与实现
把整个雷区看成一个二维数组,a[i][j]周围的雷个数是由如下8个雷区决定的(如果超出边界,应该再加以判断):
a[i-1][j-1],a[i-1][j],a[i-1][j+1],
a[i][],a[i][j+1],
a[i+1][j-1],a[i+1][j],a[i+1][j+1],
在被展开时,检查周围的雷数是否与周围标示出来的雷数相等,如果相等则展开周围未标示的雷区。
这样新的雷区展开又触发这个事件,就这样递归下去,一直蔓延到不可展开的雷区。
定义雷方块的数据结构,具体描述如下所示。
typedefstruct
UINTuRow;
//所在雷区二维数组的行
UINTuCol;
//所在雷区二位数组的列
UINTuState;
//当前状态
UINTuAttrib;
//方块属性
UINTuOldState;
//历史状态
}MINEWND;
//雷方块结构体
定义雷方块的状态类别和属性类别,具体描述如下所示。
#defineSTATE_NORMAL0//正常
#defineSTATE_FLAG1//标志有雷
#defineSTATE_DICEY2//未知状态0
#defineSTATE_BLAST3//爆炸状态
#defineSTATE_ERROR4//错误状态
#defineSTATE_MINE5//雷状态
#defineSTATE_DICEY_DOWN6//未知状态1
#defineSTATE_NUM87//周围有8雷
#defineSTATE_NUM78
#defineSTATE_NUM69
#defineSTATE_NUM510
#defineSTATE_NUM411
#defineSTATE_NUM312
#defineSTATE_NUM213
#defineSTATE_NUM114
#defineSTATE_EMPTY15//无雷
#defineATTRIB_EMPTY0//非雷
#defineATTRIB_MINE1//雷
整个游戏程序包含3个阶段:
布雷、扫雷过程和结果(并不是操作结果展示,而是在扫雷过程中,玩家通过与游戏交互后的操作结果展示)。
3.3.1布雷
随即获取一个状态为非雷的点,将它的属性标志为雷,重复这样的工作,直到布下足够的雷为止,其流程如图1.4所示。
图1.4
在CMineWnd类中添加游戏的布雷模块的处理函数,该函数的实现如下。
LayMines(UINTrow,UINTcol)
//埋下随机种子
srand((unsigned)time(NULL));
UINTi,j;
for(UINTindex=0;
index<
m_uMineNum;
)
{
//取随即数
i=rand()%m_uYNum;
j=rand()%m_uXNum;
if(i==row&
&
j==col)continue;
if(m_pMines[i][j].uAttrib!
=ATTRIB_MINE)
{
m_pMines[i][j].uAttrib=ATTRIB_MINE;
//修改属性为雷
index++;
}
}
3.3.2扫雷
鼠标左击事件
鼠标左击事件流程如图1.5
图1.5
当鼠标左键点击雷区域,并且该区域不是雷方块,需要进行打开以及拓展工作。
流程如图1.6
图1.6
鼠标左键点击事件的关键代码如下所示。
OnLButtonUp(UINTnFlags,CPointpoint)
//笑脸图按钮所在的区域
CRectrcBtn(m_uBtnRect[1],15,m_uBtnRect[2],39);
//雷区所在的区域
CRectrcMineArea(MINE_AREA_LEFT,MINE_AREA_TOP,
MINE_AREA_LEFT+m_uXNum*MINE_WIDTH,
MINE_AREA_TOP+m_uYNum*MINE_HEIGHT);
if(rcBtn.PtInRect(point))
{//点击笑脸图
Invalidate();
InitGame();
}
elseif(rcMineArea.PtInRect(point))
{//点击雷区域
CStringvalue;
UINTaround=0;
//根据不同的游戏状态作处理
switch(m_uGameState)
//游戏进行状态
caseGS_WAIT:
caseGS_RUN:
//firstgettheMINEWNDwhichifpushingdown
m_pOldMine=GetMine(point.x,point.y);
if(!
m_pOldMine)
{
ReleaseCapture();
return;
}
//检测判断当前状态是否为左右鼠标同时按下
if(m_bLRBtnDown)
m_bLRBtnDown=FALSE;
OnLRBtnUp(m_pOldMine->
uRow,m_pOldMine->
uCol);
if(m_uGameState==GS_WAIT)
{
m_uBtnState=BUTTON_NORMAL;
Invalidate();
ReleaseCapture();
return;
}
//假若周围已经标识的雷=周围真正的雷数,拓展
if(m_pOldMine->
uState!
=STATE_FLAG)
OpenAround(m_pOldMine->
if(ErrorAroundFlag(m_pOldMine->
uCol))
Dead(m_pOldMine->
else
//如果游戏尚未开始,点击左键启动游戏
if(m_uGameState==GS_WAIT)
if(m_uTimer)
{
KillTimer(ID_TIMER_EVENT);
m_uTimer=0;
}
m_uSpendTime=1;
if(m_bSoundful)
sndPlaySound((LPCTSTR)LockResource(m_pSndClock),SND_MEMORY|SND_ASYNC|SND_NODEFAULT);
//启动定时器
m_uTimer=SetTimer(ID_TIMER_EVENT,1000,NULL);
//布雷
LayMines(m_pOldMine->
//layalltheminesdown
//改变游戏状态为"
运行/GS_RUN"
m_uGameState=GS_RUN;
uOldState==STATE_NORMAL)
{//当该雷区域为正常未作标记才打开
//如果该区域为雷,则死亡
if(IsMine(m_pOldMine->
uCol))
Dead(m_pOldMine->
ReleaseCapture();
return;
//thespecialMINEWNDisnotamine
//不是雷的时候,获取其周围的雷数目
around=GetAroundNum(m_pOldMine->
//如果为空白区域,拓展,否则打开该区域(显示周围有多少雷数)
if(around==0)ExpandMines(m_pOldMine->
elseDrawDownNum(m_pOldMine,around);
elseif(m_pOldMine->
uOldState==STATE_DICEY)
//标志为“?
”问号的时候
m_pOldMine->
uState=STATE_DICEY;
//判断是否为胜利
if(Victory())
break;
caseGS_VICTORY:
caseGS_DEAD:
ReleaseCapture();
//releasethecursor
return;
default:
m_uBtnState=BUTTON_NORMAL;
Invalidate();
else
{//点击非雷区域
if(m_uGameState==GS_WAIT||m_uGameState==GS_RUN)
m_uBtnState=BUTTON_NORMAL;
InvalidateRect(rcBtn);
ReleaseCapture();
CWnd:
OnLButtonUp(nFlags,point);
在函数体的开始部分,先用rcBtn和rcMineArea两个矩形变量存储游戏的用户提示区域位置中的笑脸图区域以及雷区域的位置。
利用接口函数PtInRect()判断当前鼠标的位置(由参数point携带鼠标当前位置信息)是否在这两个区域内,如果检测到鼠标左键点击并释放在笑脸图的按钮区域rcBtn上,则调用初始化函数重新开始游戏,如果检测到鼠标左键点击并释放在雷区域rcMineArea
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- VC 设计 实现 扫雷 系统
![提示](https://static.bdocx.com/images/bang_tan.gif)