手把手教你寫貪喫蛇(C語言)

目錄

 

前言:程序包下載

一、涉及C語言知識點

二、貪喫蛇相關庫函數及其頭文件簡介:

1、#include

(1)void fflush(stdin)

(2)CONSOLE_CURSOR_INFO

(3)GetConsoleCursorInfo

(4)SetConsoleCursorInfo

2、#include

(1)rand()

(2)void srand(unsigned int seed);

3、#include

(1)int kbhit(void);

(2)int getch(void)

4、#include

(1)HANDLE GetStdHandle( DWORD nStdHandle );

(2)COORD coord={x,y};

(3)SetConsoleCursorPosition(HANDLE ,coord); 

5、#include

(1)time_t time(time_t * timer)

三、貪喫蛇相關子函數

1、光標移動到 (X,Y)

2、畫牆體、蛇、食物 

3、按鍵操作

4、食物刷新

5、蛇的狀態

6、開始遊戲

7、難度選擇

8、主菜單

9、主函數

四、項目代碼


前言:程序包下載

下載方式一:CSDN下載:https://download.csdn.net/download/qq_38351824/12142282

下載方式二:微信公衆號 ① 關注Tech雲   ② 暫不提供下載鏈接

下載方式三:文章下評論

一、涉及C語言知識點

數組、結構體、全局變量、局部變量、宏定義、條件語句、循環語句

二、貪喫蛇相關庫函數及其頭文件簡介:

1、#include<stdio.h>

(1)void fflush(stdin)

