13本文作者:唐伯虎 发布于:2010-8-28 分类:MFC编程 点击:766 很多人学了很久MFC,都不知道到底学了什么。因为他们被很多MFC的书给误导了,时下的MFC的书大部分的章节都是如何运用控件。学来学去还是不知道学到了什么,除了移动控件。我就被这些误导了好久,直到后来我尝试编写这个贪吃蛇,才开始真正理解MFC。 MFC提供的很多控件,这些只是辅助编程用的,给编好的程序添加个按钮之类的,真正要编写windows程序,归根结底还是要用c++的各种算法实现功能,MFC库则帮助我们从命令行程序过渡到GUI程序。 接下来我就开始指导贪吃蛇的编写。 首先,要创建一个基于对话框的mfc程序,取名为Snake,然后开始在这些自动生成的代码中添加自己要实现的功能。 我是怎么实现蛇的呢? 我的方法是用GUI绘图画出很多小矩形,这些矩形连在一起,就可以出现一条蛇了。于是我们便要记录每个小矩形的一个点的信息。我用了一个Body类 class Body { public:int x; int y; Body* next; Body* pre; Body(int xx,int yy) { x=xx; y=yy; next=NULL; pre=NULL; } }; 然后还需要食物,我们再建立一个food结构体 struct Food { int x; int y; }food; 有人不禁要问为何body不用结构体呢,这里我主要是想用类的构造函数。 处理好这些微观问题,我们再来宏观研究这条蛇,我们需要这条蛇可以移动,可以吃食物,所以我们要建立一个snake类,并赋予它有移动,吃食物等的功能 class Snake { public:Body* head; void move(); Snake() { Body* body1=new Body(0,0); Body* body2=new Body(1,0); body1->next=body2; body2->pre=body1; head=body2; } void creatfood(); int isGameOver(); Body* getHead(){return head;} void DrawSnake(CDC *pDC); void DrawFood(CDC *pDC); }; 由于食物的随机出现,需要保证不和蛇身重合,所以我们需要调用蛇的信息,所以我们不妨把创建食物的函数也加入到snake类中。 接下来我们来看看实现这些功能的代码 void Snake::move() { int xx=0; int yy=0; switch(direct) { case 0:{yy=head->y-1;xx=head->x;lastdirect=0;break;} case 1:{yy=head->y+1;xx=head->x;lastdirect=1;break;} case 2:{xx=head->x-1;yy=head->y;lastdirect=2;break;} case 3:{xx=head->x+1;yy=head->y;lastdirect=3;break;} } nextx=xx; nexty=yy; if(isGameOver()) return; if(food.x==xx&&food.y==yy) { Body* p=new Body(xx,yy); Body* q=new Body(0,0); q=head; q->next=p; p->pre=q; head=p; food.x=-1; food.y=-1; creatfood(); score+=10; speed+=10; } else { Body *p=head; while(p->pre) { p=p->pre; } while(p->next) { p->x=p->next->x; p->y=p->next->y; p=p->next; } head->x=xx; head->y=yy; } } 注意move()函数中的case 0 1 2 3分别映射了按键的w、s、a、s键 也即就是上下左右的按键。 void Snake::creatfood() { int t=1; int x=0; int y=0; while(t==1) { x=rand()%20; y=rand()%30; Body* p=head; t=0; while(p!=NULL) { if(p->x==x&&p->y==y) { t=1; break; } else p=p->pre; } } food.x=x;food.y=y; } int Snake::isGameOver() { Body* p=head; if(nextx>19||nextx<0||nexty>29||nexty<0) { return 1; }; p=p->pre; while(p!=NULL) { if(p->x==nextx&&p->y==nexty) return 1; else p=p->pre; } return 0; } void Snake::DrawSnake(CDC* pDC) { CBrush br1; br1.CreateSolidBrush(RGB(255,255,255)); pDC->SelectObject(&br1); pDC->Rectangle(10,10,210,310); br1.DeleteObject(); int x; int y; Body *z=getHead(); CBrush br; br.CreateSolidBrush(RGB(255,0,0)); pDC->SelectObject(&br); while(z!=NULL) { x=int(z->x); y=int(z->y); pDC->Rectangle(10*x+12,10*y+12,10*x+20,10*y+20); z=z->pre; } br.DeleteObject(); } void Snake::DrawFood(CDC* pDC) { if(food.x==-1&&food.y==-1) return ; CBrush br; br.CreateSolidBrush(RGB(255,0,0)); pDC->SelectObject(&br); pDC->Rectangle(10*food.x+12,10*food.y+12,10*food.x+20,10*food.y+20); br.DeleteObject(); } 我们除了画蛇之外,还必须要记录成绩 所以我们再建立画出成绩的函数,并且还要给用户按键提示 void DrawScore(CDC* pDC) { CString s; s.Format("分数:%d",score); pDC->SetTextColor(RGB(155,155,155)); pDC->TextOut(222,40,s); }
void DrawInit(CDC* pDC) { pDC->SetTextColor(RGB(222,222,222)); pDC->TextOut(222,70,"W:上"); pDC->TextOut(222,100,"S:下"); pDC->TextOut(222,130,"A:左"); pDC->TextOut(222,160,"D:右"); } 要想能够实现这些功能 需要先创建一个蛇的实例 Snake snake; 然后在自动生成的代码 CSnakeDlg::OnPaint()函数中实现蛇的绘画 CDC *pDC = GetDC(); DrawInit(pDC); snake.creatfood(); SetTimer(1,speed,NULL); snake.move(); snake.DrawSnake(pDC); snake.DrawFood(pDC); 那么这SetTimer()又是什么呢,这是定时器,我们必须定时刷新界面,才能显示出蛇的移动,吃食 void CSnakeDlg::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default CDC*pDC=GetDC(); if(snake.isGameOver()) { CBrush br1; br1.CreateSolidBrush(RGB(255,255,255)); pDC->SelectObject(&br1); pDC->Rectangle(10,10,210,310); br1.DeleteObject(); CFont font; font.CreatePointFont(140,"宋体"); pDC->SelectObject(&font); pDC->SetTextColor(RGB(255,0,0)); pDC->TextOut(88,50,"游戏失败"); KillTimer(1); } else{ snake.move(); snake.DrawSnake(pDC); snake.DrawFood(pDC); DrawScore(pDC); SetTimer(1,speed,NULL); } CDialog::OnTimer(nIDEvent); } |
|