教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

飛機(jī)大戰(zhàn)游戲制作教程[C++培訓(xùn)]

更新時(shí)間:2020年01月16日16時(shí)25分 來源:傳智播客 瀏覽次數(shù):

目錄


14、視頻教程
視頻教程下載地址: https://pan.baidu.com/s/1V5TRdyp85gmB-vGa5KMOQg  加QQ435946716獲取素材

IT培訓(xùn)


1、項(xiàng)目簡介

飛機(jī)大戰(zhàn)是我們大家所熟知的一款小游戲,本教程就是教大家如何制作一款自己的飛機(jī)大戰(zhàn)。

首先我們看一下效果圖

飛機(jī)大戰(zhàn)效果圖

玩家控制一架小飛機(jī),然后自動(dòng)發(fā)射子彈,如果子彈打到了飛下來的敵機(jī),則射殺敵機(jī),并且有爆炸的特效。接下來再說明一下案例的需求,也就是我們需要實(shí)現(xiàn)的內(nèi)容。

·滾動(dòng)的背景地圖

·飛機(jī)的制作和控制

·子彈的制作和射擊

·敵機(jī)的制作

·碰撞檢測

·爆炸效果

·音效添加

2、創(chuàng)建項(xiàng)目

創(chuàng)建項(xiàng)目步驟如下:

·打開Qt

·跟著向?qū)?chuàng)建項(xiàng)目
基類選擇 QWidget空窗口

第一個(gè)場景為主場景 MainScene

不帶UI界面

2.1 打開Qt

找到你安裝的Qt Creator,打開它。

如果安裝時(shí),沒有選擇在桌面上建立快捷方式,那么你的Qt軟件位置如下

C:\qt\Qt5.x.x\Tools\QtCreator\bin

在這個(gè)路徑下找到 qtcreator.exe 雙擊打開即可

2.2 按照向?qū)?chuàng)建項(xiàng)目

2.2.1 新建項(xiàng)目

點(diǎn)擊菜單 中的文件 -> 新建文件或項(xiàng)目 或者 在首頁面中點(diǎn)擊New Project

飛機(jī)大戰(zhàn)創(chuàng)建項(xiàng)目

2.2.2 選擇模板

模板選擇 Application -> Qt Widget Application

選擇模板


2.2.3 項(xiàng)目名稱和位置

給項(xiàng)目起個(gè)名稱以及選中項(xiàng)目要保存的地方

選擇項(xiàng)目位置

這一步選擇后在Kits 構(gòu)建套件中直接點(diǎn)擊下一步即可


2.2.4 類信息

基類選擇 QWidget

類名也就是我們第一個(gè)窗口場景的名稱,這里我起名為 MainScene 代表游戲中的主場景

取消創(chuàng)建界面中的內(nèi)容

選擇QWidget



2.2.5 完成創(chuàng)建

在匯總頁面中點(diǎn)擊完成,我們就邁開了項(xiàng)目的第一步!

完成項(xiàng)目創(chuàng)建


3、設(shè)置主場景

主場景設(shè)置的步驟如下:

·添加配置文件,保存游戲中所有配置數(shù)據(jù)

·初始化主場景窗口大小、標(biāo)題

3.1 配置文件添加

創(chuàng)建新的頭文件為 config.h 主要記錄程序中所有的配置數(shù)據(jù),方便后期修改

添加窗口寬度、高度的配置信息,依據(jù)背景圖大小進(jìn)行設(shè)置

/********** 游戲配置數(shù)據(jù) **********/

define GAME_WIDTH 512 //寬度

define GAME_HEIGHT 768 //高度

define GAME_TITLE "飛機(jī)大戰(zhàn) v1.0" //標(biāo)題

3.2 主場景基本設(shè)置

在mainScene.h中添加新的成員函數(shù)initScene 用來初始化游戲場景

void initScene();

在mainScene.cpp中實(shí)現(xiàn)如下代碼

void MainScene::initScene()
{
    //初始化窗口大小
    setFixedSize(GAMEWIDTH,GAMEHEIGHT);

    //設(shè)置窗口標(biāo)題

    setWindowTitle(GAME_TITLE);

}

在構(gòu)造函數(shù)MainScene中調(diào)用該函數(shù) initScene

