目錄
引言
開發環境與工具準備
1. 開發環境配置
2. 資源文件準備
游戲設計與架構
1. 游戲核心數據結構
2. 游戲全局變量
游戲核心功能實現
1. 游戲初始化
2. 游戲主循環
3. 游戲渲染
4. 游戲狀態更新
關鍵游戲機制實現
1. 敵機生成系統
2. 碰撞檢測系統
3. 敵機銷毀邏輯
4. 子彈銷毀邏輯
游戲優化與擴展
1. 性能優化技巧
2. 游戲功能擴展
開發經驗與學習建議
1. 開發經驗總結
2. 學習建議
結語
引言
飛機大戰是一款經典的射擊游戲,玩家控制一架飛機在屏幕上移動并射擊敵機。本文將詳細介紹如何使用C語言和EasyX圖形庫開發一個完整的飛機大戰游戲。這個項目不僅適合C語言初學者學習游戲開發的基本概念,也展示了如何將編程基礎知識應用到實際項目中。
首先看一下運行效果:
開發環境與工具準備
1. 開發環境配置
開發飛機大戰游戲需要以下環境配置:
- ??Visual Studio??:推薦使用VS2019或更高版本,它提供了強大的代碼編輯、調試和項目管理功能。
- ??EasyX圖形庫??:這是一個簡單易用的Windows圖形編程庫,封裝了常用圖形操作函數,特別適合初學者。
- ??Windows SDK??:安裝Visual Studio時需要勾選此項。
安裝EasyX圖形庫非常簡單,只需訪問EasyX官網下載適配VS版本的安裝包,然后按照向導完成安裝即可。
2. 資源文件準備
游戲開發需要準備以下素材文件(網上圖片隨便一搜就有),存放在項目目錄的res文件夾中:
plane.png
- 玩家飛機圖片(50x50像素)enemy.png
- 敵機圖片(50x50像素)bullet.png
- 子彈圖片(10x20像素)bg.jpg
- 游戲背景圖片(600x700像素)boom.wav
- 爆炸音效文件
游戲設計與架構
1. 游戲核心數據結構
游戲主要使用以下數據結構:
typedef struct pos {int x;int y;
}POS; // 坐標類型typedef struct plane {POS planePos; // 飛機坐標POS planeBullets[BULLET_NUM]; // 子彈數組int bulletLen; // 當前子彈數量int bulletSpeed; // 子彈移動速度
}PLANE; // 飛機類型
POS
結構體用于表示游戲對象的二維坐標,PLANE
結構體則包含了飛機的位置、子彈數組及相關屬性。
2. 游戲全局變量
游戲使用以下全局變量管理狀態:
PLANE myPlane; // 玩家飛機
PLANE enemyPlanes[ENEMY_NUM]; // 敵機數組
int enemyPlaneLen; // 當前敵機數量
time_t startTime, endTime; // 用于控制敵機生成時間
IMAGE img[3]; // 存儲游戲圖片資源
int score = 0; // 游戲得分
這些變量分別管理玩家飛機、敵機、時間控制和游戲得分等核心游戲狀態。
游戲核心功能實現
1. 游戲初始化
initGame()
函數負責初始化游戲狀態:
void initGame() {initgraph(SCREEN_WIDTH, SCREEN_HEIGTH); // 初始化圖形窗口score = 0; // 重置得分srand((unsigned)time(NULL)); // 初始化隨機數種子// 初始化玩家飛機myPlane.bulletLen = 0;myPlane.bulletSpeed = 3;myPlane.planePos = {SCREEN_WIDTH/2 - PLANE_SIZE/2, SCREEN_HEIGTH - PLANE_SIZE};enemyPlaneLen = 0; // 重置敵機數量startTime = time(NULL); // 記錄開始時間
}
該函數設置了游戲窗口、隨機數種子、玩家飛機初始位置和游戲計時器等。
2. 游戲主循環
游戲采用經典的游戲循環結構:
while(1) {drawGame(); // 渲染游戲畫面updateGame(); // 更新游戲狀態Sleep(1000/60); // 控制幀率約60FPS
}
這個循環確保游戲以大約60幀每秒的速度運行,每次循環都先繪制畫面再更新游戲狀態。
3. 游戲渲染
drawGame()
函數負責繪制游戲畫面:
void drawGame() {BeginBatchDraw(); // 開始批量繪制// 繪制背景putimage(0, 0, &img[0]);// 繪制玩家飛機putimage(myPlane.planePos.x - PLANE_SIZE/2, myPlane.planePos.y - PLANE_SIZE/2, &img[2], SRCAND);// 繪制敵機for(int i = 0; i < enemyPlaneLen; i++) {putimage(enemyPlanes[i].planePos.x - PLANE_SIZE/2, enemyPlanes[i].planePos.y - PLANE_SIZE/2, &img[1], SRCAND);}// 繪制子彈for(int i = 0; i < myPlane.bulletLen; i++) {solidcircle(myPlane.planeBullets[i].x, myPlane.planeBullets[i].y, PLANE_SIZE/4);}// 繪制分數RECT rect = {0, PLANE_SIZE, SCREEN_WIDTH, SCREEN_HEIGTH};setbkmode(TRANSPARENT);char str[30] = {0};sprintf(str, "score:%d", score);drawtext(str, &rect, DT_TOP | DT_CENTER);EndBatchDraw(); // 結束批量繪制
}
該函數使用EasyX的批量繪制功能高效地繪制游戲畫面,包括背景、玩家飛機、敵機、子彈和分數顯示。
4. 游戲狀態更新
updateGame()
函數處理游戲邏輯更新:
void updateGame() {// 處理玩家輸入if(GetAsyncKeyState('W') & 0x8000) myPlane.planePos.y -= 4;if(GetAsyncKeyState('S') & 0x8000) myPlane.planePos.y += 4;if(GetAsyncKeyState('A') & 0x8000) myPlane.planePos.x -= 4;if(GetAsyncKeyState('D') & 0x8000) myPlane.planePos.x += 4;// 發射子彈if(_kbhit()) {if(_getch() == ' ') {if(myPlane.bulletLen < BULLET_NUM) {PlaySound("img/bullet.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NOWAIT);myPlane.planeBullets[myPlane.bulletLen] = myPlane.planePos;myPlane.bulletLen++;}}}// 更新敵機位置for(int i = 0; i < enemyPlaneLen; i++) {enemyPlanes[i].planePos.y += 2;}// 更新子彈位置for(int i = 0; i < myPlane.bulletLen; i++) {myPlane.planeBullets[i].y -= myPlane.bulletSpeed;}// 調用其他更新函數initEnemyPlane();destroyEnemyPlane();destroyBullet();
}
該函數處理玩家輸入、更新游戲對象位置,并調用其他輔助函數完成敵機生成和碰撞檢測等邏輯。
關鍵游戲機制實現
1. 敵機生成系統
initEnemyPlane()
函數控制敵機的生成:
void initEnemyPlane() {endTime = time(NULL);double elapsedTime = difftime(endTime, startTime);if(elapsedTime >= ENEMY_SPEED) {if(enemyPlaneLen < ENEMY_NUM) {int x = (rand() % (SCREEN_WIDTH - 2*PLANE_SIZE)) + PLANE_SIZE;int y = -PLANE_SIZE;enemyPlanes[enemyPlaneLen].planePos.x = x;enemyPlanes[enemyPlaneLen].planePos.y = y;enemyPlaneLen++;}startTime = endTime;}
}
該函數每隔ENEMY_SPEED
秒在屏幕頂部隨機位置生成一架新敵機,確保敵機數量不超過最大限制。
2. 碰撞檢測系統
游戲使用簡單的矩形碰撞檢測:
int areInierSecting(POS c1, POS c2, int radius) {return abs(c1.x - c2.x) <= radius && abs(c1.y - c2.y) <= radius;
}
該函數檢查兩個坐標點是否在指定半徑范圍內相交,用于檢測子彈與敵機、玩家與敵機的碰撞。
3. 敵機銷毀邏輯
destroyEnemyPlane()
處理敵機銷毀:
void destroyEnemyPlane() {for(int i = 0; i < enemyPlaneLen; i++) {// 檢測玩家與敵機碰撞if(areInierSecting(myPlane.planePos, enemyPlanes[i].planePos, PLANE_SIZE)) {if(IDYES == MessageBox(GetHWnd(), "游戲結束,是否重新開始?", "提示", MB_YESNO)) {initGame();} else {exit(0);}}// 敵機飛出屏幕if(enemyPlanes[i].planePos.y > SCREEN_HEIGTH) {for(int j = i; j < enemyPlaneLen; j++) {enemyPlanes[j] = enemyPlanes[j+1];}enemyPlaneLen--;i--;}}
}
該函數檢測玩家與敵機的碰撞(游戲結束)以及敵機飛出屏幕的情況(移除敵機)。
4. 子彈銷毀邏輯
destroyBullet()
處理子彈銷毀:
void destroyBullet() {for(int i = 0; i < myPlane.bulletLen; i++) {// 子彈與敵機碰撞for(int j = 0; j < enemyPlaneLen; j++) {if(areInierSecting(myPlane.planeBullets[i], enemyPlanes[j].planePos, PLANE_SIZE/4 + PLANE_SIZE/2)) {// 移除子彈和敵機for(int x = i; x < myPlane.bulletLen; x++) {myPlane.planeBullets[x] = myPlane.planeBullets[x+1];}for(int x = j; x < enemyPlaneLen; x++) {enemyPlanes[x] = enemyPlanes[x+1];}enemyPlaneLen--;myPlane.bulletLen--;j--;score += 100;break;}}// 子彈飛出屏幕if(myPlane.planeBullets[i].y < 0) {for(int x = i; x < myPlane.bulletLen; x++) {myPlane.planeBullets[x] = myPlane.planeBullets[x+1];}myPlane.bulletLen--;i--;}}
}
該函數處理子彈與敵機的碰撞(得分并移除雙方)以及子彈飛出屏幕的情況(移除子彈)。
游戲優化與擴展
1. 性能優化技巧
- ??批量繪制??:使用
BeginBatchDraw()
和EndBatchDraw()
減少屏幕刷新次數。 - ??幀率控制??:通過
Sleep(1000/60)
將游戲幀率控制在約60FPS。 - ??對象池技術??:預分配游戲對象(如子彈、敵機)避免頻繁內存分配。
2. 游戲功能擴展
這個基礎版本可以進一步擴展:
- ??多種敵機類型??:添加不同大小、生命值和移動模式的敵機。
- ??關卡系統??:設計多個關卡,逐漸增加難度。
- ??道具系統??:實現火力增強、生命恢復等道具。
- ??背景音樂??:添加游戲背景音樂和更多音效。
- ??高分記錄??:保存玩家最高分數。
- ??難度遞增??:隨著游戲進行逐漸提高敵機速度和生成頻率。
開發經驗與學習建議
1. 開發經驗總結
- ??模塊化設計??:將游戲功能劃分為初始化、渲染、更新等模塊,便于維護。
- ??逐步實現??:先實現核心功能(移動、射擊),再添加額外特性。
- ??調試技巧??:使用Visual Studio的調試工具檢查游戲狀態和變量值。
2. 學習建議
- ??掌握C語言基礎??:熟悉指針、結構體、數組等核心概念。
- ??學習EasyX圖形庫??:從簡單圖形繪制開始,逐步學習動畫和交互。
- ??分析開源項目??:研究其他游戲項目的代碼結構和設計思路。
- ??動手實踐??:通過修改和擴展現有項目來鞏固學習成果。
結語
通過這個飛機大戰游戲項目,我們展示了如何使用C語言和EasyX圖形庫開發一個完整的游戲。從游戲設計、數據結構到核心功能實現,這個項目涵蓋了游戲開發的關鍵概念和技術。
這個項目不僅適合C語言學習者鞏固基礎知識,也為有志于游戲開發的初學者提供了一個良好的起點。通過擴展和完善這個基礎框架,你可以進一步探索游戲開發的更多可能性。
希望本文能幫助你理解游戲開發的基本原理,并激發你創造更多有趣項目的熱情!完整的源代碼已在文中展示,你可以直接運行或基于此進行二次開發。