MFC寫的貪吃蛇代碼_第1頁
MFC寫的貪吃蛇代碼_第2頁
MFC寫的貪吃蛇代碼_第3頁
MFC寫的貪吃蛇代碼_第4頁
MFC寫的貪吃蛇代碼_第5頁
已閱讀5頁,還剩20頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、手把手教“MFC版貪吃蛇教程”寫在前面的話本次貪吃蛇教程主要知識(shí)點(diǎn)包括以下幾個(gè)方面1 CView類中的消息響應(yīng)2 控件的消息響應(yīng)3 基于CView類內(nèi)的具體游戲?qū)崿F(xiàn)4 數(shù)組5 游戲圖形的實(shí)現(xiàn)用CDC類實(shí)現(xiàn)。本版游戲的具體實(shí)現(xiàn)是在CViewl類中實(shí)現(xiàn),所以其他類不用添加任何代碼。由于此次貪吃蛇需要MFC的開發(fā)環(huán)境,所以打開VisualC+新建一個(gè)MFC AppWizard單文檔工程,隨意取名一個(gè)工程名稱。本次貪吃蛇基本流程與大體思想1 定義蛇類和食物類,初始化貪吃蛇各項(xiàng)成員變量,包括圖像的出現(xiàn)在屏幕的初始位置,長度,以及蛇的行走方向。食物類的定義包括出現(xiàn)的初始位置,以及食物是否被吃掉的判斷。2

2、用數(shù)組初始化長度為3的貪吃蛇,并且默認(rèn)食物未出現(xiàn)3 在CView類上運(yùn)用MFC提供的Windows消息中WM_TIMER消息,運(yùn)用OnTimer()函數(shù)讓系統(tǒng)提供一個(gè)時(shí)鐘節(jié)拍,更新游戲4 具體游戲?qū)崿F(xiàn),包括蛇撞到自己和圍墻都將使游戲結(jié)束,判斷吃豆等,其中還包括根據(jù)蛇的長度來進(jìn)行游戲難度的改變。5 具體鍵盤游戲操作運(yùn)用到Windows消息響應(yīng)中的WM_KEYDOWN,用OnKeyDown()來響應(yīng)玩家的實(shí)際操作。/第一部分 首先在已有工程下的“ClassView”中右鍵CView類添加以下Windows信息1 WM_KEYDOWN2 WM_RBUTTONDOWN3 WM_TIMER再右鍵CVie

3、w類選擇 “ADD Virtual Funciton”選OnInitialUpdate()OnInitialUpdate()的功能如下:視圖窗口完全建立后第一個(gè)被框架調(diào)用的函數(shù)。框架在第一次調(diào)用OnDraw前會(huì)調(diào)用OnInitialUpdate,因此OnInitialUpdate是設(shè)置滾動(dòng)視圖的邏輯尺寸和映射模式的最合適的地方。時(shí)間上,兩者先后順序不同,構(gòu)造函數(shù)生成本類的對(duì)象,但沒有產(chǎn)生窗口,OnCreate后窗口產(chǎn)生,然后才是視圖的OnInitialUpDate,一般在這里對(duì)視圖的顯示做初始化。簡單點(diǎn),就是ONCREATE只是產(chǎn)生VIEW的基本結(jié)構(gòu)和變量而在OnInitialUpDate()

4、中,主要初始化視圖中控件等。對(duì)各個(gè)變量進(jìn)行初始化操作所以我們要用這個(gè)函數(shù)來進(jìn)行貪吃蛇的初始化工作。再者還要添加一個(gè)成員函數(shù)oninit()進(jìn)行貪吃蛇外觀的初始化控件的設(shè)計(jì)再者是設(shè)計(jì)游戲的一些控件來控制“游戲開始” “游戲結(jié)束” 和“游戲暫?!?。我們可以點(diǎn)擊“工作空間”的“ResourceView”進(jìn)行控件的具體設(shè)計(jì),這里我們?cè)贛enu文件夾中把“IDR_MAINFRAME”中默認(rèn)的控件全部刪除右鍵其中的標(biāo)題欄,點(diǎn)擊屬性,會(huì)得到一個(gè)菜單欄標(biāo)題,我們分別建立1個(gè)菜單欄標(biāo)題。這里我們分別建“游戲”。點(diǎn)擊并且在已有控件中的列表中點(diǎn)擊屬性,進(jìn)行“菜單項(xiàng)目屬性”的設(shè)置。我們本別建立的屬性“標(biāo)明”與對(duì)應(yīng)的I