MainScene::MainScene(QWidget *parent)
    : QWidget(parent)
{
    //初始化場景
    initScene();
}

測試運(yùn)行效果如圖:

運(yùn)行測試效果


4、資源導(dǎo)入

在主場景中其實(shí)還有一個(gè)配置項(xiàng)沒有實(shí)現(xiàn),也就是窗口左上角的那個(gè)圖標(biāo)資源。那么接下來我們將游戲中的資源進(jìn)行導(dǎo)入并且設(shè)置游戲圖標(biāo)。

資源導(dǎo)入步驟

·生成qrc文件

·項(xiàng)目同級目錄下創(chuàng)建res文件夾并將資源粘貼過來

·編輯qrc,加入前綴和文件

·利用qrc生成二進(jìn)制文件 rcc

·rcc文件放入到debug同級目錄下

·注冊二進(jìn)制文件

·添加圖標(biāo)資源

4.1 qrc文件生成

右鍵項(xiàng)目,點(diǎn)擊添加新文件

?添加新文件


選擇Qt -> Qt Resource File

選擇Qt

資源文件起名 如:res

源文件起名


生成res.qrc文件

生成res.qrc文件



4.2 創(chuàng)建res文件夾

項(xiàng)目的同級目錄下創(chuàng)建文件夾res,并將準(zhǔn)備好的資源粘貼進(jìn)去

創(chuàng)建res文件夾

4.3 編輯qrc文件

右鍵qrc文件,選中Open in Editor

右鍵qrc文件


添加前綴為 '' \ ''

添加前綴


添加文件 將res下所有文件選中即可

添加res下文件

4.4 qrc生成 rcc二進(jìn)制文件

由于資源過大,會提示錯(cuò)誤:

錯(cuò)誤提示


這個(gè)錯(cuò)誤也就是“編譯器的堆空間不足”。

由于資源文件qrc過大,超出分配的內(nèi)存范圍

因此我們需要利用二進(jìn)制資源,而生成二進(jìn)制資源就需要我們剛剛的qrc文件

利用cmd打開終端,定位到res.qrc的目錄下,輸入命令

rcc -binary .\res.qrc -o plane.rcc

輸入命令

4.5 復(fù)制rcc文件

將生成好的rcc文件,放入到debug同級目錄中一份

復(fù)制rcc文件

4.6 注冊二進(jìn)制文件

在config.h中追加配置數(shù)據(jù)

#define GAMERESPATH "./plane.rcc"     //rcc文件路徑

在main.cpp中修改代碼

#include "mainscene.h"

#include

#include

#include "config.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    //注冊外部的二進(jìn)制資源文件

    QResource::registerResource(GAME_RES_PATH);

    MainScene w;

    w.show();

    return a.exec();

此時(shí),qrc文件已經(jīng)沒用了,刪除即可!

最簡單的刪除方式就是 .pro工程文件中刪除代碼,與工程無瓜葛

刪除以下代碼:
RESOURCES += \ res.qrc

4.7 添加圖標(biāo)資源

配置文件config.h中追加代碼

虛擬資源路徑語法如下:

" : + 前綴名 + 文件路徑 "

#define GAME_ICON ":/res/app.ico"

在mainScene.cpp的 initScene函數(shù)中追加代碼:

 //設(shè)置圖標(biāo)資源
setWindowIcon(QIcon( GAME_ICON));     //加頭文件 #include <QIcon>

運(yùn)行測試:

添加圖標(biāo)資源

5、地圖滾動(dòng)

步驟:

·創(chuàng)建地圖文件和類

·添加成員函數(shù)和成員屬性 實(shí)現(xiàn)成員函數(shù)

·游戲運(yùn)行調(diào)用定時(shí)器

·啟動(dòng)定時(shí)器,監(jiān)聽定時(shí)器信號實(shí)現(xiàn)游戲循環(huán)

    -計(jì)算游戲內(nèi)元素坐標(biāo)

    -繪制到屏幕中

5.1 創(chuàng)建地圖文件和類

右鍵項(xiàng)目,添加新文件

添加新文件


選擇C++ -> C++ Class

選擇C++


修改類名為map,點(diǎn)擊下一步,直到創(chuàng)建完畢

