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

  这一篇中,我们继续继续进行我们的坦克大战。

  位置信息数据结构

  在游戏设计过程中,需要记录大量的位置信息,如果仅仅使用(x,y)坐标很容易出错。这一篇中,我们先定义两个简单的数据结构用来保存点和矩形的信息。

  在项目中新建Model目录,创建下面四个文件:

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

  代码如下:

C++代码
  1. #ifndef __POINT_H__  
  2. #define __POINT_H__  
  3.   
  4. class Point  
  5. {  
  6. public:  
  7.     Point(int x = 0, int y = 0) : m_x(x), m_y(y){};  
  8.     ~Point(){};  
  9.   
  10.     Point& operator=(const Point &p)  
  11.     {  
  12.         m_x = p.m_x;  
  13.         m_y = p.m_y;  
  14.   
  15.         return *this;  
  16.     }  
  17.   
  18.     void Set(int x, int y);  
  19.   
  20.     void SetX(int x);  
  21.     void SetY(int y);  
  22.   
  23.     int GetX();  
  24.     int GetY();  
  25.   
  26.   
  27. private:  
  28.     int m_x;  
  29.     int m_y;  
  30. };  
  31.   
  32. #endif  

  这个头文件创建了一个Point类,有两个成员变量m_x,m_y用来记录一个点的横、纵坐标。一组public方法用来完成给对象赋值和读取坐标值的操作。

  这里我们用到了C++的运算符重载功能,将“=”功能进行重载,方便我们用一个Point对象给另一个Point对象赋值,同时也能够使我们将Point作为参数进行传递。

  Point.cpp

C++代码
  1. #include "Point.h"  
  2.   
  3. void Point::Set(int x, int y)  
  4. {  
  5.     m_x = x;  
  6.     m_y = y;  
  7. }  
  8.   
  9. void Point::SetX(int x)  
  10. {  
  11.     m_x = x;  
  12. }  
  13.   
  14. void Point::SetY(int y)  
  15. {  
  16.     m_y = y;  
  17. }  
  18.   
  19. int Point::GetX()  
  20. {  
  21.     return m_x;  
  22. }  
  23.   
  24. int Point::GetY()  
  25. {  
  26.     return m_y;  
  27. }  

  这个文件中是对Point类的实现,大家一看就明白。

  这里需要强调的是,在类的封装过程中有一个非常重要的原则是不允许将成员变量用public的方法暴露在外。如果类的外部代码能够直接对类成员变量进行修改的话,程序将很不安全。正确的方法是像我们这样实现一组Get和Set方法进行管理。这样虽然代码量多了一些,但对后期维护带来的帮助是不可估量的。

  Rect.h

C++代码
  1. #ifndef __RECTANGLE_H__  
  2. #define __RECTANGLE_H__  
  3.   
  4. #include "Point.h"  
  5.   
  6. class Rect  
  7. {  
  8. public:  
  9.     Rect(int x1 = 0, int y1 = 0, int x2 = 0, int y2 = 0) : m_startPoint(x1, y1), m_endPoint(x2, y2){};  
  10.     Rect(const Point p1, const Point p2) : m_startPoint(p1), m_endPoint(p2){};  
  11.     ~Rect(){};  
  12.   
  13.     Rect& operator=(const Rect &rect)  
  14.     {  
  15.         m_startPoint = rect.GetStartPoint();  
  16.         m_endPoint = rect.GetEndPoint();  
  17.   
  18.         return *this;  
  19.     }  
  20.   
  21.     void Set(const Point pStart, const Point pEnd);  
  22.     void Set(int x1, int y1, int x2, int y2);  
  23.   
  24.     void SetStartPoint(const Point p);  
  25.     void SetEndPoint(const Point p);  
  26.   
  27.     Point GetStartPoint() const;  
  28.     Point GetEndPoint() const;  
  29.   
  30.     int GetWidth();  
  31.     int GetHeight();  
  32.   
  33. private:  
  34.     void Check();  
  35.   
  36.     Point m_startPoint;  
  37.     Point m_endPoint;  
  38. };  
  39.   
  40. #endif  

  Rect类是用来定义矩形的,它的成员变量是两个Point对象,分别表示矩形的左上角和右下角。这里我们强行规定m_startPoint表示左上角,m_endPoint表示右下角。如果创建对象时两个点顺序反了,Check()函数会自动把它们调整过来。

  这里需要注意,GetStartPoint()和GetEndPoint()两个函数都通过const修饰,表示返回值不能被修改。为什么要这么实现呢,因为这个函数的结果将会传进EasyX接口中,而这些接口大部分都要求参数是const的,如果这里不做修饰,在传参时会报错。

  Rect.cpp