① 簡介

      清空輸入緩衝區,通常是爲了確保不影響後面的數據讀取(例如在讀完一個字符串後緊接着又要讀取一個字符,此時應該先執行 fflush(stdin);

② 語法

int fflush(FILE *stream)

(2)CONSOLE_CURSOR_INFO

① 簡介

        包含有關控制檯光標的信息。

② 語法

typedef struct _CONSOLE_CURSOR_INFO {
  DWORD dwSize;
  BOOL  bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

③ 參數

       dwSize:光標填充的字符單元格的百分比。該值介於1和100之間。光標外觀會發生變化,從完全填充單元格到顯示爲單元格底部的水平線。

       bVisible:光標的可見性。如果光標可見,則此成員爲TRUE

(3)GetConsoleCursorInfo

① 簡介

        獲取有關指定控制檯屏幕緩衝區的光標大小和可見性的信息。

② 語法

BOOL WINAPI GetConsoleCursorInfo(
  _In_  HANDLE               hConsoleOutput,
  _Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);

③ 參數

參數:

  • hConsoleOutput 控制檯屏幕緩衝區的句柄。句柄必須具有GENERIC_READ訪問權限。
  • lpConsoleCursorInfo 指向CONSOLE_CURSOR_INFO結構的指針,該結構接收有關控制檯遊標的信息。

返回值:

  • 如果函數成功,則返回值爲非零值。
  • 如果函數失敗,則返回值爲零。

(4)SetConsoleCursorInfo

① 簡介

設置指定控制檯屏幕緩衝區的光標大小和可見性。

② 語法

BOOL WINAPI SetConsoleCursorInfo(
  _In_       HANDLE              hConsoleOutput,
  _In_ const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);

③ 參數

參數:

  • hConsoleOutput 控制檯屏幕緩衝區的句柄。句柄必須具有GENERIC_READ訪問權限。
  • lpConsoleCursorInfo 指向CONSOLE_CURSOR_INFO結構的指針,該結構爲控制檯屏幕緩衝區的遊標提供新規範。

返回值:

  • 如果函數成功,則返回值爲非零值。
  • 如果函數失敗,則返回值爲零。

2、#include<stdlib.h>

(1)rand()

① 簡介

        rand()函數產生隨機數的一個隨機函數。

② 語法

rand()

③ 參數

若要生成 a 與 b 之間的隨機實數,應使用: RAND()*(b-a)+a

(2)void srand(unsigned int seed);

① 簡介

        srand函數是隨機數發生器的初始化函數。

② 語法

void srand(unsigned int seed);

③ 參數

unsigned seed:隨機數產生器的初始值(種子值)。

3、#include<conio.h>

(1)int kbhit(void);

① 簡介

用於非阻塞地響應鍵盤輸入事件。

② 語法

int kbhit(void);

③ 參數

功能及返回值: 檢查當前是否有鍵盤輸入,若有則返回一個非0值,否則返回0。

(2)int getch(void)

① 簡介

一個不回顯函數,當用戶按下某個字符時,函數自動讀取,無需按回車,有的C語言命令行程序會用到此函數做遊戲

② 語法

int getch(void)

③ 參數

返回值:讀取的字符

4、#include<windows.h>

(1)HANDLE GetStdHandle( DWORD nStdHandle );

① 簡介

        GetStdHandle是一個Windows API函數。它用於從一個特定的標準設備(標準輸入、標準輸出或標準錯誤)中取得一個句柄(用來標識不同設備的數值)。

② 語法

HANDLE GetStdHandle( DWORD nStdHandle );

GetStdHandle()返回標準的輸入、輸出或錯誤的設備的句柄,也就是獲得輸入、輸出/錯誤的屏幕緩衝區的句柄。

③ 參數

含義

STD_INPUT_HANDLE

標準輸入的句柄

STD_OUTPUT_HANDLE

標準輸出的句柄

STD_ERROR_HANDLE

標準錯誤的句柄

(2)COORD coord={x,y};

① 簡介

        COORD是Windows API中定義的一種結構,表示一個字符在控制檯屏幕上的座標。

② 語法

typedef struct _COORD {

SHORT X;  水平座標
SHORT Y;  垂直座標

} COORD;

③ 參數

SHORT 爲一種數據類型,範圍-32768~+32767。

SHORT X;         水平座標
SHORT Y;         垂直座標

(3)SetConsoleCursorPosition(HANDLE ,coord); 

① 簡介

        SetConsoleCursorPosition是API中定位光標位置的函數。

② 語法

 SetConsoleCursorPosition(HANDLE ,coord);         設置控制檯光標座標(設備句柄, 光標座標) 

③ 參數

使用這個函數需要兩個參數:第一個參數類型爲HANDLE,第二個參數類型爲COORD

5、#include<time.h>

(1)time_t time(time_t * timer)

① 簡介

        獲取當前的系統時間,返回的結果是一個time_t類型

② 語法

time_t time(time_t * timer)

③ 參數

獲取當前時間:NULL。

返回的結果是一個time_t類型,其實就是一個大整數。

三、貪喫蛇相關子函數

1、光標移動到 (X,Y)

光標移動 到 (X,Y)
void gotoxy(int x, int y) 
{	
    COORD coord={x,y};                                  初始化結構體coord (座標系X,Y)
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);    獲取輸出屏幕緩衝區句柄
    SetConsoleCursorPosition(handle, coord);            移動光標
}

2、畫牆體、蛇、食物 

//畫牆體、蛇、食物 
//卍  ※ 代表牆體
//♂     代表蛇體
//♀     代表食物
void drawMap(void)
{
	//畫 --- 遊戲說明	
	gotoxy(100, 7);
	printf("按鍵說明:");
	gotoxy(100, 10);
	printf("W:向上");
	gotoxy(100, 13);
	printf("A:向左");
	gotoxy(100, 16);
	printf("S:向下");
	gotoxy(100, 19);
	printf("D:向右");
	gotoxy(100,22);
	printf("當前分數:%d",score);
	
	//畫 --- 貪喫蛇牆體 
	  // 畫左右兩面牆  
	for (int i = 0; i <= MAP_HIGHTH; i++) {  
		gotoxy(0,i);
		printf("※");
		gotoxy(MAP_WIDTH,i);
		printf("※");
		Sleep(20);
	}
	  // 畫上下兩面牆
	for (int i = 0; i <= MAP_WIDTH; i += 2) {
		gotoxy(i, 0);
		printf("卍");
		gotoxy(MAP_WIDTH-i, MAP_HIGHTH);
		printf("卍");
		Sleep(20);
	}
	
	//畫 --- 蛇身 
	snake.len = SNAKE_BEGIN_LEN;  // 賦值剛開始的蛇身長度 
	srand((unsigned)time(NULL));  // 防止僞隨機數 
	do {
		snake.x[0] = rand() % (MAP_WIDTH/2 ) + MAP_WIDTH/4;
		snake.y[0] = rand() % (MAP_HIGHTH/2 ) + MAP_HIGHTH/4;
	} while (snake.x[0] % 2 != 0); // 隨機出現蛇頭,保證蛇頭在地圖中間範圍內,而不在牆體上 ,並且符合蛇是偶數的要求 
	
	gotoxy(snake.x[0], snake.y[0]);// 光標移動到蛇頭的位置 
	printf("♂");                  // 打印蛇頭 
	for (int i = 1; i < snake.len; i++) {// 打印蛇身 
		snake.x[i] = snake.x[0];
		snake.y[i] = snake.y[0] + 1;
		gotoxy(snake.x[i], snake.y[i]);
		printf("♂");
	}
	do {
		food.x = rand() % (MAP_WIDTH - 4) + 2;
		food.y = rand() % (MAP_HIGHTH - 2) + 1;
	} while (food.x % 2 != 0); // 隨機出現食物,保證食物不在牆體上,並且符合食物是偶數的要求 
	gotoxy(food.x, food.y);    // 光標移動到食物的位置 
	printf("♀");              // 打印食物 
}

3、按鍵操作

//按鍵操作
void keyDown(void) 
{
	//按鍵的處理
	if (_kbhit()) // 檢查當前是否有鍵盤輸入 
	{
			fflush(stdin);  // 清空輸入緩衝區,讀完一個字符串後緊接着又要讀取一個字符,此時應該先執行該函數 
			key = _getch(); // 一個不回顯函數,當用戶按下某個字符時,函數自動讀取,無需按回車
			
			//這一大段代碼是爲了防止回頭自殺的情況發生,如果用戶輸入反方向或者非wasd鍵,key的值不會改變
			if (key != 'w' && key != 'W' && key != 's' && key != 'S' && key != 'A' && key != 'a' && key != 'd' && key != 'D')
				key = tempkey;    // 如果用戶輸入非wasd鍵,key的值不會改變
			else {                // 如果用戶輸入反方向,key的值不會改變
				if (tempkey == 'w' || tempkey == 'W')
					if (key == 's' || key == 'S')
						key = tempkey;
				if (tempkey == 's' || tempkey == 'S')
					if (key == 'w' || key == 'W')                                          
						key = tempkey;
				if (tempkey == 'a' || tempkey == 'A')
					if (key == 'D' || key == 'd')
						key = tempkey;
				if (tempkey == 'D' || tempkey == 'd')
					if (key == 'a' || key == 'A')
						key = tempkey;
				tempkey = key;
			}
	}
	if (!changeflag)// 如果蛇沒喫東西,蛇尾就空了 ,擦除尾巴 
	{
		gotoxy(snake.x[snake.len - 1], snake.y[snake.len - 1]);
		printf("  ");
	}
	else {   // 蛇吃了一個食物,變長了 
		snake.x[snake.len] = snake.x[snake.len - 1];
		snake.y[snake.len] = snake.y[snake.len - 1];
		snake.len++;
	}
	for (int i = snake.len-1; i>0; i--) { // 後面一個的蛇身,替換前面的一個 ,蛇頭變成食物的位置 
		snake.x[i] = snake.x[i - 1];
		snake.y[i] = snake.y[i - 1];
	}
	switch (key) {// 判斷按鍵數據 
	case 'w':
	case 'W':
		snake.y[0]--;
		break;
	case 's':
	case 'S':
		snake.y[0]++;
		break;
	case 'a':
	case 'A':
		snake.x[0] -= 2;
		break;
	case 'd':
	case 'D':
		snake.x[0] += 2;
		break;
	}
	gotoxy(snake.x[0], snake.y[0]); // 改變頭的方向 
	printf("♂");
	changeflag =false;   // 清空標誌位 
}

4、食物刷新

//食物的刷新
void createFood(void) 
{
	if (snake.x[0] == food.x&&snake.y[0] == food.y) {// 如果食物被吃了 
		score++;                                     // 分數 +1 
		gotoxy(100, 22);
		printf("當前分數:%d", score);
		srand((unsigned)time(NULL));
		while (1)
		{
			bool flag = true;
			food.x = rand() % (MAP_WIDTH - 4) + 2;   // 隨機產生食物 ,不在牆體上 
			food.y = rand() % (MAP_HIGHTH - 2) + 1;
			for (int k = 0; k < snake.len; k++)      // 判斷食物不在貪喫蛇身上
			{
				if (snake.x[k] == food.x&&snake.y[k] == food.y)
				{
					flag = false;  
					break;
				}
			}
			if (flag&&food.x % 2 == 0)// 食物合不合適標記 --- 蛇喫不到 
				break;
		}
		gotoxy(food.x, food.y);
		printf("♂");
		changeflag = true;  // 吃了食物,表示蛇長度有變化,標誌位 
	}
}

5、蛇的狀態

//蛇的狀態
bool snakeStatus(void) 
{
	if (snake.x[0] == 0 || snake.x[0] == MAP_WIDTH  || snake.y[0] == 0 || snake.y[0] == MAP_HIGHTH)
		return false;  // 如果蛇在牆上,宣佈死亡 
	for (int i = 1; i < snake.len; i++)
		if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
			return false;// 如果蛇自己吃了自己,宣佈死亡
	return true;
}

6、開始遊戲

void playGame(void)
{
	key = 'w';
	score = 0;
	//隱藏光標
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 獲取輸出屏幕緩衝區句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化結構體cci 
	GetConsoleCursorInfo(hOut, &cci); // 獲取有關指定控制檯屏幕緩衝區的光標大小和可見性的信息。
	cci.bVisible = FALSE;  // 光標的不可見
	SetConsoleCursorInfo(hOut, &cci); //設置指定控制檯屏幕緩衝區的光標大小和可見性。
	system("CLS");
	
	drawMap();  //畫牆體、蛇、食物 
	while (1) {
		keyDown();//按鍵操作
		Sleep(snake.speed); // 蛇的速度設定 
		createFood();//食物的刷新
		if (!snakeStatus())//蛇的狀態
			break;
	}
	system("CLS");
	gotoxy(50, 8);
	printf("——遊戲結束——");
	gotoxy(52, 10);
	printf("你的分數爲%d", score);
	gotoxy(53, 13);
	printf("再來一次");
	gotoxy(53, 16);
	printf("退出遊戲");
	gotoxy(53, 19);
	printf("回主菜單");
	while (1) {
		while (1) {
			if (_kbhit()) {// 檢查當前是否有鍵盤輸入 
				key3 = _getch();// 獲取鍵值 
				if (0 == key3 || 0xE0 == key3)
				    key3 = key3 << 8 | _getch();
			}
			else break;
		} 
		switch (flag3) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			key3 = _getch();// 獲取鍵值 
			if (key3 == 13) // 確定 
				playGame(); // 開始遊戲 
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			key3 = _getch();// 獲取鍵值 
			if (key3 == 13) // 確定 
				exit(0);    // 退出 
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			key3 = _getch(); // 獲取鍵值  
			if (key3 == 13)  // 確定 
				main(); // 回到主菜單 
			break;
		}
		switch (key3) { // 獲取鍵值 
		case 's':
		case 'S':
			if (flag3 < 2)
				flag3++;
			break;
		case 'W':
		case 'w':
			if (flag3 > 0)
				flag3--;
			break;
		}
	}
}