5、D有游戲開始 IDM_START游戲暫停 IDM_PAUSE游戲繼續(xù) IDM_CONTINUE游戲退出 IDM_EXIT此處控件的設(shè)計(jì)是“可見即可得”的控件操作成功設(shè)置ID之后 我們分別右鍵各項(xiàng)屬性進(jìn)行消息響應(yīng)處理函數(shù)的生成具體方法操作例子如下1右鍵“游戲開始”2 點(diǎn)擊“類向?qū)Ыⅰ? 在Message Maps頁面,在要進(jìn)行消息響應(yīng)的控件ID列表Object IDs中上選擇對(duì)應(yīng)的ID, 這里我們選擇IDM_START,具體實(shí)現(xiàn)的環(huán)境是CView類,所以我們必須把“Class name”的默認(rèn)“CMainFrame”改為“CView類”,并且在“Messages”類型設(shè)置中,用“COMMAND

6、”設(shè)置為其為命令消息。其余各項(xiàng)也按照同理進(jìn)行設(shè)置。PS:Windows消息的分類有3種,標(biāo)準(zhǔn)消息,命令消息,通告消息1 標(biāo)準(zhǔn)消息除COMMAND之外,所以WM_開頭都是標(biāo)準(zhǔn)消息2 命令消息就是COMMAND3 通告消息由控件產(chǎn)生的消息,這類信息也能以WM_COMMAND類型出現(xiàn)最后我們回到原來的ClassView去看看我們一共添加的函數(shù)所有的函數(shù)集合全部有以下在此處我們可以看到我們添加的Windows消息所有的 消息響應(yīng)函數(shù)的聲明/第二部分具體實(shí)現(xiàn)游戲、Step 1首先我們?cè)谖募_頭處分別定義 蛇 和食物 的全局變量struct Snakeint x,y;int len;int direct;

7、Snake50;struct Foodint x;int y;int isfood;Food;再者void CSNAKEView:OnInitialUpdate()CView:OnInitialUpdate();Snake0.x=10;Snake0.y=10;Snake1.x=11;Snake1.y=10;Snake2.x=12;Snake2.y=10;Snake0.direct=3;Snake0.len=3;Food.isfood=1;/ TODO: Add your specialized code here and/or call the base class代碼說明:初始化貪吃蛇起初有

8、3個(gè)節(jié)點(diǎn),長度為3,起始坐標(biāo);食物默認(rèn)為 1無0有 當(dāng)然我更加喜歡0無1有Step 2 對(duì)OnKeyDown()具體添加代碼void CSNAKEView:OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)/ TODO: Add your message handler code here and/or call defaultswitch(nChar)case VK_UP:if(Snake0.direct!=2)Snake0.direct=1;break; case VK_DOWN:if(Snake0.direct!=1)Snake0.direc

9、t=2;break;case VK_LEFT:if(Snake0.direct!=4)Snake0.direct=3;break; case VK_RIGHT:if(Snake0.direct!=3)Snake0.direct=4;break; CView:OnKeyDown(nChar, nRepCnt, nFlags);CView:OnKeyDown(nChar, nRepCnt, nFlags);OnKeyDown函數(shù)的第一個(gè)參數(shù) UINT nChar 是接收用戶鍵入的信息,然后我們用switch進(jìn)行選擇判斷代碼說明:Snake0代表的是蛇頭,我們對(duì)蛇頭的方向Snake0.direct進(jìn)

10、行判斷。case VK_UP:if(Snake0.direct!=2)Snake0.direct=1;break; 意思就是當(dāng)Snake0.direct的方向此時(shí)并不等于“下”的時(shí)候,才能做出“上”的操作動(dòng)作,否則則忽略用戶“向上”的操作按鍵效果Step 3 對(duì)OnRButtonDown()具體添加代碼void CSNAKEView:OnRButtonDown(UINT nFlags, CPoint point)/ TODO: Add your message handler code here and/or call default/ TODO: Add your message handl

11、er code here and/or call defaultCString str;str.Format("%d,%d",point.x,point.y);AfxMessageBox(str);CView:OnRButtonDown(nFlags, point);CView:OnRButtonDown(nFlags, point);CView:OnRButtonDown(nFlags, point);這個(gè)函數(shù)功能是:用鼠標(biāo)右鍵屏幕,就會(huì)馬上顯示當(dāng)前位置的坐標(biāo)信息。其實(shí)這個(gè)函數(shù)并不是本游戲中必要添加的函數(shù),只是為了在后面游戲頁面的設(shè)計(jì)的時(shí)候可以用鼠標(biāo)右鍵屏幕了解大概的屏幕