C++代码
  1. #include "Rect.h"  
  2.   
  3. void Rect::Set(Point pStart, Point pEnd)  
  4. {  
  5.     m_startPoint = pStart;  
  6.     m_endPoint = pEnd;  
  7. }  
  8.   
  9. void Rect::Set(int x1, int y1, int x2, int y2)  
  10. {  
  11.     m_startPoint.Set(x1, y1);  
  12.     m_endPoint.Set(x2, y2);  
  13. }  
  14.   
  15. void Rect::SetStartPoint(Point p)  
  16. {  
  17.     m_startPoint = p;  
  18. }  
  19.   
  20. void Rect::SetEndPoint(Point p)  
  21. {  
  22.     m_endPoint = p;  
  23. }  
  24.   
  25. Point Rect::GetStartPoint() const  
  26. {  
  27.     return m_startPoint;  
  28. }  
  29.   
  30. Point Rect::GetEndPoint() const  
  31. {  
  32.     return m_endPoint;  
  33. }  
  34.   
  35. int Rect::GetWidth()  
  36. {  
  37.     return m_endPoint.GetX() - m_startPoint.GetX();  
  38. }  
  39.   
  40. int Rect::GetHeight()  
  41. {  
  42.     return m_endPoint.GetY() - m_startPoint.GetY();  
  43. }      
  44.   
  45. void Rect::Check()  
  46. {  
  47.     if (m_startPoint.GetX() > m_endPoint.GetX() || m_startPoint.GetY() > m_endPoint.GetY())  
  48.     {  
  49.         Point p = m_startPoint;  
  50.         m_startPoint = m_endPoint;  
  51.         m_endPoint = m_startPoint;  
  52.     }  
  53. }  

  这个文件中实现了Rect类的成员函数。

  主战坦克升级

  Tank.h

  首先,我们对Tank类进行修改,新增一部分功能,代码如下:

C++代码
  1. #ifndef __TANK_H__  
  2. #define __TANK_H__  
  3.   
  4. #include "Graphic.h"  
  5.   
  6. enum Dir { UP, DOWN, LEFT, RIGHT };  
  7.   
  8. class Tank  
  9. {  
  10. public:  
  11.     // 绘图  
  12.     virtual void Display() = 0;  
  13.   
  14.     // 移动  
  15.     virtual void Move() = 0;  
  16.   
  17. protected:  
  18.     virtual void CalculateSphere() = 0;  
  19.   
  20.     Point m_pos;  
  21.     Rect m_rectSphere; // 势力范围  
  22.   
  23.     COLORREF m_color;  
  24.   
  25.     Dir m_dir;  
  26.   
  27.     int m_step;  
  28. };  
  29.   
  30. #endif  

  我们把坐标用Point对象m_pos表示,又添加了一个新属性m_rectSphere,它是一个Rect对象,用来记录坦克的形状范围。之前我们的坦克总是用一组坐标来表示,这个坐标是坦克的中心点,所有跟坦克相关的行为都通过这个点来计算位置,实现起来有些复杂,有了这个Rect对象,相当于我们记录了这个坦克所在的矩形的位置,这样在绘制坦克时更容易计算坐标。

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

  MainTank.h

