计算机图形学课程设计扫雷游戏程序设计.docx
《计算机图形学课程设计扫雷游戏程序设计.docx》由会员分享,可在线阅读,更多相关《计算机图形学课程设计扫雷游戏程序设计.docx(19页珍藏版)》请在冰豆网上搜索。
计算机图形学课程设计扫雷游戏程序设计
《计算机图形学》课程设计报告
VC++扫雷游戏的程序设计
专业班级:
小组成员:
指导老师:
日期:
2012年12月24日
1、需求分析
本课程设计实现类似于WindowsXP操作系统自带的扫雷游戏。
该设计以VisualC++6.0为开发环境,Windows7/XP为程序运行平台。
在程序设计中,把整个雷区看成一个二维数组,把雷方块定义为具有所在雷区二维数组的行和列、当前状态、方块属性、历史状态的结构体,采用了MFC机制解决问题的方法。
整个游戏程序包括了布雷、扫雷过程和结果三个阶段,在处理鼠标响应事件中伴随着GDI绘图。
程序通过调试运行,实现简单的设计目标,满足扫雷游戏初学者的需要。
通过本课程设计,以便更好的巩固计算机图形学相关知识,掌握课程设计基本的方法和技巧,同时增加同学之间的团队合作精神以及培养分析问题、解决问题的能力。
2.总体设计
2.1功能概述
扫雷游戏的游戏界面如图1所示。
在这个界面中,由众多面积均等的小方块所组成的区域称之为雷区,雷区的大小由用户设置的游戏等级决定。
图1
游戏开始时,系统会在雷区中随机布下若干个地雷。
安放地雷的小方块称之为雷方块,其他的称之为非雷方块。
部署完毕后,系统会在其他非雷方块中填充一些数字。
某一个具体数字表示与其紧邻的8个方块中有多少雷方块。
玩家可以根据这些信息去判断是否可以鼠标点击方块,并把认为是地雷的方块打上标识。
当玩家将所有地雷找出后,其余的非雷方块区域都已打开,此时游戏结束。
在游戏过程中,一旦错误地打开了雷方块则立即失败,游戏结束。
游戏规则总结:
●开始:
按左键开始游戏,按按钮或菜单重新开始。
●左键:
按下时,是雷则结束,非雷则显示数字。
●数字:
代表此数字周围一圈八格中雷的个数。
●右键:
奇次按下表示雷,偶数按下表示对上次的否定。
●结束:
左键按到雷结束,找出全部雷结束。
在游戏开始后,雷区上方有两个计数器。
右边的计数器显示用户扫雷所花费的总时间,以秒为单位;左边的计数器显示当前还剩余多少个雷方块。
2.2设计流程图
3.详细设计
3.1界面设置
在vc++运行环境下编写程序并调试,最终实现的游戏界面为图2所示,点击鼠标可开始游戏。
图2
3.2功能设计
3.2.1布雷
随即获取一个状态为非雷的点,将它的属性标志为雷,重复这样的工作,直到布下足够的雷为止,其流程图如图3所示。
图3
在CMineWnd类中添加游戏的布雷模块的处理函数,其具体实现代码如下所示。
voidCMineWnd:
:
LayMines(UINTrow,UINTcol)
{
//埋下随机种子
srand((unsigned)time(NULL));
UINTi,j;
for(UINTindex=0;index{//取随即数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.2.2扫雷3.2.2.1鼠标左击事件其流程如图4所示。图4当鼠标左键点击雷区域,并且该区域不是雷方块,需要进行打开以及拓展工作。流程如图5所示。图5鼠标左键点击事件的关键代码详见附录2所示。在函数体的开始部分,先用rcBtn和rcMineArea两个矩形变量存储游戏的用户提示区域位置中的笑脸图区域以及雷区域的位置。利用接口函数PtInRect()判断当前鼠标的位置(由参数point携带鼠标当前位置信息)是否在这两个区域内,如果检测到鼠标左键点击并释放在笑脸图的按钮区域rcBtn上,则调用初始化函数重新开始游戏,如果检测到鼠标左键点击并释放在雷区域rcMineArea,假若当前游戏状态处于已初始化完成但尚未开始的状态GS_WAIT时,则打开计时器,并且调用LayMines()函数进行布雷,然后修改游戏状态为GS_RUN进入游戏。接着判断点击在小方块的状态是否被用于通过右键标记(可以标记为雷或者未知,此时游戏规则规定左键点击不生效),如果未标记,该状态为普通状态STATE_NORMAL时,先通过IsMine()检测是否点中地雷而失败地结束游戏,如果是,则调用函数Dead()来进行失败后的工作处理,反之对它进行打开显示与拓展操作。先通过GetAroundNum()函数获取当前小方块相邻的8个位置的雷数。如果当前小方块相邻区域的雷数为0,则可以向8个方向进行拓展,并显示该方块区域,直到不可拓展为止;如果当前小方块相邻区域的雷数不为0,则显示该方块区域的相邻雷数,用作提供用户对其他位置的信息判断的提示。拓展操作的实现代码见附录2。经过打开或拓展后,最后通过Victory()判断游戏是否已经胜利结束,如果是则作胜利处理。3.2.1.2鼠标右击事件其流程如图6所示。图6其实现代码详见附录3所示。3.2.3绘图界面的设计3.2.3.1雷区、笑脸模块的绘制添加三个位图资源如图7所示。图7ID分别为ID_BTN_COLOR、ID_MINE_COLOR、ID_NUM_COLOR,并分别添加三个位图类型的变量,然后调用LoadBitmap(UINTnIDResource)函数来实现位图资源与变量的关联并添加函数DrawButton()、DrawMineArea()、DrawNumber()分别实现笑脸按钮、雷区、数字图像(计时器数字和剩余雷数数字)的绘图。绘制雷区的函数DrawMineArea()的实现代码见附录4所示。绘制笑脸按钮的函数实现类似。3.2.3.2数字模块的绘制数字图像的绘制不是由鼠标事件触发的,而是由系统时间触发的。首先在CMineWnd类中添加定时器标识的成员变量m_uTimer和一个记录游戏开始直到目前所花费的时间的成员变量m_uSpendTime。接着在游戏的开始函数布下时间种子,时间间隔为1000us,然后选择到预定时间间隔后发送Windows命令消息函数WM_TIMER。接着在CMineWnd类中找到对应的消息WM_TIMER,并为其添加重写函数OnTimer(),该函数首先判断这次的WM_TIMER命令是否为所布下的时间种子到时而产生的,如果是则让使用的时间变量m_uSpendTime自增,然后通知系统重绘图像。数字图像的绘制完成后,最终游戏界面显示如图8所示。图8雷区、笑脸按钮、3D效果外壳和数字图像的绘制都是在OnPaint()函数中实现的。其函数代码见附录5所示。5测试运行调试运行结果,直接点击鼠标左键开始游戏,踩中地雷是的界面显示如图9所示;图9未踩中地雷并扫完所有的雷时的界面显示如图10所示。图106结论及分析随着扫雷游戏的开发完成,本游戏中预期的主要功能也基本实现。本论文阐述了扫雷游戏的分析与设计的全过程,并在论文中相应的位置插入了图片、流程图以及一些具有技巧性的程序代码,更加清晰的描述了该游戏是如何实现的。扫雷游戏是一款益智类游戏,该游戏与那些网络游戏和3D游戏相比,它有编写简单容易上手等特点,非常适合人们在完成工作的时候适当的娱乐要求。这些小游戏大都是以益智和娱乐为目的,不仅给紧张工作的人们以放松,还可以让人们的大脑得到开发。通过这次课程设计收获很多,尤其是加深了对计算机图形学知识和递归算法以及VC的理解,将课堂上所学的理论知识应用到了实际中。但是由于我们对VisualC++6.0的掌握程度不够好,其中的很多知识还没有理解,所以在课程设计过程中,遇到了很多问题,我们小组利用图书馆和网上查阅相关资料,以及周围同学的帮助,最终大部分问题得到解决。设计所实现的功能是windowsxp系统自带扫雷游戏的简化,相比之下,功能不够齐全,比如声音、用户自定义以及玩家成绩的排名等都未能实现。但是通过不断上机实验,调试程序,总结经验,动手实践能力和解决问题的能力有很大提高。希望在以后的工作和学习中不断的充实自己的知识结构,把扫雷游戏的功能进一步完善,使它成为一个更具有实用价值的游戏软件。参考文献[1]杨钦等.计算机图形学.清华大学出版社,2005[2]BenSawyer.游戏软件设计与开发指南[M].北京:人民邮电出版社,1998.8~46[3]陈志泊等.VisualC++程序设计.中国铁道出版社.2005.7[4]钦科技.VisualC++游戏设计[M].北京:科海电子出版社,2003.1~211[5]杨正华等.VisualC++游戏编程导学[M].北京:清华大学出版社,2003.206~268 附录附录1:扫雷的处理函数//鼠标左键点击事件代码voidCMineWnd::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://firstgettheMINEWNDwhichifpushingdownm_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->uRow,m_pOldMine->uCol);}if(ErrorAroundFlag(m_pOldMine->uRow,m_pOldMine->uCol)){Dead(m_pOldMine->uRow,m_pOldMine->uCol);ReleaseCapture();return;}}else{//如果游戏尚未开始,点击左键启动游戏if(m_uGameState==GS_WAIT){if(m_uTimer){KillTimer(ID_TIMER_EVENT);m_uTimer=0;}m_uSpendTime=1;Invalidate();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->uRow,m_pOldMine->uCol);//改变游戏状态为"运行/GS_RUN"m_uGameState=GS_RUN;}if(m_pOldMine->uOldState==STATE_NORMAL){//当该雷区域为正常未作标记才打开//如果该区域为雷,则死亡if(IsMine(m_pOldMine->uRow,m_pOldMine->uCol)){Dead(m_pOldMine->uRow,m_pOldMine->uCol);ReleaseCapture();return;}//不是雷的时候,获取其周围的雷数目around=GetAroundNum(m_pOldMine->uRow,m_pOldMine->uCol);//如果为空白区域,拓展,否则打开该区域(显示周围有多少雷数)if(around==0)ExpandMines(m_pOldMine->uRow,m_pOldMine->uCol);elseDrawDownNum(m_pOldMine,around);}elseif(m_pOldMine->uOldState==STATE_DICEY){//标志为“?”问号的时候m_pOldMine->uState=STATE_DICEY;}//判断是否为胜利if(Victory()){Invalidate();ReleaseCapture();return;}}break;caseGS_VICTORY:caseGS_DEAD:ReleaseCapture();return;default:break;}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);}//拓展操作的实现代码voidCMineWnd::ExpandMines(UINTrow,UINTcol){UINTi,j;UINTminRow=(row==0)?0:row-1;UINTmaxRow=row+2;UINTminCol=(col==0)?0:col-1;UINTmaxCol=col+2;UINTaround=GetAroundNum(row,col);//显示该区域的方块状态m_pMines[row][col].uState=15-around;m_pMines[row][col].uOldState=15-around;//“打开”该区域,重绘DrawSpecialMine(row,col);//对周围一个雷都没有的空白区域if(around==0){for(i=minRow;i{for(j=minCol;j{//对于周围可以拓展的区域进行的规拓展if(!(i==row&&j==col)&&m_pMines[i][j].uState==STATE_NORMAL&&m_pMines[i][j].uAttrib!=ATTRIB_MINE){if(!IsInMineArea(i,j))continue;ExpandMines(i,j);//递归拓展操作}}}}}附录2:鼠标右键点击事件代码voidCMineWnd::OnRButtonDown(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);m_bLRBtnDown=FALSE;if(rcMineArea.PtInRect(point)){//点击雷区域if(m_uGameState==GS_WAIT||m_uGameState==GS_RUN){m_pNewMine=GetMine(point.x,point.y);if(!m_pNewMine)return;//检测判断当前状态是否为左右鼠标同时按下if(nFlags==(MK_LBUTTON|MK_RBUTTON)){m_bLRBtnDown=TRUE;OnLRBtnDown(m_pNewMine->uRow,m_pNewMine->uCol);}else{switch(m_pNewMine->uState){//普通状态caseSTATE_NORMAL:m_pNewMine->uState=STATE_FLAG;m_pNewMine->uOldState=STATE_FLAG;m_nLeaveNum--;break;//标记状态caseSTATE_FLAG:m_pNewMine->uState=STATE_DICEY;m_pNewMine->uOldState=STATE_DICEY;m_nLeaveNum++;break;//未知状态caseSTATE_DICEY:m_pNewMine->uState=STATE_NORMAL;m_pNewMine->uOldState=STATE_NORMAL;break;default:break;}}Invalidate();}}CWnd::OnRButtonDown(nFlags,point);}附录3:绘制雷区的函数代码voidCMineWnd::DrawMineArea(CPaintDC&dc){CDCdcMemory;//用作内存设备//源设备dcMemory.CreateCompatibleDC(&dc);//使得这个设备与dc兼容//dc是目标设备dcMemory.SelectObject(m_bmpMine);//将内存设备与位图资源关联for(UINTi=0;i{for(UINTj=0;j{//根据[i][j]区域的雷方块状态拷贝相应的图像到[i][j]雷区的特定区域dc.StretchBlt(MINEAREA_FRAME_X+16*j,MINEAREA_FRAME_Y+16*i,16,16,&dcMemory,0,16*m_pMines[i][j].uState,16,16,SRCCOPY);}}}附录4:雷区、笑脸按钮、3D效果外壳和数字图像的绘制代码voidCMineWnd::OnPaint(){CPaintDCdc(this);//创建一个CPaintDC类型的用以屏幕显示的dc设备//参数是指向当前框架窗口CDCdcMemory;//内存设备CBitmapbitmap;//创建临时的位图资源if(!dc.IsPrinting())//判断不是使用打印机来进行绘制工作{//使内存设备与dc设备兼容if(dcMemory.CreateCompatibleDC(&dc)){//使得bitmap与实际显示的dc设备兼容if(bitmap.CreateCompatibleBitmap(&dc,m_rcClient.right,m_rcClient.bottom)){//内存设备选择物件-位图dcMemory.SelectObject(&bitmap);//绘制背景框dcMemory.FillRect(&m_rcClient,&m_brsBG);DrawButton((CPaintDC&)dcMemory);//笑脸按钮绘图DrawNumber((CPaintDC&)dcMemory);//数字图像绘图DrawShel
//取随即数
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.2.2扫雷
3.2.2.1鼠标左击事件
其流程如图4所示。
图4
当鼠标左键点击雷区域,并且该区域不是雷方块,需要进行打开以及拓展工作。
流程如图5所示。
图5
鼠标左键点击事件的关键代码详见附录2所示。
在函数体的开始部分,先用rcBtn和rcMineArea两个矩形变量存储游戏的用户提示区域位置中的笑脸图区域以及雷区域的位置。
利用接口函数PtInRect()判断当前鼠标的位置(由参数point携带鼠标当前位置信息)是否在这两个区域内,如果检测到鼠标左键点击并释放在笑脸图的按钮区域rcBtn上,则调用初始化函数重新开始游戏,如果检测到鼠标左键点击并释放在雷区域rcMineArea,假若当前游戏状态处于已初始化完成但尚未开始的状态GS_WAIT时,则打开计时器,并且调用LayMines()函数进行布雷,然后修改游戏状态为GS_RUN进入游戏。
接着判断点击在小方块的状态是否被用于通过右键标记(可以标记为雷或者未知,此时游戏规则规定左键点击不生效),如果未标记,该状态为普通状态STATE_NORMAL时,先通过IsMine()检测是否点中地雷而失败地结束游戏,如果是,则调用函数Dead()来进行失败后的工作处理,反之对它进行打开显示与拓展操作。
先通过GetAroundNum()函数获取当前小方块相邻的8个位置的雷数。
如果当前小方块相邻区域的雷数为0,则可以向8个方向进行拓展,并显示该方块区域,直到不可拓展为止;如果当前小方块相邻区域的雷数不为0,则显示该方块区域的相邻雷数,用作提供用户对其他位置的信息判断的提示。
拓展操作的实现代码见附录2。
经过打开或拓展后,最后通过Victory()判断游戏是否已经胜利结束,如果是则作胜利处理。
3.2.1.2鼠标右击事件
其流程如图6所示。
图6
其实现代码详见附录3所示。
3.2.3绘图界面的设计
3.2.3.1雷区、笑脸模块的绘制
添加三个位图资源如图7所示。
图7
ID分别为ID_BTN_COLOR、ID_MINE_COLOR、ID_NUM_COLOR,并分别添加三个位图类型的变量,然后调用LoadBitmap(UINTnIDResource)函数来实现位图资源与变量的关联并添加函数DrawButton()、DrawMineArea()、DrawNumber()分别实现笑脸按钮、雷区、数字图像(计时器数字和剩余雷数数字)的绘图。
绘制雷区的函数DrawMineArea()的实现代码见附录4所示。
绘制笑脸按钮的函数实现类似。
3.2.3.2数字模块的绘制
数字图像的绘制不是由鼠标事件触发的,而是由系统时间触发的。
首先在CMineWnd类中添加定时器标识的成员变量m_uTimer和一个记录游戏开始直到目前所花费的时间的成员变量m_uSpendTime。
接着在游戏的开始函数布下时间种子,时间间隔为1000us,然后选择到预定时间间隔后发送Windows命令消息函数WM_TIMER。
接着在CMineWnd类中找到对应的消息WM_TIMER,并为其添加重写函数OnTimer(),该函数首先判断这次的WM_TIMER命令是否为所布下的时间种子到时而产生的,如果是则让使用的时间变量m_uSpendTime自增,然后通知系统重绘图像。
数字图像的绘制完成后,最终游戏界面显示如图8所示。
图8
雷区、笑脸按钮、3D效果外壳和数字图像的绘制都是在OnPaint()函数中实现的。
其函数代码见附录5所示。
5测试运行
调试运行结果,直接点击鼠标左键开始游戏,踩中地雷是的界面显示如图9所示;
图9
未踩中地雷并扫完所有的雷时的界面显示如图10所示。
图10
6结论及分析
随着扫雷游戏的开发完成,本游戏中预期的主要功能也基本实现。
本论文阐述了扫雷游戏的分析与设计的全过程,并在论文中相应的位置插入了图片、流程图以及一些具有技巧性的程序代码,更加清晰的描述了该游戏是如何实现的。
扫雷游戏是一款益智类游戏,该游戏与那些网络游戏和3D游戏相比,它有编写简单容易上手等特点,非常适合人们在完成工作的时候适当的娱乐要求。
这些小游戏大都是以益智和娱乐为目的,不仅给紧张工作的人们以放松,还可以让人们的大脑得到开发。
通过这次课程设计收获很多,尤其是加深了对计算机图形学知识和递归算法以及VC的理解,将课堂上所学的理论知识应用到了实际中。
但是由于我们对VisualC++6.0的掌握程度不够好,其中的很多知识还没有理解,所以在课程设计过程中,遇到了很多问题,我们小组利用图书馆和网上查阅相关资料,以及周围同学的帮助,最终大部分问题得到解决。
设计所实现的功能是windowsxp系统自带扫雷游戏的简化,相比之下,功能不够齐全,比如声音、用户自定义以及玩家成绩的排名等都未能实现。
但是通过不断上机实验,调试程序,总结经验,动手实践能力和解决问题的能力有很大提高。
希望在以后的工作和学习中不断的充实自己的知识结构,把扫雷游戏的功能进一步完善,使它成为一个更具有实用价值的游戏软件。
参考文献
[1]杨钦等.计算机图形学.清华大学出版社,2005
[2]BenSawyer.游戏软件设计与开发指南[M].北京:
人民邮电出版社,1998.8~46
[3]陈志泊等.VisualC++程序设计.中国铁道出版社.2005.7
[4]钦科技.VisualC++游戏设计[M].北京:
科海电子出版社,2003.1~211
[5]杨正华等.VisualC++游戏编程导学[M].北京:
清华大学出版社,2003.206~268
附录
附录1:
扫雷的处理函数
//鼠标左键点击事件代码
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;
ReleaseCapture();
//假若周围已经标识的雷=周围真正的雷数,拓展
if(m_pOldMine->uState!
=STATE_FLAG)
OpenAround(m_pOldMine->uRow,m_pOldMine->uCol);
if(ErrorAroundFlag(m_pOldMine->uRow,m_pOldMine->uCol))
Dead(m_pOldMine->uRow,m_pOldMine->uCol);
else
//如果游戏尚未开始,点击左键启动游戏
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->uRow,m_pOldMine->uCol);
//改变游戏状态为"运行/GS_RUN"
m_uGameState=GS_RUN;
if(m_pOldMine->uOldState==STATE_NORMAL)
{//当该雷区域为正常未作标记才打开
//如果该区域为雷,则死亡
if(IsMine(m_pOldMine->uRow,m_pOldMine->uCol))
{Dead(m_pOldMine->uRow,m_pOldMine->uCol);
//不是雷的时候,获取其周围的雷数目
around=GetAroundNum(m_pOldMine->uRow,m_pOldMine->uCol);
//如果为空白区域,拓展,否则打开该区域(显示周围有多少雷数)
if(around==0)ExpandMines(m_pOldMine->uRow,m_pOldMine->uCol);
elseDrawDownNum(m_pOldMine,around);
elseif(m_pOldMine->uOldState==STATE_DICEY)
//标志为“?
”问号的时候
m_pOldMine->uState=STATE_DICEY;
//判断是否为胜利
if(Victory())
break;
caseGS_VICTORY:
caseGS_DEAD:
default:
m_uBtnState=BUTTON_NORMAL;
{//点击非雷区域
if(m_uGameState==GS_WAIT||m_uGameState==GS_RUN)
InvalidateRect(rcBtn);
CWnd:
OnLButtonUp(nFlags,point);
//拓展操作的实现代码
ExpandMines(UINTrow,UINTcol)
UINTminRow=(row==0)?
0:
row-1;
UINTmaxRow=row+2;
UINTminCol=(col==0)?
col-1;
UINTmaxCol=col+2;
UINTaround=GetAroundNum(row,col);
//显示该区域的方块状态
m_pMines[row][col].uState=15-around;
m_pMines[row][col].uOldState=15-around;
//“打开”该区域,重绘
DrawSpecialMine(row,col);
//对周围一个雷都没有的空白区域
if(around==0)
for(i=minRow;i{for(j=minCol;j{//对于周围可以拓展的区域进行的规拓展if(!(i==row&&j==col)&&m_pMines[i][j].uState==STATE_NORMAL&&m_pMines[i][j].uAttrib!=ATTRIB_MINE){if(!IsInMineArea(i,j))continue;ExpandMines(i,j);//递归拓展操作}}}}}附录2:鼠标右键点击事件代码voidCMineWnd::OnRButtonDown(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);m_bLRBtnDown=FALSE;if(rcMineArea.PtInRect(point)){//点击雷区域if(m_uGameState==GS_WAIT||m_uGameState==GS_RUN){m_pNewMine=GetMine(point.x,point.y);if(!m_pNewMine)return;//检测判断当前状态是否为左右鼠标同时按下if(nFlags==(MK_LBUTTON|MK_RBUTTON)){m_bLRBtnDown=TRUE;OnLRBtnDown(m_pNewMine->uRow,m_pNewMine->uCol);}else{switch(m_pNewMine->uState){//普通状态caseSTATE_NORMAL:m_pNewMine->uState=STATE_FLAG;m_pNewMine->uOldState=STATE_FLAG;m_nLeaveNum--;break;//标记状态caseSTATE_FLAG:m_pNewMine->uState=STATE_DICEY;m_pNewMine->uOldState=STATE_DICEY;m_nLeaveNum++;break;//未知状态caseSTATE_DICEY:m_pNewMine->uState=STATE_NORMAL;m_pNewMine->uOldState=STATE_NORMAL;break;default:break;}}Invalidate();}}CWnd::OnRButtonDown(nFlags,point);}附录3:绘制雷区的函数代码voidCMineWnd::DrawMineArea(CPaintDC&dc){CDCdcMemory;//用作内存设备//源设备dcMemory.CreateCompatibleDC(&dc);//使得这个设备与dc兼容//dc是目标设备dcMemory.SelectObject(m_bmpMine);//将内存设备与位图资源关联for(UINTi=0;i{for(UINTj=0;j{//根据[i][j]区域的雷方块状态拷贝相应的图像到[i][j]雷区的特定区域dc.StretchBlt(MINEAREA_FRAME_X+16*j,MINEAREA_FRAME_Y+16*i,16,16,&dcMemory,0,16*m_pMines[i][j].uState,16,16,SRCCOPY);}}}附录4:雷区、笑脸按钮、3D效果外壳和数字图像的绘制代码voidCMineWnd::OnPaint(){CPaintDCdc(this);//创建一个CPaintDC类型的用以屏幕显示的dc设备//参数是指向当前框架窗口CDCdcMemory;//内存设备CBitmapbitmap;//创建临时的位图资源if(!dc.IsPrinting())//判断不是使用打印机来进行绘制工作{//使内存设备与dc设备兼容if(dcMemory.CreateCompatibleDC(&dc)){//使得bitmap与实际显示的dc设备兼容if(bitmap.CreateCompatibleBitmap(&dc,m_rcClient.right,m_rcClient.bottom)){//内存设备选择物件-位图dcMemory.SelectObject(&bitmap);//绘制背景框dcMemory.FillRect(&m_rcClient,&m_brsBG);DrawButton((CPaintDC&)dcMemory);//笑脸按钮绘图DrawNumber((CPaintDC&)dcMemory);//数字图像绘图DrawShel
for(j=minCol;j{//对于周围可以拓展的区域进行的规拓展if(!(i==row&&j==col)&&m_pMines[i][j].uState==STATE_NORMAL&&m_pMines[i][j].uAttrib!=ATTRIB_MINE){if(!IsInMineArea(i,j))continue;ExpandMines(i,j);//递归拓展操作}}}}}附录2:鼠标右键点击事件代码voidCMineWnd::OnRButtonDown(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);m_bLRBtnDown=FALSE;if(rcMineArea.PtInRect(point)){//点击雷区域if(m_uGameState==GS_WAIT||m_uGameState==GS_RUN){m_pNewMine=GetMine(point.x,point.y);if(!m_pNewMine)return;//检测判断当前状态是否为左右鼠标同时按下if(nFlags==(MK_LBUTTON|MK_RBUTTON)){m_bLRBtnDown=TRUE;OnLRBtnDown(m_pNewMine->uRow,m_pNewMine->uCol);}else{switch(m_pNewMine->uState){//普通状态caseSTATE_NORMAL:m_pNewMine->uState=STATE_FLAG;m_pNewMine->uOldState=STATE_FLAG;m_nLeaveNum--;break;//标记状态caseSTATE_FLAG:m_pNewMine->uState=STATE_DICEY;m_pNewMine->uOldState=STATE_DICEY;m_nLeaveNum++;break;//未知状态caseSTATE_DICEY:m_pNewMine->uState=STATE_NORMAL;m_pNewMine->uOldState=STATE_NORMAL;break;default:break;}}Invalidate();}}CWnd::OnRButtonDown(nFlags,point);}附录3:绘制雷区的函数代码voidCMineWnd::DrawMineArea(CPaintDC&dc){CDCdcMemory;//用作内存设备//源设备dcMemory.CreateCompatibleDC(&dc);//使得这个设备与dc兼容//dc是目标设备dcMemory.SelectObject(m_bmpMine);//将内存设备与位图资源关联for(UINTi=0;i{for(UINTj=0;j{//根据[i][j]区域的雷方块状态拷贝相应的图像到[i][j]雷区的特定区域dc.StretchBlt(MINEAREA_FRAME_X+16*j,MINEAREA_FRAME_Y+16*i,16,16,&dcMemory,0,16*m_pMines[i][j].uState,16,16,SRCCOPY);}}}附录4:雷区、笑脸按钮、3D效果外壳和数字图像的绘制代码voidCMineWnd::OnPaint(){CPaintDCdc(this);//创建一个CPaintDC类型的用以屏幕显示的dc设备//参数是指向当前框架窗口CDCdcMemory;//内存设备CBitmapbitmap;//创建临时的位图资源if(!dc.IsPrinting())//判断不是使用打印机来进行绘制工作{//使内存设备与dc设备兼容if(dcMemory.CreateCompatibleDC(&dc)){//使得bitmap与实际显示的dc设备兼容if(bitmap.CreateCompatibleBitmap(&dc,m_rcClient.right,m_rcClient.bottom)){//内存设备选择物件-位图dcMemory.SelectObject(&bitmap);//绘制背景框dcMemory.FillRect(&m_rcClient,&m_brsBG);DrawButton((CPaintDC&)dcMemory);//笑脸按钮绘图DrawNumber((CPaintDC&)dcMemory);//数字图像绘图DrawShel
//对于周围可以拓展的区域进行的规拓展
(i==row&&j==col)&&
m_pMines[i][j].uState==STATE_NORMAL
&&m_pMines[i][j].uAttrib!
IsInMineArea(i,j))continue;
ExpandMines(i,j);//递归拓展操作
附录2:
鼠标右键点击事件代码
OnRButtonDown(UINTnFlags,CPointpoint)
if(rcMineArea.PtInRect(point))
m_pNewMine=GetMine(point.x,point.y);
m_pNewMine)return;
//检测判断当前状态是否为左右鼠标同时按下
if(nFlags==(MK_LBUTTON|MK_RBUTTON))
{m_bLRBtnDown=TRUE;
OnLRBtnDown(m_pNewMine->uRow,m_pNewMine->uCol);
{switch(m_pNewMine->uState)
//普通状态
caseSTATE_NORMAL:
m_pNewMine->uState=STATE_FLAG;
m_pNewMine->uOldState=STATE_FLAG;
m_nLeaveNum--;
//标记状态
caseSTATE_FLAG:
m_pNewMine->uState=STATE_DICEY;
m_pNewMine->uOldState=STATE_DICEY;
m_nLeaveNum++;
//未知状态
caseSTATE_DICEY:
m_pNewMine->uState=STATE_NORMAL;
m_pNewMine->uOldState=STATE_NORMAL;
OnRButtonDown(nFlags,point);
附录3:
绘制雷区的函数代码
DrawMineArea(CPaintDC&dc)
CDCdcMemory;//用作内存设备//源设备
dcMemory.CreateCompatibleDC(&dc);//使得这个设备与dc兼容//dc是目标设备
dcMemory.SelectObject(m_bmpMine);//将内存设备与位图资源关联
for(UINTi=0;i{for(UINTj=0;j{//根据[i][j]区域的雷方块状态拷贝相应的图像到[i][j]雷区的特定区域dc.StretchBlt(MINEAREA_FRAME_X+16*j,MINEAREA_FRAME_Y+16*i,16,16,&dcMemory,0,16*m_pMines[i][j].uState,16,16,SRCCOPY);}}}附录4:雷区、笑脸按钮、3D效果外壳和数字图像的绘制代码voidCMineWnd::OnPaint(){CPaintDCdc(this);//创建一个CPaintDC类型的用以屏幕显示的dc设备//参数是指向当前框架窗口CDCdcMemory;//内存设备CBitmapbitmap;//创建临时的位图资源if(!dc.IsPrinting())//判断不是使用打印机来进行绘制工作{//使内存设备与dc设备兼容if(dcMemory.CreateCompatibleDC(&dc)){//使得bitmap与实际显示的dc设备兼容if(bitmap.CreateCompatibleBitmap(&dc,m_rcClient.right,m_rcClient.bottom)){//内存设备选择物件-位图dcMemory.SelectObject(&bitmap);//绘制背景框dcMemory.FillRect(&m_rcClient,&m_brsBG);DrawButton((CPaintDC&)dcMemory);//笑脸按钮绘图DrawNumber((CPaintDC&)dcMemory);//数字图像绘图DrawShel
for(UINTj=0;j{//根据[i][j]区域的雷方块状态拷贝相应的图像到[i][j]雷区的特定区域dc.StretchBlt(MINEAREA_FRAME_X+16*j,MINEAREA_FRAME_Y+16*i,16,16,&dcMemory,0,16*m_pMines[i][j].uState,16,16,SRCCOPY);}}}附录4:雷区、笑脸按钮、3D效果外壳和数字图像的绘制代码voidCMineWnd::OnPaint(){CPaintDCdc(this);//创建一个CPaintDC类型的用以屏幕显示的dc设备//参数是指向当前框架窗口CDCdcMemory;//内存设备CBitmapbitmap;//创建临时的位图资源if(!dc.IsPrinting())//判断不是使用打印机来进行绘制工作{//使内存设备与dc设备兼容if(dcMemory.CreateCompatibleDC(&dc)){//使得bitmap与实际显示的dc设备兼容if(bitmap.CreateCompatibleBitmap(&dc,m_rcClient.right,m_rcClient.bottom)){//内存设备选择物件-位图dcMemory.SelectObject(&bitmap);//绘制背景框dcMemory.FillRect(&m_rcClient,&m_brsBG);DrawButton((CPaintDC&)dcMemory);//笑脸按钮绘图DrawNumber((CPaintDC&)dcMemory);//数字图像绘图DrawShel
{//根据[i][j]区域的雷方块状态拷贝相应的图像到[i][j]雷区的特定区域
dc.StretchBlt(MINEAREA_FRAME_X+16*j,MINEAREA_FRAME_Y+16*i,
16,16,&dcMemory,0,16*m_pMines[i][j].uState,16,16,SRCCOPY);
附录4:
雷区、笑脸按钮、3D效果外壳和数字图像的绘制代码
OnPaint()
CPaintDCdc(this);//创建一个CPaintDC类型的用以屏幕显示的dc设备
//参数是指向当前框架窗口
CDCdcMemory;//内存设备
CBitmapbitmap;//创建临时的位图资源
dc.IsPrinting())//判断不是使用打印机来进行绘制工作
//使内存设备与dc设备兼容
if(dcMemory.CreateCompatibleDC(&dc))
//使得bitmap与实际显示的dc设备兼容
if(bitmap.CreateCompatibleBitmap(&dc,m_rcClient.right,
m_rcClient.bottom))
//内存设备选择物件-位图
dcMemory.SelectObject(&bitmap);
//绘制背景框
dcMemory.FillRect(&m_rcClient,&m_brsBG);
DrawButton((CPaintDC&)dcMemory);//笑脸按钮绘图
DrawNumber((CPaintDC&)dcMemory);//数字图像绘图DrawShel
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1