文章目錄
- 整體框架
- 選單界面繪制
- 控制音樂播放/關閉
- 實作退出游戲界面化
- 普通模式引數初始化
- 普通模式游戲界面化
- 普通模式小蛇移動
- 鍵盤控制小蛇改變方向
- 判斷食物狀態
- 該局失敗界面化
- 總結
C語言差不多學到尾端了,有了語法基礎的我就想著做一個小游戲來將自己所學的展現出來,借鑒了一些博主的博客,最終我選擇用C語言手打貪吃蛇,我的原始版本的貪吃蛇擁有界面化、可開關音樂、還有三種關卡模式,接下來讓我們先簡單體驗一下我寫的貪吃蛇吧!!!

由于我制作的 gif 動圖沒有聲音,所以大家沒有感受到音樂的妙處,但是程式本身是有的哦!!!接下來我會為大家介紹一下我用C語言手打貪吃蛇的思路,
整體框架
-
將界面選單給做出來
- 我們可以使用 EasyX 圖形庫繪制一個界面(加入頭檔案
#include <graphics.h>,而我自己是將一張圖片繪制成了界面的背景, - 在界面上增加我們想要點擊的按鈕,比如三種模式、開關音樂、退出等等,這里也只是將界面上繪制出我們想要擁有的按鍵,但是實作功能還在后面,
- 我們可以使用 EasyX 圖形庫繪制一個界面(加入頭檔案
-
將每個功能的按鍵實作
針對界面上的每個按鍵,我們可以使用 EasyX 圖形庫中獲取滑鼠資訊的功能,通過點擊按鈕,通過判斷滑鼠在哪一塊范圍,針對不同的資訊我們將其功能實作,比如點擊退出游戲,我們再繪制一個新的界面,背景就顯示再見,
-
先實作兩個最簡單的功能
- 播放和關閉音樂功能,
- 退出游戲功能,
-
實作三種關卡模式
其實三種關卡模式的邏輯是差不多的,只是我們在最基礎的普通模式上面增加了一些限制或者解除了一些限制,所以才有了另外兩種模式
-
無盡模式的實作
- 當我們的滑鼠移在普通模式按鍵的范圍后,通過點擊左鍵就開始實作我們的普通模式
- 首先我們要重新繪制一個游戲的界面,而初始界面的形成需要我們給出所有的引數,比如蛇的引數(如初始長度、初始坐標、每節的半徑、初始方向、蛇的顏色)、食物的引數(如隨機產生的食物的坐標、食物的半徑、食物的顏色、食物有沒有被吃掉的判斷標志),
- 當我們將這些引數初始化好了之后,我們就可以通過 EasyX 圖形庫的眾多功能將我們這些引數給繪制出來,
- 接下來就是讓小蛇動起來,大家可以先思考下小蛇在你不進行控制時怎樣勇敢向前的移動呢?并且遇到邊界后需要怎樣變化?蛇頭碰到身體會怎樣?
- 之后我們看到一條在圖形內移動的小蛇,我們需要通過按鍵控制小蛇的移動使它接近食物,
- 我們接著要判斷有沒有吃到食物,如果吃到了,食物的判斷標志將變成亦吃掉,并將繼續生成新的食物,
- 但是當我們走到這一步的時候,會發現好像已經沒有下一步了,所以是結束了嗎?當然不是,我們將往復的進行回圈(繪制此時蛇的引數—>蛇按蛇頭的方向移動—>控制蛇改變移動方向—>判斷是否吃到食物),直到蛇頭碰到蛇身就死亡,
- 死亡后我們就繪制一個新的界面,背景可以是小菜雞的圖片,哈哈,最終再回到開始選單,
-
仿照普通模式改變其他關卡模式的功能
選單界面繪制
選單界面的繪制其實很簡單,其實都是用的 EasyX 圖形庫里的函式,但是想要使用它還得去安裝一下,這一步很簡單的,大家可以百度,我就直接上我的代碼啦!!!
void menuInit()
{
//創建一個width寬和heigth的界面
initgraph(width, heigth);
//用圖1將這個界面填滿
loadimage(&img1, "圖1.png", width, heigth);
//圖1的左上角的頂點為(0,0)
putimage(0, 0, &img1);
//設定填制背景為透明,這樣填充文字時背景才是圖片的顏色
setbkmode(TRANSPARENT);
//在左上角坐標為(x,y)處輸出文字
outtextxy(260, 50, "貪吃蛇大作戰");
outtextxy(155, 250, "普通模式");
outtextxy(395, 250, "障礙模式");
outtextxy(275, 250, "無盡模式");
outtextxy(155, 300, "繼續游戲");
outtextxy(395, 300, "退出游戲");
outtextxy(155, 350, "打開音樂");
outtextxy(395, 350, "關閉音樂");
//在左上角(x1,y1)和右下角(x2,y2)范圍畫一個矩形,這個是設定的滑鼠點擊各按鍵時的范圍
rectangle(150, 245, 225, 270);
rectangle(270, 245, 345, 270);
rectangle(390, 245, 465, 270);
rectangle(150, 295, 225, 320);
rectangle(390, 295, 465, 320);
rectangle(150, 345, 225, 370);
rectangle(390, 345, 465, 370);
}
控制音樂播放/關閉
- 首先我們要加上播放音樂的頭檔案
#include<mmstream.h>和 多媒體設備介面#pragma comment(lib ,"winmm.lib"), - 再使用
mciSendString(TEXT("open 路徑\\音樂名.mp3 alias XXX"), NULL, 0, NULL);我們的程式就可以播放這個音樂了,并且該音樂使用時叫做 XXX - 播放音樂時我們就使用
mciSendString(TEXT("play XXX repeat"), NULL, 0, NULL);,其中repeat 是可以刪掉的,加它的含義就是讓歌曲不斷的播放不會結束, - 關閉音樂時我們就使用
mciSendString(TEXT("close XXX"), NULL, 0, NULL);
實作退出游戲界面化
這個功能超級簡單啦!我自己就是繪制了一個新界面,并用了一張可愛的圖片表示再見,

上代碼!!!
void gameExit()
{
initgraph(width, heigth);
loadimage(&img2, "圖2.jpg", width, heigth);
putimage(0, 0, &img2);
}
普通模式引數初始化
-
首先我們要初始化一個新的界面
-
我們要初始化蛇的引數,先通過結構體定義蛇的各類引數,看看我的代碼吧!
struct Snake { int size; //蛇的長度 int speed; //蛇的速度 int dir; //蛇的方向 POINT coor[SNAKE_MAX]; //蛇的最大長度 }snake;其中 POINT 本身就是一個結構體,它里面包含了 x 和 y 坐標
-
我們再初始化食物的引數,
struct Food { int x; int y; int r; bool flag; //1 沒有吃掉 0 吃掉了 DWORD color; }food; //typedef unsigned long DWORD; //DWORD 表示 32bit 無符號整數 -
接下來我們則對以上引數進行初始化,上代碼!
void gameInit1() { initgraph(width, heigth); //小蛇初始化 snake.size = 3; snake.speed = 10; snake.dir = LEFT; for (int i = snake.size - 1; i >= 0; i--) { snake.coor[i].x = 10 * i + 400; snake.coor[i].y = 30; } //食物初始化 food.r = rand() % 16 + 5; //食物半徑控制在5~15 food.x = rand() % width; //初始化x food.y = rand() % heigth; //初始化y food.color = RGB(rand() % 256, rand() % 256, rand() % 256); food.flag = true; }
普通模式游戲界面化
這也是很容易實作的部分,既然有了引數,我們只要對引數通過 EasyX 圖形庫進行繪制就行
void gameDraw1()
{
loadimage(&img4, "圖4.png", width, heigth);
putimage(0, 0, &img4);
//設定繪圖填充顏色
setfillcolor(RGB(61, 89, 171));
for (int i = 0; i < snake.size; i++)
{
//將每個坐標繪制成半徑為5的園
solidcircle(snake.coor[i].x, snake.coor[i].y, 5);
}
//將繪圖填充色修改成食物的顏色
setfillcolor(food.color);
//判斷食物存在則在坐標處繪制出半徑為食物半徑的圓
if (food.flag)
solidcircle(food.x, food.y, food.r);
//結束批量繪制
EndBatchDraw();
}
普通模式小蛇移動
-
我們該怎么讓小蛇移動呢?是不是蛇其實都是跟著蛇頭走的,再具體一點,每一節都是跟著前一節移動,所以我們只要從最后一節到第二節開始回圈,每節的坐標等于前一節就行,而蛇頭坐標則要靠此時小蛇前進的方向進行改變,可以使用一個 switch 陳述句進行改變,
-
我們要思考當蛇頭遇到邊界了該怎么辦,
-
如果蛇頭的坐標等于蛇身的坐標,那也就失敗了,我們直接上代碼吧!
bool snakeMove1() { for (int i = snake.size - 1; i > 0; i--) { snake.coor[i] = snake.coor[i - 1]; } switch (snake.dir) { case UP: snake.coor[0].y -= snake.speed; if (snake.coor[0].y <= 0) return false; break; case DOWN: snake.coor[0].y += snake.speed; if (snake.coor[0].y >= heigth) return false; break; case LEFT: snake.coor[0].x -= snake.speed; if (snake.coor[0].x <= 0) return false; break; case RIGHT: snake.coor[0].x += snake.speed; if (snake.coor[0].x >= width) return false; break; } for (int i = snake.size - 1; i > 0; i--) { if (snake.coor[0].x == snake.coor[i].x && snake.coor[0].y == snake.coor[i].y) return false; } return true; }這里當蛇頭遇到邊界時,我直接在改變后進行邊界判斷,而蛇頭碰到蛇身則是用一個回圈進行判斷,碰到了就回傳 false,因為我各個功能都是模塊化的,在使用這條函式時,用一個判斷陳述句就行,所以我只要回傳 false ,其實就會實作該局失敗的函式,你們則會看到這張圖

鍵盤控制小蛇改變方向
首先我們要先簡單知道兩個函式
-
kbhit() 函式
- 功能及回傳值:檢查當前是否有鍵盤輸入,若有則回傳一個非0值,否則回傳0
- 包含頭檔案:
<conio.h>
-
getch() 函式
- 功能:從控制臺讀取一個字符,但不顯示在螢屏上
- 包含頭檔案:
<conio.h>
所以首先要判斷我們的鍵盤上是否有輸入,沒有就下一步,有就將從鍵盤上讀取的字符進行判斷,可以使用一個 switch 陳述句,上代碼!
void keyControl()
{
if (_kbhit())
{
switch (_getch())
{
case 'W':
case 'w':
case 72:
if (snake.dir != DOWN)
snake.dir = UP;
break;
case 'S':
case 's':
case 80:
if (snake.dir != UP)
snake.dir = DOWN;
break;
case 'A':
case 'a':
case 75:
if (snake.dir != RIGHT)
snake.dir = LEFT;
break;
case 'D':
case 'd':
case 77:
if (snake.dir != LEFT)
snake.dir = RIGHT;
break;
case ' ':
while (_getch() != ' '); //給讀入的空格一個死回圈,卡住程式
break;
}
}
}
其中我們會發現每個方向都加了一個 if 條件陳述句,理由很簡單,小蛇本來是向左移動的,那我們能直接改變方向讓他向右移動嗎?程式是可以的,嘿嘿,但是邏輯上確不行,所以需要加一個判斷陳述句,
判斷食物狀態
這里我們只要蛇挨著食物的范圍就可以吃掉唄,如果吃了就重新生成新的食物的引數,就直接上代碼啦!
void Eat()
{
if (food.flag && snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r && snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r)
{
food.flag = false;
//根據食物的大小不同,蛇吃了增加到長度也將不同
switch (food.r / 5)
{
case 1:
snake.size += 1;
break;
case 2:
snake.size += 2;
break;
case 3:
snake.size += 3;
break;
default:
break;
}
}
if (!food.flag)
{
food.x = rand() % width;
food.y = rand() % heigth;
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.r = rand() % 15 + 5;
food.flag = true;
}
}
該局失敗界面化
這就可以直接上代碼啦!
void gameOver()
{
initgraph(width, heigth);
loadimage(&img3, "圖3.jpg", width, heigth);
putimage(0, 0, &img3);
}
總結
-
以上就是大致框架以及普通模式的邏輯和代碼了,其實很多地方還是有小問題的,就比如吃食物時會閃現一個點、邊界問題沒有做好、不能存檔讀檔等等,后面如果有時間的化,我會繼續優化,將越來越好的版本給分享出來,
-
然后雖然貪吃蛇是個簡單的小游戲,但是當我第一次寫出自己敲出的代碼,還是成就感滿滿,對C語言知識的理解其實也是一個很好的鞏固,
-
由于以上的代碼不是完整的,所以有需要的朋友可以去 我的 github 上查看完整代碼,希望大家喜歡咯!!!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/291734.html
標籤:其他
上一篇:基于EasyX的掃雷游戲
下一篇:影像處理-----基本運算