C++代码
  1. #ifndef __MAIN_TANK__  
  2. #define __MAIN_TANK__  
  3.   
  4. #include "Tank.h"  
  5.   
  6. class MainTank : public Tank  
  7. {  
  8. public:  
  9.     MainTank()  
  10.     {  
  11.         m_pos.Set(300, 300);  
  12.   
  13.         this->CalculateSphere();  
  14.   
  15.         m_color = YELLOW;  
  16.         m_dir = Dir::UP;  
  17.         m_step = 2;  
  18.     }  
  19.   
  20.     ~MainTank(){}  
  21.   
  22.     // 设置行驶方向  
  23.     void SetDir(Dir dir);  
  24.     void Display();  
  25.     void Move();  
  26.   
  27. protected:  
  28.     void CalculateSphere();  
  29.   
  30.     // 绘制坦克主体  
  31.     void DrawTankBody();  
  32. };  
  33.   
  34. #endif  

  这个文件中没有太大的修改,只是在成员变量初始化时做了一些调整。主战坦克的颜色改成了黄色,初始化后调用CalculateSphere()函数计算出矩形位置。

  MainTank.cpp

C++代码
  1. #include "MainTank.h"  
  2.   
  3. void MainTank::SetDir(Dir dir)  
  4. {  
  5.     m_dir = dir;  
  6. }  
  7.   
  8. void MainTank::DrawTankBody()  
  9. {  
  10.     fillrectangle(m_pos.GetX() - 6, m_pos.GetY() - 6, m_pos.GetX() + 6, m_pos.GetY() + 6);  
  11.   
  12.     switch (m_dir)  
  13.     {  
  14.     case UP:  
  15.     case DOWN:  
  16.         fillrectangle(m_rectSphere.GetStartPoint().GetX(), m_rectSphere.GetStartPoint().GetY(),  
  17.             m_rectSphere.GetStartPoint().GetX() + 4, m_rectSphere.GetEndPoint().GetY());  
  18.         fillrectangle(m_rectSphere.GetEndPoint().GetX() - 4, m_rectSphere.GetStartPoint().GetY(),  
  19.             m_rectSphere.GetEndPoint().GetX(), m_rectSphere.GetEndPoint().GetY());  
  20.         break;  
  21.     case LEFT:  
  22.     case RIGHT:  
  23.         fillrectangle(m_rectSphere.GetStartPoint().GetX(), m_rectSphere.GetStartPoint().GetY(),  
  24.             m_rectSphere.GetEndPoint().GetX(), m_rectSphere.GetStartPoint().GetY() + 4);  
  25.         fillrectangle(m_rectSphere.GetStartPoint().GetX(), m_rectSphere.GetEndPoint().GetY() - 4,  
  26.             m_rectSphere.GetEndPoint().GetX(), m_rectSphere.GetEndPoint().GetY());  
  27.         break;  
  28.     default:  
  29.         break;  
  30.     }  
  31. }  
  32.   
  33. void MainTank::Display()  
  34. {  
  35.     COLORREF fill_color_save = getfillcolor();  
  36.     COLORREF color_save = getcolor();  
  37.   
  38.     setfillcolor(m_color);  
  39.     setcolor(m_color);  
  40.   
  41.     DrawTankBody();  
  42.   
  43.     switch (m_dir)  
  44.     {  
  45.     case UP:  
  46.         line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX(), m_pos.GetY() - 15);  
  47.         break;  
  48.     case DOWN:  
  49.         line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX(), m_pos.GetY() + 15);  
  50.         break;  
  51.     case LEFT:  
  52.         line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX() - 15, m_pos.GetY());  
  53.         break;  
  54.     case RIGHT:  
  55.         line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX() + 15, m_pos.GetY());  
  56.         break;  
  57.     default:  
  58.         break;  
  59.     }  
  60.   
  61.     setcolor(color_save);  
  62.     setfillcolor(fill_color_save);  
  63. }  
  64.   
  65. void MainTank::Move()  
  66. {  
  67.     switch (m_dir)  
  68.     {  
  69.     case UP:  
  70.         m_pos.SetY(m_pos.GetY() - m_step);  
  71.         if (m_pos.GetY() < Graphic::GetBattleGround().GetStartPoint().GetY())  
  72.             m_pos.SetY(Graphic::GetBattleGround().GetEndPoint().GetY() - 1);  
  73.         break;  
  74.     case DOWN:  
  75.         m_pos.SetY(m_pos.GetY() + m_step);  
  76.         if (m_pos.GetY() > Graphic::GetBattleGround().GetEndPoint().GetY())  
  77.             m_pos.SetY(Graphic::GetBattleGround().GetStartPoint().GetY() + 1);  
  78.         break;  
  79.     case LEFT:  
  80.         m_pos.SetX(m_pos.GetX() - m_step);  
  81.         if (m_pos.GetX() < Graphic::GetBattleGround().GetStartPoint().GetX())  
  82.             m_pos.SetX(Graphic::GetBattleGround().GetEndPoint().GetX() - 1);  
  83.         break;  
  84.     case RIGHT:  
  85.         m_pos.SetX(m_pos.GetX() + m_step);  
  86.         if (m_pos.GetX() > Graphic::GetBattleGround().GetEndPoint().GetX())  
  87.             m_pos.SetX(Graphic::GetBattleGround().GetStartPoint().GetX() + 1);  
  88.         break;  
  89.     default:  
  90.         break;  
  91.     }  
  92.   
  93.     CalculateSphere();  
  94. }  
  95.   
  96. void MainTank::CalculateSphere()  
  97. {  
  98.     switch (m_dir)  
  99.     {  
  100.     case UP:  
  101.     case DOWN:  
  102.         m_rectSphere.Set(m_pos.GetX() - 13, m_pos.GetY() - 10, m_pos.GetX() + 13, m_pos.GetY() + 10);  
  103.         break;  
  104.     case LEFT:  
  105.     case RIGHT:  
  106.         m_rectSphere.Set(m_pos.GetX() - 10, m_pos.GetY() - 13, m_pos.GetX() + 10, m_pos.GetY() + 13);  
  107.         break;  
  108.     default:  
  109.         break;  
  110.     }  
  111. }  

  这个文件修改较多,是不是有些眼花缭乱了。

  • DrawTankBody()

  这个函数的参数被拿掉了,在这里我们通过坦克当前方向来判断它的形状。

  在绘制履带时,我们利用了m_rectSphere的位置坐标,虽然看起来代码变多了,但只有一个数字4是无意义的,它代表履带的宽度。如果这个宽度需要经常调整的话,我们还可以考虑把它用一个成员变量管理起来。

  在判断坦克形状时,我们利用了switch的一个特性,通过故意少写break关键字,让两个判断结果公用一段代码,这个早已经讲过,这里不多说了。

  • Display()

  之前我们用setfillcolor设置了填充颜色,这里我们加入了setcolor,这样画出来的坦克边框也是我们设置的颜色。

  • Move()

  这个函数中,比较奇怪的是出现了一个没见过的函数Graphic::GetBattleGround()。我们今天要给坦克划定一个运行区域,不能让它满屏幕行驶了,这个后面再说。

  这里要注意,每移动一次都需要调用CalculateSphere()方法重新计算坦克区域。

  • CalculateSphere()

  这个很简单,计算出左上角和右下角的Point位置即可。

  更新画布

  之前的画布颜色太深,我们要做修改。另外,我们后面需要在窗口上显示游戏信息,因此,要在右边留出一部分空间。我们给坦克划定一个新的区域,让它们在里面行驶。

  Graphic.h

