C++入門(mén)指南之貪吃蛇游戲的實(shí)現(xiàn)
參考
- 《C和C++游戲趣味編程》
貪吃蛇游戲
鍵盤(pán)控制小蛇上、下、左、右移動(dòng),遲到食物后長(zhǎng)度加1;蛇頭碰到自身或窗口邊緣,游戲失敗
程序框架
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
// 全局變量定義
void startup() // 初始化函數(shù)
{
}
void show() // 繪制函數(shù)
{
}
void updateWithoutInput() // 與輸入無(wú)關(guān)的更新
{
}
void updateWithInput() // 和輸入有關(guān)的更新
{
}
int main()
{
startup(); // 初始化函數(shù),僅執(zhí)行一次
while (1)
{
show(); // 進(jìn)行繪制
updateWithoutInput(); // 和輸入無(wú)關(guān)的更新
updateWithInput(); // 和輸入有關(guān)的更新
}
return 0;
}
繪制游戲地圖和蛇
繪制網(wǎng)格狀的游戲地圖,使用二維數(shù)組Blocks存儲(chǔ)每個(gè)網(wǎng)格的信息。二維數(shù)組Blocks中也可以記錄蛇的信息。設(shè)定元素值為0表示空,畫(huà)出灰色的方格;元素值為1表示蛇頭,蛇頭后的蛇身依次為2、3、4、5等正整數(shù),畫(huà)出彩色的方格
int i, j;
for (i = 0; i < HEIGHT; i++)
{
for (j = 0; j < WIDTH; j++)
{
if (Blocks[i][j] > 0)
{
setfillcolor(HSVtoRGB(Blocks[i][j] * 10, 0.9, 1));
}
else
{
setfillcolor(RGB(150, 150, 150));
}
fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
}
}

