课 程 设 计
扫雷游戏程序设计
2013 年 11 月 5 日
设计题目 学 号专业班级 学生姓名 指导教师
合肥工业大学课程设计任务书
一. 设计任务
(1)鼠标左击排雷,右击插小旗,打问号;
(2)方格里面的数字表示方格周围的雷数;
(3)能够显示未标记雷数和游戏用时;
(4)雷区上面的小脸可以变化,显示微笑,惊讶,痛苦,胜利。在任何情况下单击小脸可以重新开始游戏;
(5)可进行游戏难度的设定:包括预定义的难度级别(雷区行数,列数和雷数),和自行定义雷区行数,列数和雷数的功能;
(6)排行榜功能,扫雷成功时候,根据游戏用时更新排行榜。
二. 具体设计
(一)、设计思路
扫雷游戏是很经典也很有趣的一款游戏,这次的游戏程序设计要求设计出功能与原游戏相近的一款游戏,首先定义变量和类要画出游戏方格以及位图,设计游戏等级,等级越高方格越多雷数也越多;然后设置随机布雷以保证每次重新开始游戏都有不同的雷区地图;另外定义鼠标左击扫雷,左击标记周围埋雷情况,右击奇数次排雷偶数次取消上次排雷,以及扫雷第一次左击不能扫到雷。
(二)、设计表示:
1、鼠标左击排雷,右击插小旗,打问号;
2、方格里面的数字表示方格周围的雷数;
3、能够显示未标记雷数和游戏用时;
4、雷区上面的小脸可以变化,显示微笑,惊讶,痛苦,胜利。在任何情况下单击小脸可以重新开始游戏;
5、可进行游戏难度的设定:包括预定义的难度级别(雷区行数,列数和雷数);
6、任何时候开始游戏第一次点击不会扫到雷。
由于时间等原因未完成功能:
排行榜和自行定义雷区行数,列数和雷数功能。
(四)、详细设计
1、添加位图
前十二幅是在雷区的,后四幅是按钮。为了便于加载,必须各自保证其连续性。
分别表示游戏进行的状态、结果胜利或触雷。
2、预设等级方格数雷数变化
void CMywqq20112074View::OnEasy() /////////////////////////////初级
{
m_RowCount=10;//行数10
m_ColCount=10;//列数10
leinum=10;
realnum=leinum;
SetTimer(1,50,NULL);
// TODO: Add your command handler code here
second=0;//计时
secondstart=0;//1时开始计时// num=0;
leftnum=leinum;//剩余雷数
jieshu=0;//jieshu=1时停止
int aa=0;
//初始化0
for(int i=0;i { for(int j=0;j { lei[i][j].shumu=0; lei[i][j].weitu=0; } } //设置10个雷 CMywqq20112074View::OnMiddle() //////////////////////////////////中级 { m_RowCount=15;//行数15 m_ColCount=15;//列数15 leinum=40; realnum=leinum; SetTimer(1,50,NULL); // TODO: Add your command handler code here second=0;//计时 secondstart=0;//1时开始计时// num=0; leftnum=leinum;//剩余雷数 jieshu=0;//jieshu=1时停止 int aa=0; //初始化0 for(int i=0;i { for(int j=0;j { lei[i][j].shumu=0; lei[i][j].weitu=0; } } //设置40个雷 void CMywqq20112074View::OnHard() ///////////////////////////////////////////高级 { m_RowCount=25;//行数25 m_ColCount=16;//列数16 leinum=80; realnum=leinum; SetTimer(1,50,NULL); // TODO: Add your command handler code here second=0;//计时 secondstart=0;//1时开始计时// num=0; leftnum=leinum;//剩余雷数 jieshu=0;//jieshu=1时停止 int aa=0; //初始化0 for(int i=0;i { for(int j=0;j { lei[i][j].shumu=0; lei[i][j].weitu=0; } } //设置80个雷 do { int k=rand()%m_RowCount; int l=rand()%m_ColCount; if(lei[k][l].shumu!=-1) { lei[k][l].shumu=-1; aa++; } }while(aa!=leinum); 3、第一次点击不会扫到雷 if(lei[a][b].weitu==0||lei[a][b].weitu==3 ) { for(;n==0&&lei[a][b].shumu==-1;) //////////////踩到第一个雷 { int aa=0; //初始化0 for(int i=0;i { for(int j=0;j { lei[i][j].shumu=0; lei[i][j].weitu=0; } } do { int k=rand()%m_RowCount; int l=rand()%m_ColCount; if(lei[k][l].shumu!=-1) { lei[k][l].shumu=-1; aa++; } }while(aa!=leinum); //给方格赋值 for(int a=0;a for(int b=0;b if(lei[a][b].shumu==0) { for(int c=a-1;c for(int d=b-1;d if(c>=0&&c if(lei[c][d].shumu==-1) lei[a][b].shumu++; } } 4、鼠标右击插小旗,打问号 第一次用右键单击某个区域时,该区域上插上一面小红旗,此时单击左键没有任何 变化,第二次用右键单击时恢复原状。 //判断显示什么位图 //weitu=1已按下的数字区 //weitu=2显示旗 //weitu=3显示问号 for(int a=0;a for(int b=0;b { if(lei[a][b].weitu==1) { Dc.SelectObject(m_Bitmap[lei[a][b].shumu] ); pDC->BitBlt(a*15+10,b*15+50,160,16 0,&Dc,0,0,SRCCOPY); } if(lei[a][b].weitu==2) { Dc.SelectObject(m_Bitmap[9]); pDC->BitBlt(a*15+10,b*15+50,160,16 0,&Dc,0,0,SRCCOPY); } if(lei[a][b].weitu==3) { Dc.SelectObject(m_Bitmap[10]); pDC->BitBlt(a*15+10,b*15+50,160,16 0,&Dc,0,0,SRCCOPY); } //结束 (五)、调试及测试 经运行测试之后,程序出现的问题有: 1、第一次打开游戏时有时会出现不能计时的情况 2、胜利的表情不能正常显示 (六)、使用说明 A、鼠标左击排雷,右击插小旗,打问号; B、方格里面的数字表示方格周围的雷数; C、在任何情况下单击小脸可以重新开始游戏; D、可进行游戏难度的设定:包括预定义的难度级别(雷区行数,列数和雷数),和自行定义雷区行数,列数和雷数的功能; E、扫雷成功时候,游戏用时更新排行榜 (七)、运行实例 鼠标左击排雷,可翻开连片空格,方格周围 数字表示周围埋雷数目,右键单击一次插小 红旗,双击显示问号表示雷数情况未知,同 时显示剩余雷数以及游戏用时 可从预设难度等级中选择游戏难度,游戏方格数和雷数都有相应增加 ←初级 ←中级 ←高级 触雷时现出痛苦的表情,游戏结束 将雷全部排出,胜利的呐喊!! (八)、设计小结 这个短短几天的C++课程设计虽然为期不长,却让我们从实践中学到了更多的C++编程知识,一开始看到示例中让人眼花缭乱的程序代码让人很有放弃的冲动,可是最后还是坚持下来将代码读完并自己琢磨出一套代码出来,在这中间也遇到不少的困难,一开始 不知道位图是怎么做的,那些看起来可爱的表情做起来却并不简单,还要在代码里将这些表情的二维坐标对应起来,过程细致而繁杂,最终在与同学讨论之后成功做出,大家还一起开玩笑涂鸦自定义表情,轻松的话语冲淡了写代码时的烦躁。还有在设定难度时要重新画方格,右键单击时出现不同的状态,第一次点击的时候不能扫到雷等等难关,都被坚持不懈的毅力和耐力一一克服。直到最后看到各种功能在自己手下实现,这时的成就感和喜悦是之前单独实现一个个小功能所不能比的,不由更加感叹程序编程的奇妙,让我对程序员有了新的认识,也下定决心努力向他们看齐!! 附录:源程序 // wqq20112074View.cpp : implementation of the CMywqq20112074View class // #include "stdafx.h" #include "wqq20112074.h" #include "wqq20112074Doc.h" #include "wqq20112074View.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif //////////////////////////////////////////////////////////////////////////// / // CMywqq20112074View IMPLEMENT_DYNCREATE(CMywqq20112074V iew, CView) BEGIN_MESSAGE_MAP(CMywqq20112074View, CView) //{{AFX_MSG_MAP(CMywqq20112074View ) ON_WM_TIMER() ON_WM_LBUTTONDOWN() ON_WM_CREATE() ON_COMMAND(ID_START, OnStart) ON_WM_LBUTTONUP() ON_WM_RBUTTONDOWN() ON_COMMAND(ID_EASY, OnEasy) ON_COMMAND(ID_MIDDLE, OnMiddle) ON_COMMAND(ID_HARD, OnHard) //}}AFX_MSG_MAP // Standard printing commands ON_COMMAND(ID_, CView::On) ON_COMMAND(ID_, CView::On) ON_COMMAND(ID_, CView::On) END_MESSAGE_MAP() //////////////////////////////////////////////////////////////////////////// / // CMywqq20112074View construction/destruction CMywqq20112074View::CMywqq20112074View() { // TODO: add construction code here for(int ii=0;ii<16;ii++) m_Bitmap[ii].LoadBitmap(IDB_BITMAP14+ii ); for(int jj=0;jj<4;jj++) m_anniu[jj].LoadBitmap(IDB_ANNIU1+jj); second=0;//计时 secondstart=0;//1时开始计时 m_RowCount=25;//行数 m_ColCount=16;//列数 n=0;//格数 leinum=5; realnum=leinum; leftnum=leinum;//剩余雷数 jieshu=0;//jieshu=1时停止 int aa=0; //初始化0 for(int i=0;i for(int j=0;j { lei[i][j].shumu=0; lei[i][j].weitu=0; } } //获取当前时间 CTime time=GetCurrentTime(); int s; //获取秒数 s=time.GetSecond(); //设置40个雷 do { //以当前秒数为产生随机算法 int k=(rand()*s)%m_RowCount; int l=(rand()*s)%m_ColCount; if(lei[k][l].shumu!=-1) { lei[k][l].shumu=-1; aa++; } }while(aa!=leinum); //给方格赋值,计算雷数 for(int a=0;a for(int b=0;b if(lei[a][b].shumu==0) { for(int c=a-1;c for(int d=b-1;d if(c>=0&&c if(lei[c][d].shumu==-1) lei[a][b].shumu++; } } CMywqq20112074View::~CMywqq20112074View( ) { } BOOL CMywqq20112074View::PreCreateWindow(CREAT ESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs return CView::PreCreateWindow(cs); } //////////////////////////////////////////////////////////////////////////// / // CMywqq20112074View drawing void CMywqq20112074View::OnDraw(CDC* pDC) { CMywqq20112074Doc* pDoc = GetDocument(); ASSERT_V ALID(pDoc); // TODO: add draw code for native data here //画背景 CBrush mybrush1; mybrush1.CreateSolidBrush(RGB(192,192,192 )); CRect myrect1(0,0,1200,800); pDC->FillRect(myrect1,&mybrush1); //画黑框 CBrush mybrush; mybrush.CreateSolidBrush(RGB(0,0,0)); CRect myrect(20,10,70,40); pDC->FillRect(myrect,&mybrush); CRect myrect2(325,10,375,40); pDC->FillRect(myrect2,&mybrush); CPen mypen; CPen*myoldPen; mypen.CreatePen(PS_SOLID,2,RGB(255,255, 255)); myoldPen=pDC->SelectObject(&mypen); //画黑框的白线 pDC->MoveTo(20,40); pDC->LineTo(70,40); pDC->LineTo(70,10); pDC->MoveTo(325,40); pDC->LineTo(375,40); pDC->LineTo(375,10); //画雷区边线 for(int i=0;i for(int j=0;j { pDC->MoveTo(10+i*15,50+j*15+14); pDC->LineTo(10+i*15,50+j*15); pDC->LineTo(10+i*15+14,50+j*15); } pDC->SelectObject(myoldPen); CPen mypen2; CPen*myoldPen2; mypen2.CreatePen(PS_SOLID,1,RGB(0,0,0)); myoldPen2=pDC->SelectObject(&mypen2); for(int ii=0;ii for(int jj=0;jj { pDC->MoveTo(10+ii*15,50+jj*15+14); pDC->LineTo(10+ii*15+14,50+jj*15+14); pDC->LineTo(10+ii*15+14,50+jj*15); } pDC->SelectObject(myoldPen2); CDC Dc; if(Dc.CreateCompatibleDC(pDC)==FALSE) AfxMessageBox("Can't create DC"); //显示按钮 Dc.SelectObject(m_anniu[0]); pDC->BitBlt(180,10,160,160,&Dc,0,0,SRCCOPY); //判断显示什么位图 //weitu=1已按下的数字区 //weitu=2显示旗 //weitu=3显示问号 for(int a=0;a for(int b=0;b { if(lei[a][b].weitu==1) { Dc.SelectObject(m_Bitmap[lei[a][b].shumu]); pDC->BitBlt(a*15+10,b*15+50,160,160,&Dc, 0,0,SRCCOPY); } if(lei[a][b].weitu==2) { Dc.SelectObject(m_Bitmap[9]); pDC->BitBlt(a*15+10,b*15+50,160,160,&Dc, 0,0,SRCCOPY); } if(lei[a][b].weitu==3) { Dc.SelectObject(m_Bitmap[10]); pDC->BitBlt(a*15+10,b*15+50,160,160,&Dc, 0,0,SRCCOPY); } //结束 if(jieshu==1&&lei[a][b].shumu==-1) { Dc.SelectObject(m_Bitmap[11]); pDC->BitBlt(a*15+10,b*15+50,160,160,&Dc, 0,0,SRCCOPY); Dc.SelectObject(m_anniu[3]); pDC->BitBlt(180,10,160,160,&Dc,0,0,SRCCO PY); } } //显示黑框里的数字 int nOldDC=pDC->SaveDC(); pDC->SetTextColor(RGB(255,0,0)); pDC->SetBkColor(RGB(0,0,0)); CFont font; if(0==font.CreatePointFont(160,"Comic Sans MS")) { AfxMessageBox("Can't Create Font"); } pDC->SelectObject(&font); CString str; if(leftnum<10) str.Format("00%d",leftnum); else str.Format("0%d",leftnum); pDC->TextOut(25,10,str); if(second<10) str.Format("00%d",second); else if(second<100) str.Format("0%d" ,second); else str.Format("%d" ,second); pDC->TextOut(330,10,str); pDC->RestoreDC(nOldDC); ////////////////////////////////////////////// } //////////////////////////////////////////////////////////////////////////// / // CMywqq20112074View printing BOOL CMywqq20112074View::OnPreparePrinting(CPrintI nfo* pInfo) { // default preparation return DoPreparePrinting(pInfo); } void CMywqq20112074View::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add extra initialization before printing } void CMywqq20112074View::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add cleanup after printing } //////////////////////////////////////////////////////////////////////////// / // CMywqq20112074View diagnostics #ifdef _DEBUG void CMywqq20112074View::AssertValid() const { CView::AssertValid(); } void CMywqq20112074View::Dump(CDumpContext& dc) const { CView::Dump(dc); } CMywqq20112074Doc* CMywqq20112074View::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIM E_CLASS(CMywqq20112074Doc))); return (CMywqq20112074Doc*)m_pDocument; } #endif //_DEBUG //////////////////////////////////////////////////////////////////////////// / // CMywqq20112074View message handlers void CMywqq20112074View::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default //结束,返回 if(jieshu==1) return; //显示个数为0的方格 leizero(); //计时 if(secondstart>0) secondstart++; if(secondstart==20) { secondstart=1; second++; //重画时间 CRect rect3; rect3.left=325; rect3.right=375; rect3.top=10; rect3.bottom=40; InvalidateRect(&rect3); } CView::OnTimer(nIDEvent); } void CMywqq20112074View::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default //获取指针pdc CDC *pDC=GetDC(); CDC Dc; if(Dc.CreateCompatibleDC(pDC)==FALSE) AfxMessageBox("Can't create DC"); //显示按下按钮 if(point.x>180&&point.x<210&&point.y>10&&poi nt.y<40) { Dc.SelectObject(m_anniu[3]); pDC->BitBlt(180,10,160,160,&Dc,0,0,SRCCO PY); } if((point.x>=10)&&(point.x<=385)&&(point.y>=50 )&&(point.y<=290)) { if(jieshu==1) return; //显示张口按钮 Dc.SelectObject(m_anniu[1]); pDC->BitBlt(180,10,160,160,&Dc,0,0,SRCCO PY); secondstart=1; int a=(point.x-10)/15; int b=(point.y-50)/15; if(lei[a][b].weitu==0||lei[a][b].weitu==3) { for(;n==0&&lei[a][b].shumu==-1;) //////////////踩到第一个雷 { int aa=0; //初始化0 for(int i=0;i { for(int j=0;j { lei[i][j].shumu=0; lei[i][j].weitu=0; } } do { int k=rand()%m_RowCount; int l=rand()%m_ColCount; if(lei[k][l].shumu!=-1) { lei[k][l].shumu=-1; aa++; } }while(aa!=leinum); //给方格赋值 for(int a=0;a for(int b=0;b if(lei[a][b].shumu==0) { for(int c=a-1;c for(int d=b-1;d