7、難度選擇

void settingMeau(void)
{
	system("CLS");
	gotoxy(50, 8);
	printf("——遊戲難度——");
	gotoxy(53, 13);
	printf("地獄");
	gotoxy(53, 15);
	printf("狂暴");
	gotoxy(53, 17);
	printf("正常");
	gotoxy(53, 19);
	printf("平和");
	gotoxy(53, 23);
	printf("返回菜單");
	switch (setflag) {
	case 0:
		gotoxy(58, 13);
		printf("←");
		break;
	case 1:
		gotoxy(58, 15);
		printf("←");
		break;
	case 2:
		gotoxy(58, 17);
		printf("←");
		break;
	case 3:
		gotoxy(58, 19);
		printf("←");
		break;
	}
	while (1) {
		switch (flag2) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 60;
				setflag = 0;
				gotoxy(58, 13);
				printf("→");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("→");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 100;
				setflag = 1;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("→");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 200;
				setflag = 2;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("→");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 3:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 300;
				setflag = 3;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("→");
			}
			break;
		case 4:gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("→");
			key4 = _getch();// 獲取鍵值 
			if (key4 == 13) // 確定 
				main();     // 回到主菜單
		}
		switch (key4) { // 獲取鍵值 
		case 's':
		case 'S':
			if (flag2 < 4)
				flag2++;
			break;
		case 'W':
		case 'w':
			if (flag2 > 0)
				flag2--;
			break;
		}
	}
}

