花了两天时间做了一个MFC的扫雷,简单的实现了扫雷的功能,界面很草小毛病很多(比如标题。。),还望大家多多指正。
开发工具: VS2008
代码:http://files.cnblogs.com/GhostZCH/FindMine.rar
界面如下,红色的是标记,绿色的是还没有打开的各种,黄色的是已经打开的,红星是地雷(原谅程序的丑陋,唉,我也不是做美工的。。)
类图如下,主要是MineItem,MineGrid,MineView 三个类其他的基本都是自动生成的没做多大修改。MineItem和MineGird是逻辑上的格子单元和整个格子,游戏的主要逻辑操作都在MineGrid中完成,View至负责和用户交互,将用户消息翻译MineGrid可以理解的形式,并将MineGrid的数据实时的展现出来。两层式的程序结构也是我在小程序中屡试不爽的手段。MineItem的内容比较简单,这里只介绍一下MineGrid和MineViewd。
先说说MineGrid,MineGrid事实上是这个游戏的后台,所有的逻辑处理都在这个类中完成的。
定义了一个指针的指针用来存储数据:
MineItem **_grid;
初始化有点小麻烦,需要先初始化指针的指针在初始化每个指针,销毁的时候要反过来:
1 // 初始化 2 _grid = new MineItem *[w*h]; 3 for (int i=0;i<_height;i++) 4 for (int j=0;j<_width;j++) 5 _grid[i*_width+j] = new MineItem(); 6 7 // 析构 8 for (int i=0;i<_height;i++) 9 for (int j=0;j<_width;j++) 10 delete Get(i,j); 11 12 delete _grid;
// 已经标记取消标记,没有标记的进行标记,懒得写if else 了
mineItem->IsMark(!mineItem->IsMark());
成片的打开有点麻烦,好像有人在网上求算法,其实也不是很难,嵌套的时候注意把已经打开的排除不要死循环就好了:
1 void MineGrid::OpenMine( int i,int j ) 2 { 3 MineItem *mineItem = Get(i,j); 4 mineItem->IsOpen(true);// 打开自己 5 6 if(mineItem->Neighbour()==0)//周围没有雷打开周围的方格 7 { 8 for (int ii=-1;ii<=1;ii++) 9 { 10 for (int jj=-1;jj<=1;jj++) 11 { 12 int x = ii+i; 13 int y = jj+j; 14 15 if (x>=0&&x<_height&&y>=0&&y<_width&&!Get(x,y)->IsOpen()) 16 OpenMine(x,y);//嵌套打开 17 } 18 } 19 } 20 }
再来说说 MineView ,这个类是单文档MFC程序的那个文档View,本来用基于对话框的可能更好,不过上次用对话框做了一个俄罗斯方块,这次换换口味。
接受两个消息,作为打开格子和标记格子,传回后台处理逻辑,重绘处理的结果:
1 void CFindMineView::OnLButtonUp(UINT nFlags, CPoint point) 2 { 3 int i,j; 4 5 CView::OnLButtonUp(nFlags, point); 6 GetGirdLocation(point.x,point.y,i,j); 7 8 _grid->Open(i,j); 9 Invalidate(); 10 } 11 12 void CFindMineView::OnRButtonUp(UINT nFlags, CPoint point) 13 { 14 int i,j; 15 16 CView::OnRButtonUp(nFlags, point); 17 GetGirdLocation(point.x,point.y,i,j); 18 19 _grid->Mark(i,j); 20 Invalidate(); 21 }
这个程序的难点在于绘画界面,MFC在OnDraw函数使用CDC绘制界面,略去了定义资源的步骤,这里第一步是定义用于填充的图片和内存CDC(和双缓存有关):
CBitmap _openBitMap;
CBitmap _closeBitMap;
CBitmap _markBitMap;
CBitmap _mineBitMap;
CDC _menDC ;
在首次使用前初始化这些数据:
1 void CFindMineView::LoadDC(CDC *pDC) 2 { 3 if (!_isLoadDc)//只能加载一次 4 { 5 _isLoadDc = true; 6 7 _openBitMap.LoadBitmap(IDB_BTNDOWN); 8 _closeBitMap.LoadBitmap(IDB_BTNUP); 9 _markBitMap.LoadBitmap(IDB_BTNMARK); 10 _mineBitMap.LoadBitmap(IDB_BTNMINE); 11 12 _menDC.CreateCompatibleDC(pDC);//每个dc这能创建一个关联的dc 13 } 14 }
每次OnDraw重绘真个界面,逐个的画出每个格子:
1 void CFindMineView::OnDraw(CDC* pDC) 2 { 3 LoadDC(pDC); 4 5 for (int i=0;i<_grid->Height();i++) 6 for (int j=0;j<_grid->Width();j++) 7 DrawMine(i,j,pDC,_grid->Get(i,j)); 8 }
自己写的DrawMine函数是这个类里面比较复杂的函数根据不同的数据用不同的画笔绘制一个小方格:
1 void CFindMineView::DrawMine( int i,int j,CDC* pdc,MineItem* mine ) 2 { 3 if (mine->IsOpen()) 4 if (mine->IsMine()) 5 _menDC.SelectObject(&_mineBitMap); 6 else 7 _menDC.SelectObject(&_openBitMap); 8 else 9 if(mine->IsMark()) 10 _menDC.SelectObject(&_markBitMap); 11 else 12 _menDC.SelectObject(&_closeBitMap); 13 14 pdc->BitBlt(j*_wStep,i*_hStep,_wStep-1,_hStep-1,&_menDC,1,1,SRCCOPY); 15 16 if (mine->IsOpen()&&!mine->IsMine()&&(mine->Neighbour()>0)) 17 { 18 CString num; 19 num.Format(L"%d",mine->Neighbour()); 20 pdc->TextOut(j*_wStep+19,i*_hStep+17,num); 21 } 22 }
差不多就这么多吧,欢迎大家多提意见。