修改類名

至此,地圖Map的文件和類創(chuàng)建完畢

5.2 地圖的成員函數(shù)和成員屬性

在map.h中添加如下代碼

#ifndef MAP_H

#define MAP_H

#include(Qpixmap)

class Map {
public:
    //構(gòu)造函數(shù)
    Map();

   //地圖滾動(dòng)坐標(biāo)計(jì)算

   void mapPosition();

public:

    //地圖圖片對象

    QPixmap m_map1;

    QPixmap m_map2;

    //地圖Y軸坐標(biāo)

    int m_map1_posY;

    int m_map2_posY;

    //地圖滾動(dòng)幅度

    int m_scroll_speed;

};

endif // MAP_H

5.3 實(shí)現(xiàn)成員函數(shù)

在config.h中添加新的配置數(shù)據(jù)

 /********** 地圖配置數(shù)據(jù) **********/

define MAPPATH ":/res/imgbglevel1.jpg"     //地圖圖片路徑

define MAPSCROLLSPEED 2     //地圖滾動(dòng)速度

在map.cpp中實(shí)現(xiàn)成員函數(shù)

#include "map.h"

#include "config.h"

Map::Map()
{
    //初始化加載地圖對象
    mmap1.load(MAPPATH);
    mmap2.load(MAPPATH);

   
    //設(shè)置地圖其實(shí)y軸坐標(biāo)

    m_map1_posY = -GAME_HEIGHT;

    m_map2_posY = 0;

    //設(shè)置地圖滾動(dòng)速度

    m_scroll_speed = MAP_SCROLL_SPEED;

}

void Map::mapPosition()
{
    //處理第一張圖片滾動(dòng)
    mmap1posY += MAPSCROLLSPEED;
    if(mmap1posY >= 0)
    {
        mmap1posY =-GAME_HEIGHT;
   }

        //處理第二張圖片滾動(dòng)

        m_map2_posY += MAP_SCROLL_SPEED;

        if(m_map2_posY >= GAME_HEIGHT )

       {

            m_map2_posY =0;

       }

}

5.4 定時(shí)器添加

在mainScene.h中添加新的定時(shí)器對象

QTimer m_Timer;

在 config.h中添加 屏幕刷新間隔

#define GAME_RATE 10         //刷新間隔,幀率 單位毫秒

在MainScene.cpp的initScene中追加代碼

//定時(shí)器設(shè)置
m_Timer.setInterval(GAME_RATE);

5.5 啟動(dòng)定時(shí)器實(shí)現(xiàn)地圖滾動(dòng)

在MainScene.h中添加新的成員函數(shù)以及成員對象

//啟動(dòng)游戲 用于啟動(dòng)定時(shí)器對象
void playGame();
//更新坐標(biāo)
void updatePosition();
//繪圖事件
void paintEvent(QPaintEvent *event);

//地圖對象

Map m_map;

在MainScene.cpp中實(shí)現(xiàn)成員函數(shù)

void MainScene::playGame()
{
    //啟動(dòng)定時(shí)器
    m_Timer.start();

    //監(jiān)聽定時(shí)器

    connect(&m_Timer,&QTimer::timeout,[=](){
        //更新游戲中元素的坐標(biāo)

        updatePosition();

         //重新繪制圖片

        update();

    });

}

void MainScene::updatePosition()
{
    //更新地圖坐標(biāo)
    m_map.mapPosition();
}

void MainScene::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    //繪制地圖

    painter.drawPixmap(0,m_map.m_map1_posY , m_map.m_map1);

    painter.drawPixmap(0,m_map.m_map2_posY , m_map.m_map2);

測試運(yùn)行游戲,實(shí)現(xiàn)地圖滾動(dòng)

6、英雄飛機(jī)

步驟如下:

·創(chuàng)建英雄文件和類

·添加成員函數(shù)和成員屬性

·實(shí)現(xiàn)成員函數(shù)

·創(chuàng)建飛機(jī)對象并顯示

·拖拽飛機(jī)

6.1 創(chuàng)建英雄文件和類

創(chuàng)建HeroPlane類以及生成對應(yīng)的文件

和創(chuàng)建地圖的步驟一樣,這里就不在詳細(xì)截圖了