12、坐標(biāo)信息,才特地做的這里補(bǔ)充一個(gè)知識(shí)點(diǎn)WIN32坐標(biāo)系有3種1 世界坐標(biāo)系2 頁面坐標(biāo)系3 設(shè)備坐標(biāo)系世界坐標(biāo)系與頁面坐標(biāo)系稱謂“邏輯空間”,實(shí)際上就等同于我們現(xiàn)實(shí)生活中的數(shù)學(xué)坐標(biāo)系一樣。設(shè)備坐標(biāo)系則不然,電腦屏幕就是一個(gè)用設(shè)備坐標(biāo)系的,特征為以用戶區(qū)窗口左上角為(0 , 0)原點(diǎn),X坐標(biāo)向右為正,Y坐標(biāo)向下為正。Step 4void CSNAKEView:oninit()CDC *pDC=GetDC();CBrush DrawBrush=(RGB(100,100,100);CBrush *Drawbrush=pDC->SelectObject(&DrawBrush);for(i

13、nt i=0;i<=snake0.l-1;i+)pDC->Rectangle(snakei.x*20,snakei.y*20,(snakei.x+1)*20,(snakei.y+1)*20);pDC->SelectObject(DrawBrush);代碼說明:利用Windows給我們提供的CDC類來進(jìn)行畫圖,我們首先用一個(gè)指向CDC類的指針去接受與該窗口相關(guān)聯(lián)的DC句柄,然后用定義畫刷一個(gè)DrawBrush對(duì)象,并且用RGB(100,100,100)來給畫刷初始化顏色。并且用SelectObject(&DrawBrush);函數(shù)把對(duì)象畫刷選入到設(shè)備描述表中,用for循

14、環(huán)依次把貪吃蛇的3個(gè)節(jié)點(diǎn)畫出來。void far rectangle(int left, int top, int right, int bottom);這個(gè)函數(shù)的的功能根據(jù)函數(shù)功能:該函數(shù)畫一個(gè)矩形,用當(dāng)前的畫筆畫矩形輪廓,用當(dāng)前畫刷進(jìn)行填充.Step 5 控件添加代碼void CSNAKEView:OnStart()/ TODO: Add your command handler code hereSetTimer(1,3000,NULL);AfxMessageBox("3秒后開始游戲!");void CSNAKEView:OnPause()/ TODO: Add yo

15、ur command handler code hereKillTimer(1);AfxMessageBox("暫停游戲.");void CSNAKEView:OnExit()/ TODO: Add your command handler code hereAfxMessageBox("退出游戲.");exit 0;void CSNAKEView:OnContinue()/ TODO: Add your command handler code hereSetTimer(1,10,NULL);代碼說明:由于之前我們?cè)O(shè)定了WM_TIMER消息,我們能運(yùn)用

16、計(jì)時(shí)器功能用WM_TIMER來設(shè)置定時(shí)器先請(qǐng)看SetTimer這個(gè)API函數(shù)的原型UINT_PTR SetTimer(HWND hWnd, / 窗口句柄UINT_PTR nIDEvent, / 定時(shí)器ID,多個(gè)定時(shí)器時(shí),可以通過該ID判斷是哪個(gè)定時(shí)器UINT uElapse, / 時(shí)間間隔,單位為毫秒TIMERPROC lpTimerFunc / 回調(diào)函數(shù));SetTimer(m_hWnd,1,1000,NULL); /一個(gè)1秒觸發(fā)一次的定時(shí)器在MFC程序中SetTimer被封裝在CWnd類中,調(diào)用就不用指定窗口句柄了所以我們這里可以只去后3個(gè)參數(shù)寫成SetTimer(1,10,NULL);1

