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

  现在我们的游戏已经初具规模,但如果主战坦克一直是无敌状态那也很无趣。今天我们来让敌人的炮火发挥作用。

  主战坦克被击中

  当敌人的炮弹和主战坦克接触时,主战坦克生命值减一。我们预设的主战坦克共有三条命,被击中三次后游戏结束。

  为主战坦克添加碰撞检测大家应该很熟悉,修改之前的CheckCrash()函数如下:

C++代码
  1. void CheckCrash()  
  2. {  
  3.     // Check enermy tank damage  
  4.     for (list<Object*>::iterator it = lstMainTankBullets.begin(); it != lstMainTankBullets.end(); it++)  
  5.     {  
  6.         for (list<Tank*>::iterator itt = lstTanks.begin(); itt != lstTanks.end(); itt++)  
  7.         {  
  8.             if (Shape::CheckIntersect((*it)->GetSphere(), (*itt)->GetSphere()))  
  9.             {  
  10.                 (*itt)->SetDisappear();  
  11.                 (*it)->SetDisappear();  
  12.             }  
  13.         }  
  14.     }  
  15.   
  16.     // Check main tank damage  
  17.     for (list<Object*>::iterator it = lstBullets.begin(); it != lstBullets.end(); it++)  
  18.     {  
  19.         if (Shape::CheckIntersect((*it)->GetSphere(), mainTank.GetSphere()))  
  20.         {  
  21.             Setting::Die();  
  22.   
  23.             if (Setting::GetLife() > 0)  
  24.             {  
  25.                 (*it)->SetDisappear();  
  26.             }  
  27.             else  
  28.             {  
  29.                 mainTank.SetDisappear();  
  30.             }              
  31.         }  
  32.     }  
  33. }  

  新加入的代码通过一个for循环把主战坦克的Sphere和每一个敌人坦克的炮弹的Sphere进行碰撞检测,如果相交就调用Setting::Die()函数使主战坦克的生命减一。当生命值为0时,通过SetDisappear()函数让主战坦克消失。这个逻辑和敌人坦克被击毁时相同。

  生命值计算

  在Setting类中添加Die函数如下:

C++代码
  1. static void Die()  
  2. {  
  3.     m_nLife -= 1;  
  4. }  

  每调用一次这个函数,主战坦克的生命值减1。

  在这里修改了生命值属性后,绘制界面时会自动更新生命数值。

  游戏结束界面

  当游戏结束时,需要显示一个“GameOver”的界面。在Graphic类中添加函数如下:

C++代码
  1. void Graphic::ShowGameOver()  
  2. {  
  3.     COLORREF fill_color_save = getfillcolor();  
  4.     COLORREF color_save = getcolor();  
  5.   
  6.     rectangle(BATTLE_GROUND_X1 + 100, BATTLE_GROUND_Y1 + 200, BATTLE_GROUND_X1 + 700, BATTLE_GROUND_Y1 + 380);  
  7.   
  8.     LOGFONT fontBak;  
  9.     gettextstyle(&fontBak);               // 获取当前字体设置  
  10.   
  11.     LOGFONT f = fontBak;  
  12.     f.lfHeight = 48;                      // 设置字体高度为 48  
  13.     _tcscpy_s(f.lfFaceName, _T("黑体"));  // 设置字体为“黑体”  
  14.     f.lfQuality = ANTIALIASED_QUALITY;    // 设置输出效果为抗锯齿    
  15.     settextstyle(&f);                     // 设置字体样式  
  16.     wsprintf((LPWSTR)m_pArray, _T("GAME OVER"));  
  17.     outtextxy(BATTLE_GROUND_X1 + 300, BATTLE_GROUND_Y1 + 250, (LPWSTR)m_pArray);  
  18.   
  19.     f.lfHeight = 18;  
  20.     settextstyle(&f);  
  21.     wsprintf((LPWSTR)m_pArray, _T("按 Enter 键退出"));  
  22.     outtextxy(BATTLE_GROUND_X1 + 550, BATTLE_GROUND_Y1 + 350, (LPWSTR)m_pArray);  
  23.   
  24.     settextstyle(&fontBak);  
  25.   
  26.     setcolor(color_save);  
  27.     setfillcolor(fill_color_save);  
  28. }  

  这个函数的实现和ShowGameLevel()函数非常类似。后期我们可以优化一下把它们的公用部分解耦出来。

  程序流程控制

  最后,我们把main()函数修改如下:

C++代码
  1. void main()  
  2. {  
  3.     Init();  
  4.   
  5.     bool loop = true;  
  6.     bool skip = false;  
  7.     bool bGameOver = false;  
  8.     while (loop)  
  9.     {  
  10.         if (kbhit())  
  11.         {  
  12.              int key = getch();  
  13.             if (skip && key != 13)  
  14.             {  
  15.                 continue;  
  16.             }  
  17.   
  18.             switch (key)  
  19.             {  
  20.             // Up  
  21.             case 72:  
  22.                 mainTank.SetDir(Dir::UP);  
  23.                 break;  
  24.             // Down  
  25.             case 80:   
  26.                 mainTank.SetDir(Dir::DOWN);  
  27.                 break;  
  28.             // Left  
  29.             case 75:   
  30.                 mainTank.SetDir(Dir::LEFT);  
  31.                 break;  
  32.             // Right  
  33.             case 77:   
  34.                 mainTank.SetDir(Dir::RIGHT);  
  35.                 break;  
  36.             case 224: // 方向键高8位  
  37.                 break;  
  38.             // Esc  
  39.             case 27:  
  40.                 loop = false;  
  41.                 break;  
  42.             // Space  
  43.             case 32:  
  44.                 mainTank.Shoot(lstMainTankBullets);  
  45.                 break;  
  46.             // Enter  
  47.             case 13:  
  48.                 if (skip)  
  49.                     skip = false;  
  50.                 else  
  51.                     skip = true;  
  52.                 break;  
  53.             default:   
  54.                 break;  
  55.             }  
  56.         }  
  57.   
  58.         if (!skip)  
  59.         {  
  60.             if (bGameOver)  
  61.             {  
  62.                 break;  
  63.             }  
  64.   
  65.             // Draw Background  
  66.             cleardevice();  
  67.             Graphic::DrawBattleGround();  
  68.   
  69.             CheckCrash();  
  70.   
  71.             Graphic::ShowScore();  
  72.   
  73.             // New Game Level  
  74.             if (Setting::m_bNewLevel)  
  75.             {  
  76.                 Setting::m_bNewLevel = false;  
  77.   
  78.                 Setting::NewGameLevel();  
  79.   
  80.                 Graphic::ShowGameLevel(Setting::GetGameLevel());  
  81.   
  82.                 for (int i = 0; i < Setting::GetTankNum(); i++)  
  83.                 {  
  84.                     EnemyTank* p = new EnemyTank();  
  85.                     lstTanks.push_back(p);  
  86.                 }  
  87.   
  88.                 // 设置暂停,按Enter开始  
  89.                 skip = true;  
  90.                 continue;  
  91.             }  
  92.   
  93.             if (mainTank.IsDisappear())  
  94.             {  
  95.                 skip = true;  
  96.                 bGameOver = true;  
  97.   
  98.                 Graphic::ShowGameOver();  
  99.   
  100.                 continue;  
  101.             }  
  102.   
  103.             mainTank.Move();  
  104.             mainTank.Display();  
  105.   
  106.             /* Draw Tanks */  
  107.             for (list<Tank*>::iterator it = lstTanks.begin(); it != lstTanks.end();)  
  108.             {  
  109.                 (*it)->Move();  
  110.   
  111.                 if ((*it)->IsDisappear())  
  112.                 {  
  113.                     Setting::TankDamaged();  
  114.   
  115.                     // Add a bomb  
  116.                     (*it)->Boom(lstBombs);  
  117.   
  118.                     // Delete the tank  
  119.                     delete *it;  
  120.                     it = lstTanks.erase(it);  
  121.                     continue;  
  122.                 }  
  123.   
  124.                 (*it)->Display();  
  125.   
  126.                 if ((*it)->NeedShoot())  
  127.                 {  
  128.                     EnemyTank* p = (EnemyTank*)*it;  
  129.                     p->Shoot(lstBullets);  
  130.                 }  
  131.                 it++;  
  132.             }  
  133.   
  134.             /* Draw Bullets */  
  135.             for (list<Object*>::iterator it = lstMainTankBullets.begin(); it != lstMainTankBullets.end();)  
  136.             {  
  137.                 (*it)->Move();  
  138.   
  139.                 if ((*it)->IsDisappear())  
  140.                 {  
  141.                     // Add a bomb  
  142.                     (*it)->Boom(lstBombs);  
  143.   
  144.                     // Delete the bullet  
  145.                     delete *it;  
  146.                     it = lstMainTankBullets.erase(it);  
  147.                     continue;  
  148.                 }  
  149.   
  150.                 (*it)->Display();  
  151.                 it++;  
  152.             }  
  153.   
  154.             for (list<Object*>::iterator it = lstBullets.begin(); it != lstBullets.end();)  
  155.             {  
  156.                 (*it)->Move();  
  157.   
  158.                 if ((*it)->IsDisappear())  
  159.                 {  
  160.                     // Add a bomb  
  161.                     (*it)->Boom(lstBombs);  
  162.   
  163.                     // Delete the bullet  
  164.                     delete *it;  
  165.                     it = lstBullets.erase(it);  
  166.                     continue;  
  167.                 }  
  168.   
  169.                 (*it)->Display();  
  170.                 it++;  
  171.             }  
  172.   
  173.             /* Draw Bombs */  
  174.             for (list<Object*>::iterator it = lstBombs.begin(); it != lstBombs.end();)  
  175.             {  
  176.                 (*it)->Move();  
  177.   
  178.                 if ((*it)->IsDisappear())  
  179.                 {  
  180.                     delete *it;  
  181.                     it = lstBombs.erase(it);  
  182.                     continue;  
  183.                 }  
  184.   
  185.                 (*it)->Display();  
  186.                 it++;  
  187.             }  
  188.         }  
  189.   
  190.         Sleep(100);  
  191.     }  
  192.   
  193.     // Destroy  
  194.     Dispose();  
  195. }  

  新加入一个bGameOver变量,用来表示游戏结束。

  当循环中发现主战坦克消失时,打印出游戏结束界面,之后通过skip变量暂停程序流程,等待用户按下Enter键。

C++代码
  1. if (mainTank.IsDisappear())  
  2. {  
  3.     skip = true;  
  4.     bGameOver = true;  
  5.   
  6.     Graphic::ShowGameOver();  
  7.     continue;  
  8. }  

  之后的下一次循环,程序通过判断bGameOver变量跳出循环,游戏结束。

  好了现在来试试我们的成功吧,看你能够过几关。

  今天的代码请在GitHub中下载。

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