創(chuàng)建好后生成HeroPlane.h 和 HeroPlane.cpp兩個(gè)文件

創(chuàng)建英雄文件

6.2 飛機(jī)的成員函數(shù)和成員屬性

在HeroPlane.h中添加代碼

 class HeroPlane
{
    public: HeroPlane();

    //發(fā)射子彈

    void shoot();

    //設(shè)置飛機(jī)位置

    void setPosition(int x, int y);

public:
    //飛機(jī)資源 對象
    QPixmap m_Plane;

    //飛機(jī)坐標(biāo)

    int m_X;

    int m_Y;

    //飛機(jī)的矩形邊框

    QRect m_Rect;

}; 

6.3 成員函數(shù)實(shí)現(xiàn)

這里飛機(jī)有個(gè)發(fā)射子彈的成員函數(shù),由于我們還沒有做子彈

因此這個(gè)成員函數(shù)先寫成空實(shí)現(xiàn)即可

在config.h中追加飛機(jī)配置參數(shù)

/********** 飛機(jī)配置數(shù)據(jù) **********/

define HERO_PATH ":/res/hero2.png"

heroPlane.cpp中實(shí)現(xiàn)成員函數(shù)代碼:

#include "heroplane.h"

#include "config.h"

HeroPlane::HeroPlane()
{
    //初始化加載飛機(jī)圖片資源
    mPlane.load(HEROPATH);

    //初始化坐標(biāo)

    m_X = GAME_WIDTH * 0.5 - m_Plane.width()*0.5;

    m_Y = GAME_HEIGHT - m_Plane.height();

   //初始化矩形框

    m_Rect.setWidth(m_Plane.width());

    m_Rect.setHeight(m_Plane.height());

    m_Rect.moveTo(m_X,m_Y);

}

void HeroPlane::setPosition(int x, int y)
{
    mX = x;
    mY = y;
    m_Rect.moveTo(mX,mY);
}

void HeroPlane::shoot()
{

}

6.4 創(chuàng)建飛機(jī)對象并顯示

在MainScene.h中追加新的成員屬性

//飛機(jī)對象
HeroPlane m_hero;

在MainScene.cpp的paintEvent中追加代碼

//繪制英雄
painter.drawPixmap(m_hero.m_X,m_hero.m_Y,m_hero.m_Plane);

測試飛機(jī)顯示到屏幕中

飛機(jī)大戰(zhàn)測試

6.5 拖拽飛機(jī)

在MainScene.h中添加鼠標(biāo)移動(dòng)事件

//鼠標(biāo)移動(dòng)事件
void mouseMoveEvent(QMouseEvent *event);

重寫鼠標(biāo)移動(dòng)事件

void MainScene::mouseMoveEvent(QMouseEvent event)
{
    int x = event->x() - mhero.mRect.width()0.5;     //鼠標(biāo)位置 - 飛機(jī)矩形的一半
    int y = event->y() - mhero.mRect.height()*0.5;


     //邊界檢測

    if(x <= 0 )

{

    x = 0;

}

if(x >= GAME_WIDTH - m_hero.m_Rect.width())

{

    x = GAME_WIDTH - m_hero.m_Rect.width();

}

if(y <= 0)

{

    y = 0;

}

if(y >= GAME_HEIGHT - m_hero.m_Rect.height())

{

    y = GAME_HEIGHT - m_hero.m_Rect.height();

}

m_hero.setPosition(x,y);

}

測試飛機(jī)可以拖拽

飛機(jī)測試2

7、子彈制作

制作步驟如下:

·創(chuàng)建子彈文件和類

·添加子彈類中的成員函數(shù)和成員屬性

·實(shí)現(xiàn)成員函數(shù)

·測試子彈

7.1 創(chuàng)建子彈文件和類

創(chuàng)建Bullet類以及生成對應(yīng)的文件

創(chuàng)建好后生成bullet.h 和 bullet.cpp兩個(gè)文件

生成文件夾

7.2 子彈的成員函數(shù)和成員屬性

在Bullet.h中添加代碼

#ifndef BULLET_H

#define BULLET_H

#include "config.h"

#include<QPixamp>

class Bullet {
    public: Bullet();