小蛇向右移動(dòng)
假設(shè)小蛇初始元素值為54321,其中1位蛇頭,5432位蛇身。首先將二維數(shù)組中所有大于0的元素加1,得到65432;然后將最大值6變成0,即去除了原來(lái)的蛇尾;最后將2右邊的元素由0變成1,即實(shí)現(xiàn)了小蛇向右移動(dòng)
void moveSnake()
{
int i, j;
for (i = 0; i < HEIGHT; i++)
{
for (j = 0; j < WIDTH; j++)
{
if (Blocks[i][j] > 0)
{
Blocks[i][j]++;
}
}
}
int oldTail_i, oldTail_j, oldHead_i, oldHead_j;
int max = 0;
for (i = 0; i < HEIGHT; i++)
{
for (j = 0; j < WIDTH; j++)
{
if (max < Blocks[i][j])
{
max = Blocks[i][j];
oldTail_i = i;
oldTail_j = j;
}
if (Blocks[i][j] == 2)
{
oldHead_i = i;
oldHead_j = j;
}
}
}
int newHead_i = oldHead_i;
int newHead_j = oldHead_j;
newHead_j = oldHead_j + 1;
Blocks[newHead_i][newHead_j] = 1;
Blocks[oldTail_i][oldTail_j] = 0;
}
void updateWithoutInput() // 與輸入無(wú)關(guān)的更新
{
moveSnake();
Sleep(100);
}
控制小蛇4個(gè)方向移動(dòng)
變量oldHead_i、oldHead_j存儲(chǔ)移動(dòng)前的蛇頭位置,newHead_i、newHead_j存儲(chǔ)移動(dòng)后的蛇頭位置。小蛇向上移動(dòng),只需把新蛇頭的坐標(biāo)設(shè)為舊蛇頭的上方即可
newHead_i = oldHead_i - 1;
讓玩家用A、S、D、W鍵控制游戲角色移動(dòng),定義字符變量moveDirection表示小蛇運(yùn)動(dòng)方向,在moveSnake函數(shù)中對(duì)其值進(jìn)行判斷,取A向左運(yùn)動(dòng)、D向右運(yùn)動(dòng)、W向上運(yùn)動(dòng)、S向下運(yùn)動(dòng):
if (moveDirection == 'A')
{
newHead_j = oldHead_j - 1;
}
else if (moveDirection == 'D')
{
newHead_j = oldHead_j + 1;
}
else if (moveDirection == 'W')
{
newHead_i = oldHead_i - 1;
}
else if (moveDirection == 'S')
{
newHead_i = oldHead_i + 1;
}
在updateWithInput()函數(shù)中獲得用戶按鍵輸入,如果是A、S、D、W鍵之一,就更新moveDirection變量,執(zhí)行moveSnake()函數(shù)讓小蛇向?qū)?yīng)方向移動(dòng):
void updateWithInput() // 和輸入有關(guān)的更新
{
if (_kbhit())
{
char input = _getch();
if (input == 'A' || input == 'S' || input == 'D' || input == 'W')
{
moveDirection = input;
moveSnake();
}
}
}
時(shí)間控制的改進(jìn)
在Sleep()函數(shù)運(yùn)行時(shí),整個(gè)程序都會(huì)暫停,包括用戶輸入模塊。用戶會(huì)感覺(jué)到卡頓
利用靜態(tài)變量,將updateWithoutInput()修改如下:
void updateWithoutInput() // 與輸入無(wú)關(guān)的更新
{
static int waitIndex = 1;
waitIndex++; // 每一幀加1
if (waitIndex == 10)
{
moveSnake();
waitIndex = 1;
}
}
其中,updateWithoutInput()每次運(yùn)行時(shí),waitIndex加1,每隔10幀,才執(zhí)行一次移動(dòng)函數(shù)moveSnake()。這樣可在不影響用戶按鍵輸入的情況下,降低小蛇的移動(dòng)速度
失敗判斷與顯示
定義全局變量isFailure表示游戲是否失敗,初始化為0:
int isFailure = 0;
當(dāng)小蛇碰到畫(huà)面邊界時(shí),則認(rèn)為游戲失?。划?dāng)蛇頭與蛇身發(fā)生碰撞時(shí),游戲也失敗。由于每次只有蛇頭是新生成的位置,所以在moveSnake()函數(shù)中只需判斷蛇頭是否越過(guò)邊界和碰撞:
if (newHead_i >= HEIGHT || newHead_i < 0 || newHead_j >= WIDTH || newHead_j < 0 || Blocks[newHead_i][newHead_j] > 0)
{
isFailure = 1;
return;
}
在show()函數(shù)中添加游戲失敗后的顯示信息:
if (isFailure) // 游戲失敗
{
setbkmode(TRANSPARENT); // 文字字體透明
settextcolor(RGB(255, 0, 0));
settextstyle(80, 0, _T("宋體"));
outtextxy(240, 220, _T("游戲失敗"));
}
在updateWithoutInput()中添加代碼,當(dāng)isFailure為1時(shí),直接返回:
void updateWithoutInput() // 與輸入無(wú)關(guān)的更新
{
if (isFailure)
{
return;
}
//...
}
在updateWithInput()中,只有當(dāng)按下鍵盤(pán)且isFailure為0時(shí),才進(jìn)行相應(yīng)的處理:
void updateWithInput() // 和輸入有關(guān)的更新
{
if (_kbhit() && isFailure == 0)
{
// ...
}
}
添加食物
添加全局變量記錄食物的位置:
int food_i, food_j;
在startup()函數(shù)中初始化食物的位置:
void startup() // 初始化函數(shù)
{
food_i = rand() % (HEIGHT - 5) + 2;
food_j = rand() % (WIDTH - 5) + 2;
}
在show()函數(shù)中在食物位置處繪制一個(gè)綠色小方塊:
setfillcolor(RGB(0, 255, 0)); fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE, (food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);
當(dāng)新蛇頭碰到食物時(shí),只需保留原蛇尾,即可讓蛇的長(zhǎng)度加1。當(dāng)吃到食物時(shí),食物位置重新隨機(jī)出現(xiàn),蛇長(zhǎng)度加1;當(dāng)沒(méi)有遲到食物時(shí),舊蛇尾變成空白,蛇長(zhǎng)度保持不變:
Blocks[newHead_i][newHead_j] = 1;// 新蛇頭位置數(shù)值為1
if (newHead_i == food_i && newHead_j == food_j) // 如果新蛇頭碰到食物
{
food_i = rand() % (HEIGHT - 5) + 2; // 食物重新隨機(jī)位置
food_j = rand() % (WIDTH - 5) + 2;
}
else
{
Blocks[oldTail_i][oldTail_j] = 0; // 舊蛇尾變成空白
}
完整代碼
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#define BLOCK_SIZE 20 // 每個(gè)小格子的長(zhǎng)寬
#define HEIGHT 30 // 高度上一共30個(gè)小格子
#define WIDTH 40 // 寬度上一共40個(gè)小格子
// 全局變量定義
int Blocks[HEIGHT][WIDTH] = { 0 };
char moveDirection;
int isFailure = 0;
int food_i, food_j; // 食物的位置
void startup() // 初始化函數(shù)
{
int i;
Blocks[HEIGHT / 2][WIDTH / 2] = 1; // 畫(huà)面中間畫(huà)蛇頭
for (i = 1; i <= 4; i++) // 向左依次4個(gè)蛇身
{
Blocks[HEIGHT / 2][WIDTH / 2 - i] = i + 1;
}
moveDirection = 'D';
food_i = rand() % (HEIGHT - 5) + 2;
food_j = rand() % (WIDTH - 5) + 2;
initgraph(WIDTH * BLOCK_SIZE, HEIGHT * BLOCK_SIZE);
setlinecolor(RGB(200, 200, 200));
BeginBatchDraw(); // 開(kāi)始批量繪制
}
void show() // 繪制函數(shù)
{
cleardevice();
int i, j;
for (i = 0; i < HEIGHT; i++)
{
for (j = 0; j < WIDTH; j++)
{
if (Blocks[i][j] > 0)
{
setfillcolor(HSVtoRGB(Blocks[i][j] * 10, 0.9, 1));
}
else
{
setfillcolor(RGB(150, 150, 150));
}
fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
}
}
setfillcolor(RGB(0, 255, 0)); // 食物顏色為綠色
fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE, (food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);
if (isFailure) // 游戲失敗
{
setbkmode(TRANSPARENT); // 文字字體透明
settextcolor(RGB(255, 0, 0));
settextstyle(80, 0, _T("宋體"));
outtextxy(240, 220, _T("游戲失敗"));
}
FlushBatchDraw(); // 批量繪制
}
void moveSnake()
{
int i, j;
for (i = 0; i < HEIGHT; i++)
{
for (j = 0; j < WIDTH; j++)
{
if (Blocks[i][j] > 0) // 大于0的為小蛇元素
{
Blocks[i][j]++;
}
}
}
int oldTail_i, oldTail_j, oldHead_i, oldHead_j; // 存儲(chǔ)舊蛇
int max = 0;
for (i = 0; i < HEIGHT; i++)
{
for (j = 0; j < WIDTH; j++)
{
if (max < Blocks[i][j])
{
max = Blocks[i][j];
oldTail_i = i;
oldTail_j = j;
}
if (Blocks[i][j] == 2) // 舊蛇頭
{
oldHead_i = i;
oldHead_j = j;
}
}
}
int newHead_i = oldHead_i; // 設(shè)定變量存儲(chǔ)新蛇頭
int newHead_j = oldHead_j;
if (moveDirection == 'A') // 根據(jù)用戶按鍵,設(shè)定新蛇頭的位置
{
newHead_j = oldHead_j - 1;
}
else if (moveDirection == 'D')
{
newHead_j = oldHead_j + 1;
}
else if (moveDirection == 'W')
{
newHead_i = oldHead_i - 1;
}
else if (moveDirection == 'S')
{
newHead_i = oldHead_i + 1;
}
if (newHead_i >= HEIGHT || newHead_i < 0 || newHead_j >= WIDTH || newHead_j < 0 || Blocks[newHead_i][newHead_j] > 0) // 失敗條件
{
isFailure = 1;
return;
}
Blocks[newHead_i][newHead_j] = 1; // 新蛇頭位置數(shù)值為1
if (newHead_i == food_i && newHead_j == food_j) // 如果新蛇頭碰到食物
{
food_i = rand() % (HEIGHT - 5) + 2; // 食物重新隨機(jī)位置
food_j = rand() % (WIDTH - 5) + 2;
}
else
{
Blocks[oldTail_i][oldTail_j] = 0; // 舊蛇尾變成空白
}
}
void updateWithoutInput() // 與輸入無(wú)關(guān)的更新
{
if (isFailure)
{
return;
}
static int waitIndex = 1;
waitIndex++; // 每一幀加1
if (waitIndex == 10)
{
moveSnake();
waitIndex = 1;
}
}
void updateWithInput() // 和輸入有關(guān)的更新
{
if (_kbhit() && isFailure == 0)
{
char input = _getch();
if (input == 'A' || input == 'S' || input == 'D' || input == 'W')
{
moveDirection = input;
moveSnake();
}
}
}
int main()
{
startup(); // 初始化函數(shù),僅執(zhí)行一次
while (1)
{
show(); // 進(jìn)行繪制
updateWithoutInput(); // 和輸入無(wú)關(guān)的更新
updateWithInput(); // 和輸入有關(guān)的更新
}
return 0;
}
總結(jié)
到此這篇關(guān)于C++入門(mén)指南之貪吃蛇游戲?qū)崿F(xiàn)的文章就介紹到這了,更多相關(guān)C++實(shí)現(xiàn)貪吃蛇游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++ 情懷游戲貪吃蛇的實(shí)現(xiàn)流程詳解
- 利用C/C++實(shí)現(xiàn)貪吃蛇游戲
- C++實(shí)現(xiàn)簡(jiǎn)易貪吃蛇游戲
- C++實(shí)現(xiàn)簡(jiǎn)單貪吃蛇小游戲
- C++實(shí)現(xiàn)貪吃蛇游戲
- C++代碼實(shí)現(xiàn)貪吃蛇小游戲
- 基于easyx的C++實(shí)現(xiàn)貪吃蛇
- C++控制臺(tái)循環(huán)鏈表實(shí)現(xiàn)貪吃蛇
- C++控制臺(tái)實(shí)現(xiàn)貪吃蛇游戲
- C++通過(guò)類(lèi)實(shí)現(xiàn)控制臺(tái)貪吃蛇
- C++結(jié)構(gòu)體數(shù)組實(shí)現(xiàn)貪吃蛇
- c++實(shí)現(xiàn)超簡(jiǎn)單的貪吃蛇游戲?qū)嵗榻B
相關(guān)文章
關(guān)于vs strcpy_s()和strcat_s()用法探究
這篇文章主要介紹了關(guān)于vs strcpy_s()strcat_s()用法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
Qt網(wǎng)絡(luò)編程之TCP通信及常見(jiàn)問(wèn)題
這篇文章主要為大家詳細(xì)介紹了Qt網(wǎng)絡(luò)編程之TCP通信及常見(jiàn)問(wèn)題,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
C語(yǔ)言版約瑟夫問(wèn)題算法實(shí)現(xiàn)
大家好,本篇文章主要講的是C語(yǔ)言版約瑟夫問(wèn)題算法實(shí)現(xiàn),感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你又幫助的話記得收藏一下,方便下次瀏覽2021-12-12
詳細(xì)解析C語(yǔ)言中的開(kāi)方實(shí)現(xiàn)
這篇文章主要介紹了詳細(xì)解析C語(yǔ)言中的開(kāi)方實(shí)現(xiàn),包括一道要求精度的整數(shù)開(kāi)方的題目,需要的朋友可以參考下2015-08-08

