当前位置:网站首页>C實現貪吃蛇小遊戲

C實現貪吃蛇小遊戲

2022-07-04 06:03:00 浪雨123

貪吃蛇小遊戲可以說是很多小夥伴的遊戲啟蒙之物,今天我們一起來用C語言複刻童年經典遊戲貪吃蛇,在編寫這個遊戲之前,需要了解一些easyx圖形庫的知識,因為遊戲的窗口就是靠此來提供。

這篇文章主要介紹編寫這個遊戲要實現的功能,另外我會再寫一篇關於這個遊戲常見的一些BUG及其解决辦法,如果已經能完成部分的編寫,而困於某些BUG的夥伴可以移步另一篇文章,可能會有你遇到的一些BUG。

實現貪吃蛇遊戲,首先我們需要設置一個遊戲窗口,利用easyx圖形庫,設置一個經典的640*480大小的窗口。窗口設定好了,接下來就可以定義蛇了,我們用結構體來定義蛇的類型,分析一下蛇需要用到那些數據,我們首先需要知道這個蛇有多長,便於遊戲開始時蛇的長度設定,及遊戲結束後蛇長的統計,那我們就用 int n 來錶示蛇的長度。蛇在移動過程中是要有一個移動方向的,因此我們也需要設置一個int direction 來錶示蛇移動的方向,因為方向就四個,我們幹脆就把四個方向枚舉出來,然後蛇的移動方向就用枚舉變量來錶示

//枚舉方向
enum direction
{
	up = 72,
	down = 80,
	left = 75,
	right = 77
};

其次我們需要知道蛇的每一節身體的坐標,這樣我們通過坐標來讓蛇移動,以及把蛇繪制到窗口上,因為蛇的身體有很多節,坐標又有x , y兩個值。我們就把坐標單獨定義為結構體,然後身體的每一節都對應著一個坐標,我們用數組來錶示身體

//定義蛇的坐標
struct coor
{
	int x;
	int y;
}coor;

最終蛇的定義如下

//定義蛇的數據類型
struct snake
{
	int n = 3;
	direction site;
	coor szb[NUM];
}snake;

//其中NUM是該數組的最大值即蛇的身體最長長度,自行定義

定義完蛇的類型,接下來我們就把蛇初始化以下,寫一個初始化函數, initsnake() 假設蛇剛開始長度為3, 方向向左, 坐標依次為

    snake.n = 3;
	snake.site = left;
	snake.szb[0].x = 320;
	snake.szb[0].y = 240;
	snake.szb[1].x = 310;
	snake.szb[1].y = 240;
	snake.szb[2].x = 300;
	snake.szb[2].y = 240;	

初始化完成後,我們就把蛇繪制到窗口上,寫一個 Drawsnake() 函數,其實就是一個循環,以蛇的長度為判定條件,從蛇頭的坐標開始依次繪制,畫矩形或者圓都可,這裏我用的是矩形

void Drawsnake()
{	
	for (int i = 0; i < snake.n; i++)
	{
		rectangle(snake.szb[i].x, snake.szb[i].y, snake.szb[i].x+10, snake.szb[i].y+10);
	}
}

繪制完成之後,接下來就要讓蛇動起來了,這點很關鍵,如何實現讓蛇動起來,分析可知,蛇移動的原理是讓蛇頭的坐標朝向某個方向不斷的改變,身體再跟著蛇頭一起改變,坐標每變換一次就重新繪制蛇的身體,因為這個過程足够快,我們將其處理的速度用Sleep()再延緩一點,就能形成我們眼睛所能識別的幀率,從而看著蛇是連續移動的。既然要不斷的變換蛇頭的坐標,並且重新繪制蛇的身體,那就要將這兩個函數放到循環裏面,蛇的移動我們編寫一個Movesnake()。

int main()
{
    initgraph(640, 480);
	initgame();

    while (ture)
   {
     cleardevice();
     Movesnake();
     Drawsnake();
     Sleep(100);
   }
 
}

那麼如何讓蛇的坐標不斷朝著某個方向改變呢?接下來我們就仔細解决Movesnake(),首先我們要判斷具體往哪個方向移動,還記得在定義蛇的時候,設置了一個方向變量site嘛,現在它派上用場了。我們在初始化的時候,將這個變量初始化向左移動,然後程序將蛇頭坐標的x减少10,即向左改變了10個像素點,因為在循環裏,只要site的值不變,蛇頭就一直向左移動,第二節身體的坐標移動到原先蛇頭的比特置,其他節身體的坐標依次移動到它上一節身體的坐標處,這樣就實現了蛇的移動,別忘了加上一個cleardevice()函數,清除上一次繪制的圖形。

