美文网首页技术宅c++程序员
C++代码训练营 | 坦克大战(5)

C++代码训练营 | 坦克大战(5)

作者: 天花板 | 来源:发表于2016-12-22 15:52 被阅读873次

    上一篇中我们给主战坦克添加了发射炮弹的功能。不过有一个问题,炮弹飞到战场边缘时,自动消失的感觉不太好。我们今天来给炮弹加上一个爆炸的效果。

    爆炸功能

    爆炸的效果不仅仅用在炮弹上,当坦克被击中后也应该有这么一个爆炸效果。我们给所有的元素都抽象一个爆炸的功能,放在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)

    相关文章

      网友评论

      • 37304b439d88:为什么加入了爆炸类还是看不到爆炸效果,只是偶尔子弹出现在墙的边缘会有红边黑洞的情况发生
        7b4b61689e2a:我也是,你找到原因了吗,参考一下?

      本文标题:C++代码训练营 | 坦克大战(5)

      本文链接:https://www.haomeiwen.com/subject/archvttx.html