C++入門指南之貪吃蛇游戲的實現(xiàn)
參考
- 《C和C++游戲趣味編程》
貪吃蛇游戲
鍵盤控制小蛇上、下、左、右移動,遲到食物后長度加1;蛇頭碰到自身或窗口邊緣,游戲失敗
程序框架
#include <graphics.h> #include <conio.h> #include <stdio.h> // 全局變量定義 void startup() // 初始化函數(shù) { } void show() // 繪制函數(shù) { } void updateWithoutInput() // 與輸入無關的更新 { } void updateWithInput() // 和輸入有關的更新 { } int main() { startup(); // 初始化函數(shù),僅執(zhí)行一次 while (1) { show(); // 進行繪制 updateWithoutInput(); // 和輸入無關的更新 updateWithInput(); // 和輸入有關的更新 } return 0; }
繪制游戲地圖和蛇
繪制網(wǎng)格狀的游戲地圖,使用二維數(shù)組Blocks存儲每個網(wǎng)格的信息。二維數(shù)組Blocks中也可以記錄蛇的信息。設定元素值為0表示空,畫出灰色的方格;元素值為1表示蛇頭,蛇頭后的蛇身依次為2、3、4、5等正整數(shù),畫出彩色的方格
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); } }
小蛇向右移動
假設小蛇初始元素值為54321,其中1位蛇頭,5432位蛇身。首先將二維數(shù)組中所有大于0的元素加1,得到65432;然后將最大值6變成0,即去除了原來的蛇尾;最后將2右邊的元素由0變成1,即實現(xiàn)了小蛇向右移動
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() // 與輸入無關的更新 { moveSnake(); Sleep(100); }
控制小蛇4個方向移動
變量oldHead_i、oldHead_j存儲移動前的蛇頭位置,newHead_i、newHead_j存儲移動后的蛇頭位置。小蛇向上移動,只需把新蛇頭的坐標設為舊蛇頭的上方即可
newHead_i = oldHead_i - 1;
讓玩家用A、S、D、W鍵控制游戲角色移動,定義字符變量moveDirection表示小蛇運動方向,在moveSnake函數(shù)中對其值進行判斷,取A向左運動、D向右運動、W向上運動、S向下運動:
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ù)讓小蛇向對應方向移動:
void updateWithInput() // 和輸入有關的更新 { if (_kbhit()) { char input = _getch(); if (input == 'A' || input == 'S' || input == 'D' || input == 'W') { moveDirection = input; moveSnake(); } } }
時間控制的改進
在Sleep()函數(shù)運行時,整個程序都會暫停,包括用戶輸入模塊。用戶會感覺到卡頓
利用靜態(tài)變量,將updateWithoutInput()修改如下:
void updateWithoutInput() // 與輸入無關的更新 { static int waitIndex = 1; waitIndex++; // 每一幀加1 if (waitIndex == 10) { moveSnake(); waitIndex = 1; } }
其中,updateWithoutInput()每次運行時,waitIndex加1,每隔10幀,才執(zhí)行一次移動函數(shù)moveSnake()。這樣可在不影響用戶按鍵輸入的情況下,降低小蛇的移動速度
失敗判斷與顯示
定義全局變量isFailure表示游戲是否失敗,初始化為0:
int isFailure = 0;
當小蛇碰到畫面邊界時,則認為游戲失?。划斏哳^與蛇身發(fā)生碰撞時,游戲也失敗。由于每次只有蛇頭是新生成的位置,所以在moveSnake()函數(shù)中只需判斷蛇頭是否越過邊界和碰撞:
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()中添加代碼,當isFailure為1時,直接返回:
void updateWithoutInput() // 與輸入無關的更新 { if (isFailure) { return; } //... }
在updateWithInput()中,只有當按下鍵盤且isFailure為0時,才進行相應的處理:
void updateWithInput() // 和輸入有關的更新 { 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ù)中在食物位置處繪制一個綠色小方塊:
setfillcolor(RGB(0, 255, 0)); fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE, (food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);
當新蛇頭碰到食物時,只需保留原蛇尾,即可讓蛇的長度加1。當吃到食物時,食物位置重新隨機出現(xiàn),蛇長度加1;當沒有遲到食物時,舊蛇尾變成空白,蛇長度保持不變:
Blocks[newHead_i][newHead_j] = 1;// 新蛇頭位置數(shù)值為1 if (newHead_i == food_i && newHead_j == food_j) // 如果新蛇頭碰到食物 { food_i = rand() % (HEIGHT - 5) + 2; // 食物重新隨機位置 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 // 每個小格子的長寬 #define HEIGHT 30 // 高度上一共30個小格子 #define WIDTH 40 // 寬度上一共40個小格子 // 全局變量定義 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; // 畫面中間畫蛇頭 for (i = 1; i <= 4; i++) // 向左依次4個蛇身 { 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(); // 開始批量繪制 } 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; // 存儲舊蛇 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; if (moveDirection == 'A') // 根據(jù)用戶按鍵,設定新蛇頭的位置 { 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; // 食物重新隨機位置 food_j = rand() % (WIDTH - 5) + 2; } else { Blocks[oldTail_i][oldTail_j] = 0; // 舊蛇尾變成空白 } } void updateWithoutInput() // 與輸入無關的更新 { if (isFailure) { return; } static int waitIndex = 1; waitIndex++; // 每一幀加1 if (waitIndex == 10) { moveSnake(); waitIndex = 1; } } void updateWithInput() // 和輸入有關的更新 { 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(); // 進行繪制 updateWithoutInput(); // 和輸入無關的更新 updateWithInput(); // 和輸入有關的更新 } return 0; }
總結
到此這篇關于C++入門指南之貪吃蛇游戲實現(xiàn)的文章就介紹到這了,更多相關C++實現(xiàn)貪吃蛇游戲內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C++中priority_queue模擬實現(xiàn)的代碼示例
在c++語言中數(shù)據(jù)結構中的堆結構可以通過STL庫中的priority_queue 優(yōu)先隊列來實現(xiàn),這樣做極大地簡化了我們的工作量,這篇文章主要給大家介紹了關于C++中priority_queue模擬實現(xiàn)的相關資料,需要的朋友可以參考下2021-08-08C語言數(shù)據(jù)結構之二叉鏈表創(chuàng)建二叉樹
這篇文章主要介紹了C語言數(shù)據(jù)結構之?二叉鏈表創(chuàng)建二叉樹,下文我們?yōu)榱烁奖愕氖褂枚鏄浣Y構體,可以使用?typedef?對結構體進行命名,具體內容需要的小伙伴可以參考一下2022-02-02C++實現(xiàn)LeetCode(205.同構字符串)
這篇文章主要介紹了C++實現(xiàn)LeetCode(205.同構字符串),本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下2021-07-07