8、主菜單

void meau(void)
{
	//隱藏光標
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 獲取輸出屏幕緩衝區句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化結構體cci 
	GetConsoleCursorInfo(hOut, &cci); // 獲取有關指定控制檯屏幕緩衝區的光標大小和可見性的信息。
	cci.bVisible = FALSE;  // 光標的不可見
	SetConsoleCursorInfo(hOut, &cci); //設置指定控制檯屏幕緩衝區的光標大小和可見性。
	
	system("CLS"); // 調用系統命令cls完成清屏操作
	gotoxy(50, 8);
	printf("——貪喫蛇——");
	gotoxy(53, 13);
	printf("開始遊戲");
	gotoxy(53, 16);
	printf("遊戲難度");
	gotoxy(53, 19);
	printf("退出遊戲");
	
	gotoxy(44, 22);
	printf("※ ※ CSDN:Sumjess ※ ※");
	gotoxy(43, 25);
	printf("※ ※ 公衆號 :Tech雲 ※ ※");	
			
	while (1) {
		switch (flag) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			key2 = _getch();// 獲取鍵值 
			if (key2 == 13) // 如果是確定 
				playGame(); // 進入遊戲 
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			key2 = _getch();// 獲取鍵值 
			if (key2 == 13) // 如果是確定 
			  settingMeau();// 進入難度設定 
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			key2 = _getch();// 獲取鍵值 
			if (key2 == 13) // 如果是確定 
				exit(0);    // 退出 
			break;
		}
		switch (key2) {// 獲取鍵值 
		case 's':
		case 'S':
			if (flag < 2)
				flag++;
			break;
		case 'W':
		case 'w':
			if (flag > 0)
				flag--;
			break;
		}
	}
}