    //更新子彈坐標(biāo)

void updatePosition();

public:
        //子彈資源對象
        QPixmap mBullet;
        //子彈坐標(biāo)
        int mX;
        int mY;
        //子彈移動(dòng)速度
        int mSpeed;
        //子彈是否閑置
        bool mFree;
        //子彈的矩形邊框(用于碰撞檢測)
        QRect mRect;
};

#endif         // BULLET_H

7.3 子彈類成員函數(shù)實(shí)現(xiàn)

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

 /********** 子彈配置數(shù)據(jù) **********/

#define BULLETPATH ":/res/bullet11.png"      //子彈圖片路徑

#define BULLET_SPEED 5      //子彈移動(dòng)速度

在bullet.cpp中實(shí)現(xiàn)成員函數(shù),代碼如下:

#include "bullet.h"

Bullet::Bullet() {
        //加載子彈資源
        mBullet.load(BULLETPATH);

        //子彈坐標(biāo) 初始坐標(biāo)可隨意設(shè)置,后期會重置

        m_X = GAME_WIDTH*0.5 - m_Bullet.width()*0.5;

        m_Y = GAME_HEIGHT;

        //子彈狀態(tài)

        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() {

    //如果子彈是空閑狀態(tài),不需要坐標(biāo)計(jì)算
    //玩家飛機(jī)可以控制子彈的空閑狀態(tài)為false
    if(m_Free) {
        return;
}

    //子彈向上移動(dòng)

    m_Y  -= m_Speed;

    m_Rect.moveTo(m_X,m_Y);

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

    {

        m_Free = true;

    }

7.4 測試子彈

子彈本身應(yīng)該由飛機(jī)發(fā)射,測試階段我們寫一段輔助代碼,看看效果即可

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

在MainScene.h中添加測試代碼

//測試子彈代碼
Bullet temp_bullet;

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

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

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

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

運(yùn)行程序,此時(shí)會有一發(fā)子彈從屏幕中射出

子彈測試

測試完畢后,測試代碼刪除或注釋即可

8、玩家發(fā)射子彈

玩家發(fā)射子彈制作步驟如下:

·英雄飛機(jī)添加新的成員屬性

·實(shí)現(xiàn)發(fā)射成員函數(shù)

·主場景控制子彈發(fā)射

8.1 飛機(jī)添加新成員屬性

在config.h中添加新的配置數(shù)據(jù)

#define BULLET_NUM 30        //彈匣中子彈總數(shù)

#define BULLET_INTERVAL 20       //發(fā)射子彈時(shí)間間隔

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

//彈匣
Bullet mbullets[BULLETNUM];

//發(fā)射間隔記錄

int m_recorder;

8.2 成員函數(shù)補(bǔ)充

在構(gòu)造函數(shù) HeroPlane 中初始化發(fā)生間隔記錄

//初始化發(fā)射間隔記錄
m_recorder = 0;

之前在英雄飛機(jī)類中預(yù)留的一個(gè)shoot函數(shù)我們進(jìn)行實(shí)現(xiàn),代碼如下:

void HeroPlane::shoot() {
        //累加時(shí)間間隔記錄變量
        mrecorder++;
        //判斷如果記錄數(shù)字,未達(dá)到發(fā)射間隔,直接
        return if(mrecorder < BULLETINTERVAL) { return; }
        //到達(dá)發(fā)射時(shí)間處理
        //重置發(fā)射時(shí)間間隔記錄
        mrecorder = 0;

        //發(fā)射子彈

        for(int i = 0 ; i < BULLET_NUM;i++)

        {

            //如果是空閑的子彈進(jìn)行發(fā)射

            if(m_bullets[i].m_Free)

        {

                //將改子彈空閑狀態(tài)改為假

                m_bullets[i].m_Free = false;

                //設(shè)置發(fā)射的子彈坐標(biāo)

               m_bullets[i].m_X = m_X + m_Rect.width()*0.5 - 10;

               m_bullets[i].m_Y = m_Y - 25 ;

               break;

         }

     }

}

8.3 主場景中實(shí)現(xiàn)發(fā)射子彈

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

//發(fā)射子彈
m_hero.shoot();
//計(jì)算子彈坐標(biāo)
for(int i = 0 ;i < BULLET_NUM;i++)
{
    //如果子彈狀態(tài)為非空閑,計(jì)算發(fā)射位置
    if(!m_hero.m_bullets[i].m_Free)
    {
        m_hero.m_bullets[i].updatePosition();
    }
}

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

//繪制子彈
for(int i = 0 ;i < BULLET_NUM;i++)
{
      //如果子彈狀態(tài)為非空閑,計(jì)算發(fā)射位置
      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);        
       }
}

