從0開始如何開發貪喫蛇遊戲

      作爲一款經典遊戲,貪喫蛇相信很多朋友都玩過,規則簡單易上手。但您有沒有想過自己製作一款貪喫蛇遊戲呢?鄙人最近有意學習MFC,所以先拿貪喫蛇練了一下手。接下來就介紹一下這款最簡陋的貪喫蛇的開發歷程。

      首先是開發環境的搭建,我的選擇是visual studio2013,在微軟官網上下載。安裝好以後,打開vs13,選擇菜單欄中的文件 ->新建 ->項目,然後在彈出列表中選擇 visual c++下的MFC應用程序,輸入自己的工程名稱,我的是自制貪喫蛇,點擊下一步,選擇 單文檔和MFC標準,然後直接點擊完成,至此我們就生成了一個MFC空框架。但這個空框架什麼也幹不了,如果我們想在它的基礎上構建一款遊戲,那麼自然需要我們手動DIY點什麼,所以接下來,是真正的步入正軌:

      1)首先找到【自制貪喫蛇】的資源管理器,在頭文件裏面,找到自制貪喫蛇view.h的視圖類,在C自制貪喫蛇view類體聲明中,添加幾個private變量,如圖所示:

源代碼是:

private:
	int  exist; //用它來記錄蛇是否還活着
	bool flag;  //用它來記錄地圖中的食物是否被喫掉,是則重新生成食物
	int  head;  //它表示蛇頭位置
	int  tail;  //它表示蛇尾位置
	int  trend; //它表示當前蛇的走向
	int  map_arr[61 * 61]; //這是蛇所能活動的整個地圖
	const int ROW = 61;    //它表示地圖的寬度


      2)接着在源文件中,打開自制貪喫蛇view.cpp文件,在裏面找到C自制貪喫蛇view類的構造函數的定義,最後在函數體中添加這幾行代碼:

for (int i = 0; i < 61 * 61; i++) map_arr[i] = 0; //先把地圖清空
	score = 0;
	flag = false;
	exist = 1;
	head = 62; //遊戲開始,我們先把蛇放在左上角的位置
	tail = 62; //同上
	trend = ROW; //開始時蛇的默認走向是向下走
	map_arr[head] = 1;

      3)貪喫蛇作爲一款單機遊戲,它是需要玩家與機器交互的,既然是交互,那麼程序就需要接收並處理外部消息,比如鼠標按下、鍵盤被按下等,在這裏我們只需要得到鍵盤被按下的消息就行了,所以我們在框架中增加一個消息相應函數OnChar,用它來處理鍵盤被按下時的消息,具體做法如下:在菜單欄中點擊工程->類嚮導,在彈出的對話框中的類名一欄中選擇C自制貪喫蛇view,然後在消息欄之下找到WM_CHAR,如圖所示:

再然後雙擊它,點擊編輯代碼,最後我們在新生成的OnChar函數體中添加如下代碼:

switch (nChar)
    {
    case 119: //按下w表示向上走
        if (trend == ROW)  break;
        trend = -ROW;
        break;
    case 97:  //a向左走
        if (trend == 1)  break;
        trend = -1;
        break;
    case 115: //s向下走
        if (trend == -ROW)  break;
        trend = ROW;
        break;
    case 100: //d向右走
        if (trend == -1)  break;
        trend = 1;
        break;
    }


      4)我最早接觸貪喫蛇遊戲,是在我四叔的諾基亞手機上,借完他的手機時,我想和他開個玩笑:衝擊貪喫蛇的榜單top1,然後把玩家ID改成【我是大傻逼】。。。記得當時我使用了N中策略:先是使用快蛇得分王,無奈咱反應跟不上,然後又用慢蛇蝸牛爬,又沒那耐心,所以最後未能如願(笑)。但快蛇與慢蛇,它們的速度是怎麼調節的呢?好吧,在遊戲設置裏面。但我想您肯定明白我問的不是這個,所以,蛇的速度在內部代碼層裏,究竟是在怎麼調節的呢??有一種方法是使用定時器策略:在微軟下,通過user32.dll中的SetTimer()函數告訴系統一個時間間隔,然後系統每隔一段時間,就會觸發我們的程序一次,既相當於告訴我們的應用程序中的時間相應函數:“嗨,又輪到你了,該幹嘛幹嘛吧”。。。具體的做法如下,首先,就像第三步驟中創建OnChar函數一樣:打開項目中的類嚮導,在消息欄下找到WM_CREATE,然後雙擊,點擊編輯代碼,在新生成的OnCreate函數體中添加如下一行代碼:

SetTimer(1, 130, NULL);

