C++學習教程,QT飛機大戰教程(含詳細步驟教程二)

7 子彈製作

製作步驟如下:

  • 創建子彈文件和類
  • 添加子彈類中的成員函數和成員屬性
  • 實現成員函數
  • 測試子彈

7.1 創建子彈文件和類

創建Bullet類以及生成對應的文件

創建好後生成bullet.h 和 bullet.cpp兩個文件

C++學習教程,QT飛機大戰教程(含詳細步驟教程二)

7.2 子彈的成員函數和成員屬性

在Bullet.h中添加代碼

<code>#ifndef BULLET_H
#define BULLET_H
#include "config.h"
#include <qpixmap>

class Bullet
{
public:
Bullet();

//更新子彈座標
void updatePosition();

public:
//子彈資源對象
QPixmap m_Bullet;
//子彈座標
int m_X;
int m_Y;
//子彈移動速度
int m_Speed;
//子彈是否閒置
bool m_Free;
//子彈的矩形邊框(用於碰撞檢測)
QRect m_Rect;
};

#endif // BULLET_H
/<qpixmap>/<code>

7.3 子彈類成員函數實現

在config.h中追加子彈配置信息

<code>/**********  子彈配置數據 **********/
#define BULLET_PATH ":/res/bullet_11.png" //子彈圖片路徑

#define BULLET_SPEED 5 //子彈移動速度
/<code>

在bullet.cpp中實現成員函數,代碼如下:

<code>#include "bullet.h"

Bullet::Bullet()
{
//加載子彈資源
m_Bullet.load(BULLET_PATH);

//子彈座標 初始座標可隨意設置,後期會重置
m_X = GAME_WIDTH*0.5 - m_Bullet.width()*0.5;
m_Y = GAME_HEIGHT;

//子彈狀態
m_Free = true;

//子彈速度
m_Speed = BULLET_SPEED;

//子彈矩形框
m_Rect.setWidth(m_Bullet.width());
m_Rect.setHeight(m_Bullet.height());
m_Rect.moveTo(m_X,m_Y);
}
void Bullet::updatePosition()
{
//如果子彈是空閒狀態,不需要座標計算
//玩家飛機可以控制子彈的空閒狀態為false
if(m_Free)
{
return;
}

//子彈向上移動
m_Y -= m_Speed;
m_Rect.moveTo(m_X,m_Y);

if(m_Y <= -m_Rect.height())

{
m_Free = true;
}
}
/<code>

7.4 測試子彈

子彈本身應該由飛機發射,測試階段我們寫一段輔助代碼,看看效果即可

測試過後,這些代碼可以刪除掉

在MainScene.h中添加測試代碼

<code>//測試子彈代碼
Bullet temp_bullet;
/<code>

在MainScene.cpp中的updatePosition裡添加測試代碼

<code>//測試子彈代碼
temp_bullet.m_Free = false;
temp_bullet.updatePosition();
/<code>

在MainScene.cpp中的paintEvent裡添加測試代碼

<code>//測試子彈代碼
painter.drawPixmap(temp_bullet.m_X,temp_bullet.m_Y,temp_bullet.m_Bullet);
/<code>

運行程序,此時會有一發子彈從屏幕中射出

C++學習教程,QT飛機大戰教程(含詳細步驟教程二)

測試完畢後,測試代碼刪除或註釋即可

8 玩家發射子彈

玩家發射子彈製作步驟如下:

  • 英雄飛機添加新的成員屬性
  • 實現發射成員函數
  • 主場景控制子彈發射

8.1 飛機添加新成員屬性

在config.h中添加新的配置數據

<code>#define BULLET_NUM 30   //彈匣中子彈總數
#define BULLET_INTERVAL 20 //發射子彈時間間隔
/<code>

在HeroPlane.h中新增成員屬性如下:

<code>//彈匣
Bullet m_bullets[BULLET_NUM];

//發射間隔記錄
int m_recorder;
/<code>

8.2 成員函數補充

在構造函數 HeroPlane 中初始化發生間隔記錄

<code>//初始化發射間隔記錄
m_recorder = 0;
/<code>

之前在英雄飛機類中預留的一個shoot函數我們進行實現,代碼如下:

<code>void HeroPlane::shoot()
{
//累加時間間隔記錄變量
m_recorder++;
//判斷如果記錄數字 未達到發射間隔,直接return
if(m_recorder < BULLET_INTERVAL)
{
return;
}
//到達發射時間處理
//重置發射時間間隔記錄
m_recorder = 0;

//發射子彈
for(int i = 0 ; i < BULLET_NUM;i++)
{
//如果是空閒的子彈進行發射
if(m_bullets[i].m_Free)
{
//將改子彈空閒狀態改為假
m_bullets[i].m_Free = false;
//設置發射的子彈座標
m_bullets[i].m_X = m_X + m_Rect.width()*0.5 - 10;
m_bullets[i].m_Y = m_Y - 25 ;
break;
}
}
}
/<code>