測試運(yùn)行,玩家可以發(fā)射子彈

測試玩家發(fā)子彈

9、敵機(jī)制作

敵機(jī)制作與子彈制作原理類似,也是每隔一定的時(shí)間讓敵機(jī)出場

制作步驟如下:

·創(chuàng)建敵機(jī)文件和類

·添加敵機(jī)類中的成員函數(shù)和成員屬性

·實(shí)現(xiàn)成員函數(shù)

·敵機(jī)出場

·測試敵機(jī)

9.1 創(chuàng)建敵機(jī)文件和類

創(chuàng)建EnemyPlane類以及生成對應(yīng)的文件

創(chuàng)建好后生成enemyPlane.h 和 enemyPlane.cpp兩個(gè)文件



9.2 敵機(jī)成員函數(shù)和成員屬性

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

#ifndef ENEMYPLANE_H

#define ENEMYPLANE_H

#include<QPixmap>

class EnemyPlane {
public:
    EnemyPlane();

    //更新坐標(biāo)

    void updatePosition();

public:
    //敵機(jī)資源對象
    QPixmap m_enemy;

    //位置

    int m_X;

    int m_Y;

    //敵機(jī)的矩形邊框(碰撞檢測)

    QRect m_Rect;

    //狀態(tài)

    bool m_Free;

    //速度

    int m_Speed;

};

#endif    // ENEMYPLANE_H

9.3 敵機(jī)成員函數(shù)實(shí)現(xiàn)

在config.h中追加敵機(jī)配置信息

/********** 敵機(jī)配置數(shù)據(jù) **********/

#define ENEMYPATH ":/res/img-plane5.png"    //敵機(jī)資源圖片

#define ENEMY_SPEED 5    //敵機(jī)移動(dòng)速度

#define ENEMY_NUM 20    //敵機(jī)總數(shù)量

#define ENEMY_INTERVAL 30     //敵機(jī)出場時(shí)間間隔

在enemyPlane.cpp中實(shí)現(xiàn)成員函數(shù),代碼如下:

EnemyPlane::EnemyPlane()
{
    //敵機(jī)資源加載
    menemy.load(ENEMYPATH);

    //敵機(jī)位置

    m_X = 0;

    m_Y = 0;

    //敵機(jī)狀態(tài)

    m_Free = true;

    //敵機(jī)速度

    m_Speed = ENEMY_SPEED;

    //敵機(jī)矩形

    m_Rect.setWidth(m_enemy.width());

    m_Rect.setHeight(m_enemy.height());

    m_Rect.moveTo(m_X,m_Y);

}

void EnemyPlane::updatePosition()
{
    //空閑狀態(tài),不計(jì)算坐標(biāo) 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;

    }

9.4 敵機(jī)出場

在MainScene.h中追加敵機(jī)出場的成員函數(shù)

在MainScene.h中追加敵機(jī)數(shù)組 和 敵機(jī)出場間隔記錄 的成員屬性

    //敵機(jī)出場
    void enemyToScene();

    //敵機(jī)數(shù)組

    EnemyPlane m_enemys[ENEMY_NUM];

    //敵機(jī)出場間隔記錄

    int m_recorder;

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

#m_recorder = 0;

實(shí)現(xiàn)成員函數(shù) enemyToScene