9、主函數

int main(void)
{
	//隱藏光標
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 獲取輸出屏幕緩衝區句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化結構體cci 
	GetConsoleCursorInfo(hOut, &cci); // 獲取有關指定控制檯屏幕緩衝區的光標大小和可見性的信息。
	cci.bVisible = FALSE;  // 光標的不可見
	SetConsoleCursorInfo(hOut, &cci); //設置指定控制檯屏幕緩衝區的光標大小和可見性。
	meau();
}

四、項目代碼

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<windows.h>
#include<time.h>


//輔助宏定義
#define GAME_H
#define MAP_HIGHTH 29            //地圖高度
#define MAP_WIDTH 90              //地圖寬度 
#define SNAKE_MAX_LEN 1000         //蛇的最大長度    
#define SNAKE_BEGIN_LEN 3         //蛇的初始長度
//數據設計 
//蛇結構體
struct
{
	int x[SNAKE_MAX_LEN];
	int y[SNAKE_MAX_LEN];
	int len;                      
	int speed=150;
}snake;
//食物結構體
struct
{
	int x;
	int y;
}food;
//其他全局變量
char key = 'w'; 
char key2;                    //key2代表在初始菜單界面的鍵盤輸入
char key3;                    //key3代表在遊戲結束後菜單的鍵盤輸入
char key4;                    //key4代表在遊戲難度內的菜單的鍵盤輸入
int tempkey = 'w';            //這裏的tempkey是爲了防止貪喫蛇回頭自殺而設定的,詳細見keyDown()函數
bool changeflag = 0; 
int flag = 0;                 //這三個flag是爲了反映按下w與s鍵後的狀態,詳細請看下面代碼
int flag2 = 0;
int flag3 = 0;
int setflag = 2;              //這個setflag是爲了記錄遊戲難度裏選擇難度後再難度後面打印◇     
int score = 0;                //記錄分數
//函數模塊設計
//畫地圖
void drawMap(void);
//按鍵操作
void keyDown(void);
//食物的刷新
void createFood(void);
//蛇的狀態
bool snakeStatus(void);
//光標移動
void gotoxy(int x, int y);