switch (snake.site)
	{
		case up:
			snake.szb[0].y-=10;
			break;
		case down:
			snake.szb[0].y+=10;
			break;
		case left:
			snake.szb[0].x-=10;
			break;
		case right:
			snake.szb[0].x+=10;
			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;
	}

蛇的移動完成之後,接下來就是蛇在移動的時候,方向的改變,如果不讓蛇的方向改變,那也沒法玩呢,首先我們只有在按下方向鍵的時候蛇頭的方向才會改變,那就先設置一個變量用來接收我們按下的方向鍵,然後再把這個方向鍵賦值給蛇頭的方向變量,我們就編寫一個Chdirection()函數來實現

void Chdirection()
{
	char key;
	key = _getch();
	
	switch(key)
	{
	case up:
		if (snake.site != down)
		{
			snake.site = up;
		}
		break;
	case down:
		if (snake.site != up)
		{
			snake.site = down;
		}
		break;
	case left:
		if (snake.site != right)
		{
			snake.site = left;
		}
		break;
	case right:
		if (snake.site != left)
		{
			snake.site = right;
		}
		break;
	}
}

這裏為什麼要在每一個分支後面加上 if 判斷語句呢,其實就是為了防止蛇直接就調頭了,這和現實是不符的,顯示中的掉頭要繞圈子的。

 其實,直接掉頭也是可以的,還蠻有意思的,像火車一樣兩頭開,哈哈,感興趣可以試試。

轉向的問題搞定了,但是程序怎麼知道什麼時候轉向呢?因為這個程序是放在循環裏的,我們不妨加一個判斷語句,用kbhit()函數來檢測玩家是否按下了按鍵,如果按下了就返回真值,進入轉向函數,並在其內部判斷該轉向哪裏,沒有按下就為假,不進入轉向函數。

int main()
{
    initgraph(640, 480);
	initgame();

    while (ture)
   {
     cleardevice();
     Movesnake();
     Drawsnake();
     Sleep(100);

     if ( kbhit() )
      {
        Chdirection();     
      }
    
   }
 
}

小蛇能跑起來了,接下就是吃食物了,那吃食物的效果又該如何實現呢?同樣,我們要給食物定義一個數據類型,首先就是食物的坐標,其次是食物此刻的狀態,是被吃了還是沒被吃,狀態我們就用bool變量

//定義食物的數據類型
struct food
{
	int x;
	int y;
	bool state;
}food;

接著我們回到初始化函數處,給第一個食物的狀態設定一下,定義為true,錶示被吃了,為何設定為被吃了,等會解釋,我們接著編寫Createfood(),用來創造食物,就是給食物的坐標附上隨機值,隨機值就用隨機值生成種子,一旦我們創建一個食物,那創建的瞬間,需要將食物的狀態改成false,錶示未被吃。

void Creatfood()
{
	while (1)
	{
		food.x = rand() % 62 * 10;
		food.y = rand() % 46 * 10;
		if (food.x >= 20 && food.y >= 20)
		{
			food.state = false;
			break;
		}
	}
}
  
//這裏我加循環是不想讓食物生成到邊界點

那麼什麼時候生成食物呢,那當然是食物的狀態為true錶示被吃了的時候,這也是為什麼我們要在開頭處將食物的狀態設置為true,就是為了開局就生成一個食物。同樣要用到一個判斷語句

int main()
{
	initgraph(LENGTH, WIDTH);
	initgame();

	while (1)
	{
		if (food.state)
		{
		Creatfood();
		}  
		cleardevice();
		Movesnake();
		Drawsnake();
		Drawfood();
		Sleep(100);	
		EndBatchDraw();
		if (_kbhit())
		{
		Chdirection();
		}
	}

食物創建好了,現在就剩下最後一步了,那就是實現吃的過程,就編寫個Eatfood(),當蛇頭的坐標與食物的坐標重合時就可以吃了,然後食物的狀態改為true,蛇的身長加1.

void Eatfood()
{
	if (snake.szb[0].x == food.x  &&  snake.szb[0].y == food.y)
	{
		snake.n++;
		food.state = true;
	}
}

就這樣,簡易的貪吃蛇就完成了,還有一個繪制食物,這個蠻簡單的,各比特夥伴自己實現吧。

int main()
{
	initgraph(LENGTH, WIDTH);
	initgame();

	while (1)
	{
		if (food.state)
		{
		Creatfood();
		}  
		
        cleardevice();
		Movesnake();
		Drawsnake();
		Drawfood();
		Eatfood();
		Sleep(100);	
		
        if (_kbhit())
		{
		Chdirection();
		}
	}

	getchar();	
}

原网站

版权声明
本文为[浪雨123]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/185/202207040602236436.html