目前我们的主战坦克已经能够开炮击毁敌人坦克了,但敌人坦克不会开炮貌似比较欺负人。今天我们让敌人坦克也拥有开炮功能。同时,我们要为游戏设定关卡,让它变得好玩起来。
让敌人坦克开炮
在EnemyTank类中,添加Shoot函数几乎和MainTank中完全相同,代码如下:
- void EnemyTank::Shoot(list<Object*>& lstBullets)
- {
- Bullet* pBullet = new Bullet(m_pos, m_dir, m_color);
- lstBullets.push_back(pBullet);
- m_bNeedShoot = false;
- }
这里唯一的区别是最后一句话,我们对m_bNeedShoot属性进行了赋值。
敌人坦克和主战坦克最大的区别在于自动开炮,它不像主战坦克那样通过相应空格键执行开炮函数,它是通过一定的算法完成自主开炮。因此我们在main函数中通过判断m_bNeedShoot属性来执行Shoot函数。
在Move函数中,我们添加下面这段代码:
- if (m_stepCnt % MAX_STEP_SHOOT == 0)
- {
- m_bNeedShoot = true;
- }
这段代码有点类似于敌人坦克的自动转向功能,我们让它每移动MAX_STEP_SHOOT步就通过将m_bNeedShoot设为true来执行开炮功能。
最后,我们在main函数中绘制坦克时加入下面这段代码:
- if ((*it)->NeedShoot())
- {
- EnemyTank* p = (EnemyTank*)*it;
- p->Shoot(lstBullets);
- }
当判断NeedShoot()返回值为true时,调用Shoot()函数进行开炮。
关卡设置
我们在玩游戏时,有一些必要的参数非常重要。比如,主要人物的生命值,等级,敌人数,分数等等。下面我们就给坦克大战中添加这些元素,让它变得更有游戏的乐趣。
我们为工程添加一组新的文件Setting.h和Setting.cpp,代码如下:
Setting.h
- #ifndef __SETTING_H__
- #define __SETTING_H__
- #include <list>
- using namespace std;
- class Setting
- {
- public:
- static void NewGameLevel();
- static void TankDamaged();
- static int GetLife()
- {
- return m_nLife;
- }
- static int GetGameLevel()
- {
- return m_nGameLevel;
- }
- static int GetTankLevel()
- {
- return m_nTankLevel;
- }
- static int GetTankNum()
- {
- return m_nTankNum;
- }
- static int GetSumScore()
- {
- return m_nSumScore;
- }
- static int GetTankSum()
- {
- return m_nTankSum;
- }
- static bool m_bNewLevel;
- private:
- static int m_nLife; // 生命值
- static int m_nGameLevel; // 当前游戏关卡
- static int m_nTankLevel; // 当前坦克级别
- static int m_nTankNum; // 当前坦克数
- static int m_nSumScore; // 总分
- static int m_nTankScore; // 击毁坦克得分
- static int m_nTankSum; // 共击毁坦克数
- };
- #endif
在Setting类中,我们添加了一组static属性。同时,添加了访问这些参数的static接口。这里重点关注两个函数:
NewGameLevel()函数,在每一关开始时初始化每一关的属性信息。TankDamaged()函数,在每次击毁一个坦克时调用,完成相关属性的数据更新。具体如下:
Setting.cpp
include "Setting.h"
- bool Setting::m_bNewLevel = true;
- int Setting::m_nLife = 3;
- int Setting::m_nGameLevel = 0;
- int Setting::m_nTankLevel = 1;
- int Setting::m_nTankNum = 5;
- int Setting::m_nSumScore = 0;
- int Setting::m_nTankScore = 5;
- int Setting::m_nTankSum = 0;
- void Setting::NewGameLevel()
- {
- m_nGameLevel++;
- m_nTankNum = 10 + 5 * (m_nGameLevel - 1);
- m_nTankScore += 5;
- }
- void Setting::TankDamaged()
- {
- m_nTankNum--;
- m_nSumScore += m_nTankScore;
- m_nTankLevel = m_nSumScore / 150 + 1;
- m_nTankSum++;
- if (m_nTankNum == 0)
- {
- m_bNewLevel = true;
- }
- }
前一部分是static属性的初始化,后面是两个函数的实现。相信大家一看就懂。这里的具体规则大家可以自行修改,制定规则才是游戏开发中最有乐趣的一部分。
绘制成绩
相关的数据我们需要绘制在屏幕的右半部分。我们在Graphic类中添加一个static函数:
- static void ShowScore();
实现如下:
- const int SCORE_LEFT = 810;
- const int SCORE_TOP = 5;
- void Graphic::ShowScore()
- {
- COLORREF fill_color_save = getfillcolor();
- COLORREF color_save = getcolor();
- //setfillcolor(m_color);
- //setcolor(m_color);
- rectangle(SCORE_LEFT, SCORE_TOP, SCORE_LEFT + 200, SCORE_TOP + 40);
- RECT r = { SCORE_LEFT, SCORE_TOP, SCORE_LEFT + 200, SCORE_TOP + 40 };
- wsprintf((LPWSTR)m_pArray, _T("第 %d 关"), Setting::GetGameLevel());
- drawtext((LPWSTR)m_pArray, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
- r.top += 50;
- r.bottom += 50;
- wsprintf((LPWSTR)m_pArray, _T("分 数 : %d"), Setting::GetSumScore());
- drawtext((LPWSTR)m_pArray, &r, DT_VCENTER | DT_SINGLELINE);
- r.top += 50;
- r.bottom += 50;
- wsprintf((LPWSTR)m_pArray, _T("级 别 : %d"), Setting::GetTankLevel());
- drawtext((LPWSTR)m_pArray, &r, DT_VCENTER | DT_SINGLELINE);
- r.top += 50;
- r.bottom += 50;
- wsprintf((LPWSTR)m_pArray, _T("生 命 : %d"), Setting::GetLife());
- drawtext((LPWSTR)m_pArray, &r, DT_VCENTER | DT_SINGLELINE);
- r.top += 50;
- r.bottom += 50;
- wsprintf((LPWSTR)m_pArray, _T("敌人数 : %d"), Setting::GetTankNum());
- drawtext((LPWSTR)m_pArray, &r, DT_VCENTER | DT_SINGLELINE);
- r.top += 50;
- r.bottom += 50;
- line(SCORE_LEFT, r.bottom, SCREEN_WIDTH - 5, r.bottom);
- r.top += 50;
- r.bottom += 50;
- wsprintf((LPWSTR)m_pArray, _T("共击毁敌人数 : %d"), Setting::GetTankSum());
- drawtext((LPWSTR)m_pArray, &r, DT_VCENTER | DT_SINGLELINE);
- setcolor(color_save);
- setfillcolor(fill_color_save);
- }
最后,需要把这些调用放在main函数中,这里不做说明,请大家自己试一下。如果自己写不出来请在我的GitHub中下载完整代码。
执行效果就是文章开头的图片效果。
转自:简书 作者:天花板
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。