void playGame(void);
void meau(void);
void settingMeau(void);
int main(void)
{
	//隱藏光標
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 獲取輸出屏幕緩衝區句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化結構體cci 
	GetConsoleCursorInfo(hOut, &cci); // 獲取有關指定控制檯屏幕緩衝區的光標大小和可見性的信息。
	cci.bVisible = FALSE;  // 光標的不可見
	SetConsoleCursorInfo(hOut, &cci); //設置指定控制檯屏幕緩衝區的光標大小和可見性。
	meau();    // 主菜單 
}

void meau(void)
{
	//隱藏光標
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 獲取輸出屏幕緩衝區句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化結構體cci 
	GetConsoleCursorInfo(hOut, &cci); // 獲取有關指定控制檯屏幕緩衝區的光標大小和可見性的信息。
	cci.bVisible = FALSE;  // 光標的不可見
	SetConsoleCursorInfo(hOut, &cci); //設置指定控制檯屏幕緩衝區的光標大小和可見性。
	
	system("CLS"); // 調用系統命令cls完成清屏操作
	gotoxy(50, 8);
	printf("——貪喫蛇——");
	gotoxy(53, 13);
	printf("開始遊戲");
	gotoxy(53, 16);
	printf("遊戲難度");
	gotoxy(53, 19);
	printf("退出遊戲");
	
	gotoxy(44, 22);
	printf("※ ※ CSDN:Sumjess ※ ※");
	gotoxy(43, 25);
	printf("※ ※ 公衆號 :Tech雲 ※ ※");	
			
	while (1) {
		switch (flag) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			key2 = _getch();// 獲取鍵值 
			if (key2 == 13) // 如果是確定 
				playGame(); // 進入遊戲 
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			key2 = _getch();// 獲取鍵值 
			if (key2 == 13) // 如果是確定 
			  settingMeau();// 進入難度設定 
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			key2 = _getch();// 獲取鍵值 
			if (key2 == 13) // 如果是確定 
				exit(0);    // 退出 
			break;
		}
		switch (key2) {// 獲取鍵值 
		case 's':
		case 'S':
			if (flag < 2)
				flag++;
			break;
		case 'W':
		case 'w':
			if (flag > 0)
				flag--;
			break;
		}
	}
}
void settingMeau(void)
{
	system("CLS");
	gotoxy(50, 8);
	printf("——遊戲難度——");
	gotoxy(53, 13);
	printf("地獄");
	gotoxy(53, 15);
	printf("狂暴");
	gotoxy(53, 17);
	printf("正常");
	gotoxy(53, 19);
	printf("平和");
	gotoxy(53, 23);
	printf("返回菜單");
	switch (setflag) {
	case 0:
		gotoxy(58, 13);
		printf("←");
		break;
	case 1:
		gotoxy(58, 15);
		printf("←");
		break;
	case 2:
		gotoxy(58, 17);
		printf("←");
		break;
	case 3:
		gotoxy(58, 19);
		printf("←");
		break;
	}
	while (1) {
		switch (flag2) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 60;
				setflag = 0;
				gotoxy(58, 13);
				printf("→");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("→");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 100;
				setflag = 1;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("→");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 200;
				setflag = 2;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("→");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 3:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 300;
				setflag = 3;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("→");
			}
			break;
		case 4:gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("→");
			key4 = _getch();// 獲取鍵值 
			if (key4 == 13) // 確定 
				main();     // 回到主菜單
		}
		switch (key4) { // 獲取鍵值 
		case 's':
		case 'S':
			if (flag2 < 4)
				flag2++;
			break;
		case 'W':
		case 'w':
			if (flag2 > 0)
				flag2--;
			break;
		}
	}
}
void playGame(void)
{
	key = 'w';
	score = 0;
	//隱藏光標
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 獲取輸出屏幕緩衝區句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化結構體cci 
	GetConsoleCursorInfo(hOut, &cci); // 獲取有關指定控制檯屏幕緩衝區的光標大小和可見性的信息。
	cci.bVisible = FALSE;  // 光標的不可見
	SetConsoleCursorInfo(hOut, &cci); //設置指定控制檯屏幕緩衝區的光標大小和可見性。
	system("CLS");
	
	drawMap();  //畫牆體、蛇、食物 
	while (1) {
		keyDown();//按鍵操作
		Sleep(snake.speed); // 蛇的速度設定 
		createFood();//食物的刷新
		if (!snakeStatus())//蛇的狀態
			break;
	}
	system("CLS");
	gotoxy(50, 8);
	printf("——遊戲結束——");
	gotoxy(52, 10);
	printf("你的分數爲%d", score);
	gotoxy(53, 13);
	printf("再來一次");
	gotoxy(53, 16);
	printf("退出遊戲");
	gotoxy(53, 19);
	printf("回主菜單");
	while (1) {
		while (1) {
			if (_kbhit()) {// 檢查當前是否有鍵盤輸入 
				key3 = _getch();// 獲取鍵值 
				if (0 == key3 || 0xE0 == key3)
				    key3 = key3 << 8 | _getch();
			}
			else break;
		} 
		switch (flag3) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			key3 = _getch();// 獲取鍵值 
			if (key3 == 13) // 確定 
				playGame(); // 開始遊戲 
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			key3 = _getch();// 獲取鍵值 
			if (key3 == 13) // 確定 
				exit(0);    // 退出 
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			key3 = _getch(); // 獲取鍵值  
			if (key3 == 13)  // 確定 
				main(); // 回到主菜單 
			break;
		}
		switch (key3) { // 獲取鍵值 
		case 's':
		case 'S':
			if (flag3 < 2)
				flag3++;
			break;
		case 'W':
		case 'w':
			if (flag3 > 0)
				flag3--;
			break;
		}
	}
}
//畫牆體、蛇、食物 
//卍  ※ 代表牆體
//♂     代表蛇體
//♀     代表食物
void drawMap(void)
{
	//畫 --- 遊戲說明	
	gotoxy(100, 7);
	printf("按鍵說明:");
	gotoxy(100, 10);
	printf("W:向上");
	gotoxy(100, 13);
	printf("A:向左");
	gotoxy(100, 16);
	printf("S:向下");
	gotoxy(100, 19);
	printf("D:向右");
	gotoxy(100,22);
	printf("當前分數:%d",score);
	
	gotoxy(98, 25);
	printf("※CSDN:Sumjess ※");
	gotoxy(97, 28);
	printf("※公衆號 :Tech雲 ※");	
	
	//畫 --- 貪喫蛇牆體 
	  // 畫左右兩面牆  
	for (int i = 0; i <= MAP_HIGHTH; i++) {  
		gotoxy(0,i);
		printf("※");
		gotoxy(MAP_WIDTH,i);
		printf("※");
		Sleep(20);
	}
	  // 畫上下兩面牆
	for (int i = 0; i <= MAP_WIDTH; i += 2) {
		gotoxy(i, 0);
		printf("卍");
		gotoxy(MAP_WIDTH-i, MAP_HIGHTH);
		printf("卍");
		Sleep(20);
	}
	
	//畫 --- 蛇身 
	snake.len = SNAKE_BEGIN_LEN;  // 賦值剛開始的蛇身長度 
	srand((unsigned)time(NULL));  // 防止僞隨機數 
	do {
		snake.x[0] = rand() % (MAP_WIDTH/2 ) + MAP_WIDTH/4;
		snake.y[0] = rand() % (MAP_HIGHTH/2 ) + MAP_HIGHTH/4;
	} while (snake.x[0] % 2 != 0); // 隨機出現蛇頭,保證蛇頭在地圖中間範圍內,而不在牆體上 ,並且符合蛇是偶數的要求 
	
	gotoxy(snake.x[0], snake.y[0]);// 光標移動到蛇頭的位置 
	printf("♂");                  // 打印蛇頭 
	for (int i = 1; i < snake.len; i++) {// 打印蛇身 
		snake.x[i] = snake.x[0];
		snake.y[i] = snake.y[0] + 1;
		gotoxy(snake.x[i], snake.y[i]);
		printf("♂");
	}
	do {
		food.x = rand() % (MAP_WIDTH - 4) + 2;
		food.y = rand() % (MAP_HIGHTH - 2) + 1;
	} while (food.x % 2 != 0); // 隨機出現食物,保證食物不在牆體上,並且符合食物是偶數的要求 
	gotoxy(food.x, food.y);    // 光標移動到食物的位置 
	printf("♀");              // 打印食物 
}
//按鍵操作
void keyDown(void) 
{
	//按鍵的處理
	if (_kbhit()) // 檢查當前是否有鍵盤輸入 
	{
			fflush(stdin);  // 清空輸入緩衝區,讀完一個字符串後緊接着又要讀取一個字符,此時應該先執行該函數 
			key = _getch(); // 一個不回顯函數,當用戶按下某個字符時,函數自動讀取,無需按回車
			
			//這一大段代碼是爲了防止回頭自殺的情況發生,如果用戶輸入反方向或者非wasd鍵,key的值不會改變
			if (key != 'w' && key != 'W' && key != 's' && key != 'S' && key != 'A' && key != 'a' && key != 'd' && key != 'D')
				key = tempkey;    // 如果用戶輸入非wasd鍵,key的值不會改變
			else {                // 如果用戶輸入反方向,key的值不會改變
				if (tempkey == 'w' || tempkey == 'W')
					if (key == 's' || key == 'S')
						key = tempkey;
				if (tempkey == 's' || tempkey == 'S')
					if (key == 'w' || key == 'W')                                          
						key = tempkey;
				if (tempkey == 'a' || tempkey == 'A')
					if (key == 'D' || key == 'd')
						key = tempkey;
				if (tempkey == 'D' || tempkey == 'd')
					if (key == 'a' || key == 'A')
						key = tempkey;
				tempkey = key;
			}
	}
	if (!changeflag)// 如果蛇沒喫東西,蛇尾就空了 ,擦除尾巴 
	{
		gotoxy(snake.x[snake.len - 1], snake.y[snake.len - 1]);
		printf("  ");
	}
	else {   // 蛇吃了一個食物,變長了 
		snake.x[snake.len] = snake.x[snake.len - 1];
		snake.y[snake.len] = snake.y[snake.len - 1];
		snake.len++;
	}
	for (int i = snake.len-1; i>0; i--) { // 後面一個的蛇身,替換前面的一個 ,蛇頭變成食物的位置 
		snake.x[i] = snake.x[i - 1];
		snake.y[i] = snake.y[i - 1];
	}
	switch (key) {// 判斷按鍵數據 
	case 'w':
	case 'W':
		snake.y[0]--;
		break;
	case 's':
	case 'S':
		snake.y[0]++;
		break;
	case 'a':
	case 'A':
		snake.x[0] -= 2;
		break;
	case 'd':
	case 'D':
		snake.x[0] += 2;
		break;
	}
	gotoxy(snake.x[0], snake.y[0]); // 改變頭的方向 
	printf("♂");
	changeflag =false;   // 清空標誌位 
}
//食物的刷新
void createFood(void) 
{
	if (snake.x[0] == food.x&&snake.y[0] == food.y) {// 如果食物被吃了 
		score++;                                     // 分數 +1 
		gotoxy(100, 22);
		printf("當前分數:%d", score);
		srand((unsigned)time(NULL));
		while (1)
		{
			bool flag = true;
			food.x = rand() % (MAP_WIDTH - 4) + 2;   // 隨機產生食物 ,不在牆體上 
			food.y = rand() % (MAP_HIGHTH - 2) + 1;
			for (int k = 0; k < snake.len; k++)      // 判斷食物不在貪喫蛇身上
			{
				if (snake.x[k] == food.x&&snake.y[k] == food.y)
				{
					flag = false;  
					break;
				}
			}
			if (flag&&food.x % 2 == 0)// 食物合不合適標記 --- 蛇喫不到 
				break;
		}
		gotoxy(food.x, food.y);
		printf("♂");
		changeflag = true;  // 吃了食物,表示蛇長度有變化,標誌位 
	}
}
//蛇的狀態
bool snakeStatus(void) 
{
	if (snake.x[0] == 0 || snake.x[0] == MAP_WIDTH  || snake.y[0] == 0 || snake.y[0] == MAP_HIGHTH)
		return false;  // 如果蛇在牆上,宣佈死亡 
	for (int i = 1; i < snake.len; i++)
		if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
			return false;// 如果蛇自己吃了自己,宣佈死亡
	return true;
}
//光標移動 到 (X,Y)
void gotoxy(int x, int y) 
{	
    COORD coord={x,y};                                  // 初始化結構體coord (座標系X,Y)
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);    // 獲取輸出屏幕緩衝區句柄
    SetConsoleCursorPosition(handle, coord);            // 移動光標
}

 

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