点击Bitmap,按顺序加入14*14的位图12张,同样按顺序加入30*30的位图4张,如图:
ANNIU系列位图自绘为:,,,。
BITMAP系列自绘为:,到的数字,后几幅为,,。
定义新类:
对于雷,我们是单独定义一个类,这样有利于程序的操作。
class Lei
{
public:
//显示哪一个位图
int weitu;
//这个位置相应的值
int shumu;
};
视图类变量: (最好在类向导中自动添加,直接在代码中写要修改好几个地方)
接着是在View类添加变量和函数:
//剩下雷数
int leftnum;
//雷数
int leinum;
//结束
int jieshu;
//计时
short second;
//开始计时
int secondstart;
//位图数组
CBitmap m_Bitmap[12];
//按扭位图数组
CBitmap m_anniu[4];
//雷区行数
int m_RowCount;
//雷区列数
int m_ColCount;
//最大雷区
Lei lei[50][50];
//这个位置周围雷数为0
void leizero();
//计时器函数
afx_msg void OnTimer(UINT nIDEvent);
//鼠标按下左键
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//鼠标按下右键
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
//初始化函数
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//鼠标左键松开
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
删去工具栏:(在CMainFrame::OnCreate函数中注释掉创建工具栏)可以在return 0前加上这几句,自定义自己的图标
m_hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ICON1));//加载自定义图标
SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hIcon);//将当前图标变换为自己定义的
设置窗口显示在最前端:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWndEx::PreCreateWindow(cs) )
return FALSE;
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
cs.dwExStyle=WS_EX_TOPMOST;
// cs.style=WS_SYSMENU|WS_OVERLAPPED|WS_MINIMIZEBOX;//加上的话就不能调窗口大小了
//设置窗口大小:
cs.cx=395;
cs.cy=320;
cs.lpszName=_T("扫雷");//换标题
return TRUE;
}
构造函数:
由于构造函数是程序运行时就执行的,所以,除了对变量赋值之外,我们还可以把游戏的核心结构即内部数组赋值:先是把全部格子的位图和雷数赋值为0,然后调用随机函数按指定雷数赋值为-1,最后把不是雷的格子的雷数赋值为相应的值。
CMy2_1扫雷View::CMy2_1扫雷View()
: leftnum(0)
{
// TODO: 在此处添加构造代码
for(int ii=0;ii<12;ii++)
m_Bitmap[ii].LoadBitmap(IDB_BITMAP14+ii);//给每个m_Bitmap[]数组加载图像
for(int jj=0;jj<4;jj++)
m_anniu[jj].LoadBitmap(IDB_ANNIU1+jj);//同上
//计时
second=0;
//1时开始计时
secondstart=0;
//行数
m_RowCount=25;
//列数
m_ColCount=16;
//雷数
leinum=80;
//剩余雷数
leftnum=leinum;
//jieshu=1时停止
jieshu=0;
int aa=0;
//初始化为0
for(int i=0;i=0&&c=0&&d
界面函数:
现在,可以开始画界面了。如下函数:
很明显,前面部分是用画的方法画出整个界面,但是,后面for循环显示的位图并不是现在画界面的内容,为什么要写呢?
这是为了用户框重画的需要,当我们的游戏玩了一半后最小化,或是把部分窗口移出屏幕,或是执行了新的应用程序覆盖了原来的程序时,必须重画。我们调用重画函数,它都要重新执行OnDraw(CDC* pDC)函数,那么,此时它就必须把已经显示出来的位图也显示出来。而开始时雷区位图是不可见的,并不影响界面的初始化。
void CMy2_1扫雷View::OnDraw(CDC* pDC)
{
CMy2_1扫雷Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
//画背景
CBrush mybrush1;
mybrush1.CreateSolidBrush(RGB(192,192,192));//嘛颜色,运行看看?
CRect myrect1(0,0,1200,800);//以0,0,位置开始,整个背景
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);
//画雷区边线
//左上角是白线,右下角是黑线,以显示立体感(可以吧背景分成ROW*COL个小正方形)
for(int i=0;iMoveTo(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;iiMoveTo(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)
MessageBox(_T("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;aBitBlt(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,SRCCOPY);
}
}
//显示黑框里的数字
int nOldDC=pDC->SaveDC();
pDC->SetTextColor(RGB(255,0,0));//文字颜色为红色
pDC->SetBkColor(RGB(0,0,0));//背景色
CFont font;
if(0==font.CreatePointFont(160,_T("Comic Sans MS")))
{
MessageBox(_T("Can't Create Font"));
}
pDC->SelectObject(&font);//选择字体
CString str;
//利用判断显示位数,不够三位前面加0
if(leftnum<10)
str.Format(_T("00%d"),leftnum);//
else
str.Format(_T("0%d"),leftnum);
pDC->TextOut(25,10,str);
if(second<10)
str.Format(_T("00%d"),second);
else if(second<100)
str.Format(_T("0%d") ,second);
else
str.Format(_T("%d") ,second);
pDC->TextOut(330,10,str);
pDC->RestoreDC(nOldDC);
}
我们可以把上面的函数细分为几个小函数,然后在这个函数里面分别调用。
按下鼠标左键:
用if语句判断,如果在按钮上面,则显示按钮按下位图;如果在扫雷区,先把按钮位图改为张口位图,再判断按下的是否是雷,是就结束,重画,以显示所有的雷;否则,重画相应格子以显示数字。
(类向导->添加消息处理->找到WM-LBUTTONDOWN,添加,编辑)
void CMy2_1扫雷View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
//获取指针pdc
//MessageBox(_T("确实按下了!"));
CDC *pDC=GetDC();
CDC Dc;
if(Dc.CreateCompatibleDC(pDC)==FALSE)
AfxMessageBox(_T("Can't create DC"));
//显示按下按钮
if(point.x>180&&point.x<210&&point.y>10&&point.y<40)
{
Dc.SelectObject(m_anniu[3]);
pDC->BitBlt(180,10,160,160,&Dc,0,0,SRCCOPY);
}
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,SRCCOPY);
// secondstart为1时计时有效
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)
{
if(lei[a][b].shumu==-1)
{
jieshu=1;
//结束时,释放Timer
KillTimer(1);
//重画,因为这次重画将显示全部的雷,
//不能用部分重画
Invalidate();
}
else
{
lei[a][b].weitu=1;
CRect rect;
rect.left=a*15+10;
rect.right=a*15+25;
rect.top=b*15+50;
rect.bottom=b*15+65;
InvalidateRect(&rect);
}
}
}
CView::OnLButtonDown(nFlags, point);
松开鼠标左键:
(添加WM_LBUTTONUP消息响应函数)
void CMy2_1扫雷View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CDC *pDC=GetDC();
CDC Dc;
if(Dc.CreateCompatibleDC(pDC)==FALSE)
AfxMessageBox(_T("Can't create DC"));
//显示按钮
Dc.SelectObject(m_anniu[0]);
pDC->BitBlt(180,10,160,160,&Dc,0,0,SRCCOPY);
if(jieshu==1)
{
//显示按扭位图
Dc.SelectObject(m_anniu[2]);
pDC->BitBlt(180,10,160,160,&Dc,0,0,SRCCOPY);
}
//如果按下的是按扭,重新开始(屏幕中间的小黄人)
if(point.x>180&&point.x<210&&point.y>10&&point.y<40)
OnStart();
CView::OnLButtonUp(nFlags, point);
}
按下鼠标右键:
如果是雷,我们按右键,显示旗子,并减少一个剩下雷数;如果我们认为那旗子的格子不是雷,我们按右键,显示问号,并在剩下雷数加上1。有关函数:
void CMy2_1扫雷View::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if(jieshu==1) //结束,返回
return;
if((point.x>=10)&&(point.x<=385)&&(point.y>=50)&&(point.y<=290))
{
int a=(point.x-10)/15;//当前坐标转换到对应行和列
int b=(point.y-50)/15;
if(lei[a][b].weitu==0||lei[a][b].weitu==3)
{
lei[a][b].weitu=2;//赋值为2,重绘时调用OnDraw()函数在里面具体操作
leftnum--;
}
else
if(lei[a][b].weitu==2)
{
lei[a][b].weitu=3;
leftnum++;
}
//重画剩下雷数
CRect rect2;
rect2.left=20;
rect2.right=70;
rect2.top=10;
rect2.bottom=40;
InvalidateRect(&rect2);
//重画打击格子
CRect rect;
rect.left=a*15+10;//转换为当前坐标
rect.right=a*15+25;
rect.top=b*15+50;
rect.bottom=b*15+65;
InvalidateRect(&rect);
}
CView::OnRButtonDown(nFlags, point);
}
显示没有雷的区域:
运行,玩一下,你会发现当按下的是一个周围没有雷的格子是它并不会象Window里面的扫雷游戏一样显示它周围的格子雷数。怎么实现呢?
添加一个如下函数:
void CMy2_1扫雷View::leizero()
{
for(int i=0;i=0&&n<25&&m>=0&&m
再运行,效果是有的,只是它只显示一部分,即这个周围的几个。那么我们应该怎样使它显示全部呢?可以利用计时器函数。
计时器函数:
OnTimer(UINT nIDEvent)函数,同时也可以实现计时显示。添加OnCreate(LPCREATESTRUCT lpCreateStruct)和 OnTimer(UINT nIDEvent):
int CMy2_1扫雷View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
//20次为一秒
SetTimer(1,50,NULL);
return 0;
}
void CMy2_1扫雷View::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//结束,返回
if(jieshu==1)
return;
//显示个数为0的方格
leizero();
//计时
if(secondstart>0)
secondstart++;
//二十次为一秒
if(secondstart==2)
{
secondstart=1;
second++;
//重画时间
CRect rect3;
rect3.left=325;
rect3.right=375;
rect3.top=10;
rect3.bottom=40;
InvalidateRect(&rect3);
}
CView::OnTimer(nIDEvent);
}
修改菜单:
游戏已经可以玩了,只是点到雷之后就完了,无法重新开始。还有,菜单还没有改。下面就修改菜单并实现重新开始功能:
设置完后,在view类添加函数OnStart();
添加代码(其实就是初始化):
void CMy2_1扫雷View::OnStart()
{
SetTimer(1,50,NULL);
// TODO: 在此添加命令处理程序代码
second=0;//计时
secondstart=0;//1时开始计时
// num=0;
leftnum=leinum;//剩余雷数
jieshu=0;//jieshu=1时停止
int aa=0;
//初始化0
for(int i=0;i=0&&c=0&&d
OK,运行就行了,其他方面你可以自由发挥。。。
有时候会运行出错,在网上查是没有数据库的原因,这个我也不懂。。。
出错再运行就行了。。。
我的界面:
,
左下角状态栏鼠标位置显示啥的,是我前几天学习过程中学的自己实践一下,本文没有介绍,很简单,可以问度娘。。。。。。
完~,菜鸟MFC学习笔记。。。。。