您好,登錄后才能下訂單哦!
這篇“怎么用C語言實現(xiàn)開發(fā)飛機游戲”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“怎么用C語言實現(xiàn)開發(fā)飛機游戲”文章吧。
[設計難度 : ★☆☆☆☆
[參考書籍:《C語言課程設計與游戲開發(fā)實踐教程》
[主要涉及知識:函數(shù)封裝 + 循環(huán)判斷語句
[程序運行效果圖:
[主要的游戲功能:
通過按鍵’w’,‘s’,‘a’,'d’分別實現(xiàn)飛機的上下左右移動
按空格鍵發(fā)射子彈
按ESC實現(xiàn)游戲暫停
按q鍵返回菜單界面
實現(xiàn)子彈和敵機位置的自動更新
敵機的生成速度和下落速度隨分數(shù)的增加而變快
實時打印得分和生命值。生命值為0時游戲結束
以下為飛機游戲全部的代碼,大家可以直接拷貝運行:
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <windows.h> #include <conio.h> #include <time.h> #define height 25 //設置游戲邊界 #define width 50 #define enemy_max 5 enum Option //枚舉增加代碼可讀性 { EXIT, PLAY, GUIDE, }; enum Condition //表示游戲幕布上的情況 { backspace, enemy, bullet, }; int canvas[height][width]; //游戲幕布存儲對應的信息 int score; int x, y; //飛機頭部坐標 int Std_Speed; //敵機標準下落速度 int Std_Time; //敵機生成的標準速度 int HP; //玩家生命值 int enemy_num; int times; void gotoxy(int x, int y) //清屏函數(shù) { HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = x; pos.Y = y; SetConsoleCursorPosition(handle, pos); } void HideCursor() //光標隱藏函數(shù) { CONSOLE_CURSOR_INFO cursor_info = { 1, 0 }; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); } void Initgame() { for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) //將幕布上先初始化為空格 canvas[i][j] = backspace; } HP = 3; score = 0; x = width / 2; //初始化飛機位置 y = height / 2; enemy_num = 0; Std_Speed = 60; Std_Time = 60; } void show() { gotoxy(0, 0); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (i == y && j == x) //打印飛機 printf("*"); else if (i == y + 1 && j == x - 2) { printf("*****"); j += 4; } else if (i == y + 2 && j == x - 1) { printf("* *"); j += 2; } else if (canvas[i][j] == bullet) // 打印子彈 printf("|"); else if (canvas[i][j] == enemy) printf("@"); else printf(" "); } printf("|\n"); //打印游戲邊框 } for (int j = 0; j < width; j++) //打印游戲邊框 printf("-"); printf("\n[得分:>%d\n", score); //打印游戲分數(shù)和血量 printf("[生命值:>%d\n", HP); } int updateWithinput() { if (_kbhit()) { int input = _getch(); switch (input) { case 'w': if (y > 0) //防止飛機飛出游戲邊界 y--; break; case 's': if (y < height - 3) y++; break; case 'a': if (x > 2) x--; break; case 'd': if (x < width - 3) x++; break; case 27: system("pause"); break; //ESC的ascll碼值為27 case ' ': if (y > 0) canvas[y - 1][x] = bullet; break; case 'q': return 1; //退出游戲 } } return 0; } int enemy_update() { static int enemy_speed = 0; static int enemy_time = 0; int flag = 0; if (enemy_speed < Std_Speed) //依靠循環(huán)來減速 enemy_speed++; if (enemy_time < Std_Time) enemy_time++; if (enemy_num < enemy_max && enemy_time >= Std_Time) { int i, j; do { i = rand() % (height / 5); j = rand() % (width - 4) + 2; //j的范圍:[2, width - 3] } while (canvas[i][j] != backspace); canvas[i][j] = enemy; enemy_num++; enemy_time = 0; } if (enemy_speed >= Std_Speed) { flag = 1; enemy_speed = 0; } for (int i = height - 1; i >= 0; i--) { for (int j = width - 1; j >= 0; j--) { if (canvas[i][j] == enemy) //遇到敵機的情況 { if (i == height - 1) //敵機飛到邊界 { score--; HP--; if (HP == 0) return 1; enemy_num--; canvas[i][j] = backspace; } else if (i < height - 1 && canvas[i + 1][j] == bullet)//檢測是否被子彈擊中 { score++; printf("\a"); enemy_num--; if (score % 5 == 0 && Std_Speed >= 12) //分數(shù)到達一定程度后下落加快,生成加快 { Std_Speed -= 3; //下落加快 Std_Time -= 3; //生成速度加快 } canvas[i][j] = backspace; } else if (flag) //flag為1更新敵機位置 { canvas[i + 1][j] = enemy; canvas[i][j] = backspace; } } } } return 0; } void bullet_update() { for (int i = 0; i < height; i++) //控制子彈的移動 { for (int j = 0; j < width; j++) { if (canvas[i][j] == bullet) { if (i > 0 && canvas[i - 1][j] == enemy) { score++; printf("\a"); enemy_num--; if (score % 5 == 0 && Std_Speed >= 6) //分數(shù)到達一定程度后下落加快,生成加快 { Std_Speed -= 3; //下落加快 Std_Time -= 3; //生成速度加快 } canvas[i - 1][j] = bullet; } else if (i > 0) canvas[i - 1][j] = bullet; canvas[i][j] = backspace; } } } } void gamebody() { system("cls"); Initgame(); HideCursor(); srand((unsigned int)time(NULL)); while (1) { show(); bullet_update(); if (updateWithinput() || enemy_update()) { show(); printf("[本次游戲結束:>"); system("pause"); break; } } } void menu() { printf("*****************\n"); printf("** 飛機游戲 **\n"); printf("**-------------**\n"); printf("** 1.PLAY **\n"); printf("** 2.GUIDE **\n"); printf("** 0.EXIT **\n"); printf("*****************\n"); } void guide() { printf("******************\n"); printf("** 游戲操作指南 **\n"); printf("**--------------**\n"); printf("** w->上移 **\n"); printf("** s->下移 **\n"); printf("** a->左移 **\n"); printf("** d->右移 **\n"); printf("** q->返回 **\n"); printf("** ESC->暫停 **\n"); printf("** 空格->射擊 **\n"); printf("******************\n\n\n"); } int main() { int input = 0; do { menu(); printf("[請選擇:>"); scanf("%d", &input); switch (input) { case PLAY: gamebody(); break; case GUIDE: guide(); break; case EXIT: printf("成功退出游戲!\n"); break; default: printf("輸入錯誤,請重新選擇\n"); } } while (input); return 0; }
如果覺得還挺有意思的,那就繼續(xù)保持著輕松的心情看下去吧!
一個基本的游戲初始選擇框架:
int main() { int input = 0; do { menu(); printf("[請選擇:>"); scanf("%d", &input); switch (input) { case xxx: case xxx: case xxx: default: } }while (input); return 0; }
我們根據(jù)游戲所包含的功能設計好相應的menu選項以及其對應的case事件即可。作為我們飛機游戲的第一個簡單版本,我們先不考慮其他的模式和功能,僅包含PLAY
(游戲)功能、GUIDE
(操作說明)、EXIT
(退出游戲)三種功能。根據(jù)這個思路,我們寫下這樣的menu函數(shù)
void menu() { printf("*****************\n"); printf("** 飛機游戲 **\n"); printf("**-------------**\n"); printf("** 1.PLAY **\n"); printf("** 2.GUIDE **\n"); printf("** 0.EXIT **\n"); printf("*****************\n"); }
為了增加代碼的可讀性
,我們在頭文件處創(chuàng)建枚舉變量。
enum Option //枚舉增加代碼可讀性 { EXIT, // printf("%d", EXIT);的結果為 0 PLAY, // printf("%d", PLAY);的結果為 1 GUIDE, // printf("%d", GUIDE);的結果為 2 };
每個枚舉常量都是有值的,第一個枚舉成員的值默認為0(不人為修改的話),之后的隨前一個遞增。這恰好與我們的menu中功能序號相對應,于是我們可以用枚舉變量作為case的整形常量表達語句,最終寫出的主函數(shù)是這樣的:
int main() { int input = 0; srand((unsigned int)time(NULL)); //初始化rand函數(shù),只需要初始化一次即可,所以放在主函數(shù)內 do { menu(); printf("[請選擇:>"); scanf("%d", &input); switch (input) { case PLAY: gamebody(); break; case GUIDE: guide(); break; case EXIT: printf("成功退出游戲!\n"); break; default: printf("輸入錯誤,請重新選擇\n"); } }while (input); return 0; }
說明按鍵對應的功能,很簡單就不贅述了
void guide() { printf("******************\n"); printf("** 游戲操作指南 **\n"); printf("**--------------**\n"); printf("** w->上移 **\n"); printf("** s->下移 **\n"); printf("** a->左移 **\n"); printf("** d->右移 **\n"); printf("** q->返回 **\n"); printf("** ESC->暫停 **\n"); printf("** 空格->射擊 **\n"); printf("******************\n\n\n"); }
void gamebody() { Initgame(); //初始化游戲函數(shù) while(1) { show(); //展示函數(shù) updateWithInput(); //與用戶輸入有關的更新, updateWithoutInput();//與用戶輸入無關的更新,如子彈、敵機的移動 } }
以這個游戲框架為基礎,我們建立起我們的設計邏輯
在正式介紹gamebody函數(shù)之前,我們先看看定義在頭文件的全局變量以及他們的作用
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <windows.h> #include <time.h> #include <conio.h> //需要用到的函數(shù)頭文件 #define height 25 //宏定義游戲邊界的高度 #define width 50 //宏定義游戲邊界的寬度 #define enemy_max 5 //宏定義敵人的最多數(shù)量 enum Option //枚舉增加代碼可讀性 { EXIT, PLAY, GUIDE, }; enum Condition //表示游戲幕布上的情況 { backspace, //空 enemy, //敵人 bullet, //子彈 }; int canvas[height][width]; //游戲幕布存儲對應位置上的Condition信息 int score; //記錄游戲分數(shù) int x, y; //飛機頭部的xy坐標 int Std_Speed; //敵機標準下落速度,與之后的加速下落有關 int Std_Time; //敵機生成的標準速度,與之后的加速生成有關 int HP; //玩家生命值 int enemy_num; //此時的敵機數(shù)量 void gamebody();
我們的游戲畫面完全是靠printf函數(shù)打印出來的,因此清屏函數(shù)是必不可少的。
直接使用system("cls")
函數(shù)會造成屏幕畫面閃爍嚴重,因此我們可以自行封裝一個gotoxy
函數(shù),函數(shù)的功能是將光標移到原點,從原點開始重新繪制
,相當于實現(xiàn)清屏的效果。雖然還是會閃爍,但防屏閃效果有了顯著提升。
首先給大家介紹幾個平時不常用的函數(shù):
①SetConsoleCursorPosition
頭文件:#include<windows.h>
參數(shù)①:hConssoleOutput → 指向屏幕緩沖區(qū)的句柄
參數(shù)②:dwCursorPosition → 指定包含新光標位置的COORD結構
函數(shù)功能:設置光標在指定的控制臺屏幕緩沖區(qū)中的位置
COORD結構體:
②GetStdHandle函數(shù)
頭文件:#include<windows.h>
參數(shù)①:nStdHandle 指定返回句柄的標準設備,nStdHandle有以下三種選擇
val | Meanig |
---|---|
STD_INPUT_HANDLE | Standard input handle |
STD_OUTPUT_HANDLE | Standard output handle |
STD_ERROR_HANDLE | Standard error handle |
函數(shù)功能:獲取標準輸入、標準輸出或標準錯誤設備的句柄
什么是句柄?
將①②函數(shù)組合后就可以構造出我們需要的gotoxy函數(shù)了
void gotoxy(int x, int y) { HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = x; pos.Y = y; SetConsoleCursorPosition(handle, pos); }
光標一直閃爍會影響我們的游戲體驗,所以我們封裝一個HideCursor函數(shù)。光標的信息定義在CONSOLE_CURSOR_INFO結構體中,其具體定義如下:
dwSize
結構體成員指定這光標的大小,bVisible
決定光標是否可見,因此我們只需對將它設置為false即可。實際的修改還需要借助SetConsoleCursorInfo
函數(shù)
參數(shù)①:標準輸出設備的句柄,我們可以用上面提到的GetStdHandle函數(shù)獲取
參數(shù)②:CONSOLE CURSOR INFO結構體類型的指針,該結構體包含屏幕緩沖區(qū)新規(guī)范 有了上面的知識,我們可以寫下這樣的代碼:
void HideCursor() { CONSOLE_CURSOR_INFO cursor_info = {1, 0};//將“是否可見”設置為false SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); }
因為我們使用了全局變量,并且要求設計出來的游戲能能夠重復的play,所以我們在每次游戲開始時都要對全局變量進行必要的 初始化
void Initgame() { for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) //將幕布首先初始化為空格 canvas[i][j] = backspace; } HP = 3; score = 0; x = width / 2; //初始化飛機位置 y = height / 2; enemy_num = 0; Std_Speed = 60; //初始化“標準下落速度” Std_Time = 60; //初始化“標準生成速度” }
遍歷canvas數(shù)組,根據(jù)canvas數(shù)組中的內容決定打印什么:
backspace → 空格
enemy → 敵機(@)
bullet → 子彈(|)
void show() { gotoxy(0, 0); //在打印之前先清屏 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (i == y && j == x) //打印飛機 printf("*"); else if (i == y + 1 && j == x - 2) { printf("*****"); j += 4; } else if (i == y + 2 && j == x - 1) { printf("* *"); j += 2; } else if (canvas[i][j] == bullet) // 打印子彈 printf("|"); else if (canvas[i][j] == enemy) printf("@"); else printf(" "); } printf("|\n"); //打印游戲右邊框 } for (int j = 0; j < width; j++) //打印游戲底邊框 printf("-"); printf("\n[得分:>%d\n", score); //打印游戲分數(shù)和血量 printf("[生命值:>%d\n", HP); }
[設計難點:
當我們鍵盤沒有輸入的時候,函數(shù)不執(zhí)行效果·;
當我們按下相應的游戲按鍵而不需要按下回車時,數(shù)據(jù)就可以被讀取
現(xiàn)在介紹兩個大家平時可能不常用到的函數(shù)來滿足我們上面的設計要求:
_kbhit
函數(shù)用來監(jiān)測鍵盤是否有輸入,如果有輸入則返回一個非0值。
即使沒有按下回車鍵,_getch
函數(shù)可以從控制臺中讀取字符
有了上面的基礎知識儲備,我們來實現(xiàn)updateWithinput函數(shù)
int updateWithinput() { if (_kbhit()) { int input = _getch(); switch (input) { case 'w': if(y > 0) //先要加以判斷,防止飛機飛出游戲邊界 y--; break; case 's': if (y < height - 3) y++; break; case 'a': if (x > 2) x--; break; case 'd': if (x < width - 3) x++; break; case 27: system("pause"); break; //ESC的ascll碼值為27 case ' ': if(y > 0) canvas[y - 1][x] = bullet; break; case 'q': return 1; //退出游戲 } } return 0; //我們根據(jù)返回值判斷是否需要退出游戲 }
我們將updateWithoutinput函數(shù)拆分成對子彈位置更新的函數(shù)和對敵機位置更新的函數(shù)。子彈的位置是實時更新的:
void bullet_update() { for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (canvas[i][j] == bullet) { if (i > 0 && canvas[i - 1][j] == enemy) //檢測是否擊中敵機 { score++; printf("\a"); enemy_num--; if (score % 5 == 0 && Std_Speed >= 6) //分數(shù)到達一定程度后下落加快,生成加快 { Std_Speed -= 3; //下落加快 Std_Time -= 3; //生成速度加快 } canvas[i - 1][j] = bullet; } else if (i > 0) canvas[i - 1][j] = bullet; canvas[i][j] = backspace; } } } }
與子彈稍微不同的一點是敵機的位置更新受“標準速度”的限制,我們通過循環(huán)
實現(xiàn)敵機的速度控制,但每次仍需要檢測是否和子彈相撞。由于敵機是向y增大的方向上運動的,若for正向循環(huán)則,則敵機一直被往前推,視覺上是“瞬移”的效果,所以我們需要反向遍歷。
int enemy_update() { static int enemy_speed = 0; static int enemy_time = 0; int flag = 0; if (enemy_speed < Std_Speed) //依靠循環(huán)來減速 enemy_speed++; if (enemy_time < Std_Time) enemy_time++; if (enemy_num < enemy_max && enemy_time >= Std_Time) { int i, j; do { i = rand() % (height / 5); j = rand() % (width - 4) + 2; //j的范圍:[2, width - 3] } while (canvas[i][j] != backspace); canvas[i][j] = enemy; enemy_num++; enemy_time = 0; } if (enemy_speed >= Std_Speed) { flag = 1; enemy_speed = 0; } for (int i = height - 1; i >= 0; i--) { for (int j = width - 1; j >= 0; j--) { if (canvas[i][j] == enemy) //遇到敵機的情況 { if (i == height - 1) //敵機飛到邊界 { score--; HP--; if (HP == 0) return 1; enemy_num--; canvas[i][j] = backspace; } else if (i < height - 1 && canvas[i+1][j] == bullet)//檢測是否被子彈擊中 { score++; printf("\a"); enemy_num--; if (score % 5 == 0 && Std_Speed >= 12) //分數(shù)到達一定程度后下落加快,生成加快 { Std_Speed -= 3; //下落加快 Std_Time -= 3; //生成速度加快 } canvas[i][j] = backspace; } else if (flag) //flag為1更新敵機位置 { canvas[i + 1][j] = enemy; canvas[i][j] = backspace; } } } } return 0; }
void gamebody() { system("cls"); Initgame(); HideCursor(); while (1) { show(); bullet_update(); if (updateWithinput() || enemy_update()) { show(); printf("[本次游戲結束:>"); system("pause"); break; } } }
以上就是關于“怎么用C語言實現(xiàn)開發(fā)飛機游戲”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。