文章目錄
- 游戲說明
- 游戲效果展示
- 游戲代碼
- 游戲代碼詳解
- 游戲框架構建
- 隱藏游標
- 游標跳轉
- 初始化界面
- 顏色設定
- 初始化蛇
- 隨機生成食物
- 列印蛇與覆寫蛇
- 移動蛇
- 游戲主體邏輯函式
- 執行按鍵
- 判斷得分與結束
- 從檔案讀取最高分
- 更新最高分到檔案
- 主函式
游戲說明
游戲界面當中沒有列印相關的按鍵說明,這里先逐一列出,貪吃蛇游戲按鍵說明:
- 按方向鍵上下左右,可以實作蛇移動方向的改變,
- 短時間長按方向鍵上下左右其中之一,可實作蛇向該方向的短時間加速移動,
- 按空格鍵可實作暫停,暫停后按任意鍵繼續游戲,
- 按Esc鍵可直接退出游戲,
- 按R鍵可重新開始游戲,
除此之外,本游戲還擁有計分系統,可保存玩家的歷史最高記錄,
游戲效果展示
貪吃蛇游戲當中蛇的移動速度可以進行調整,動圖當中把速度調得較慢(速度太快導致動圖上蛇身顯示不全),下面給出的代碼當中將蛇的速度調整到了合適的位置,大家可以試試,

游戲代碼
博友們可以將以下代碼復制到自己的編譯器當中運行:
#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#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 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:4996) //消除警告
max = 0, grade = 0; //初始化變數
system("title 貪吃蛇"); //設定cmd視窗的名字
system("mode con cols=84 lines=23"); //設定cmd視窗的大小
HideCursor(); //隱藏游標
ReadGrade(); //從檔案讀取最高分到max變數
InitInterface(); //初始化界面
InitSnake(); //初始化蛇
srand((unsigned int)time(NULL)); //設定亂數生成起點
RandFood(); //隨機生成食物
DrawSnake(1); //列印蛇
Game(); //開始游戲
return 0;
}
//隱藏游標
void HideCursor()
{
CONSOLE_CURSOR_INFO curInfo; //定義游標資訊的結構體變數
curInfo.dwSize = 1; //如果沒賦值的話,游標隱藏無效
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(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(7); //顏色設定為白色
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;
}
//隨機生成食物
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("與最高記錄持平,加油再創佳績", 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;
}
//蛇移動后蛇頭位置資訊變為第0個蛇身的位置資訊
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)
{
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(); //重新執行主函式
}
}
}
游戲代碼詳解
游戲框架構建
首先定義游戲界面的大小,定義游戲區行數和列數,
#define ROW 22 //游戲區行數
#define COL 42 //游戲區列數
這里將蛇活動的區域稱為游戲區,將分數提示的區域稱為提示區(提示區占一行),

此外,我們還需要兩個結構體用于表示蛇頭和蛇身,蛇頭結構體當中存盤著當前蛇身的長度以及蛇頭的位置坐標,
//蛇頭
struct Snake
{
int len; //記錄蛇身長度
int x; //蛇頭橫坐標
int y; //蛇頭縱坐標
}snake;
蛇身結構體當中存盤著該段蛇身的位置坐標,
//蛇身
struct Body
{
int x; //蛇身橫坐標
int y; //蛇身縱坐標
}body[ROW*COL]; //開辟足以存盤蛇身的結構體陣列
同時我們需要一個二維陣列來標記游戲區各個位置的狀態(空、墻、食物、蛇頭以及蛇身),
int face[ROW][COL]; //標記游戲區各個位置的狀態
為了增加代碼的可讀性,最好運用宏來定義各個位置的狀態,而不是在代碼中用干巴巴的數字對各個位置的狀態進行切換,
#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 //退出
隱藏游標
隱藏游標比較簡單,定義一個游標資訊的結構體變數,然后對游標資訊進行賦值,最后用這個游標資訊的結構體變數進行游標資訊設定即可,
//隱藏游標
void HideCursor()
{
CONSOLE_CURSOR_INFO curInfo; //定義游標資訊的結構體變數
curInfo.dwSize = 1; //如果沒賦值的話,游標隱藏無效
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); //設定游標位置
}
初始化界面
初始化界面完成游戲區“墻”的列印,和提示區的列印即可,

