C++实战项目:坦克大战(五)

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

  爆炸功能

  爆炸的效果不仅仅用在炮弹上,当坦克被击中后也应该有这么一个爆炸效果。我们给所有的元素都抽象一个爆炸的功能,放在Object类中。代码如下:

C++代码
  1. #ifndef __OBJECT_H__  
  2. #define __OBJECT_H__  
  3.   
  4. #include <list>  
  5.   
  6. #include "Graphic.h"  
  7.   
  8. using namespace std;  
  9.   
  10. enum Dir { UP, DOWN, LEFT, RIGHT };  
  11.   
  12. class Object  
  13. {  
  14. public:  
  15.     // 绘图  
  16.     virtual void Display() = 0;  
  17.   
  18.     // 移动  
  19.     virtual void Move() = 0;  
  20.   
  21.     // 爆炸  
  22.     virtual void Boom(list<Object*>& lstBombs) = 0;  
  23.   
  24.     // 判断是否消失  
  25.     virtual bool IsDisappear() = 0;  
  26.   
  27. protected:  
  28.     // 计算势力范围  
  29.     virtual void CalculateSphere() = 0;  
  30.   
  31.     // 位置  
  32.     Point m_pos;  
  33.     // 势力范围  
  34.     Rect m_rectSphere;   
  35.     // 颜色  
  36.     COLORREF m_color;  
  37.     // 方向  
  38.     Dir m_dir;  
  39.     // 存在状态  
  40.     bool m_bDisappear;  
  41.     // 单次前进步长  
  42.     int m_step;  
  43. };  
  44.   
  45. #endif  

  与之前的代码相比,其实只是添加了这个虚函数:

C++代码
  1. virtual void Boom(list<Object*>& lstBombs) = 0;  

  这个函数的作用是创建一个新的Object对象,添加进传进来的list中。这个方法和发射炮弹的shoot函数功能完全相同。

  在这里定义之后,我们需要给所有继承Object的类中都添加这个函数的实现。这样一来我们的主战坦克、敌人坦克、炮弹都能够完成爆炸这个动作了。

  修改你的Tank、MainTank、EnemyTank、Bullet类,添加Boom函数的实现。可以先写成空函数保证编译通过。

  爆炸类

  我们把爆炸也作为一个独立的元素来管理,从Object继承一个新类Bomb来实现。创建Bomb.h和Bomb.cpp两个文件,内容如下:

  Bomb.h

C++代码
  1. #ifndef __BOMB_H__  
  2. #define __BOMB_H__  
  3.   
  4. #include "Object.h"  
  5.   
  6. enum BombType  
  7. {  
  8.     LARGE,  
  9.     SMALL  
  10. };  
  11.   
  12. class Bomb : public Object  
  13. {  
  14. public:  
  15.     Bomb();  
  16.     Bomb(Point pos, BombType type);  
  17.     ~Bomb(){}  
  18.   
  19.     void Display();  
  20.   
  21.     void Move();  
  22.   
  23.     void Boom(list<Object*>& lstBombs);  
  24.   
  25.     bool IsDisappear();  
  26.   
  27. protected:  
  28.     void CalculateSphere();  
  29.   
  30.     BombType m_type;  
  31.     int m_timer;  
  32. };  
  33.   
  34. #endif  

  由于继承了Object类,所有的虚函数都要被继承。新加入了两个属性,m_type是BombType类型,表示爆炸的种类。目前我们定义了两个种类:LARGE和SMALL,分别用来表示坦克爆炸和炮弹爆炸。m_timer用来控制爆炸显示的状态。我们的爆炸应该是一个动画效果,而不是一个静态形状。

  下面我们来看看具体的实现方法。

  Bomb.cpp

C++代码
  1. #include "Bomb.h"  
  2.   
  3. Bomb::Bomb()  
  4. {  
  5.     this->m_bDisappear = false;  
  6.     this->m_color = YELLOW;  
  7.     this->m_dir = UP;  
  8. }  
  9.   
  10. Bomb::Bomb(Point pos, BombType type) : Bomb()  
  11. {  
  12.     this->m_pos = pos;  
  13.     this->m_type = type;  
  14.   
  15.     switch (m_type)  
  16.     {  
  17.     case LARGE:  
  18.         m_timer = 8;  
  19.         break;  
  20.     case SMALL:  
  21.         m_timer = 4;  
  22.         break;  
  23.     default:  
  24.         break;  
  25.     }  
  26. }  
  27.   
  28. void Bomb::Display()  
  29. {  
  30.     COLORREF fill_color_save = getfillcolor();  
  31.     COLORREF color_save = getcolor();  
  32.   
  33.     setfillcolor(m_color);  
  34.     setcolor(RED);  
  35.   
  36.     fillcircle(m_pos.GetX(), m_pos.GetY(), 8 - m_timer);  
  37.   
  38.     setcolor(color_save);  
  39.     setfillcolor(fill_color_save);  
  40. }  
  41.   
  42. void Bomb::Move()  
  43. {  
  44.     m_timer -= 2;  
  45.   
  46.     if (m_timer < 0)  
  47.     {  
  48.         m_bDisappear = true;  
  49.     }  
  50.   
  51. }  
  52.   
  53. bool Bomb::IsDisappear()  
  54. {  
  55.     return m_bDisappear;  
  56. }  
  57.   
  58. void Bomb::Boom(list<Object*>& lstBombs)  
  59. {  
  60.     // Do nothing  
  61. }  
  62.   
  63. void Bomb::CalculateSphere()  
  64. {  
  65.     // Do nothing  
  66. }  

  在创建Bomb时,我们需要给它两个参数,位置和种类。

  在Display()中,我们画了一个中间是黄色边缘是红色的圆形,这个圆形会随着m_timer的减小而增大。

  Move()函数通过m_timer属性的自减来控制爆炸图形的大小,当m_timer小于0时表示爆炸生命周期结束。

  炮弹的爆炸方法

  在Bullet.cpp中,我们要加入爆炸函数的实现,代码如下:

C++代码
  1. void Bullet::Boom(list<Object*>& lstBombs)  
  2. {  
  3.     lstBombs.push_back(new Bomb(m_pos, SMALL));  
  4. }  

  和坦克的shoot方法几乎完全相同,这里创建了一个Bomb对象加入lstBombs这个爆炸链表中。爆炸的位置是炮弹的当前位置。

  爆炸的管理

  在main函数中,我们需要把爆炸对象管理起来。先创建一个爆炸链表:

C++代码
  1. // Bomb List  
  2. list<Object*> lstBombs;  
  3. lstBombs.clear();  

  当炮弹生命周期结束时,调用爆炸方法:

C++代码
  1. // Draw Bullets  
  2. for (list<Object*>::iterator it = lstBullets.begin(); it != lstBullets.end();)  
  3. {  
  4.     (*it)->Move();  
  5.   
  6.     if ((*it)->IsDisappear())  
  7.     {  
  8.         // Add a bomb  
  9.         (*it)->Boom(lstBombs);  
  10.   
  11.         // Delete the bullet  
  12.         delete *it;  
  13.         it = lstBullets.erase(it);  
  14.         continue;  
  15.     }  
  16.   
  17.     (*it)->Display();  
  18.     it++;  
  19. }  

  绘制爆炸:

C++代码
  1. // Draw Bombs  
  2. for (list<Object*>::iterator it = lstBombs.begin(); it != lstBombs.end();)  
  3. {  
  4.     (*it)->Move();  
  5.   
  6.     if ((*it)->IsDisappear())  
  7.     {  
  8.         delete *it;  
  9.         it = lstBombs.erase(it);  
  10.         continue;  
  11.     }  
  12.   
  13.     (*it)->Display();  
  14.     it++;  
  15. }  

  最后不要忘记在程序结束时释放所有的爆炸对象:

C++代码
  1. for (list<Object*>::iterator it = lstBombs.begin(); it != lstBombs.end(); it++)  
  2. {  
  3.     delete *it;  
  4. }  
  5. lstBombs.clear();  

  好了,看看效果吧。

  爆炸位置处理

  可能有人注意了,炮弹的爆炸位置并不都在战场边沿处。特别是向右边开炮时,炮弹大部分都在战场外爆炸。如图所示:

C++实战项目:坦克大战(五)

  这是因为我们判断炮弹出界的函数有时间差,当发现出界时有的炮弹已经出界了一段距离了。解决方法很简单,修改Bullet::Move()函数如下:

C++代码
  1. void Bullet::Move()  
  2. {  
  3.     switch (m_dir)  
  4.     {  
  5.     case UP:  
  6.         m_pos.SetY(m_pos.GetY() - m_step);  
  7.         CalculateSphere();  
  8.         if (m_rectSphere.GetStartPoint().GetY() < Graphic::GetBattleGround().GetStartPoint().GetY())  
  9.         {  
  10.             m_pos.SetY(Graphic::GetBattleGround().GetStartPoint().GetY());  
  11.             m_bDisappear = true;  
  12.         }  
  13.         break;  
  14.     case DOWN:  
  15.         m_pos.SetY(m_pos.GetY() + m_step);  
  16.         CalculateSphere();  
  17.         if (m_rectSphere.GetEndPoint().GetY() > Graphic::GetBattleGround().GetEndPoint().GetY())  
  18.         {  
  19.             m_pos.SetY(Graphic::GetBattleGround().GetEndPoint().GetY());  
  20.             m_bDisappear = true;  
  21.         }  
  22.         break;  
  23.     case LEFT:  
  24.         m_pos.SetX(m_pos.GetX() - m_step);  
  25.         CalculateSphere();  
  26.         if (m_rectSphere.GetStartPoint().GetX() < Graphic::GetBattleGround().GetStartPoint().GetX())  
  27.         {  
  28.             m_pos.SetX(Graphic::GetBattleGround().GetStartPoint().GetX());  
  29.             m_bDisappear = true;  
  30.         }  
  31.         break;  
  32.     case RIGHT:  
  33.         m_pos.SetX(m_pos.GetX() + m_step);  
  34.         CalculateSphere();  
  35.         if (m_rectSphere.GetEndPoint().GetX() > Graphic::GetBattleGround().GetEndPoint().GetX())  
  36.         {  
  37.             m_pos.SetX(Graphic::GetBattleGround().GetEndPoint().GetX());  
  38.             m_bDisappear = true;  
  39.         }  
  40.         break;  
  41.     default:  
  42.         break;  
  43.     }      
  44. }  

  再来看一下最新的效果,是不是和这篇最开始的图片效果相同呢?

  由于本文修改的代码过多,不便全部展示,请在我的GitHub中下载完整的代码。

本系列转自简书

除非特别注明,鸡啄米文章均为原创
转载请标明本文地址:http://www.jizhuomi.com/software/678.html
2017年1月4日
作者:鸡啄米 分类:软件开发 浏览: 评论:3