這裏我們主要注意第二個參數,就是那個130,它就是我們設置的時間間隔,單位是毫秒。這裏的意思也就是說,每隔0.13秒,我們的貪喫蛇遊戲會更新一次。


      5)設置了時間間隔,那麼我們的MFC程序就要捕獲並處理這個時間間隔,就像我們創建OnChar函數一樣,在類嚮導裏面創建一個消息響應函數OnTimer(),然後在裏面寫上我們自己的代碼。所以接下來纔是整個程序的重點:如何讓我們的小蛇走位、轉彎、喫東西、變長變粗。。。可以說它纔是這個MFC程序的靈魂所在。關於貪喫蛇,每個程序猿都可能有自己的一套實現算法。但我個人喜歡質樸簡潔,不想把本來很簡單的遊戲規則用太複雜的算法實現,比如又是結構體又是類什麼的,這款MFC的簡單不費力,纔是我坐在這裏寫這篇博客的主要原因。但接下來的代碼,可能讀者朋友閱讀起來有些困難,所以我儘量多添加註釋,以方便您理解:

srand((unsigned)time(NULL));
	int newhead1 = head , newhead2=head;
	newhead1 += trend;
	newhead2 += (trend * 2); //蛇前進使用的是二連進方式

	if (!exist) goto gameover;
	if (newhead2 < 0 || newhead2 >= 3700 || (trend*trend < 2 && newhead2 / ROW != head / ROW) || map_arr[newhead2] == 1)
		exist = 0; //檢測蛇是否撞到牆壁或自己,是的話蛇死
	
	else
	{
		/***********檢測蛇是否喫到食物,**********/
		if (-1 == map_arr[newhead2])
		{
			map_arr[newhead1] = 1;
			map_arr[newhead2] = 1;
			head = newhead2;
			score++;
			flag = false;
		}
		/***********是則加分並更新蛇頭位置*******/
		
		else 
		{
			map_arr[newhead1] = 1;
			map_arr[newhead2] = 1;
			head = newhead2; //以上三行代碼用於更新蛇頭位置

			int temp = (map_arr[tail - ROW] > 0)*(tail - ROW) + (map_arr[tail + 1] > 0)*(tail + 1) +
				(map_arr[tail + ROW] > 0)*(tail + ROW) + (map_arr[tail - 1] > 0)*(tail - 1) - tail;
			map_arr[tail] = 0;
			map_arr[tail + temp] = 0;
			tail = tail + temp * 2; //以上代碼用於更新蛇尾位置
		}

		if (false == flag) //檢測食物是否被喫到,是的話隨機生成一個新的食物
		{
			flag = true;
			int i, j;
		crefood:
			i = (rand() % 30) * 2 + 1;
			j = (rand() % 30) * 2 + 1;

			if (map_arr[i*ROW + j] > 0) goto crefood;
			else map_arr[i*ROW + j] = -1;
		}
	}
	gameover:
	OnDraw((CDC*)this); //調用該函數繪畫新局面

對了,將上面這些代碼插入到OnTimer函數之前,您需要先在C自制貪喫蛇view.cpp文件的前面,加上一句【#include <time.h>】,因爲裏面要用到隨機生成函數。


      6)遊戲的主邏輯都已經完成,但現在只有電腦能理解我們遊戲的狀態。如果我們玩家想要實時瞭解蛇的動態該怎麼辦?這就需要在視圖類中繪製遊戲了。一般的繪製,都是先做好N個素材圖片,然後保存在N個path下,最後在需要的時候露下臉。但我說過我是個簡單的傢伙,所以這款貪喫蛇,不光算法簡單,連外觀界面也醜陋到家了【自己不會美工,又懶得去網絡上摳圖,將就湊合吧】:蛇身子我用的是大寫字母O,食物用的是大寫F,空地用的是空格,兩面牆壁用的是簡單的兩條黑線。。。但麻雀雖醜,五臟俱全,讀者朋友能夠通過這個遊戲瞭解這個程序的真諦,纔是我感到欣慰的。閒言打住,正文開始:我把所有的繪製代碼都放在一個OnDraw函數中了,這個函數不需要我們新建,在一個MFC程序生成時,vs在view類中會自動幫我們創建一個OnDraw函數,只需在函數體的適當位置添加如下代碼即可:

CClientDC dc(this);

	CString nul("   ");
	CString snake("O");
	CString food("F");
	
	dc.MoveTo(450, 0);
	dc.LineTo(450, 450);
	dc.MoveTo(0, 451); //如果這裏是450,因爲相同區域重繪的緣故,牆壁會忽隱忽現
	dc.LineTo(451, 451);
	for (int y = 0; y < 30;y++)
	for (int x = 0; x < 30; x++)
	{
		if (1 == map_arr[(y * 2 + 1)*ROW + (x * 2 + 1)]) //繪畫蛇身子
			dc.TextOut(x * 15, y * 15, snake);
		else if (-1 == map_arr[(y * 2 + 1)*ROW + (x * 2 + 1)]) //繪畫食物
			dc.TextOut(x * 15, y * 15, food);
		else dc.TextOut(x * 15, y * 15, nul); //繪畫空地
	}
	
	wchar_t sco_str[30];
	if (!exist)   swprintf_s(sco_str, L"遊戲結束,您分數是:%d", score);
	else swprintf_s(sco_str, L"您已得了%d分", score);
	dc.TextOutW(451, 0, sco_str);

      7)最後的最後,讓我們來看一下游戲效果:


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