8.3 主場景中實現發射子彈

在MainScene.cpp的updatePosition成員函數中追加如下代碼

<code>//發射子彈
m_hero.shoot();
//計算子彈座標
for(int i = 0 ;i < BULLET_NUM;i++)
{
//如果子彈狀態為非空閒,計算發射位置
if(!m_hero.m_bullets[i].m_Free)
{
m_hero.m_bullets[i].updatePosition();
}
}
/<code>

在MainScene.cpp的paintEvent成員函數中追加如下代碼:

<code>//繪製子彈
for(int i = 0 ;i < BULLET_NUM;i++)
{
//如果子彈狀態為非空閒,計算發射位置
if(!m_hero.m_bullets[i].m_Free)
{
painter.drawPixmap(m_hero.m_bullets[i].m_X,m_hero.m_bullets[i].m_Y,m_hero.m_bullets[i].m_Bullet);
}
}
/<code>

測試運行,玩家可以發射子彈

C++學習教程,QT飛機大戰教程(含詳細步驟教程二)

9 敵機制作

敵機制作與子彈製作原理類似,也是每隔一定的時間讓敵機出場

製作步驟如下:

  • 創建敵機文件和類
  • 添加敵機類中的成員函數和成員屬性
  • 實現成員函數
  • 敵機出場
  • 測試敵機

9.1 創建敵機文件和類

創建EnemyPlane類以及生成對應的文件

創建好後生成enemyPlane.h 和 enemyPlane.cpp兩個文件

C++學習教程,QT飛機大戰教程(含詳細步驟教程二)

9.2 敵機成員函數和成員屬性

在enemyPlane.h中添加如下代碼:

<code>#ifndef ENEMYPLANE_H
#define ENEMYPLANE_H
#include <qpixmap>

class EnemyPlane
{
public:
EnemyPlane();

//更新座標
void updatePosition();
public:
//敵機資源對象
QPixmap m_enemy;

//位置
int m_X;
int m_Y;

//敵機的矩形邊框(碰撞檢測)
QRect m_Rect;

//狀態
bool m_Free;

//速度
int m_Speed;
};

#endif // ENEMYPLANE_H
/<qpixmap>/<code>

9.3 敵機成員函數實現

在config.h中追加敵機配置信息

<code>/**********  敵機配置數據 **********/ 

#define ENEMY_PATH ":/res/img-plane_5.png" //敵機資源圖片
#define ENEMY_SPEED 5 //敵機移動速度
#define ENEMY_NUM 20 //敵機總數量
#define ENEMY_INTERVAL 30 //敵機出場時間間隔
/<code>

在enemyPlane.cpp中實現成員函數,代碼如下:

<code>EnemyPlane::EnemyPlane()
{
//敵機資源加載
m_enemy.load(ENEMY_PATH);

//敵機位置
m_X = 0;
m_Y = 0;

//敵機狀態
m_Free = true;

//敵機速度
m_Speed = ENEMY_SPEED;

//敵機矩形
m_Rect.setWidth(m_enemy.width());
m_Rect.setHeight(m_enemy.height());
m_Rect.moveTo(m_X,m_Y);
}

void EnemyPlane::updatePosition()
{
//空閒狀態,不計算座標
if(m_Free)
{
return;
}

m_Y += m_Speed;
m_Rect.moveTo(m_X,m_Y);

if(m_Y >= GAME_HEIGHT + m_Rect.height())
{
m_Free = true;

}
}
/<code>

9.4 敵機出場

在MainScene.h中追加敵機出場的成員函數

在MainScene.h中追加敵機數組 和 敵機出場間隔記錄 的成員屬性

<code>//敵機出場
void enemyToScene();

//敵機數組
EnemyPlane m_enemys[ENEMY_NUM];

//敵機出場間隔記錄
int m_recorder;
/<code>

初始化間隔記錄屬性,在MainScene.cpp的 initScene 成員函數中追加

<code>m_recorder = 0;
/<code>

實現成員函數 enemyToScene

<code>void MainScene::enemyToScene()
{
m_recorder++;
if(m_recorder < ENEMY_INTERVAL)
{
return;
}

m_recorder = 0;

for(int i = 0 ; i< ENEMY_NUM;i++)
{
if(m_enemys[i].m_Free)
{
//敵機空閒狀態改為false

m_enemys[i].m_Free = false;
//設置座標
m_enemys[i].m_X = rand() % (GAME_WIDTH - m_enemys[i].m_Rect.width());
m_enemys[i].m_Y = -m_enemys[i].m_Rect.height();
break;
}
}
}
/<code>

