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); } |
|