C++代码
  1. #ifndef __GRAPHIC_H__  
  2. #define __GRAPHIC_H__  
  3.   
  4. #include <graphics.h>  
  5.   
  6. #include "model/Rect.h"  
  7.   
  8. #define SCREEN_WIDTH    1024  
  9. #define SCREEN_HEIGHT    768  
  10.   
  11. #define BATTLE_GROUND_X1 5  
  12. #define BATTLE_GROUND_Y1 5  
  13. #define BATTLE_GROUND_X2 800  
  14. #define BATTLE_GROUND_Y2 (SCREEN_HEIGHT - BATTLE_GROUND_Y1)  
  15.   
  16. class Graphic  
  17. {  
  18. public:  
  19.     static void Create();  
  20.     static void Destroy();  
  21.   
  22.     static void DrawBattleGround();  
  23.   
  24.     static int GetScreenWidth();  
  25.     static int GetScreenHeight();  
  26.   
  27.     static Rect GetBattleGround();  
  28.   
  29. private:  
  30.     static Rect m_rectScreen;  
  31.     static Rect m_rectBattleGround;  
  32. };  
  33.   
  34. #endif  

  文件中通过一组宏来定义战场区域的位置。另外通过m_rectBattleGround这个Rect对象来保存。

  细心的读者应该发现了,我们在引用Rect类时用了下面这句话:

C++代码
  1. #include "model/Rect.h"  

  Rect.h文件的路径需要加上model目录,否则找不到。需要说明的是这个目录指的是项目文件夹下真实存在的model目录。如果你用的是VS,"Solution Explorer"中新建的文件夹是逻辑目录,不需要加载include路径中。

  简单说,include后面写的路径是给编译器看的,它只认Windows资源管理器中看到的路径,与IDE中的逻辑路径无关。

  Graphic.cpp

C++代码
  1. #include "Graphic.h"  
  2.   
  3. Rect Graphic::m_rectScreen;   
  4. Rect Graphic::m_rectBattleGround;  
  5.   
  6. void Graphic::Create()  
  7. {  
  8.     m_rectScreen.Set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);  
  9.     initgraph(SCREEN_WIDTH, SCREEN_WIDTH);  
  10.     setbkcolor(DARKGRAY);  
  11.   
  12.     m_rectBattleGround.Set(BATTLE_GROUND_X1, BATTLE_GROUND_Y1, BATTLE_GROUND_X2, BATTLE_GROUND_Y2);  
  13. }  
  14.   
  15. void Graphic::Destroy()  
  16. {  
  17.     closegraph();  
  18. }  
  19.   
  20. void Graphic::DrawBattleGround()  
  21. {  
  22.     rectangle(m_rectBattleGround.GetStartPoint().GetX(), m_rectBattleGround.GetStartPoint().GetY(),  
  23.         m_rectBattleGround.GetEndPoint().GetX(), m_rectBattleGround.GetEndPoint().GetY());  
  24. }  
  25.   
  26. int Graphic::GetScreenWidth()  
  27. {  
  28.     return SCREEN_WIDTH;  
  29. }  
  30.   
  31. int Graphic::GetScreenHeight()  
  32. {  
  33.     return SCREEN_HEIGHT;  
  34. }  
  35.   
  36. Rect Graphic::GetBattleGround()  
  37. {  
  38.     return m_rectBattleGround;  
  39. }  

  代码在创建画布是,重新指定了背景颜色。DrawBattleGround()函数在屏幕上画出了战场的范围。

  main.cpp

  主函数中,我们只需要再循环中添加一个DrawBattleGround函数的调用即可。

C++代码
  1. if (!skip)  
  2. {  
  3.     cleardevice();  
  4.   
  5.     Graphic::DrawBattleGround();  
  6.   
  7.     mainTank.Move();  
  8.     mainTank.Display();  
  9. }  

  好了,运行一下程序,看看效果吧。

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

  敌人坦克

  屏幕上只有一个自己的坦克看着有些孤单,我们再添加上些敌人的坦克。

  新建文件EnemyTank.h和EnemyTank.cpp。实现一个敌人坦克类。

  EnemyTank.h

C++代码
  1. #ifndef __ENEMY_TANK__  
  2. #define __ENEMY_TANK__  
  3.   
  4. #include "Tank.h"  
  5.   
  6. class EnemyTank : public Tank  
  7. {  
  8. public:  
  9.     EnemyTank()  
  10.     {  
  11.         RandomTank();  
  12.     }  
  13.   
  14.     ~EnemyTank(){}  
  15.   
  16.     void Display();  
  17.     void Move();  
  18.   
  19. protected:  
  20.     void CalculateSphere();  
  21.     void RandomTank();  
  22. };  
  23.   
  24. #endif  

  有了Tank这个抽象类,所有的坦克都从它继承就好了。除了抽象类中继承的函数之外,我们加了一个RandomTank()用来随机地在战场区域生成一个坦克。

  EnemyTank.cpp

