C++ 開發貪吃蛇遊戲總結(未完待續....)

一、思考貪吃蛇的思路:

1.在一個窗口繪製貪吃蛇,肯定少不了繪製的API,所以必不可少需要引入graphics.h這個圖形界面庫(裏面封裝了WIN 32大部分繪製API,如果沒有的話,可以用GDI)。

2.貪吃蛇吃掉食物就會增長一點,可以把它看成是一節一節的。

3.蛇要移動,肯定會有座標的變化。

4.既然蛇會移動,那麼蛇吃掉食物的時候食物當然也是隨機變化的,所以食物也有座標。

5.蛇的碰撞檢測(蛇頭碰到邊界或者碰到自己的身體某部分,就死亡(game over))。

6.蛇要有方向纔可以

7.需要讀取鍵盤操作,改變蛇的方向

二、補充graphics.h圖形庫的基礎知識

1.創建一個繪製窗口

API:

void initgraph(int width,int height)    //創建寬x,高y的圖形窗口 

 2.清除窗口屏幕所有繪製

API:

void cleardevice()     //清空整個窗口的繪製

 3.在指定位置輸出文字

API:

void settextxy(int x,int y, LPCTSTR str//在座標爲(x,y)處輸出文字str

void outtextxy( int x, int y, TCHAR c )     // 兩種字符串類型

 4.繪製矩形

API:

fillrectangle(int left, int top, int right, int bottom)  //畫填充矩形,從起點(left,top) 到終點(right,bottom)

5.繪製圓角矩形

API:

fillroundrect(int left, int top, int right, int bottom, int ellipsewidth, int ellipseheight) //畫填充矩形,從起點(left,top) 到終點(right,bottom)  ellipsewidth構成圓角矩形的圓角的橢圓的寬度。ellipseheight構成圓角矩形的圓角的橢圓的高度。若後兩個相當相當於繪製一個圓形 不過不是以圓心的 是按照矩形的。

6.設置當前文字填充色

API:

settextcolor(COLOR color)   //設置當前文字也就是下一行文字的顏色 宏定義RED,BLUE,YELLOW等

7.設置當前文字風格

API:

settextstyle(int nHeight, int nWidth, LPCTSTR lpszFace, int nEscapement, int nOrientation, int nWeight, bool bItalic, bool bUnderline, bool bStrikeOut)  //設置文字寬高 如果 width = 0就是自適應大小,LPCTSTR輸出的文字後面可以省略掉

 三、如何創建蛇和食物

蛇和食物有一個共同的特點就是都有座標

所以

//創建座標
struct Coor {		// x y 座標結構體
	int x;
	int y;
};

其次創建蛇和食物結構(其實可以見蛇和食物的對象都可以的)

//蛇
struct Snake {		//蛇結構
	int n;	//當前的節數
	Coor szb[SNAKELENGTH];	//蛇座標 最大長度是500
	Ch ch;	//蛇的方向
}snake;		//結構體變量

			
//食物
struct Food
{
	Coor fzb;	//食物的座標 只有一個
	int flag;		//是否被吃的標誌
}food;

當然,少不了方向 方向無非就是上下左右 可以使會用枚舉類型定義

enum Ch
{
	up = 72,
	down = 80,
	left = 75,
	right = 77
};

四、編寫思路(過程)

定義完這些結構體後 我們知道 這個蛇也就是snake.szb[0].x 和snake.szb[0].y就是蛇頭

所以只需要移動蛇頭就可以。

爲什麼?because 首先蛇是一節一節的也就是繪製的一個一個小矩形,並且這個矩形是根據上面定義的snake結構的座標結構數組存放的x,y座標來繪製的,當蛇移動的時候 只需要將座標加一個步長就可以了。

這樣就真的對了嗎?每次移動就在那個放下加或者減?

其實不對,爲什麼?because 如果是蛇變了很多次方向就像這樣

應該使用怎麼樣的思路來進行所有蛇節方向改變呢?

可以讓蛇的最後一節n的x,y座標每次繪製的時候等於蛇的n-1的座標 這樣不就可以改變方向並且按蛇頭走的路原封不動的走一遍嗎? 對吧

所以 移動蛇的時候應該有一個循環,根據蛇節數來遍歷的

	for (int i = snake.n - 1; i > 0; i--)
	{
		snake.szb[i].x = snake.szb[i - 1].x;
		snake.szb[i].y = snake.szb[i - 1].y;
	}

創建蛇(這裏一共創建了4個蛇節 初始化的時候就是四個蛇節):

	snake.szb[3].x = 50;		//當前節數的X座標
	snake.szb[3].y = 0;		//當前節數的Y座標
	snake.szb[2].x = 40;
	snake.szb[2].y = 0;
	snake.szb[1].x = 30;
	snake.szb[1].y = 0;
	snake.szb[0].x = 20;
	snake.szb[0].y = 0;

那麼蛇的如何判斷方向呢(你需要引入頭文件 <conio.h>)?

	//*****這是判斷按鍵 來改變方向
    int move = _getch();
	switch (move)
	{
	case up:	//不能向下走
		if(snake.ch!=down)
			snake.ch = up;
		break;
	case down:
		if (snake.ch != up)
			snake.ch = down;
		break;
	case right:
		if (snake.ch != left)
			snake.ch = right;
		break;
	case left:
		if (snake.ch != right)
			snake.ch = left;
		break;
	}
//***********/
//********根據方向變化來移動蛇頭
    //先將後一節的座標等於前一節的座標
    for (int i = snake.n - 1; i > 0; i--)
	{
		snake.szb[i].x = snake.szb[i - 1].x;
		snake.szb[i].y = snake.szb[i - 1].y;
	}
    switch (snake.ch)
	{
	case up:
		snake.szb[0].y -= NUM;
		break;
	case down:
		snake.szb[0].y += NUM;
		break;
	case right:
		snake.szb[0].x += NUM;
		break;
	case left:
		snake.szb[0].x -= NUM;
		break;
	}
//********/

這裏的_getch 用於獲取按鍵ASCII 比如 _getch 獲取鍵盤按鍵   _kbhit() 檢測是否按下某個鍵無則返回0;

 接下來就是每次移動繪製蛇:

//繪製蛇  (根據存儲的蛇節n)來
for (int i =0; i  < snake.n ; i++)
	{
		if (i == 0)
		{
			setfillcolor(YELLOW);		//填充顏色
		}
		else {
			setfillcolor(RED);		//填充顏色
		}
		fillrectangle(snake.szb[i].x, snake.szb[i].y, snake.szb[i].x + size, snake.szb[i].y + size);
	}

接下來應該是食物了

食物是隨機變化的 並且必須是蛇頭吃掉食物才能再次隨機生成一個食物。

所以開始的時候應該封裝一個函數進行初始化食物


food.fzb.x = (rand() % WINDOW_WIDTH / 10) * 10; // 0 1 2 3 4 .. 639
	food.fzb.y = (rand() % WINDOW_HEIGHT / 10) * 10;	// 0 1 2 3 ... 479;
	//蛇的座標是10 20 30 40 有規律的 10的倍數
	food.flag = 1;	//沒有吃掉 1 隨機出現

爲什麼要 *10?因爲設置的窗口是640 * 480 食物的座標必須和蛇節大到小和每次的步長一致,這樣當蛇頭與食物重合就可以判斷吃掉了食物

//繪製食物
void DrawFood()
{
	setfillcolor(GREEN);
	//繪製圓形食物
	
	fillroundrect(food.fzb.x, food.fzb.y, food.fzb.x + 10, food.fzb.y + 10,10,10);
}

有了上面說的碰撞的思路

部分代碼:

if (snake.szb[0].x < 0 || snake.szb[0].x >= WINDOW_WIDTH || snake.szb[0].y >= WINDOW_HEIGHT || snake.szb[0].y < 0)
	{
		return true;
	}

。下面是main


	//初始化隨機種子
	srand((unsigned int)time(NULL));
	//初始化後會有一個像素矩陣,可以使用座標系去表示它
	InitSnake();
	/*
		改變方向.按鍵之後
		按鍵之前程序在繼續移動和繪圖
	*/
	 char buffer[100];
	while (1)
	{
		while (!_kbhit())		//檢測是否按下某個鍵 沒有按下就返回0
		{
			if (food.flag==0)
			{
				CoorFood();
			}
		
			cleardevice();		//清除重繪
			if (isCrash())		//如果碰撞了
			{
				
				NUM = 0;	//表示撞牆 每次移動的步伐10 變成0 不走了
				
			}
			if (NUM != 0)		//
			{
				rectangle(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
				settextstyle(30, 0, TEXT("微軟雅黑"));
				outtextxy(WINDOW_WIDTH + ((WINDOW_WIDTH + 200) - WINDOW_WIDTH) / 2 - 30, 50, TEXT("貪吃蛇"));
				settextstyle(25, 0, TEXT("微軟雅黑"));
				settextcolor(YELLOW);
				outtextxy(WINDOW_WIDTH + ((WINDOW_WIDTH + 200) - WINDOW_WIDTH) / 2 - 60, 80, TEXT("分數:"));
				settextstyle(60, 0, TEXT("微軟雅黑"));
				settextcolor(WHITE);
				_itoa_s(FenShu, buffer, _countof(buffer), 10);
				outtextxy(WINDOW_WIDTH + ((WINDOW_WIDTH + 200) - WINDOW_WIDTH) / 2 - 60, 120, LPCTSTR(buffer));
				MoveSnake();
				DrawFood();
				DrawSnake(10);
				EatFood();
				Sleep(100);
			}
			else {
				break;
			}
		}
		if (NUM == 0)
		{
			break;
		}
		ChangeSnakeCh();	//按下後執行方向改變函數
		
	}
	settextstyle(65, 0, TEXT("仿宋"));
	outtextxy((WINDOW_WIDTH + 200) / 2 - 120, WINDOW_HEIGHT / 2 - 50, TEXT("你掛了!"));
	settextstyle(33, 0, TEXT("仿宋"));
	settextcolor(YELLOW);
	outtextxy((WINDOW_WIDTH + 200) / 2 - 120, WINDOW_HEIGHT / 2 + 20, TEXT("分數:"));
	outtextxy((WINDOW_WIDTH + 200) / 2 , WINDOW_HEIGHT / 2 + 20, LPCTSTR(buffer));

我的貪吃蛇的源代碼已經在github上面

github地址:https://github.com/IAmWilling/SnakeGame

源碼裏面有詳細的註釋,可以對照代碼根據這篇總結,來讀。

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