在PlayGame成員函數的timeout信號發送時候,槽函數中首先追加 enemyToScene

<code>//敵機出場
enemyToScene();
/<code>
C++學習教程,QT飛機大戰教程(含詳細步驟教程二)

更新敵機座標,在updatePosition成員函數中追加代碼

<code>//敵機座標計算
for(int i = 0 ; i< ENEMY_NUM;i++)
{
//非空閒敵機 更新座標
if(m_enemys[i].m_Free == false)
{
m_enemys[i].updatePosition();
}
}
/<code>

繪製敵機,在paintEvent成員函數中追加繪製敵機代碼

<code>//繪製敵機
for(int i = 0 ; i< ENEMY_NUM;i++)
{
if(m_enemys[i].m_Free == false)
{
painter.drawPixmap(m_enemys[i].m_X,m_enemys[i].m_Y,m_enemys[i].m_enemy);
}
}
/<code>

添加隨機數種子

在MainScene.cpp中 initScene 成員函數里添加隨機數種子

<code>//隨機數種子
srand((unsigned int)time(NULL)); //頭文件 #include <ctime>
/<ctime>/<code>

運行測試敵機出場效果

C++學習教程,QT飛機大戰教程(含詳細步驟教程二)

10 碰撞檢測

實現碰撞檢測步驟如下:

  • 添加並實現碰撞檢測成員函數
  • 調用並測試函數

10.1 添加並實現碰撞檢測函數

在MainScene.h中添加新的成員函數

<code>void collisionDetection();
/<code>

在MainScene.cpp中實現該成員函數

<code>void MainScene::collisionDetection()
{
//遍歷所有非空閒的敵機
for(int i = 0 ;i < ENEMY_NUM;i++)
{
if(m_enemys[i].m_Free)
{
//空閒飛機 跳轉下一次循環
continue;
}

//遍歷所有 非空閒的子彈
for(int j = 0 ; j < BULLET_NUM;j++)
{
if(m_hero.m_bullets[j].m_Free)
{
//空閒子彈 跳轉下一次循環
continue;
}


//如果子彈矩形框和敵機矩形框相交,發生碰撞,同時變為空閒狀態即可
if(m_enemys[i].m_Rect.intersects(m_hero.m_bullets[j].m_Rect))
{
m_enemys[i].m_Free = true;
m_hero.m_bullets[j].m_Free = true;
}
}
}
}
/<code>

10.2 調用並測試函數

在MainScene.cpp中 playGame成員函數里,追加碰撞檢測代碼

C++學習教程,QT飛機大戰教程(含詳細步驟教程二)

運行查看效果,子彈和敵機碰撞後會同時消失

11 爆炸效果

爆炸效果功能實現步驟如下:

  • 創建爆炸文件和類
  • 添加爆炸類中的成員函數和成員屬性
  • 實現成員函數
  • 調用並測試效果

11.1 創建爆炸文件和類

創建Bomb類以及生成對應的文件

創建好後生成bomb.h 和 bomb.cpp兩個文件

C++學習教程,QT飛機大戰教程(含詳細步驟教程二)

11.2 爆炸成員函數和成員屬性

在config.h中加入爆炸配置數據

<code>#define BOMB_PATH  ":/res/bomb-%1.png"   //爆炸資源圖片
#define BOMB_NUM 20 //爆炸數量
#define BOMB_MAX 7 //爆炸圖片最大索引
#define BOMB_INTERVAL 20 //爆炸切圖時間間隔
/<code>

在bomb.h中添加如下代碼:

<code>#ifndef BOMB_H
#define BOMB_H
#include "config.h"
#include <qpixmap>
#include <qvector>

class Bomb
{
public:
Bomb();

//更新信息(播放圖片下標、播放間隔)
void updateInfo();

public:

//放爆炸資源數組
QVector<qpixmap> m_pixArr;

//爆炸位置
int m_X;
int m_Y;

//爆炸狀態
bool m_Free;

//爆炸切圖的時間間隔

int m_Recoder;

//爆炸時加載的圖片下標
int m_index;
};

#endif // BOMB_H
/<qpixmap>/<qvector>/<qpixmap>/<code>

11.3 實現成員函數