C++代码
  1. #include "EnemyTank.h"  
  2.   
  3. void EnemyTank::RandomTank()  
  4. {  
  5.     m_pos.SetX(rand() % Graphic::GetBattleGround().GetWidth());  
  6.     m_pos.SetY(rand() % Graphic::GetBattleGround().GetHeight());  
  7.     m_color = WHITE;  
  8.     m_dir = (Dir)(Dir::UP + (rand() % 4));  
  9.     m_step = 2;  
  10. }  
  11.   
  12. void EnemyTank::Display()  
  13. {  
  14.     COLORREF fill_color_save = getfillcolor();  
  15.     COLORREF color_save = getcolor();  
  16.   
  17.     setfillcolor(m_color);  
  18.     setcolor(m_color);  
  19.   
  20.     fillrectangle(m_pos.GetX() - 6, m_pos.GetY() - 6, m_pos.GetX() + 6, m_pos.GetY() + 6);  
  21.   
  22.     fillrectangle(m_rectSphere.GetStartPoint().GetX(), m_rectSphere.GetStartPoint().GetY(),  
  23.         m_rectSphere.GetStartPoint().GetX() + 4, m_rectSphere.GetStartPoint().GetY() + 4);  
  24.     fillrectangle(m_rectSphere.GetEndPoint().GetX() - 4, m_rectSphere.GetStartPoint().GetY(),  
  25.         m_rectSphere.GetEndPoint().GetX(), m_rectSphere.GetStartPoint().GetY() + 4);  
  26.   
  27.     fillrectangle(m_rectSphere.GetStartPoint().GetX(), m_rectSphere.GetEndPoint().GetY() - 4,  
  28.         m_rectSphere.GetStartPoint().GetX() + 4, m_rectSphere.GetEndPoint().GetY());  
  29.     fillrectangle(m_rectSphere.GetEndPoint().GetX() - 4, m_rectSphere.GetEndPoint().GetY() - 4,  
  30.         m_rectSphere.GetEndPoint().GetX(), m_rectSphere.GetEndPoint().GetY());  
  31.   
  32.     switch (m_dir)  
  33.     {  
  34.     case UP:  
  35.         line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX(), m_pos.GetY() - 15);  
  36.         break;  
  37.     case DOWN:  
  38.         line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX(), m_pos.GetY() + 15);  
  39.         break;  
  40.     case LEFT:  
  41.         line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX() - 15, m_pos.GetY());  
  42.         break;  
  43.     case RIGHT:  
  44.         line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX() + 15, m_pos.GetY());  
  45.         break;  
  46.     default:  
  47.         break;  
  48.     }  
  49.   
  50.     setcolor(color_save);  
  51.     setfillcolor(fill_color_save);  
  52. }  
  53.   
  54. void EnemyTank::Move()  
  55. {  
  56.     switch (m_dir)  
  57.     {  
  58.     case UP:  
  59.         m_pos.SetY(m_pos.GetY() - m_step);  
  60.         if (m_pos.GetY() < Graphic::GetBattleGround().GetStartPoint().GetY())  
  61.             m_pos.SetY(Graphic::GetBattleGround().GetEndPoint().GetY() - 1);  
  62.         break;  
  63.     case DOWN:  
  64.         m_pos.SetY(m_pos.GetY() + m_step);  
  65.         if (m_pos.GetY() > Graphic::GetBattleGround().GetEndPoint().GetY())  
  66.             m_pos.SetY(Graphic::GetBattleGround().GetStartPoint().GetY() + 1);  
  67.         break;  
  68.     case LEFT:  
  69.         m_pos.SetX(m_pos.GetX() - m_step);  
  70.         if (m_pos.GetX() < Graphic::GetBattleGround().GetStartPoint().GetX())  
  71.             m_pos.SetX(Graphic::GetBattleGround().GetEndPoint().GetX() - 1);  
  72.         break;  
  73.     case RIGHT:  
  74.         m_pos.SetX(m_pos.GetX() + m_step);  
  75.         if (m_pos.GetX() > Graphic::GetBattleGround().GetEndPoint().GetX())  
  76.             m_pos.SetX(Graphic::GetBattleGround().GetStartPoint().GetX() + 1);  
  77.         break;  
  78.     default:  
  79.         break;  
  80.     }  
  81.   
  82.     CalculateSphere();  
  83. }  
  84.   
  85. void EnemyTank::CalculateSphere()  
  86. {  
  87.     switch (m_dir)  
  88.     {  
  89.     case UP:  
  90.     case DOWN:  
  91.         m_rectSphere.Set(m_pos.GetX() - 13, m_pos.GetY() - 10, m_pos.GetX() + 13, m_pos.GetY() + 10);  
  92.         break;  
  93.     case LEFT:  
  94.     case RIGHT:  
  95.         m_rectSphere.Set(m_pos.GetX() - 10, m_pos.GetY() - 13, m_pos.GetX() + 10, m_pos.GetY() + 13);  
  96.         break;  
  97.     default:  
  98.         break;  
  99.     }  
  100. }  

  这个文件实在没什么可讲的,基本都用的之前提到的方法。随机生成坦克用到了星空中随机产生星星的方法,相信大家都能看懂。

  main.cpp

  最后是main函数,代码如下:

C++代码
  1. #define MAX_TANKS 10  
  2.   
  3. void main()  
  4. {  
  5.     srand((unsigned)time(NULL));  
  6.   
  7.     Graphic::Create();  
  8.   
  9.     MainTank mainTank;  
  10.   
  11.     Tank* pTank[MAX_TANKS];  
  12.   
  13.     for (int i = 0; i < MAX_TANKS; i++)  
  14.     {  
  15.         pTank[i] = new EnemyTank();  
  16.     }  
  17.   
  18.     bool loop = true;  
  19.     bool skip = false;  
  20.     while (loop)  
  21.     {  
  22.         if (kbhit())  
  23.         {  
  24.             int key = getch();  
  25.   
  26.             switch (key)  
  27.             {  
  28.             // Up  
  29.             case 72:  
  30.                 mainTank.SetDir(Dir::UP);  
  31.                 break;  
  32.             // Down  
  33.             case 80:   
  34.                 mainTank.SetDir(Dir::DOWN);  
  35.                 break;  
  36.             // Left  
  37.             case 75:   
  38.                 mainTank.SetDir(Dir::LEFT);  
  39.                 break;  
  40.             // Right  
  41.             case 77:   
  42.                 mainTank.SetDir(Dir::RIGHT);  
  43.                 break;  
  44.             case 224: // 方向键高8位  
  45.                 break;  
  46.             // Esc  
  47.             case 27:  
  48.                 loop = false;  
  49.                 break;  
  50.             // Space  
  51.             case 32:  
  52.                 break;  
  53.             // Enter  
  54.             case 13:  
  55.                 if (skip)  
  56.                     skip = false;  
  57.                 else  
  58.                     skip = true;  
  59.                 break;  
  60.             default:   
  61.                 break;  
  62.             }  
  63.         }  
  64.   
  65.         if (!skip)  
  66.         {  
  67.             cleardevice();  
  68.   
  69.             Graphic::DrawBattleGround();  
  70.   
  71.             mainTank.Move();  
  72.             mainTank.Display();  
  73.   
  74.             for (int i = 0; i < MAX_TANKS; i++)  
  75.             {  
  76.                 pTank[i]->Move();  
  77.                 pTank[i]->Display();  
  78.             }  
  79.         }  
  80.   
  81.         Sleep(200);  
  82.     }  
  83.   
  84.     for (int i = 0; i < MAX_TANKS; i++)  
  85.     {  
  86.         delete pTank[i];  
  87.     }  
  88.   
  89.     Graphic::Destroy();  
  90. }  

  与之前相比,添加了下面这几个内容:

  • 新建了一个宏MAX_TANKS用来设置坦克的数量

  • 用一个指针数组保存每个坦克的指针

  • 循环创建坦克,这里用了new的方法把坦克对象创建在堆空间中

  • 每次擦屏之后,遍历指针数组,绘制出每个坦克。调用的是Move()和Display()方法。

  • 退出程序前,释放每一个坦克所占的堆空间。

  注意:C++中依然是new和delete成对出现,有申请就要有释放。否则会出现内存泄露。

  下面运行一下代码,看看我们今天的成果。

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

  是不是一下热闹了很多呢?

  这一篇就到这里,源码请到GitHub上下载。

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