目錄
一、游戲說明
1.1游戲按鍵說明
1.2計分系統
二、游戲運行
2.1游戲效果展示
2.2一個報錯的糾正
2.3 游戲代碼
三、游戲框架構建
3.1游戲界面的大小
3.2蛇頭和蛇身
3.2.1蛇頭
3.2.2蛇身
3.3標記游戲區
3.3.1存盤游戲區的各個位置是什么
3.3.2 用宏來使某些數字具有特殊意義
3.4選單欄的設定
四.隱藏游標的設定
4.1 游標資訊的結構體成員
4.2隱藏游標的實作
4.3GetStdHandle函式
使用介紹
4.4 SetConsoleCursorInfo函式
使用介紹
五.游標跳轉的設定
5.1 游標位置的結構體型別
5.2 SetConsoleCursorPosition函式
使用介紹
5.3 游標跳轉的實作
六.初始化界面
6.1代碼
6.2 system函式
使用介紹
6.3 SetConsoleTextAttribute函式
使用介紹
七.初始化蛇
八.列印蛇與覆寫蛇
九、隨機生成食物
9.1效果展示
9.2 srand與rand函式
十,移動蛇
十一、游戲主體邏輯函式
11.1主體邏輯函式:
11.2執行按鍵函式
11.3判斷得分與結束
11.4從檔案讀取歷史資料
11.5更新資料到檔案
11.6 主函式
11.7 游戲背景音樂
一、游戲說明
1.1游戲按鍵說明
按方向鍵上下左右,可以實作蛇移動方向的改變,
短時間長按方向鍵上下左右其中之一,可實作蛇向該方向的短時間加速移動,
按空格鍵可實作暫停,暫停后按任意鍵繼續游戲,
按Esc鍵可直接退出游戲,按R鍵可重新開始游戲,
1.2計分系統
保存玩家的歷史最高記錄
二、游戲運行
2.1游戲效果展示

2.2一個報錯的糾正
如果出現這種情況請要相信這是編譯器的問題

(為了防止本文篇幅過長因此一些不影響游戲邏輯的知識點以鏈接形式展現)
解決辦法
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 //游戲區列數 |
這里將蛇活動的區域稱為游戲區,將分數提示的區域稱為提示區(提示區占一行),
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(應用程式編程介面) } |

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)位置墻的變化再進行分析

|
//列印蛇與覆寫蛇 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; } |

9.2 srand與rand函式
使用說明
十,移動蛇
移動蛇函式的作用就是先覆寫當前所顯示的蛇,然后再列印移動后的蛇,
引數說明:
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); //列印移動后的蛇 } |
十一、游戲主體邏輯函式
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變數當中,并關閉檔案即可,
![]()
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; } |
11.7 游戲背景音樂
PlaySound函式介紹
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/375054.html
標籤:其他
下一篇:【C語言】三子棋
