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