C++代碼訓練營 | 坦克大戰(5)

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

爆炸功能

爆炸的效果不僅僅用在炮彈上,當坦克被擊中后也應該有這么一個爆炸效果。我們給所有的元素都抽象一個爆炸的功能,放在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;
    // 存在狀態
    bool m_bDisappear;
    // 單次前進步長
    int m_step;
};

#endif

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

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

這個函數的作用是創建一個新的Object對象,添加進傳進來的list中。這個方法和發射炮彈的shoot函數功能完全相同。

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

修改你的Tank、MainTank、EnemyTank、Bullet類,添加Boom函數的實現。可以先寫成空函數保證編譯通過。

爆炸類

我們把爆炸也作為一個獨立的元素來管理,從Object繼承一個新類Bomb來實現。創建Bomb.h和Bomb.cpp兩個文件,內容如下:

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類,所有的虛函數都要被繼承。新加入了兩個屬性,m_type是BombType類型,表示爆炸的種類。目前我們定義了兩個種類:LARGE和SMALL,分別用來表示坦克爆炸和炮彈爆炸。m_timer用來控制爆炸顯示的狀態。我們的爆炸應該是一個動畫效果,而不是一個靜態形狀。

下面我們來看看具體的實現方法。

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
}

在創建Bomb時,我們需要給它兩個參數,位置和種類。

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

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

炮彈的爆炸方法

在Bullet.cpp中,我們要加入爆炸函數的實現,代碼如下:

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

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

爆炸的管理

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

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

當炮彈生命周期結束時,調用爆炸方法:

// 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++;
}

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

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

好了,看看效果吧。

爆炸位置處理

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

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

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中下載完整的代碼。

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


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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容