中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C語言如何實現貪吃蛇

發布時間:2021-12-07 14:02:55 來源:億速云 閱讀:114 作者:iii 欄目:開發技術

本篇內容主要講解“C語言如何實現貪吃蛇”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“C語言如何實現貪吃蛇”吧!

    一、游戲說明

    1.1游戲按鍵說明

    按方向鍵上下左右,可以實現蛇移動方向的改變。

    短時間長按方向鍵上下左右其中之一,可實現蛇向該方向的短時間加速移動。

    按空格鍵可實現暫停,暫停后按任意鍵繼續游戲。

    按Esc鍵可直接退出游戲。按R鍵可重新開始游戲。

    1.2計分系統

    保存玩家的歷史最高記錄

    二、游戲運行

    2.1游戲效果展示

    C語言如何實現貪吃蛇

    2.2一個報錯的糾正

    如果出現這種情況請要相信這是編譯器的問題

    C語言如何實現貪吃蛇

    (為了防止本文篇幅過長因此一些不影響游戲邏輯的知識點以鏈接形式展現) 

     解決辦法

    2.3 游戲代碼

    說明:該代碼測試環境是visual studio 2017 

    音樂文件(提取碼6666)

    #include <stdio.h>
    #include <Windows.h>
    #include <stdlib.h>
    #include <time.h>
    #include <conio.h>
    #include<mmsystem.h>
    #pragma comment(lib,"Winmm.lib") 
     
    //首先定義游戲界面的大小,定義游戲區行數和列數
    #define ROW 22 //游戲區行數
    #define COL 42 //游戲區列數
     
    #define KONG 0 //標記空(什么也沒有)
    #define WALL 1 //標記墻
    #define FOOD 2 //標記食物
    #define HEAD 3 //標記蛇頭
    #define BODY 4 //標記蛇身
     
    #define UP 72 //方向鍵:上
    #define DOWN 80 //方向鍵:下
    #define LEFT 75 //方向鍵:左
    #define RIGHT 77 //方向鍵:右
     
    #define SPACE 32 //暫停
    #define ESC 27 //退出
     
    //蛇頭
    struct Snake
    {
    	int len; //記錄蛇身長度
    	int x; //蛇頭橫坐標
    	int y; //蛇頭縱坐標
    }snake;
     
    //蛇身
    struct Body
    {
    	int x; //蛇身橫坐標
    	int y; //蛇身縱坐標
    }body[ROW*COL]; //開辟足以存儲蛇身的結構體數組
     
    int face[ROW][COL]; 存儲游戲區各個位置是什么,比如是墻還是空還是蛇身、蛇頭,通過存儲不同的數字便可達到目的
     
    //菜單欄
    void menu();
    //隱藏光標
    void HideCursor();
    //光標跳轉
    void CursorJump(int x, int y);
    //初始化界面
    void InitInterface();
    //顏色設置
    void color(int c);
    //從文件讀取最高分
    void ReadGrade();
    //更新最高分到文件
    void WriteGrade();
    //初始化蛇
    void InitSnake();
    //隨機生成食物
    void RandFood();
    //判斷得分與結束
    void JudgeFunc(int x, int y);
    //打印蛇與覆蓋蛇
    void DrawSnake(int flag);
    //移動蛇
    void MoveSnake(int x, int y);
    //執行按鍵
    void run(int x, int y);
    //游戲主體邏輯函數
    void Game();
     
    int max, grade; //全局變量
    int main()
    {
    	//#pragma warning(disable: n)將某個警報置為失效
    #pragma warning (disable:4996) //可以使用標準C語言提供的庫函數
    	menu();
    	max = 0, grade = 0; //初始化變量
    	srand((size_t)time(NULL));//根據當前時間生成隨機種子
    	system("title 貪吃蛇"); //設置cmd窗口的名字
    	system("mode con cols=84 lines=23"); //設置cmd窗口的大小
    	HideCursor(); //隱藏光標
    	ReadGrade(); //從文件讀取最高分到max變量
    	InitInterface(); //初始化界面
    	InitSnake(); //初始化蛇
    	RandFood(); //隨機生成食物
    	DrawSnake(1); //打印蛇
    	PlaySound(TEXT("bgmusic.wav"), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
    	Game(); //開始游戲
    	return 0;
    }
     
    void menu() {
    	system("title 貪吃蛇");
    	system("mode con cols=84 lines=23"); //設置cmd窗口的大小
    	color(14);//設置文字為淡黃色
    	printf("*****************************************************************\n");
    	printf("******************歡迎來到貪吃蛇的游戲里!!!*******************\n");
    	printf("*****************************************************************\n");
    	printf("*********按方向鍵上下左右,可以實現蛇移動方向的改變**************\n");
    	printf("*****************************************************************\n");
    	printf("**********按空格鍵可實現暫停,暫停后按任意鍵繼續游戲*************\n");
    	printf("*****************************************************************\n");
    	printf("*********************按Esc鍵可直接退出游戲***********************\n");
    	printf("*********************按R鍵可重新開始游戲*************************\n");
    	printf("*****************************************************************\n");
    	system("pause");
    }
     
    //用C語言開發游戲程序時,對于光標閃爍問題,可以通過隱藏光標函數解決
    void HideCursor()
    {
    	CONSOLE_CURSOR_INFO curInfo; //定義光標信息的結構體變量,頭文件<windows.h>
    	curInfo.dwSize = 1; //如果沒賦值的話,光標隱藏無效
    	//curInfo.bVisible = TRUE; //將光標設置為可見
    	curInfo.bVisible = FALSE; //將光標設置為不可見
    	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //獲取控制臺句柄
    	SetConsoleCursorInfo(handle, &curInfo); //設置光標信息
    }
    //光標跳轉
    void CursorJump(int x, int y)
    {
    	COORD pos; //定義光標位置的結構體變量
    	pos.X = x; //橫坐標
    	pos.Y = y; //縱坐標
    	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //獲取控制臺句柄
    	SetConsoleCursorPosition(handle, pos); //設置光標位置
    }
    //初始化界面
    void InitInterface()
    {
    	color(3); //顏色設置為湖藍色
    	for (int i = 0; i < ROW; i++)
    	{
    		for (int j = 0; j < COL; j++)
    		{
    			if (j == 0 || j == COL - 1)
    			{
    				face[i][j] = WALL; //標記該位置為墻
    				CursorJump(2 * j, i);
    				printf("■");
    			}
    			else if (i == 0 || i == ROW - 1)
    			{
    				face[i][j] = WALL; //標記該位置為墻
    				printf("■");
    			}
    			else
    			{
    				face[i][j] = KONG; //標記該位置為空
    			}
    		}
    	}
    	color(4); //顏色設置為紅色
    	CursorJump(0, ROW);
    	printf("當前得分:%d", grade);
    	CursorJump(COL, ROW);
    	printf("歷史最高得分:%d", max);
    }
    //顏色設置
    void color(int c)
    {
    	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //顏色設置
    	//注:SetConsoleTextAttribute是一個API(應用程序編程接口)
    }
    //從文件讀取最高分
    void ReadGrade()
    {
    	FILE* pf = fopen("貪吃蛇最高得分記錄.txt", "r"); //以只讀的方式打開文件
    	if (pf == NULL) //打開文件失敗
    	{
    		pf = fopen("貪吃蛇最高得分記錄.txt", "w"); //以只寫的方式打開文件
    		fwrite(&max, sizeof(int), 1, pf); //將max寫入文件(此時max為0),即將最高得分初始化為0
    	}
    	fseek(pf, 0, SEEK_SET); //使文件指針pf指向文件開頭
    	fread(&max, sizeof(int), 1, pf); //讀取文件當中的最高得分到max當中
    	fclose(pf); //關閉文件
    	pf = NULL; //文件指針及時置空
    }
    //更新最高分到文件
    void WriteGrade()
    {
    	FILE* pf = fopen("貪吃蛇最高得分記錄.txt", "w"); //以只寫的方式打開文件
    	if (pf == NULL) //打開文件失敗
    	{
    		printf("保存最高得分記錄失敗\n");
    		exit(0);
    	}
    	fwrite(&grade, sizeof(int), 1, pf); //將本局游戲得分寫入文件當中
    	fclose(pf); //關閉文件
    	pf = NULL; //文件指針及時置空
    }
    //初始化蛇
    void InitSnake()
    {
    	snake.len = 2; //蛇的身體長度初始化為2
    	snake.x = COL / 2; //蛇頭位置的橫坐標
    	snake.y = ROW / 2; //蛇頭位置的縱坐標
    	//蛇身坐標的初始化
    	body[0].x = COL / 2 - 1;
    	body[0].y = ROW / 2;
    	body[1].x = COL / 2 - 2;
    	body[1].y = ROW / 2;
    	//將蛇頭和蛇身位置進行標記
    	face[snake.y][snake.x] = HEAD;
    	face[body[0].y][body[0].x] = BODY;
    	face[body[1].y][body[1].x] = BODY;
    }
    //隨機生成食物
    int my_time = 1;
    void RandFood()
    {
    	int i, j;
    	do
    	{
    		//隨機生成食物的橫縱坐標
    		i = rand() % ROW;
    		j = rand() % COL;
     
    	} while (face[i][j] != KONG); //確保生成食物的位置為空,若不為空則重新生成
    	face[i][j] = FOOD; //將食物位置進行標記
    	color(12); //顏色設置為紅色
    	CursorJump(2 * j, i); //光標跳轉到生成的隨機位置處
    	printf("●"); //打印食物
    }
    //判斷得分與結束
    void JudgeFunc(int x, int y)
    {
    	//若蛇頭即將到達的位置是食物,則得分
    	if (face[snake.y + y][snake.x + x] == FOOD)
    	{
    		snake.len++; //蛇身加長
    		grade += 10; //更新當前得分
    		color(7); //顏色設置為白色
    		CursorJump(0, ROW);
    		printf("當前得分:%d", grade); //重新打印當前得分
    		RandFood(); //重新隨機生成食物
    	}
    	//若蛇頭即將到達的位置是墻或者蛇身,則游戲結束
    	else if (face[snake.y + y][snake.x + x] == WALL || face[snake.y + y][snake.x + x] == BODY)
    	{
    		Sleep(1000); //留給玩家反應時間
    		system("cls"); //清空屏幕
    		color(7); //顏色設置為白色
    		CursorJump(2 * (COL / 3), ROW / 2 - 3);
    		if (grade > max)
    		{
    			printf("恭喜你打破最高記錄,最高記錄更新為%d", grade);
    			WriteGrade();
    		}
    		else if (grade == max)
    		{
    			printf("與最高記錄:%d持平,加油再創佳績", grade);
    		}
    		else
    		{
    			printf("請繼續加油,當前與最高記錄相差%d", max - grade);
    		}
    		CursorJump(2 * (COL / 3), ROW / 2);
    		printf("GAME OVER");
    		while (1) //詢問玩家是否再來一局
    		{
    			char ch;
    			CursorJump(2 * (COL / 3), ROW / 2 + 3);
    			printf("再來一局?(y/n):");
    			scanf("%c", &ch);
    			if (ch == 'y' || ch == 'Y')
    			{
    				system("cls");
    				main();
    			}
    			else if (ch == 'n' || ch == 'N')
    			{
    				CursorJump(2 * (COL / 3), ROW / 2 + 5);
    				exit(0);
    			}
    			else
    			{
    				CursorJump(2 * (COL / 3), ROW / 2 + 5);
    				printf("選擇錯誤,請再次選擇");
    			}
    		}
    	}
    }
    //打印蛇與覆蓋蛇
    void DrawSnake(int flag)
    {
    	if (flag == 1) //打印蛇
    	{
    		color(10); //顏色設置為綠色
    		CursorJump(2 * snake.x, snake.y);
    		printf("■"); //打印蛇頭
    		for (int i = 0; i < snake.len; i++)
    		{
    			CursorJump(2 * body[i].x, body[i].y);
    			printf("□"); //打印蛇身
    		}
    	}
    	else //覆蓋蛇
    	{
    		if (body[snake.len - 1].x != 0) //防止len++(即蛇變長)后將(0, 0)位置的墻覆蓋
    		{
    			//將蛇尾覆蓋為空格即可
    			CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
    			printf("  ");
    		}
    	}
    }
    //移動蛇
    void MoveSnake(int x, int y)
    {
    	DrawSnake(0); //先覆蓋當前所顯示的蛇
    	face[body[snake.len - 1].y][body[snake.len - 1].x] = KONG; //蛇移動后蛇尾重新標記為空
    	face[snake.y][snake.x] = BODY; //蛇移動后蛇頭的位置變為蛇身
    	//蛇移動后各個蛇身位置坐標需要更新
    	for (int i = snake.len - 1; i > 0; i--)
    	{
    		body[i].x = body[i - 1].x;
    		body[i].y = body[i - 1].y;
    	}
    	//蛇移動后蛇頭位置信息變為第1個蛇身的位置信息
    	body[0].x = snake.x;
    	body[0].y = snake.y;
    	//蛇頭的位置更改
    	snake.x = snake.x + x;
    	snake.y = snake.y + y;
    	DrawSnake(1); //打印移動后的蛇
    }
    //執行按鍵
    void run(int x, int y)
    {
    	int t = 0;
    	while (1)
    	{
    		if (t == 0)
    			t = 3000; //這里t越小,蛇移動速度越快(可以根據次設置游戲難度)
    		while (--t)//控制移動速度的,循環3000次會占用一點時間
    		{
    			if (kbhit() != 0) //若鍵盤被敲擊,則退出循環
    				break;
    		}
    		
    		if (t == 0) //鍵盤未被敲擊
    		{
    			JudgeFunc(x, y); //判斷到達該位置后,是否得分與游戲結束
    			MoveSnake(x, y); //移動蛇
    		}
    		else //鍵盤被敲擊
    		{
    			break; //返回Game函數讀取鍵值
    		}
    	}
     
    }
    //游戲主體邏輯函數
    void Game()
    {
    	int n = RIGHT; //開始游戲時,默認向后移動
    	int tmp = 0; //記錄蛇的移動方向
    	goto first; //第一次進入循環先向默認方向前進
    	while (1)
    	{
    		
    		n = getch(); //讀取鍵值
    		//在執行前,需要對所讀取的按鍵進行調整
    		switch (n)
    		{
    		case UP:
    		case DOWN: //如果敲擊的是“上”或“下”
    			if (tmp != LEFT && tmp != RIGHT) //并且上一次蛇的移動方向不是“左”或“右”
    			{
    				n = tmp; //那么下一次蛇的移動方向設置為上一次蛇的移動方向
    			}
    			break;
    		case LEFT:
    		case RIGHT: //如果敲擊的是“左”或“右”
    			if (tmp != UP && tmp != DOWN) //并且上一次蛇的移動方向不是“上”或“下”
    			{
    				n = tmp; //那么下一次蛇的移動方向設置為上一次蛇的移動方向
    			}
    		case SPACE:
    		case ESC:
    		case 'r':
    		case 'R':
    			break; //這四個無需調整
    		default:
    			n = tmp; //其他鍵無效,默認為上一次蛇移動的方向
    			break;
    		}
    	first: //第一次進入循環先向默認方向前進
    		switch (n)
    		{
    		case UP: //方向鍵:上
    			run(0, -1); //向上移動(橫坐標偏移為0,縱坐標偏移為-1)
    			tmp = UP; //記錄當前蛇的移動方向
    			break;
    		case DOWN: //方向鍵:下
    			run(0, 1); //向下移動(橫坐標偏移為0,縱坐標偏移為1)
    			tmp = DOWN; //記錄當前蛇的移動方向
    			break;
    		case LEFT: //方向鍵:左
    			run(-1, 0); //向左移動(橫坐標偏移為-1,縱坐標偏移為0)
    			tmp = LEFT; //記錄當前蛇的移動方向
    			break;
    		case RIGHT: //方向鍵:右
    			run(1, 0); //向右移動(橫坐標偏移為1,縱坐標偏移為0)
    			tmp = RIGHT; //記錄當前蛇的移動方向
    			break;
    		case SPACE: //暫停
    			system("pause>nul"); //暫停后按任意鍵繼續
    			break;
    		case ESC: //退出
    			system("cls"); //清空屏幕
    			color(7); //顏色設置為白色
    			CursorJump(COL - 8, ROW / 2);
    			printf("  游戲結束  ");
    			CursorJump(COL - 8, ROW / 2 + 2);
    			exit(0);
    		case 'r':
    		case 'R': //重新開始
    			system("cls"); //清空屏幕
    			main(); //重新執行主函數
    		}
    	}
    }

    三、游戲框架構建

    3.1游戲界面的大小

    首先定義游戲界面的大小,定義游戲區行數和列數。

    #define ROW 22 //游戲區行數
    #define COL 42 //游戲區列數

    這里將蛇活動的區域稱為游戲區,將分數提示的區域稱為提示區(提示區占一行)。

    C語言如何實現貪吃蛇

    3.2蛇頭和蛇身

    此外,我們還需要兩個結構體用于表示蛇頭和蛇身。蛇頭結構體當中存儲著當前蛇身的長度以及蛇頭的位置坐標。

    3.2.1蛇頭
    struct Snake
    {
           int len; //記錄蛇身長度
           int x; //蛇頭橫坐標
           int y; //蛇頭縱坐標
    }snake;
    3.2.2蛇身

    蛇身結構體當中存儲著該段蛇身的位置坐標

    struct Body
    {
           int x; //蛇身橫坐標
           int y; //蛇身縱坐標
    }body[ROW*COL]; //開辟足以存儲蛇身的結構體數組

    3.3標記游戲區

    3.3.1存儲游戲區的各個位置是什么

    同時我們需要一個二維數組來存儲游戲區各個位置是什么(該位置為空、墻、食物、蛇頭以及蛇身)。

    int face[ROW][COL]; //存儲游戲區各個位置是什么,通過存儲不同的數字便可達到目的

    3.3.2 用宏來使某些數字具有特殊意義

    為了增加代碼的可讀性,最好運用宏來定義空、墻、食物、蛇頭以及蛇身,

    #define KONG 0 //標記空(什么也沒有)
    #define WALL 1 //標記墻
    #define FOOD 2 //標記食物
    #define HEAD 3 //標記蛇頭
    #define BODY 4 //標記蛇身

    當然,為了代碼的可讀性,我們最好也將需要用到的按鍵的鍵值用宏進行定義

    #define UP 72 //方向鍵:上
    #define DOWN 80 //方向鍵:下
    #define LEFT 75 //方向鍵:左
    #define RIGHT 77 //方向鍵:右
    #define SPACE 32 //暫停
    #define ESC 27 //退出

    3.4菜單欄的設置 

    void menu() {
    	system("title 貪吃蛇");//設置窗口標題
    	system("mode con cols=84 lines=23"); //設置cmd窗口的大小
    	color(14);//設置文字為淡黃色
    	printf("*****************************************************************\n");
    	printf("******************歡迎來到貪吃蛇的游戲里!!!*******************\n");
    	printf("*****************************************************************\n");
    	printf("*********按方向鍵上下左右,可以實現蛇移動方向的改變**************\n");
    	printf("*****************************************************************\n");
    	printf("**********按空格鍵可實現暫停,暫停后按任意鍵繼續游戲*************\n");
    	printf("*****************************************************************\n");
    	printf("*********************按Esc鍵可直接退出游戲***********************\n");
    	printf("*********************按R鍵可重新開始游戲*************************\n");
    	printf("*****************************************************************\n");
    	system("pause");//暫停程序,按任意鍵繼續
    }

    這里面出現的函數都會在下面進行說明,保證不讓大家疑惑! 

    四.隱藏光標的設置

    隱藏光標比較簡單,定義一個光標信息的結構體變量(該結構體類型系統已經定義好了),然后對光標信息進行賦值,最后用這個光標信息的結構體變量進行光標信息設置即可。

    4.1 光標信息的結構體成員

    成員詳解

    4.2隱藏光標的實現

    void HideCursor(){
             CONSOLE_CURSOR_INFO curInfo; //定義光標信息的結構體變量,頭文件<windows.h>
             curInfo.dwSize = 1; //如果沒賦值的話則為隨機值,光標無效
             curInfo.bVisible = FALSE; //將光標設置為不可見
             HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //獲取控制臺句柄
             SetConsoleCursorInfo(handle, &curInfo); //設置光標信息
    }

    4.3GetStdHandle函數

    使用介紹

    4.4 SetConsoleCursorInfo函數

    使用介紹

    五.光標跳轉的設置

    光標跳轉,也就是讓光標跳轉到指定位置進行輸出。與隱藏光標的操作步驟類似,先定義一個光標位置的結構體變量,然后設置光標的橫縱坐標,最后用這個光標位置的結構體變量進行光標位置設置即可。

    5.1 光標位置的結構體類型

    typedef struct _COORD {
        SHORT X;
        SHORT Y;
    } COORD, *PCOORD;

    其中typedef short SHORT;

    5.2 SetConsoleCursorPosition函數

    使用介紹

    5.3 光標跳轉的實現

    void CursorJump(int x, int y){
        COORD pos; //定義光標位置的結構體變量
        pos.X = x; //橫坐標
        pos.Y = y; //縱坐標
        HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //獲取控制臺句柄
        SetConsoleCursorPosition(handle, pos); //設置光標位置
    }

    六.初始化界面

    初始化界面完成游戲區“墻”的打印,和提示區的打印即可。

    6.1代碼

    int main()
    {
    //#pragma warning(disable: n)將某個警報置為失效
    #pragma warning (disable:4996) //可以使用標準C語言提供的庫函數
        system("title 貪吃蛇"); //設置cmd窗口的名字
        system("mode con cols=84 lines=23"); //設置cmd窗口的大小
        HideCursor(); //隱藏光標 
        InitInterface(); //初始化界面
        Sleep(10000);//暫停10000ms,頭文件為<Windows.h>
        return 0;
    }
    //初始化界面
    void InitInterface()
    {
        color(6); //顏色設置為土黃色
        for (int i = 0; i < ROW; i++)
        {
           for (int j = 0; j < COL; j++)
           {
               if (j == 0 || j == COL - 1)
               {
                  face[i][j] = WALL; //標記該位置為墻
                  CursorJump(2 * j, i);
                  printf("■");
               }
               else if (i == 0 || i == ROW - 1)
               {
                  face[i][j] = WALL; //標記該位置為墻
                  printf("■");
               }
               else
               {
                  face[i][j] = KONG; //標記該位置為空
               }
           }
        }
        color(4); //顏色設置為紅色
        CursorJump(0, ROW);
        printf("當前得分:%d", grade);
        CursorJump(COL, ROW);
        printf("歷史最高得分:%d", max);
    }
    
    //顏色設置
    void color(int c)
    {
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //顏色設置
        //注:SetConsoleTextAttribute是一個API(應用程序編程接口)
    }

    C語言如何實現貪吃蛇

    6.2 system函數

    使用介紹

    注意:一個程序中永遠是最后一個system(“color xx”)起作用

    因此本游戲中不使用system(“color xx”)來控制游戲界面顏色,而是使用SetConsoleTextAttribute函數

    6.3 SetConsoleTextAttribute函數

    使用介紹

    七.初始化蛇

    初始化蛇時將蛇身的長度初始化為2,蛇頭的起始位置在游戲區的中央,蛇頭向左依次是第0個蛇身、第1個蛇身。

    //初始化蛇
    void InitSnake(){
        snake.len = 2; //蛇的身體長度初始化為2
        snake.x = COL / 2; //蛇頭位置的橫坐標
        snake.y = ROW / 2; //蛇頭位置的縱坐標
    
        //蛇身坐標的初始化
        body[0].x = COL / 2 - 1;
        body[0].y = ROW / 2;
        body[1].x = COL / 2 - 2;
        body[1].y = ROW / 2;
    
        //將蛇頭和蛇身位置進行標記
        face[snake.y][snake.x] = HEAD;
        face[body[0].y][body[0].x] = BODY;
        face[body[1].y][body[1].x] = BODY;
    }

    八.打印蛇與覆蓋蛇

    打印蛇和覆蓋蛇這里直接使用一個函數來實現,若傳入參數flag為1,則打印蛇;若傳入參數為0,則用空格覆蓋蛇。

    打印蛇:

    先根據結構體變量snake獲取蛇頭的坐標,到相應位置打印蛇頭。然后根據結構體數組body依次獲取蛇身的坐標,到相應位置進行打印即可。

    覆蓋蛇(請看完移動蛇之后再回來看這部分):

    用空格覆蓋最后一段蛇身即可。

    但需要注意在覆蓋前判斷覆蓋的位置是否為(0,0)位置,因為當得分后蛇身長度增加,而此時新的蛇尾還未進行賦值(編譯器一般默認初始化為0),不需要覆蓋當前新的蛇尾(只需要將新的蛇尾賦值然后打印蛇就可以了),我們根據最后一段蛇身獲取到的坐標便是(0,0),如果進行覆蓋,則會用空格對(0,0)位置進行覆蓋,而(0,0)位置是墻,那么就會導致(0,0)位置墻消失了。

    將該判斷去掉,觀察蛇吃到食物后(0,0)位置墻的變化再進行分析

    C語言如何實現貪吃蛇

    //打印蛇與覆蓋蛇
    void DrawSnake(int flag)
    {
        if (flag == 1) //打印蛇
        {
           color(10); //顏色設置為綠色
           CursorJump(2 * snake.x, snake.y);
           printf("■"); //打印蛇頭
           for (int i = 0; i < snake.len; i++)
           {
               CursorJump(2 * body[i].x, body[i].y);
               printf("□"); //打印蛇身
           }
        }
        else //覆蓋蛇
        {
           if (body[snake.len - 1].x != 0) //防止len++(即蛇變長)后將(0, 0)位置的墻覆蓋
           {
               //將蛇尾覆蓋為空格即可
               CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
               printf("  ");
           }
        }
    }

    九、隨機生成食物

    //隨機生成食物
    void RandFood()
    {
        int i, j;
        do
        {
           //隨機生成食物的橫縱坐標
           i = rand() % ROW;
           j = rand() % COL;
        } while (face[i][j] != KONG); //確保生成食物的位置為空,若不為空則重新生成
        face[i][j] = FOOD; //將食物位置進行標記
        color(12); //顏色設置為紅色
        CursorJump(2 * j, i); //光標跳轉到生成的隨機位置處
        printf("●"); //打印食物
    }

    9.1效果展示

    int main(){
    //#pragma warning(disable: n)將某個警報置為失效
    #pragma warning (disable:4996) //可以使用標準C語言提供的庫函數
        srand((size_t)time(NULL));//根據當前時間生成隨機種子
        system("title 貪吃蛇"); //設置cmd窗口的名字
        system("mode con cols=84 lines=23"); //設置cmd窗口的大小
        HideCursor(); //隱藏光標 
        InitInterface(); //初始化界面
        InitSnake(); //初始化蛇
        DrawSnake(1); //打印蛇
        RandFood(); //隨機生成食物
        Sleep(10000);
        return 0;
    }

    C語言如何實現貪吃蛇

    9.2 srand與rand函數

    使用說明

    十、移動蛇

    移動蛇函數的作用就是先覆蓋當前所顯示的蛇,然后再打印移動后的蛇。

    參數說明:

    x:蛇移動后的橫坐標相對于當前蛇的橫坐標的變化。

    y:蛇移動后的縱坐標相對于當前蛇的縱坐標的變化。

    蛇移動后,各種信息需要變化:

    最后一段蛇身在游戲區當中需要被重新標記為空。蛇頭位置在游戲區當中需要被重新標記為蛇身。存儲蛇身坐標信息的結構體數組body當中,需要將第i段蛇身的坐標信息更新為第i-1段蛇身的坐標信息,而第0段,即第一段蛇身的坐標信息需要更新為當前蛇頭的坐標信息。蛇頭的坐標信息需要根據傳入的參數x和y,進行重新計算。(以上過程請想象蛇移動的情景)

     C語言如何實現貪吃蛇

    //移動蛇
    void MoveSnake(int x, int y){
        DrawSnake(0); //先覆蓋當前所顯示的蛇
        face[body[snake.len - 1].y][body[snake.len - 1].x] = KONG; //蛇移動后蛇尾重新標記為空
        face[snake.y][snake.x] = BODY; //蛇移動后蛇頭的位置變為蛇身
        //蛇移動后各個蛇身位置坐標需要更新
        for (int i = snake.len - 1; i > 0; i--)
        {
           body[i].x = body[i - 1].x;
           body[i].y = body[i - 1].y;
        }
        //蛇移動后蛇頭位置信息變為第0個蛇身的位置信息
        body[0].x = snake.x;
        body[0].y = snake.y;
        //蛇頭的位置更改
        snake.x = snake.x + x;
        snake.y = snake.y + y;
        DrawSnake(1); //打印移動后的蛇
    }

    十一、游戲主體邏輯函數

    11.1主體邏輯函數

    首先第一次進入該函數Game,默認蛇向右移動,進而執行run函數。直到鍵盤被敲擊,再從run函數返回到Game函數進行按鍵讀取。讀取到鍵值后需要對讀取到的按鍵進行調整(這是必要的)。調整后再進行按鍵執行,然后再進行按鍵讀取,如此循環進行。

    //游戲主體邏輯函數
    void Game(){
        int n = RIGHT; //開始游戲時,默認向后移動
        int tmp = 0; //記錄蛇的移動方向
        goto first; //第一次進入循環先向默認方向前進
        while (1){
           n = getch(); //讀取鍵值
           //在執行前,需要對所讀取的按鍵進行調整
           switch (n){
           case UP:
           case DOWN: //如果敲擊的是“上”或“下”
               if (tmp != LEFT && tmp != RIGHT) {//并且上一次蛇的移動方向不是“左”或“右”
                  n = tmp; //那么下一次蛇的移動方向設置為上一次蛇的移動方向
               }
               break;
           case LEFT:
           case RIGHT: //如果敲擊的是“左”或“右”
               if (tmp != UP && tmp != DOWN) {//并且上一次蛇的移動方向不是“上”或“下”
                  n = tmp; //那么下一次蛇的移動方向設置為上一次蛇的移動方向
               }
           case SPACE:
           case ESC:
           case 'r':
           case 'R':
               break; //這四個無需調整
           default:
               n = tmp; //其他鍵無效,默認為上一次蛇移動的方向
               break;
           }
    
        first: //第一次進入循環先向默認方向前進
           switch (n){
           case UP: //方向鍵:上
               run(0, -1); //向上移動(橫坐標偏移為0,縱坐標偏移為-1)
               tmp = UP; //記錄當前蛇的移動方向
               break;
           case DOWN: //方向鍵:下
               run(0, 1); //向下移動(橫坐標偏移為0,縱坐標偏移為1)
               tmp = DOWN; //記錄當前蛇的移動方向
               break;
           case LEFT: //方向鍵:左
               run(-1, 0); //向左移動(橫坐標偏移為-1,縱坐標偏移為0)
               tmp = LEFT; //記錄當前蛇的移動方向
               break;
           case RIGHT: //方向鍵:右
               run(1, 0); //向右移動(橫坐標偏移為1,縱坐標偏移為0)
               tmp = RIGHT; //記錄當前蛇的移動方向
               break;
           case SPACE: //暫停
               system("pause>nul"); //暫停后按任意鍵繼續
               break;
           case ESC: //退出
               system("cls"); //清空屏幕
               color(7); //顏色設置為白色
               CursorJump(COL - 8, ROW / 2);
               printf("  游戲結束  ");
               CursorJump(COL - 8, ROW / 2 + 2);
               exit(0);
           case 'r':
           case 'R': //重新開始
               system("cls"); //清空屏幕
               main(); //重新執行主函數
           }
        }
    }

    11.2執行按鍵函數

    按鍵調整機制:

    如果敲擊的是“上”或“下”鍵,并且上一次蛇的移動方向不是“左”或“右”,那么將下一次蛇的移動方向設置為上一次蛇的移動方向,即移動方向不變。如果敲擊的是“左”或“右”鍵,并且上一次蛇的移動方向不是“上”或“下”,那么將下一次蛇的移動方向設置為上一次蛇的移動方向,即移動方向不變。如果敲擊的按鍵是空格、Esc、r或是R,則不作調整。其余按鍵無效,下一次蛇的移動方向設置為上一次蛇的移動方向,即移動方向不變。

    //執行按鍵
    void run(int x, int y){
        int t = 0;
        while (1){
           if (t == 0)
               t = 3000; //這里t越小,蛇移動速度越快(可以根據次設置游戲難度)
           while (--t){ //控制移動速度的,循環3000次會占用一點時間
               if (kbhit() != 0) //若鍵盤被敲擊,則退出循環
                  break;
           }
    
           if (t == 0) //鍵盤未被敲擊{
               JudgeFunc(x, y); //判斷到達該位置后,是否得分與游戲結束
               MoveSnake(x, y); //移動蛇
           }
           else //鍵盤被敲擊{
               break; //返回Game函數讀取鍵值
           }
        }
    }

    kbhit()函數

    Return Value

    kbhit returns a nonzero value if a key has been pressed. Otherwise, it returns 0.

    執行按鍵

    參數說明:

    x:蛇移動后的橫坐標相對于當前蛇的橫坐標的變化。

    y:蛇移動后的縱坐標相對于當前蛇的縱坐標的變化。

    給定一定的時間間隔,若在該時間間隔內鍵盤被敲擊,則退出run函數,返回Game函數進行按鍵讀取。若未被敲擊,則先判斷蛇到達移動后的位置后是否得分或是游戲結束,然后再移動蛇的位置。 若鍵盤一直未被敲擊,則就會一直執行run函數當中的while函數,蛇就會一直朝一個方向移動,直到游戲結束

    11.3判斷得分與結束

    判斷得分: 若蛇頭即將到達的位置是食物,則得分。得分后需要將蛇身加長,并且更新當前得分,除此之外,還需要重新生成食物。

    判斷結束: 若蛇頭即將到達的位置是墻或者蛇身,則游戲結束。游戲結束后比較本局得分和歷史最高得分,給出相應的提示語句,并且詢問玩家是否再來一局,可自由發揮。

    void JudgeFunc(int x, int y){
        //若蛇頭即將到達的位置是食物,則得分
        if (face[snake.y + y][snake.x + x] == FOOD){
           snake.len++; //蛇身加長
           grade += 10; //更新當前得分
           color(7); //顏色設置為白色
           CursorJump(0, ROW);
           printf("當前得分:%d", grade); //重新打印當前得分
           RandFood(); //重新隨機生成食物
        }
    
        //若蛇頭即將到達的位置是墻或者蛇身,則游戲結束
        else if (face[snake.y + y][snake.x + x] == WALL || face[snake.y + y][snake.x + x] == BODY){
           Sleep(1000); //留給玩家反應時間
           system("cls"); //清空屏幕
           color(7); //顏色設置為白色
           CursorJump(2 * (COL / 3), ROW / 2 - 3);
           if (grade > max){
               printf("恭喜你打破最高記錄,最高記錄更新為%d", grade);
               WriteGrade();
           }
           else if (grade == max){
               printf("與最高記錄:%d持平,加油再創佳績", grade);
           }
           else{
               printf("請繼續加油,當前與最高記錄相差%d", max - grade);
           }
           CursorJump(2 * (COL / 3), ROW / 2);
           printf("GAME OVER");
           while (1) {//詢問玩家是否再來一局
               char ch;
               CursorJump(2 * (COL / 3), ROW / 2 + 3);
               printf("再來一局?(y/n):");
               scanf("%c", &ch);
               if (ch == 'y' || ch == 'Y'){
                  system("cls");
                  main();
               }
               else if (ch == 'n' || ch == 'N'){
                  CursorJump(2 * (COL / 3), ROW / 2 + 5);
                  exit(0);
               }
               else{
                  CursorJump(2 * (COL / 3), ROW / 2 + 5);
                  printf("選擇錯誤,請再次選擇");
               }
           }
        }
    }

    11.4從文件讀取歷史數據

    首先需要使用fopen函數打開“貪吃蛇最高得分記錄.txt”文件,若是第一次運行該代碼,則會自動創建該文件,并將歷史最高記錄設置為0,之后再讀取文件當中的歷史最高記錄存儲在max變量當中,并關閉文件即可。

    C語言如何實現貪吃蛇

    11.5更新數據到文件

    首先使用fopen函數打開“貪吃蛇最高得分記錄.txt”,然后將本局游戲的分數grade寫入文件當中即可(覆蓋式)。

    //更新最高分到文件
    void WriteGrade()
    {
        FILE* pf = fopen("貪吃蛇最高得分記錄.txt", "w"); //以只寫的方式打開文件
        if (pf == NULL) //打開文件失敗
        {
           printf("保存最高得分記錄失敗\n");
           exit(0);
        }
        fwrite(&grade, sizeof(int), 1, pf); //將本局游戲得分寫入文件當中
        fclose(pf); //關閉文件
        pf = NULL; //文件指針及時置空
    }

    11.6 主函數

    int main()
    {
        //#pragma warning(disable: n)將某個警報置為失效
    #pragma warning (disable:4996) //可以使用標準C語言提供的庫函數
        menu();
        max = 0, grade = 0; //初始化變量
        srand((size_t)time(NULL));//根據當前時間生成隨機種子
        system("title 貪吃蛇"); //設置cmd窗口的名字
        system("mode con cols=84 lines=23"); //設置cmd窗口的大小
        HideCursor(); //隱藏光標
        ReadGrade(); //從文件讀取最高分到max變量
        InitInterface(); //初始化界面
        InitSnake(); //初始化蛇
        RandFood(); //隨機生成食物
        DrawSnake(1); //打印蛇
        PlaySound(TEXT("bgmusic.wav"), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
        Game(); //開始游戲
        return 0;
    }

    到此,相信大家對“C語言如何實現貪吃蛇”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    华阴市| 措美县| 石棉县| 禄丰县| 正定县| 岚皋县| 广昌县| 本溪市| 当雄县| 成武县| 宁陵县| 台北县| 万安县| 积石山| 海伦市| 马龙县| 东丰县| 沈丘县| 西宁市| 文安县| 阜南县| 广汉市| 扶风县| 安泽县| 高安市| 博兴县| 安化县| 武宣县| 沙洋县| 翁源县| 周宁县| 双辽市| 三穗县| 黄冈市| 安阳市| 呼伦贝尔市| 龙山县| 阳城县| 三江| 通江县| 静乐县|