17、000為1秒關(guān)于afxMessageBox的研究在本博客中有寫有,這里就不在贅述Step 6 對(duì)OnDraw()的添加代碼OnDraw()函數(shù)眾所周知視圖類中的輸出.視圖類的輸出基本上都是在視圖類的OnDraw函數(shù)中處理的,系統(tǒng)會(huì)準(zhǔn)備好入?yún)ⅲ缓笳{(diào)用OnDraw函數(shù)本人也沒有過多的去研究過次函數(shù),僅知道一些皮毛。不過這里有個(gè)知識(shí)點(diǎn)那便是OnPaint()與OnDraw()的區(qū)別,OnPaint()派生于CWnd類,響應(yīng)WM_PAINT消息。OnDraw是CView類的成員函數(shù),并且沒有消息響應(yīng)功能,這就是為什么視圖類沒有只有OnDraw()而沒有OnPaint()的原因。OnDraw()維護(hù)視

18、圖客戶區(qū)(例如通過試表在視圖中畫圖),而OnPaint()維護(hù)窗口的客戶區(qū)void CSNAKEView:OnDraw(CDC* pDC)CSNAKEDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);/ TODO: add draw code for native data hereCBrush backBrush(RGB(100,100,0);CBrush* pOldBrush = pDC->SelectObject(&backBrush);CRect rect;pDC->GetClipBox(&rect);pDC->

19、PatBlt(rect.left, rect.top, rect.Width(), rect.Height(),PATCOPY);pDC->SelectObject(pOldBrush);pDC->Rectangle(19,19,501,501);oninit();代碼說明:此處是用畫刷畫一個(gè)背景,并且畫出3個(gè)矩形區(qū)域函數(shù)原型:int GetClipBox(HDC hdc, LPRECT lprc);該函數(shù)得到一個(gè)能夠完包含當(dāng)前可見區(qū)域的最小矩形的大小。 函數(shù)原型:int nYLeft, int nWidth, int nHeight, DWORD dwRop);該函數(shù)使用當(dāng)前選入

20、指定設(shè)備環(huán)境中的刷子繪制給定的矩形區(qū)域。Step 7 對(duì)OnTime()的添加代碼void CSNAKEView:OnTimer(UINT nIDEvent)/ TODO: Add your message handler code here and/or call defaultCDC *pDC=GetDC();CString soure;if(Snake0.len=2)SetTimer(1,370,NULL);if(Snake0.len=3)SetTimer(1,270,NULL);if(Snake0.len=6)SetTimer(1,200,NULL);if(Snake0.len=9)S

21、etTimer(1,100,NULL);soure.Format("得分:%d!",(Snake0.len-3)*10); /撞界判斷if(Snake0.x*20<=37|Snake0.y*20<=37|Snake0.x*20>=462|Snake0.y*20>=462)KillTimer(1);AfxMessageBox(soure);/ s=0;/蛇身相撞判斷if(Snake0.len>3)for(int sn=Snake0.len-1;sn>0;sn-)if(Snake0.x*20=Snakesn.x*20&&Sna

22、ke0.y*20=Snakesn.y*20)KillTimer(1);AfxMessageBox(soure);/ s=0;/pDC->SelectStockObject(WHITE_PEN);pDC->Rectangle(SnakeSnake0.len-1.x*20,SnakeSnake0.len-1.y*20,(SnakeSnake0.len-1.x+1)*20,(SnakeSnake0.len-1.y+1)*20);for(int i=Snake0.len-1;i>0;i-)Snakei.x=Snakei-1.x;Snakei.y=Snakei-1.y;/行走方向判斷i

