贪吃蛇小游戏(c)
首先,介紹下游戲規則:
蛇初始有三個結點。
本游戲如果蛇撞到墻或者撞到自己,游戲失敗,上下左右控制蛇的移動;
F1、F2控制蛇的速度。如果沒有按 F1、F2,則以常速300ms移動一步,
吃到一個食物為10分,當scoreprefood<20分時,第一次加速使每吃到一個食物增加5分,速度變為250ms一步,依次類推,當scoreprefood>=20時,加速使每吃到一個食物增加10分,要將scoreprefood控制在40分,因為此時速度很快,若以此速度,失敗概率很高……..
當scoreprefood>10,才可以減速,一次將速度減少50ms/步。(有的電腦f1,f2可以加速減速;有的電腦需要fn+f1,fn+f2才可以加速減速)。
解釋代碼中的三個函數:
GetStdHandle是一個Windows API函數。它用于從一個特定的標準設備(標準輸入、標準輸出或標準錯誤)中取得一個句柄。
GetStdHandle(STD_OUTPUT_HANDLE)是指標準輸出句柄。
SetConsoleCursorPosition是一個計算機函數(API中定位光標位置的函數),如果用戶定義了 COORD pos,那么pos其實是一個結構體變量,其中X和Y是它的成員,通過修改pos.X和pos.Y的值就可以實現光標的位置控制。
GetAsyncKeyState是一個用來判斷函數調用時指定虛擬鍵的狀態,確定用戶當前是否按下了鍵盤上的一個鍵的函數。
↑.↓.←.→分別是VK_UP 、VK_DOWN、VK_LEFT、VK_RIGHT
VK_SPACE空格鍵、VK_ESCAPEESC鍵、VK_F1是F1鍵、VK_F2是F2鍵。
UI.c
#include"UI.h" #include<windows.h> #include<stdio.h> static void CoordinatePosATXY(const struct UI *pUI, int x, int y);//將字符個數轉化為最后字符寬度 struct UI* UIInitalize(int width, int height)//初始化界面 width.height指游戲區域字符個數 {const int left = 3;//左邊緣寬度const int top = 4;//上邊緣寬度const int descwidth = 50;//指墻右邊的寬度const int descheight = 3;//指墻下的高度struct UI *pUI = (struct UI*)malloc(sizeof(struct UI));pUI->margintop = top;pUI->marginleft = left;pUI->gamewidth = width;//pUI->gameheight = height;pUI->snackblock = pUI->wallblock = pUI->foodblock = "█";pUI->blockwidth = strlen(pUI->snackblock);pUI->windowHeight = top + (2 + height) * 1 + descheight;//窗口高度pUI->windowWidth = left + (2 + width) *(pUI->blockwidth) + descwidth;//窗口寬度system("mode con cols=100 lines=40");//控制黑框框大小return pUI; } static void SetPos(int x, int y)//光標移到x,y處,注意,這里是相對整個屏幕的,而不是游戲區域 {COORD position = { x,y };//COORD是結構體HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(hOutput, position); } static void DisplayWall(const struct UI *pUI)//打印墻 {int i = 0;int x, y;//上for (i = 0; i < pUI->gamewidth+2; i++){x = pUI->marginleft + pUI->blockwidth*i;//第一個光標就是左邊緣后y = pUI->margintop;//墻的上邊框高度一樣SetPos(x, y);printf("%s", pUI->wallblock);}//下for (i = 0; i < pUI->gamewidth+2; i++){x = pUI->marginleft + pUI->blockwidth*i;//第一個光標就是左邊緣后y = pUI->margintop + (1 + pUI->gameheight) * 1;//加1是因為有墻的上邊框,*1是字符的高度SetPos(x, y);printf("%s", pUI->wallblock);}//左for (i = 0; i < pUI->gameheight + 2; i++){x = pUI->marginleft;//左邊框寬度一樣y = pUI->margintop + i;SetPos(x, y);printf("%s", pUI->wallblock);}//右for (i = 0; i < pUI->gameheight + 2; i++){x = pUI->marginleft + (1 + pUI->gamewidth)*(pUI->blockwidth);//加1是因為除了游戲區域還有墻y = pUI->margintop + i;SetPos(x, y);printf("%s", pUI->wallblock);}printf("\n"); } static void DisplyDesc(const struct UI *pUI)//顯示右側信息 {int left = pUI->marginleft + (2 + pUI->gamewidth)*(pUI->blockwidth)+2;//先找到右側信息輸出位置const char *message[] = {"不能穿墻,不能咬到自己。","用 ↑.↓.←.→分別控制蛇的移動。","F1為加速,F2為減速。","ESC退出游戲,SPACE暫停游戲。"};int i = 0;for (i = 0; i < sizeof(message) / sizeof(char *)/*sizeof(message[0])*/; i++){SetPos(left, pUI->margintop+ 11+ i);printf("%s", message[i]);}printf("\n"); } void UIDisplayWizard(const struct UI *pUI)//顯示游戲導向 {int i = 0;const char *message[] = {"歡迎來到貪吃蛇小游戲","用 ↑.↓.←.→分別控制蛇的移動,F1為加速,F2為減速。","加速將能得到更高的分數。"};for (i = 0; i < sizeof(message) / sizeof(char *); i++){SetPos((pUI->windowWidth / 2) - (strlen(message[i]) / 2), pUI->windowHeight / 2+i);//置光標函數printf("%s\n", message[i]);}system("pause");system("cls"); } static void ResetCursorPosition(const struct UI *pUI)//重置光標到屏幕下方,只要是為了顯示的美觀 {SetPos(0, pUI->windowHeight - 1); } void UIDisplyFoodAtXY(const struct UI *pUI, int x, int y)//在x y處顯示食物,x,y都是字符個數 {CoordinatePosATXY(pUI, x, y);//將個數轉為寬度printf(pUI->foodblock);ResetCursorPosition(pUI); } void UIDisplySnackBlockAtXY(const struct UI *pUI, int x, int y) //在x y處顯示蛇的結點,x,y都是字符個數 {CoordinatePosATXY(pUI, x, y);//將個數轉為寬度printf(pUI->snackblock);ResetCursorPosition(pUI); } void UICleanBlockAtXY(const struct UI *pUI, int x, int y)//清空x y處的顯示塊,x,y是字符個數 {CoordinatePosATXY(pUI, x, y);//將個數轉為寬度int i = 0;for (i = 0; i < pUI->blockwidth; i++){printf(" ");}ResetCursorPosition(pUI); } void UIDisplayScore(const struct UI *pUI, int score, int scorePrefood)//顯示分數 {int left = pUI->marginleft + (2 + pUI->gamewidth)*(pUI->blockwidth) + 10;//先找到右側信息輸出位置SetPos(left, pUI->margintop + 2);printf("得分:%d,每個食物得分:%d", score, scorePrefood);ResetCursorPosition(pUI); } void UIDisplayGameWindow(const struct UI*pUI, int score, int scorePerfood)//顯示游戲整體 {DisplayWall(pUI);//顯示墻UIDisplayScore(pUI, score, scorePerfood);//顯示分數DisplyDesc(pUI);//顯示右側信息ResetCursorPosition(pUI); }void UIShowMessage(const struct UI *pUI, const char *message)//在游戲區域居中顯示游戲退出消息 {//左填充寬度+1(左邊界個數)*每個字符寬度+游戲區域寬度/2*每個字符寬度-字符串寬度/2int left = pUI->marginleft + 1 * pUI->blockwidth +pUI->gamewidth / 2 * pUI->blockwidth - strlen(message)/2;SetPos(left, pUI->margintop + 1 + pUI->gameheight / 2);printf("%s\n", message);ResetCursorPosition(pUI); } void UIDeinitialize(struct UI *pUI)//銷毀UI {free(pUI); } static void CoordinatePosATXY(const struct UI *pUI, int x, int y)//將字符個數轉化為最后字符寬度 {SetPos(pUI->marginleft + (1 + x)*pUI->blockwidth, pUI->margintop + 1 + y); }game.h
#pragma once #include"snake.h"struct Game *CreateGame();//創建并初始化游戲void StartGame(struct Game *pGame);//開始游戲void DestoryGame(struct Game *pGame);//銷毀游戲資源Snake.h
#pragma once //看到游戲的界面,首先需要將游戲的結構體定義出來 //首先有蛇,那么定義蛇的結構體 //蛇可以用數組存,也可以用鏈表,但是蛇的身體會變長半短,也就是增刪,用鏈表更方便 struct Position//游戲區域坐標 {int x;int y; }; struct Node//鏈表結點 {struct Position position; //一個結點用坐標存,那么就要再定義一個坐標結構體struct Node *pnext;//下一個結點 }; enum Direction//定義蛇的方向 {UP,//上DOWN,//下LEFT,//左RIGHT//右 }; struct Snack//蛇結構體 {enum Directon direction;//蛇的方向struct Node *pbody;//蛇的身體 }; struct Game//游戲 {int width;//游戲區域的寬度,不包括墻,對應到UI寬度上字符的個數int height;//游戲區域的高度,不包括墻,對應到UI高度上對應字符的個數int socre;//當前得分int scoreprefood;//每個食物得分struct Snack snack;//蛇struct Position foodposition;//當前食物坐標 };Main.c
#include"game.h" #include"UI.h" #include<stdio.h> #include<time.h> #include<stdlib.h> int main() {srand((unsigned int)time(NULL));//設置時間戳種子struct Game *pGame = CreateGame();StartGame(pGame);//開始游戲DestoryGame(pGame);//銷毀游戲資源system("pause");return 0; }game.c
#include"game.h" #include"UI.h" #include<stdlib.h> #include<windows.h> #include<assert.h> int speed = 300; enum ExitStatus {KILLED_BY_WALL,KILLED_BY_SELF,QUIT }; void InitSnake(struct Snack *psnake);//初始化蛇結構體 void GenerateFood(struct Game *pGame);//設置食物 void HandleDirective(struct Game *pGame);//方向的處理 struct Position GetNextPosition(struct Snack * psnake);//得到下一個坐標 int IsWillEatFood(struct Position foodposition, struct Position nextposition);//是否吃到食物 int IsKilledByWall(struct Snack *snake, int wideth, int height);//是否撞到墻 int IsOverlapSnake(int x, int y, struct Snack *psnake);//食物是否和蛇結點重復 void GrowAndDisplay(struct Snack *psnake, struct Position foodposition, struct UI *pUI); //增加蛇的結點并且打印增加蛇的結點 void MoveAndDisplay(struct Snack *psnake, struct Position nextposition, struct UI *pUI);//蛇移動并打印 int IsSnakeAlive(struct Game* pGame, enum ExitStatus * exitstatus);//蛇是否存活 int IsKilledBySelf(struct Node *phead, struct Snack *snake);//被自己撞死 void _Pause();//暫停 void DisplaySnake(struct UI* pUI, struct Snack *psnack);//顯示整個蛇 void ControlSpeed(struct UI *pUI, struct Game *pGame);//控制速度 struct Game *CreateGame()//創建并初始化游戲 {struct Game *pGame = (struct Game *)malloc(sizeof(struct Game));assert(pGame);pGame->width = 28;pGame->height = 27;pGame->socre = 0;pGame->scoreprefood = 10;InitSnake(&(pGame->snack));//初始化蛇結構體GenerateFood(pGame);//放食物return pGame; } void InitSnake(struct Snack * psnake)//初始化蛇結構體 {const int length = 3;//蛇最開始有三個結點const int x = 5;const int y = 5;int i = 0;struct Node*pNode;psnake->direction = RIGHT;psnake->pbody = NULL;for (i = 0; i < length; i++){pNode = (struct Node*)malloc(sizeof(struct Node));pNode->position.x = x + i;pNode->position.y = y;pNode->pnext = psnake->pbody;// (5,5)<-(6,5)<-(7,5) (7,5)是pbodypsnake->pbody = pNode;} } void GenerateFood(struct Game *pGame)//放置食物 {int x, y;do{x = rand() % pGame->width;y = rand() % pGame->height;} while (IsOverlapSnake(x, y, &pGame->snack));//當不重合時退出循環pGame->foodposition.x = x;pGame->foodposition.y = y; } int IsOverlapSnake(int x, int y, struct Snack *psnake) {struct Node*pNode;for (pNode = psnake->pbody; pNode != NULL; pNode = pNode->pnext){if (pNode->position.x == x&&pNode->position.y == y)return 1;//重合是1}return 0;//不重合是0 } void StartGame(struct Game *pGame)//開始游戲 {struct UI *pUI = UIInitalize(pGame->width, pGame->height);//初始化界面enum ExitStatus exitstatus = QUIT;UIDisplayWizard(pUI);//顯示游戲導向UIDisplayGameWindow(pUI, pGame->socre, pGame->scoreprefood);//顯示游戲整體DisplaySnake(pUI, &pGame->snack);UIDisplyFoodAtXY(pUI, pGame->foodposition.x, pGame->foodposition.y);//在x y處顯示食物,x,y都是字符個數while (1){if (GetAsyncKeyState(VK_ESCAPE)){break;}else if (GetAsyncKeyState(VK_SPACE))_Pause();HandleDirective(pGame);struct Position nextposition = GetNextPosition(&pGame->snack);if (IsWillEatFood(pGame->foodposition, nextposition))//吃到食物{pGame->socre = pGame->socre + pGame->scoreprefood;UIDisplayScore(pUI, pGame->socre, pGame->scoreprefood);//顯示分數GrowAndDisplay(&pGame->snack, pGame->foodposition, pUI);// 增加蛇的結點并且打印增加蛇的結點GenerateFood(pGame);//設置食物UIDisplyFoodAtXY(pUI, pGame->foodposition.x, pGame->foodposition.y);//在x y處顯示食物,x,y都是字符個數}else{MoveAndDisplay(&pGame->snack, nextposition, pUI);}if (IsSnakeAlive(pGame, &exitstatus)){break; //蛇撞墻或者撞到自己}ControlSpeed(pUI, pGame);//控制速度 }char *massage[3];massage[QUIT] = "游戲結束";massage[KILLED_BY_WALL] = "游戲結束,撞到墻";massage[KILLED_BY_SELF] = "游戲結束,撞到自己";UIShowMessage(pUI, massage[exitstatus]);UIDeinitialize(pUI);//銷毀UI} void HandleDirective(struct Game *pGame)//方向的處理 {if (GetAsyncKeyState(VK_UP) && (pGame->snack.direction != DOWN))pGame->snack.direction = UP;else if (GetAsyncKeyState(VK_DOWN) && pGame->snack.direction != UP)pGame->snack.direction = DOWN;else if (GetAsyncKeyState(VK_LEFT) && pGame->snack.direction != RIGHT)pGame->snack.direction = LEFT;else if (GetAsyncKeyState(VK_RIGHT) && pGame->snack.direction != LEFT)pGame->snack.direction = RIGHT; } struct Position GetNextPosition(struct Snack * psnake)//得到下一個坐標 {int nextX=psnake->pbody ->position .x ;int nextY = psnake->pbody->position.y;switch (psnake->direction){case UP:nextX = nextX;nextY -= 1;break;case DOWN:nextX = nextX;nextY += 1;break;case RIGHT:nextX += 1;nextY = nextY;break;case LEFT:nextX -= 1;nextY = nextY;break;}struct Position nextposition = { nextX,nextY };return nextposition; } int IsWillEatFood(struct Position foodposition, struct Position nextposition) { //是否吃到食物if (foodposition.x == nextposition.x &&foodposition.y == nextposition.y )return 1;return 0; } void GrowAndDisplay(struct Snack *psnake, struct Position foodposition, struct UI *pUI) { //增加蛇的結點并且打印增加蛇的結點struct Node *pNode = (struct Node *)malloc(sizeof(struct Node));pNode->position.x = foodposition.x;pNode->position.y = foodposition.y;pNode->pnext = psnake->pbody;psnake->pbody = pNode;UIDisplySnackBlockAtXY(pUI, foodposition.x, foodposition.y);//在x y處顯示蛇的結點,x,y都是字符個數 } void MoveAndDisplay(struct Snack *psnake, struct Position nextposition, struct UI *pUI)//蛇移動并打印 {struct Node *pNode = (struct Node *)malloc(sizeof(struct Node));pNode->position.x = nextposition.x;pNode->position.y = nextposition.y;pNode->pnext = psnake->pbody;psnake->pbody = pNode;UIDisplySnackBlockAtXY(pUI, nextposition.x, nextposition.y);struct Node *cur = psnake->pbody;//尾刪 前提是cur->next不為空,蛇初始三個結點保證不為空while (cur->pnext->pnext){cur = cur->pnext;}struct Node *tail=cur->pnext;UICleanBlockAtXY(pUI, tail->position.x, tail->position.y); free(tail);cur->pnext = NULL; }int IsSnakeAlive(struct Game* pGame, enum ExitStatus * exitstatus)//蛇是否存活 {struct Node *phead = pGame->snack.pbody;if (IsKilledByWall(&pGame->snack, pGame->width, pGame->height))//被墻撞到{*exitstatus = KILLED_BY_WALL;return 1;}if (IsKilledBySelf(phead, &pGame->snack )){*exitstatus = KILLED_BY_SELF;return 1;}return 0; }int IsKilledByWall(struct Snack *snake,int wideth,int height) {if (snake->pbody->position.x >= 0 && snake->pbody->position.x < wideth&&snake->pbody->position.y >= 0 && snake->pbody->position.y < height)return 0;return 1;//被墻撞到 } int IsKilledBySelf(struct Node *phead, struct Snack *snake) {//前提是蛇至少有兩個結點 struct Node *pNode=snake->pbody ->pnext ;while (pNode){if (pNode->position.x == phead->position.x&&pNode->position.y == phead->position.y)return 1;//被自己撞到pNode = pNode->pnext;}return 0; } void DestoryGame(struct Game *pGame)//銷毀游戲資源 {struct Node *pnode=pGame->snack .pbody , *pnext;while (pnode){pnext = pnode->pnext;free(pnode);pnode = pnext;}free(pGame); } void _Pause() {while (1){Sleep(300);//停住if (GetAsyncKeyState(VK_SPACE))//再按空格就退出循環,繼續游戲break;} } void DisplaySnake(struct UI* pUI, struct Snack *psnack)//顯示整個蛇 {struct Node *pNode=psnack->pbody ;if (psnack->pbody == NULL)return;while (pNode){UIDisplySnackBlockAtXY(pUI,pNode->position .x,pNode->position .y );//在x y處顯示蛇的結點,x,y都是字符個數pNode = pNode->pnext;} } void ControlSpeed(struct UI *pUI, struct Game *pGame) {if (GetAsyncKeyState(VK_F1))//加速{if (pGame->scoreprefood < 40){if (pGame->scoreprefood < 20){pGame->scoreprefood += 5;}if (pGame->scoreprefood >= 20){pGame->scoreprefood += 10;}speed = speed - 50;UIDisplayScore(pUI, pGame->socre, pGame->scoreprefood);//顯示分數}}if (GetAsyncKeyState(VK_F2))//減速{if (pGame->scoreprefood > 10)//小于10無法減速{pGame->scoreprefood -= 10;speed = speed + 50;UIDisplayScore(pUI, pGame->socre, pGame->scoreprefood);//顯示分數}}Sleep(speed); }蛇結點的初始狀態及食物的隨機放置界面:
游戲過程:
以上就是純c貪吃蛇小游戲。
總結
- 上一篇: 三层神经网络的训练模型,神经网络训练模型
- 下一篇: guacamole集成到自己的项目中