C++代碼訓(xùn)練營 | 坦克大戰(zhàn)(5)

上一篇中我們給主戰(zhàn)坦克添加了發(fā)射炮彈的功能。不過有一個問題,炮彈飛到戰(zhàn)場邊緣時,自動消失的感覺不太好。我們今天來給炮彈加上一個爆炸的效果。

爆炸功能

爆炸的效果不僅僅用在炮彈上,當(dāng)坦克被擊中后也應(yīng)該有這么一個爆炸效果。我們給所有的元素都抽象一個爆炸的功能,放在Object類中。代碼如下:

#ifndef __OBJECT_H__
#define __OBJECT_H__

#include <list>

#include "Graphic.h"

using namespace std;

enum Dir { UP, DOWN, LEFT, RIGHT };

class Object
{
public:
    // 繪圖
    virtual void Display() = 0;

    // 移動
    virtual void Move() = 0;

    // 爆炸
    virtual void Boom(list<Object*>& lstBombs) = 0;

    // 判斷是否消失
    virtual bool IsDisappear() = 0;

protected:
    // 計算勢力范圍
    virtual void CalculateSphere() = 0;

    // 位置
    Point m_pos;
    // 勢力范圍
    Rect m_rectSphere; 
    // 顏色
    COLORREF m_color;
    // 方向
    Dir m_dir;
    // 存在狀態(tài)
    bool m_bDisappear;
    // 單次前進(jìn)步長
    int m_step;
};

#endif

與之前的代碼相比,其實只是添加了這個虛函數(shù):

virtual void Boom(list<Object*>& lstBombs) = 0;

這個函數(shù)的作用是創(chuàng)建一個新的Object對象,添加進(jìn)傳進(jìn)來的list中。這個方法和發(fā)射炮彈的shoot函數(shù)功能完全相同。

在這里定義之后,我們需要給所有繼承Object的類中都添加這個函數(shù)的實現(xiàn)。這樣一來我們的主戰(zhàn)坦克、敵人坦克、炮彈都能夠完成爆炸這個動作了。

修改你的Tank、MainTank、EnemyTank、Bullet類,添加Boom函數(shù)的實現(xiàn)??梢韵葘懗煽蘸瘮?shù)保證編譯通過。

爆炸類

我們把爆炸也作為一個獨(dú)立的元素來管理,從Object繼承一個新類Bomb來實現(xiàn)。創(chuàng)建Bomb.h和Bomb.cpp兩個文件,內(nèi)容如下:

Bomb.h

#ifndef __BOMB_H__
#define __BOMB_H__

#include "Object.h"

enum BombType
{
    LARGE,
    SMALL
};

class Bomb : public Object
{
public:
    Bomb();
    Bomb(Point pos, BombType type);
    ~Bomb(){}

    void Display();

    void Move();

    void Boom(list<Object*>& lstBombs);
    
    bool IsDisappear();

protected:
    void CalculateSphere();
    
    BombType m_type;
    int m_timer;
};

#endif

由于繼承了Object類,所有的虛函數(shù)都要被繼承。新加入了兩個屬性,m_type是BombType類型,表示爆炸的種類。目前我們定義了兩個種類:LARGE和SMALL,分別用來表示坦克爆炸和炮彈爆炸。m_timer用來控制爆炸顯示的狀態(tài)。我們的爆炸應(yīng)該是一個動畫效果,而不是一個靜態(tài)形狀。

下面我們來看看具體的實現(xiàn)方法。

Bomb.cpp

#include "Bomb.h"

Bomb::Bomb()
{
    this->m_bDisappear = false;
    this->m_color = YELLOW;
    this->m_dir = UP;
}

Bomb::Bomb(Point pos, BombType type) : Bomb()
{
    this->m_pos = pos;
    this->m_type = type;

    switch (m_type)
    {
    case LARGE:
        m_timer = 8;
        break;
    case SMALL:
        m_timer = 4;
        break;
    default:
        break;
    }
}

void Bomb::Display()
{
    COLORREF fill_color_save = getfillcolor();
    COLORREF color_save = getcolor();

    setfillcolor(m_color);
    setcolor(RED);

    fillcircle(m_pos.GetX(), m_pos.GetY(), 8 - m_timer);

    setcolor(color_save);
    setfillcolor(fill_color_save);
}

void Bomb::Move()
{
    m_timer -= 2;

    if (m_timer < 0)
    {
        m_bDisappear = true;
    }

}

bool Bomb::IsDisappear()
{
    return m_bDisappear;
}

void Bomb::Boom(list<Object*>& lstBombs)
{
    // Do nothing
}

void Bomb::CalculateSphere()
{
    // Do nothing
}

在創(chuàng)建Bomb時,我們需要給它兩個參數(shù),位置和種類。