void MainScene::enemyToScene()
{
    mrecorder++;
    if(mrecorder < ENEMY_INTERVAL)
    {
        return;
    }

    m_recorder = 0;

for(int i = 0 ; i< ENEMY_NUM;i++)

{

    if(m_enemys[i].m_Free)

    {

        //敵機(jī)空閑狀態(tài)改為false

        m_enemys[i].m_Free = false;

        //設(shè)置坐標(biāo)

        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;

    }

  }

在PlayGame成員函數(shù)的timeout信號發(fā)送時(shí)候,槽函數(shù)中首先追加 enemyToScene

//敵機(jī)出場
enemyToScene();

敵機(jī)出廠

更新敵機(jī)坐標(biāo),在updatePosition成員函數(shù)中追加代碼

//敵機(jī)坐標(biāo)計(jì)算
for(int i = 0 ; i< ENEMY_NUM;i++)
{
    //非空閑敵機(jī) 更新坐標(biāo)
    if(m_enemys[i].m_Free == false)
    {
        m_enemys[i].updatePosition();
    }
}

繪制敵機(jī),在paintEvent成員函數(shù)中追加繪制敵機(jī)代碼

//繪制敵機(jī)
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);
    }
}

添加隨機(jī)數(shù)種子

在MainScene.cpp中 initScene 成員函數(shù)里添加隨機(jī)數(shù)種子

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

運(yùn)行測試敵機(jī)出場效果

敵方飛機(jī)出現(xiàn)

10、碰撞檢測

實(shí)現(xiàn)碰撞檢測步驟如下:

·添加并實(shí)現(xiàn)碰撞檢測成員函數(shù)

·調(diào)用并測試函數(shù)

10.1 添加并實(shí)現(xiàn)碰撞檢測函數(shù)

在MainScene.h中添加新的成員函數(shù)

void collisionDetection();

在MainScene.cpp中實(shí)現(xiàn)該成員函數(shù)

void MainScene::collisionDetection()
{
    //遍歷所有非空閑的敵機(jī)
    for(int i = 0 ;i < ENEMYNUM;i++)
    {
        if(menemys[i].m_Free)
        {
            //空閑飛機(jī) 跳轉(zhuǎn)下一次循環(huán)
            continue;
        }

        //遍歷所有 非空閑的子彈

        for(int j = 0 ; j < BULLET_NUM;j++)

    {

        if(m_hero.m_bullets[j].m_Free)

        {

            //空閑子彈 跳轉(zhuǎn)下一次循環(huán)

            continue;

        }

        //如果子彈矩形框和敵機(jī)矩形框相交,發(fā)生碰撞,同時(shí)變?yōu)榭臻e狀態(tài)即可

        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;

        }

    }

  }

10.2 調(diào)用并測試函數(shù)

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

追加檢測代碼


運(yùn)行查看效果,子彈和敵機(jī)碰撞后會同時(shí)消失

11、爆炸效果

爆炸效果功能實(shí)現(xiàn)步驟如下:

·創(chuàng)建爆炸文件和類

·添加爆炸類中的成員函數(shù)和成員屬性

·實(shí)現(xiàn)成員函數(shù)

·調(diào)用并測試效果

11.1 創(chuàng)建爆炸文件和類

創(chuàng)建Bomb類以及生成對應(yīng)的文件

創(chuàng)建好后生成bomb.h 和 bomb.cpp兩個(gè)文件

創(chuàng)建爆炸文件和類

11.2 爆炸成員函數(shù)和成員屬性

在config.h中加入爆炸配置數(shù)據(jù)

define BOMB_PATH ":/res/bomb-%1.png"    //爆炸資源圖片

define BOMB_NUM 20    //爆炸數(shù)量

define BOMB_MAX 7    //爆炸圖片最大索引

define BOMB_INTERVAL 20    //爆炸切圖時(shí)間間隔

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

#ifndef BOMB_H

#define BOMB_H

#include "config.h"

#include <QPixmap>

#include <QVector>

class Bomb
{
public:
    Bomb();

    //更新信息(播放圖片下標(biāo)、播放間隔)

    void updateInfo();

public:

    //放爆炸資源數(shù)組

    QVector<QPixmap> m_pixArr;

    //爆炸位置

    int m_X;

    int m_Y;

    //爆炸狀態(tài)

    bool m_Free;

    //爆炸切圖的時(shí)間間隔

    int m_Recoder;

    //爆炸時(shí)加載的圖片下標(biāo)

    int m_index;

};

#endif     // BOMB_H

11.3 實(shí)現(xiàn)成員函數(shù)