在列印程序中需要注意兩點:
- 在cmd視窗中一個小方塊占兩個單位的橫坐標,一個單位的縱坐標,
- 游標跳轉函式CursorJump接收的是游標將要跳至位置的橫縱坐標,
例如,要用CursorJump函式跳轉至 i 行 j 列(以一個小方塊為一個單位),就等價于讓游標跳轉至坐標(2*j,i)處,
//初始化界面
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(7); //顏色設定為白色
CursorJump(0, ROW);
printf("當前得分:%d", grade);
CursorJump(COL, ROW);
printf("歷史最高得分:%d", max);
}
注意: 在初始化界面的同時,記得對游戲區相應位置的狀態進行標記,
顏色設定
顏色設定函式的作用是,將此后輸出的內容顏色都更為所指定的顏色,接收的引數c是顏色代碼,十進制顏色代碼表如下:

//顏色設定
void color(int c)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //顏色設定
//注:SetConsoleTextAttribute是一個API(應用程式編程介面)
}
設定顏色函式在其頭檔案當中的宣告如下:

初始化蛇
初始化蛇時將蛇身的長度初始化為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;
}
隨機生成食物
隨機在游戲區生成食物,需要對生成后的坐標進行判斷,只有該位置為空才能在此生成食物,否則需要重新生成坐標,食物坐標確定后,需要對游戲區該位置的狀態進行標記,
//隨機生成食物
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("●"); //列印食物
}
列印蛇與覆寫蛇
列印蛇和覆寫蛇這里直接使用一個函式來實作,若傳入引數flag為1,則列印蛇;若傳入引數為0,則用空格覆寫蛇,
列印蛇:
- 先根據結構體變數snake獲取蛇頭的坐標,到相應位置列印蛇頭,
- 然后根據結構體陣列body依次獲取蛇身的坐標,到相應位置進行列印即可,
覆寫蛇:
- 用空格覆寫最后一段蛇身即可,
但需要注意在覆寫前判斷覆寫的位置是否為(0,0)位置,因為當得分后蛇身長度增加,需要覆寫當前的蛇(進而列印長度增加后的蛇),而此時新加蛇身還未進行賦值(編譯器一般默認初始化為0),我們根據最后一段蛇身獲取到的坐標便是(0,0),則會用空格對(0,0)位置的墻進行覆寫,需要看完后面的移動蛇函式的實作后再進行理解,(也可以先將該判斷去掉,觀察蛇吃到食物后(0,0)位置墻的變化再進行分析)
//列印蛇與覆寫蛇
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(" ");
}
}
}
移動蛇
移動蛇函式的作用就是先覆寫當前所顯示的蛇,然后再列印移動后的蛇,
引數說明:
- x:蛇移動后的橫坐標相對于當前蛇的橫坐標的變化,
- y:蛇移動后的縱坐標相對于當前蛇的縱坐標的變化,
蛇移動后,各種資訊需要變化:
- 最后一段蛇身在游戲區當中需要被重新標記為空,
- 蛇頭位置在游戲區當中需要被重新標記為蛇身,
- 存盤蛇身坐標資訊的結構體陣列body當中,需要將第i段蛇身的坐標資訊更新為第i-1段蛇身的坐標資訊,而第0段,即第一段蛇身的坐標資訊需要更新為當前蛇頭的坐標資訊,
- 蛇頭的坐標資訊需要根據傳入的引數x和y,進行重新計算,
//移動蛇
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); //列印移動后的蛇
}
游戲主體邏輯函式
主體邏輯:
- 首先第一次進入該函式,默認蛇向右移動,進而執行run函式,
- 直到鍵盤被敲擊,再從run函式回傳到Game函式進行按鍵讀取,
- 讀取到鍵值后需要對讀取到的按鍵進行調整(這是必要的),
- 調整后再進行按鍵執行,然后再進行按鍵讀取,如此回圈進行,
按鍵調整機制:
- 如果敲擊的是“上”或“下”鍵,并且上一次蛇的移動方向不是“左”或“右”,那么將下一次蛇的移動方向設定為上一次蛇的移動方向,即移動方向不變,
- 如果敲擊的是“左”或“右”鍵,并且上一次蛇的移動方向不是“上”或“下”,那么將下一次蛇的移動方向設定為上一次蛇的移動方向,即移動方向不變,
- 如果敲擊的按鍵是空格、Esc、r或是R,則不作調整,
- 其余按鍵無效,下一次蛇的移動方向設定為上一次蛇的移動方向,即移動方向不變,
//游戲主體邏輯函式
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(); //重新執行主函式
}
}
}
執行按鍵
引數說明:
- x:蛇移動后的橫坐標相對于當前蛇的橫坐標的變化,
- y:蛇移動后的縱坐標相對于當前蛇的縱坐標的變化,
給定一定的時間間隔,若在該時間間隔內鍵盤被敲擊,則退出run函式,回傳Game函式進行按鍵讀取,若未被敲擊,則先判斷蛇到達移動后的位置后是否得分或是游戲結束,然后再移動蛇的位置,
若鍵盤一直未被敲擊,則就會一直執行run函式當中的while函式,蛇就會一直朝一個方向移動,直到游戲結束,
//執行按鍵
void run(int x, int y)
{
int t = 0;
while (1)
{
if (t == 0)
t = 3000; //這里t越小,蛇移動速度越快(可以根據次設定游戲難度)
while (--t)
{
if (kbhit() != 0) //若鍵盤被敲擊,則退出回圈
break;
}
if (t == 0) //鍵盤未被敲擊
{
JudgeFunc(x, y); //判斷到達該位置后,是否得分與游戲結束
MoveSnake(x, y); //移動蛇
}
else //鍵盤被敲擊
{
break; //回傳Game函式讀取鍵值
}
}
}
判斷得分與結束
判斷得分:
若蛇頭即將到達的位置是食物,則得分,得分后需要將蛇身加長,并且更新當前得分,除此之外,還需要重新生成食物,
判斷結束:
若蛇頭即將到達的位置是墻或者蛇身,則游戲結束,游戲結束后比較本局得分和歷史最高得分,給出相應的提示陳述句,并且詢問玩家是否再來一局,可自由發揮,
//判斷得分與結束
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("與最高記錄持平,加油再創佳績", 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("選擇錯誤,請再次選擇");
}
}
}
}
注意: 若本局得分大于歷史最高得分,需要更新最高分到檔案,
從檔案讀取最高分
首先需要使用fopen函式打開“貪吃蛇最高得分記錄.txt”檔案,若是第一次運行該代碼,則會自動創建該檔案,并將歷史最高記錄設定為0,之后再讀取檔案當中的歷史最高記錄存盤在max變數當中,并關閉檔案即可,

//從檔案讀取最高分
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; //檔案指標及時置空
}
更新最高分到檔案
首先使用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; //檔案指標及時置空
}
主函式
有了以上函式的支撐,寫出主函式是相當簡單的,但需要注意以下三點:
- 全域變數grade需要在主函式內初始化為0,不能在全域范圍初始化為0,因為當玩家按下R鍵進行重玩時我們需要將當前分數grade重新設定為0,
- 亂數的生成起點建議設定在主函式當中,
- 主函式當中的#pragma陳述句是用于消除類似以下警告的:

int max, grade; //全域變數
int main()
{
#pragma warning (disable:4996) //消除警告
max = 0, grade = 0; //初始化變數
system("title 貪吃蛇"); //設定cmd視窗的名字
system("mode con cols=84 lines=23"); //設定cmd視窗的大小
HideCursor(); //隱藏游標
ReadGrade(); //從檔案讀取最高分到max變數
InitInterface(); //初始化界面
InitSnake(); //初始化蛇
srand((unsigned int)time(NULL)); //設定亂數生成起點
RandFood(); //隨機生成食物
DrawSnake(1); //列印蛇
Game(); //開始游戲
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/295031.html
標籤:其他