23、f(Snake0.direct=1)Snake0.y-;if(Snake0.direct=2)Snake0.y+;if(Snake0.direct=3)Snake0.x-;if(Snake0.direct=4)Snake0.x+;pDC->SelectStockObject(BLACK_PEN);CBrush DrawBrush=(RGB(100,100,100);CBrush *Drawbrush=pDC->SelectObject(&DrawBrush);pDC->Rectangle(Snake0.x*20,Snake0.y*20,(Snake0.x+1)*20,

24、(Snake0.y+1)*20);pDC->SelectObject(DrawBrush);/判斷吃豆的條件,撞到就吃if(Snake0.x*20=Food.x*20&&Snake0.y*20=Food.y*20)Snake0.len+;Food.isfood=1;SnakeSnake0.len-1.x=SnakeSnake0.len-2.x;SnakeSnake0.len-1.y=SnakeSnake0.len-2.y;/如果食物被吃了 就生成if(Food.isfood=1)srand(unsigned)time(NULL);dofor(int isfo=Snake0

25、.len-1;isfo>=0;isfo-)if(Snake0.x*20=Snakeisfo.x*20&&Snake0.y*20=Snakeisfo.y*20)Food.x=rand()%;Food.y=rand()%;while(Food.x*20<70|Food.y*20<70|Food.x*20>430|Food.y*20>430);pDC->Rectangle(Food.x*20,Food.y*20,(Food.x+1)*20,(Food.y+1)*20);Food.isfood=0;CView:OnTimer(nIDEvent);/下

26、面由我來詳細(xì)分析代碼if(Snake0.len=2)SetTimer(1,370,NULL);if(Snake0.len=3)SetTimer(1,270,NULL);if(Snake0.len=6)SetTimer(1,200,NULL);if(Snake0.len=9)SetTimer(1,100,NULL);此段代碼的作用是根據(jù)蛇的長度來進(jìn)行SetTimer()函數(shù)的定義,可以根據(jù)長度來進(jìn)行游戲難度的設(shè)定,如上代碼分別370ms 270ms 200ms 100ms進(jìn)行一次新的移動(dòng)。/撞界判斷if(Snake0.x*20<=37|Snake0.y*20<=37|Snake0.x

27、*20>=462|Snake0.y*20>=462)KillTimer(1);AfxMessageBox(soure);這里為什么要乘以20呢?由于我們初始化的時(shí)候是Snake0.x=10;Snake0.y=10;Snake1.x=11;Snake1.y=10;Snake2.x=12;Snake2.y=10;所以只是把貪吃蛇的起始位置“搬移”到20倍的位置 當(dāng)然可以等價(jià)于Snake0.x=10*20;Snake0.y=10*20;Snake1.x=11*20;Snake1.y=10*20;Snake2.x=12*20;Snake2.y=10*20;蛇的一節(jié)身體為一個(gè)矩形塊,這樣表示

28、每個(gè)矩形塊只需起點(diǎn)坐標(biāo)x 和y身體是不斷增長的,所以用數(shù)組存放每一節(jié)的坐標(biāo)/蛇身相撞判斷if(Snake0.len>3)for(int sn=Snake0.len-1;sn>0;sn-)if(Snake0.x*20=Snakesn.x*20&&Snake0.y*20=Snakesn.y*20)KillTimer(1);AfxMessageBox(soure);/ s=0;這段是最好理解的了,由于判斷蛇自己是否咬到了自己,根據(jù)蛇長sn,進(jìn)行sn次for sn-1次循環(huán)并且和Snake0.x進(jìn)行比較(之所以進(jìn)行sn-1次那肯定是不包括蛇頭而且蛇的長度也必須大于3才會(huì)發(fā)生

29、自己咬自己的情況)KillTimer(1);是停止計(jì)時(shí)器;和之前的SetTime()對(duì)應(yīng)而已AfxMessageBox(soure);這里不深究,總之就是輸出一個(gè)原樣輸出內(nèi)容pDC->SelectStockObject(WHITE_PEN);/把白色的“PEN”選入設(shè)備進(jìn)行畫圖pDC->Rectangle(SnakeSnake0.len-1.x*20,SnakeSnake0.len-1.y*20,(SnakeSnake0.len-1.x+1)*20,(SnakeSnake0.len-1.y+1)*20);/讓它去畫最后一個(gè)節(jié)點(diǎn)for(int i=Snake0.len-1;i>

30、0;i-)/貪吃蛇的蛇身移動(dòng)Snakei.x=Snakei-1.x;Snakei.y=Snakei-1.y;這段的知識(shí)點(diǎn)要嚴(yán)重的說明一下1 為什么要專門用白筆畫最后一個(gè)呢?因?yàn)槲覀兊挠螒蚪缑媸怯冒咨模澇陨咭苿?dòng)的時(shí)候,肯定是蛇頭向前走一單位,而尾部肯定要“擦除”掉一個(gè)單位,那怎么擦除呢?我們只能用和背景色一樣的畫筆。原來蛇的位置和新蛇的位置差一個(gè)單位,所以看起來蛇會(huì)多一節(jié)身體,所以將蛇的最后一節(jié)用背景色覆蓋SelectStockObject(WHITE_PEN) 讓它起到“消失”最后一個(gè)節(jié)點(diǎn)的功能讓我們以為蛇是向前走了。如 假設(shè)現(xiàn)在貪吃蛇有4個(gè)單位0123SnakeSnake0.len-1.x*20 <=>SnakeSnake0.4-1.x*20 =Snake3.x*20數(shù)組3 恰好是最后一個(gè)節(jié)點(diǎn)的下標(biāo),別忘了數(shù)組是從0開始算的請(qǐng)仔細(xì)領(lǐng)悟2for(int i=Snake0.len-1;i>0;i-)Snakei.x=Snakei-1.x;Snakei.y=Snakei-1.y;進(jìn)行次數(shù)為"長度-1"次的循環(huán)為什么要進(jìn)行長度-1次呢?我這里要說明一下,這里是不包括蛇頭的進(jìn)行的平移,用數(shù)組賦值的方法,把

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論