接上,本系列博文最終將實現一個MFC版的“智能尋路貪吃蛇”。
上篇說道,蛇有了,怎麼在屏幕上顯示出來呢?
先說,我爲了圖省事,用MFC的生成嚮導建了一個基於對話框的程序界面如下所示:
這也是本程序的最終界面,添加按鈕,爲按鈕綁定函數什麼的就不多說了.
其中所謂的開啓和關閉外掛就是我們後面要討論的所謂的智能尋路(AI),這個後面再說.
首先我們可編寫一個ShowSnake()函數,用於蛇身,食物,以及黑不溜秋的背景顯示.
void CSnakeDlg::ShowSnake(CDC &dc)
{
HBRUSH hBrush = CreateSolidBrush(RGB(0,0,0));
dc.SelectObject(hBrush);
CPen pen(PS_SOLID,0,RGB(200,200,200));
dc.SelectObject(&pen);
dc.Rectangle(&m_rc);
hBrush = CreateSolidBrush(RGB(200,200,200));
dc.SelectObject(hBrush);
dc.Ellipse(&m_food);
list<SnakeNode>::iterator be=m_snake.m_snake.begin();
for(unsigned i=0;i<m_snake.m_snake.size();i++,be++)
{
dc.Rectangle(&be->rc);
}
}
背景和食物的顯示就不說了(就是簡單的利用GDI畫個矩形和圓),說一下蛇身的繪製.
[注意:背景應該在食物和蛇身之前繪製,不然背景會把它們覆蓋掉 !]
其實蛇身的繪製也沒什麼好說的,就是遍歷整個list,因爲前面設計的時候,咱們蛇身節點就是用舉行表示的,所以非常方便就畫出來了...
dc.Rectangle(&be->rc);
[注意:蛇每走一步,都應該刷新一下屏幕,刷新屏幕可用InvalidateRect(&RECT),該函數會自動調用OnPaint()函數,所以ShowSnake()函數也應該在OnPaint()中調用.]
到此爲止,我們已經能把蛇顯示在屏幕上了,那麼怎麼讓它動起來呢?
[在MFC中,一切的運動都離不開計時器,那麼什麼是計時器?]
其實很好理解,計時器的作用就是:隔一段時間就調用一下某個函數
計時器的用法也很簡單,由於篇幅有限,加上本文的目的不在於此,所以具體用法可百度谷歌...
也可以參見這篇博客: 計時器的使用
我們只要在OnTimer()函數中添加如下代碼即可,關於CreateGoodFood()函數稍後再說~
if(m_snake.SMove(m_dr,m_food))
{
CreateGoodFood();
}
InvalidateRect(&m_rc);
蛇的移動速度是由m_tim=SetTimer(1,100,NULL);中的第二個參數決定的
!
還有就是我們應該怎樣控制蛇的方向問題,我們可以通過鍵盤上的上下左右四個鍵控制.
但是MFC有一個弊端,注意到我們平時寫的應用程序,按鈕之間的切換也可以通過上下左右四個鍵進行.
可以判斷,MFC肯定對這幾個按鍵進行了響應,所以我們直接重載OnLButtonDown()函數是行不通的,我們應該在MFC處理它之前就把它們攔截下來.
具體說來可以這麼辦:
BOOL CSnakeDlg::PreTranslateMessage(MSG* pMsg)
{
if(m_ispc)//不處理消息..
return CDialog::PreTranslateMessage(pMsg);
//獲取四個方向鍵
switch(pMsg->wParam)
{
case VK_UP:
m_dr=DR_UP;
return true;
break;
case VK_DOWN:
m_dr=DR_DOWN;
return true;
break;
case VK_LEFT:
m_dr=DR_LEFT;
return true;
break;
case VK_RIGHT:
m_dr=DR_RIGHT;
return true;
break;
}
return CDialog::PreTranslateMessage(pMsg);
}
我們可以重載PreTranslateMessage()這個函數,在MFC處理它之前就攔下來,這樣就不會衝突了.
那麼怎麼控制方向呢? 我們在CSnakeDlg這個類中添加一個變量m_dr用來表示當前蛇應該移動的方向,每次按下某個方向鍵時就更新一下這個變量即可.
這樣一來,這條蛇現在不僅可以動,而且可以受人控制了~
最後一個問題,蛇要吃東西.
同樣的,我用矩形表示一塊食物.
void CSnakeDlg::CreateFood()
{
srand((unsigned)time(0));
int x=rand()%25;
int y=rand()%15;
m_food.left=m_po.x*x;
m_food.top=50+m_po.y*y;
m_food.right=m_food.left+m_po.x;
m_food.bottom=m_food.top+m_po.y;
}
通過上面的代碼,我們就可以產生一塊食物了,只要在ShowSnake()把它畫出來就可以了,但是這個函數還是存在一個小問題,實物很有可能跟蛇的身體的一個節點重合,而且隨着蛇身變長,這種可能性也越來越大
!
那麼怎麼產生一個不與蛇身重合的食物呢?其實很簡單:
void CSnakeDlg::CreateGoodFood()
{
CreateFood();
list<SnakeNode>::iterator iter=m_snake.m_snake.begin();
for(unsigned i=0;i<m_snake.m_snake.size();i++,iter++)
{
if(m_food.left==iter->rc.left&&
m_food.top==iter->rc.top)
{
CreateFood();
iter=m_snake.m_snake.begin();
i=0;
}
}
}
每次CreateFood()後,掃描一遍蛇身,如果重合,再產生一遍就是了~
(最後不得不吐槽一句,CSDN的博客編輯系統真心難用..)
好了,到此爲止,我們已經實現了一條可以自由移動,吃食物,長身子的蛇了,下一篇博文中,我們就可以讓這條蛇自動的吃食物了.