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.vkzldl.live/software/671.html
2016年12月13日
作者:雞啄米 分類:軟件開發 瀏覽: 評論:3