<code>Bomb::Bomb()
{
//初始化爆炸圖片數組
for(int i = 1 ;i <= BOMB_MAX ;i++)
{
//字符串拼接,類似 ":/res/bomb-1.png"
QString str = QString(BOMB_PATH).arg(i);
m_pixArr.push_back(QPixmap(str));
}

//初始化座標
m_X = 0;
m_Y = 0;

//初始化空閒狀態
m_Free = true;

//當前播放圖片下標
m_index = 0;

//爆炸間隔記錄
m_Recoder = 0;
}

void Bomb::updateInfo()
{
//空閒狀態
if(m_Free)
{
return;
}

m_Recoder++;

if(m_Recoder < BOMB_INTERVAL)
{
//記錄爆炸間隔未到,直接return,不需要切圖
return;
}
//重置記錄
m_Recoder = 0;

//切換爆炸播放圖片
m_index++;
//注:數組中的下標從0開始,最大是6
//如果計算的下標大於6,重置為0
if(m_index > BOMB_MAX-1)
{
m_index = 0;
m_Free = true;
}
}
/<code>

11.4 加入爆炸數組

在MainScene.h中加入爆炸數組 成員屬性

<code>//爆炸數組
Bomb m_bombs[BOMB_NUM];
/<code>

在碰撞檢測成員函數中,當發生碰撞時,設置爆炸對象的信息

<code>//播放爆炸效果
for(int k = 0 ; k < BOMB_NUM;k++)
{
if(m_bombs[k].m_Free)
{
//爆炸狀態設置為非空閒
m_bombs[k].m_Free = false;
//更新座標


m_bombs[k].m_X = m_enemys[i].m_X;
m_bombs[k].m_Y = m_enemys[i].m_Y;
break;
}
}
/<code>

在 MainScene.cpp的updatePosition中追加代碼

<code>//計算爆炸播放的圖片
for(int i = 0 ; i < BOMB_NUM;i++)
{
if(m_bombs[i].m_Free == false)
{
m_bombs[i].updateInfo();
}
}
/<code>

在 MainScene.cpp的paintEvent 中追加繪製爆炸代碼

<code>//繪製爆炸圖片
for(int i = 0 ; i < BOMB_NUM;i++)
{
if(m_bombs[i].m_Free == false)
{
painter.drawPixmap(m_bombs[i].m_X,m_bombs[i].m_Y,m_bombs[i].m_pixArr[m_bombs[i].m_index]);
}
}
/<code>

測試,實現爆炸效果

C++學習教程,QT飛機大戰教程(含詳細步驟教程二)

12 音效添加

音效添加步驟如下:

  • 添加多媒體模塊
  • 播放音效

12.1 添加多媒體模塊

在工程文件planeWar.pro 中修改代碼

<code>QT  += core gui multimedia
/<code>
C++學習教程,QT飛機大戰教程(含詳細步驟教程二)

12.2 播放音效

在config.h中 添加音效的配置路徑

<code>#define SOUND_BACKGROUND ":/res/bg.wav"
#define SOUND_BOMB ":/res/bomb.wav"
/<code>

注: QSound使用時候要包含頭文件 #include \\

在PlayGame中添加背景音樂

<code>//啟動背景音樂
QSound::play(SOUND_BACKGROUND);
/<code>

在爆炸時候添加爆炸音效

<code>//播放音效
QSound::play(SOUND_BOMB);
/<code>

測試音效

13 打包發佈

  1. 確定環境變量配置好 PATH: C:\\Qt\\Qt5.x.x\\5.x.x\\mingw53_32\\bin
  2. 在QT中把運行模式切換成 release 模式, 編譯。 在外層目錄中會有 release 版本的目錄.
  3. 將目錄中的 rcc 二進制資源文件、可執行程序文件(.exe) 拷貝到另外一個單獨的文件夾中.
  4. 進入 cmd 命令模式,切換到可執行程序所在的目錄. 執行以下命令,將可執行程序所需的庫文件拷貝到當前目錄:

    c windeployqt PlaneWar.exe
  5. 額外可以將 ico 圖標也拷貝到當前可執行程序所在的目錄.
  6. 啟動 HM NIS EDIT 軟件,在軟件中選擇: 文件->新建腳本嚮導, 接下來跟著嚮導操作.
  7. 為了讓安裝包安裝軟件也有快捷方式圖標,在生成的腳本里。進行修改:
    c CreateShortCut "$DESKTOP\\飛機大戰.lnk" "$INSTDIR\\PlaneWar.exe" CreateShortCut "$DESKTOP\\飛機大戰.lnk" "$INSTDIR\\PlaneWar.exe" "" "$INSTDIR\\app.ico
  8. 點擊菜單欄的 NSIS ,然後選擇編譯,在桌面生成安裝包.


分享到:


相關文章: