智能尋路貪吃蛇系列之 簡單貪吃蛇的MFC實現(下)

接上,本系列博文最終將實現一個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的博客編輯系統真心難用..)

好了,到此爲止,我們已經實現了一條可以自由移動,吃食物,長身子的蛇了,下一篇博文中,我們就可以讓這條蛇自動的吃食物了.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章