在Display()中,我們畫了一個中間是黃色邊緣是紅色的圓形,這個圓形會隨著m_timer的減小而增大。

Move()函數(shù)通過m_timer屬性的自減來控制爆炸圖形的大小,當(dāng)m_timer小于0時表示爆炸生命周期結(jié)束。

炮彈的爆炸方法

在Bullet.cpp中,我們要加入爆炸函數(shù)的實現(xiàn),代碼如下:

void Bullet::Boom(list<Object*>& lstBombs)
{
    lstBombs.push_back(new Bomb(m_pos, SMALL));
}

和坦克的shoot方法幾乎完全相同,這里創(chuàng)建了一個Bomb對象加入lstBombs這個爆炸鏈表中。爆炸的位置是炮彈的當(dāng)前位置。

爆炸的管理

在main函數(shù)中,我們需要把爆炸對象管理起來。先創(chuàng)建一個爆炸鏈表:

// Bomb List
list<Object*> lstBombs;
lstBombs.clear();

當(dāng)炮彈生命周期結(jié)束時,調(diào)用爆炸方法:

// Draw Bullets
    for (list<Object*>::iterator it = lstBullets.begin(); it != lstBullets.end();)
    {
        (*it)->Move();
    
        if ((*it)->IsDisappear())
        {
            // Add a bomb
            (*it)->Boom(lstBombs);

            // Delete the bullet
            delete *it;
            it = lstBullets.erase(it);
            continue;
        }

        (*it)->Display();
        it++;
    }

繪制爆炸:

// Draw Bombs
for (list<Object*>::iterator it = lstBombs.begin(); it != lstBombs.end();)
{
    (*it)->Move();

    if ((*it)->IsDisappear())
    {
        delete *it;
        it = lstBombs.erase(it);
        continue;
    }

    (*it)->Display();
    it++;
}

最后不要忘記在程序結(jié)束時釋放所有的爆炸對象:

for (list<Object*>::iterator it = lstBombs.begin(); it != lstBombs.end(); it++)
{
    delete *it;
}
lstBombs.clear();

好了,看看效果吧。

爆炸位置處理

可能有人注意了,炮彈的爆炸位置并不都在戰(zhàn)場邊沿處。特別是向右邊開炮時,炮彈大部分都在戰(zhàn)場外爆炸。如圖所示:

這是因為我們判斷炮彈出界的函數(shù)有時間差,當(dāng)發(fā)現(xiàn)出界時有的炮彈已經(jīng)出界了一段距離了。解決方法很簡單,修改Bullet::Move()函數(shù)如下:

void Bullet::Move()
{
    switch (m_dir)
    {
    case UP:
        m_pos.SetY(m_pos.GetY() - m_step);
        CalculateSphere();
        if (m_rectSphere.GetStartPoint().GetY() < Graphic::GetBattleGround().GetStartPoint().GetY())
        {
            m_pos.SetY(Graphic::GetBattleGround().GetStartPoint().GetY());
            m_bDisappear = true;
        }
        break;
    case DOWN:
        m_pos.SetY(m_pos.GetY() + m_step);
        CalculateSphere();
        if (m_rectSphere.GetEndPoint().GetY() > Graphic::GetBattleGround().GetEndPoint().GetY())
        {
            m_pos.SetY(Graphic::GetBattleGround().GetEndPoint().GetY());
            m_bDisappear = true;
        }
        break;
    case LEFT:
        m_pos.SetX(m_pos.GetX() - m_step);
        CalculateSphere();
        if (m_rectSphere.GetStartPoint().GetX() < Graphic::GetBattleGround().GetStartPoint().GetX())
        {
            m_pos.SetX(Graphic::GetBattleGround().GetStartPoint().GetX());
            m_bDisappear = true;
        }
        break;
    case RIGHT:
        m_pos.SetX(m_pos.GetX() + m_step);
        CalculateSphere();
        if (m_rectSphere.GetEndPoint().GetX() > Graphic::GetBattleGround().GetEndPoint().GetX())
        {
            m_pos.SetX(Graphic::GetBattleGround().GetEndPoint().GetX());
            m_bDisappear = true;
        }
        break;
    default:
        break;
    }   
}

再來看一下最新的效果,是不是和這篇最開始的圖片效果相同呢?

由于本文修改的代碼過多,不便全部展示,請在我的GitHub中下載完整的代碼。

我是天花板,讓我們一起在軟件開發(fā)中自我迭代。
如有任何問題,歡迎與我聯(lián)系。


上一篇:C++代碼訓(xùn)練營 | 坦克大戰(zhàn)(4)
下一篇:C++代碼訓(xùn)練營 | 坦克大戰(zhàn)(6)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容