Bomb::Bomb()
{
    //初始化爆炸圖片數(shù)組
    for(int i = 1 ;i <= BOMBMAX ;i++)
    {
        //字符串拼接,類似 ":/res/bomb-1.png"
        QString str = QString(BOMBPATH).arg(i);
        mpixArr.pushback(QPixmap(str));
    }

    //初始化坐標(biāo)

    m_X = 0;

    m_Y = 0;

    //初始化空閑狀態(tài)

    m_Free = true;

    //當(dāng)前播放圖片下標(biāo)

    m_index = 0;

    //爆炸間隔記錄

    m_Recoder = 0;

}

void Bomb::updateInfo()
{
    //空閑狀態(tài)
    if(m_Free) { return;
}

m_Recoder++;

if(m_Recoder < BOMB_INTERVAL)

{

    //記錄爆炸間隔未到,直接return,不需要切圖

    return;

}

//重置記錄

m_Recoder = 0;

//切換爆炸播放圖片

m_index++;

//注:數(shù)組中的下標(biāo)從0開始,最大是6

//如果計(jì)算的下標(biāo)大于6,重置為0

    if(m_index > BOMB_MAX-1)

    {

        m_index = 0;

        m_Free = true;

    }

}


11.4 加入爆炸數(shù)組

在MainScene.h中加入爆炸數(shù)組 成員屬性

//爆炸數(shù)組
Bomb m_bombs[BOMB_NUM];

在碰撞檢測成員函數(shù)中,當(dāng)發(fā)生碰撞時(shí),設(shè)置爆炸對象的信息

//播放爆炸效果
for(int k = 0 ; k < BOMBNUM;k++) { if(mbombs[k].m_Free) { //爆炸狀態(tài)設(shè)置為非空閑 mbombs[k].mFree = false; //更新坐標(biāo)

                    m_bombs[k].m_X = m_enemys[i].m_X;

                    m_bombs[k].m_Y = m_enemys[i].m_Y;

                    break;

                }

            }

在 MainScene.cpp的updatePosition中追加代碼

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

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

//繪制爆炸圖片
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]);
    }
}

測試,實(shí)現(xiàn)爆炸效果

爆炸效果

12、音效添加

音效添加步驟如下:

·添加多媒體模塊

·播放音效

12.1 添加多媒體模塊

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

QT += core gui multimedia

12.2 播放音效

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

#define SOUND_BACKGROUND ":/res/bg.wav"

#define SOUND_BOMB ":/res/bomb.wav"

注: QSound使用時(shí)候要包含頭文件 #include<QSound>

在PlayGame中添加背景音樂

//啟動(dòng)背景音樂
QSound::play(SOUND_BACKGROUND);

在爆炸時(shí)候添加爆炸音效

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

測試音效

13、打包發(fā)布

1、確定環(huán)境變量配置好 PATH: C:\Qt\Qt5.x.x\5.x.x\mingw53_32\bin

2、在QT中把運(yùn)行模式切換成 release 模式, 編譯。 在外層目錄中會有 release 版本的目錄.

3、將目錄中的 rcc 二進(jìn)制資源文件、可執(zhí)行程序文件(.exe) 拷貝到另外一個(gè)單獨(dú)的文件夾中.

4、進(jìn)入 cmd 命令模式,切換到可執(zhí)行程序所在的目錄. 執(zhí)行以下命令,將可執(zhí)行程序所需的庫文件拷貝到當(dāng)前目錄:

windeployqt PlaneWar.exe

5、額外可以將 ico 圖標(biāo)也拷貝到當(dāng)前可執(zhí)行程序所在的目錄。

6、啟動(dòng) HM NIS EDIT 軟件,在軟件中選擇: 文件->新建腳本向?qū)В?接下來跟著向?qū)Р僮?

7、為了讓安裝包安裝軟件也有快捷方式圖標(biāo),在生成的腳本里。進(jìn)行修改:

CreateShortCut "$DESKTOP\飛機(jī)大戰(zhàn).lnk" "$INSTDIR\PlaneWar.exe"
CreateShortCut "$DESKTOP\飛機(jī)大戰(zhàn).lnk" "$INSTDIR\PlaneWar.exe" "" "$INSTDIR\app.ico"

8、點(diǎn)擊菜單欄的 NSIS ,然后選擇編譯,在桌面生成安裝包。

0 分享到:
和我們在線交談!