C++代码训练营 | 坦克大战(6)
上一篇中,我们的主战坦克发出的炮弹还没有实际的作用,今天我们就让它拥有击毁敌军坦克的功能。
新增基础API
Rect类
在Rect类中,我们添加下面两个新接口。
Point GetTRPoint() const; // Get Top Right Point
Point GetBLPoint() const; // Get Bottom Left Point
这两个函数的作用是返回矩形右上角和左下角的两个点。这样方便我们在计算炮弹是否击中坦克时使用。
实现如下:
Point Rect::GetTRPoint() const
{
Point p = m_startPoint;
p.SetX(m_endPoint.GetX());
return p;
}
Point Rect::GetBLPoint() const
{
Point p = m_startPoint;
p.SetY(m_endPoint.GetY());
return p;
}
Object类
在判断炮弹是否击中坦克时,我们需要通过势力范围m_rectSphere这个属性。我们为Object类中添加下面这个虚函数函数:
virtual Rect GetSphere() = 0;
在所有的继承类中,都要对这个函数进行实现,实现方法很简单就是返回m_rectSphere属性即可。
Rect GetSphere()
{
return m_rectSphere;
}
Tank类
在坦克类中,我们需要添加一个设置坦克消失的接口,当坦克被击中时,我们通过这个接口把m_bDisappear属性设为true。
void SetDisappear()
{
m_bDisappear = true;
}
位置判断
在游戏设计中,有个概念叫做“碰撞检测”,用来判断两个元素是否碰在一起。比如我们的坦克大战中,如何判断炮弹是否击中了坦克。另外,一些格斗类游戏中,人物出拳或踢腿是否击中目标都需要有相应的算法来判断。
网上有一些开源的库可以帮助我们完成一些复杂形状间的碰撞检测。在这里,我们把炮弹和坦克之间的碰撞检测简化为两个形状势力范围是否重叠的判断。也就是判断两个矩形是否重叠。
我们创建一个目录Utils来存放一些功能性的代码,里面先创建下面一组文件。
Shape.h
#ifndef __SHAPE_H__
#define __SHAPE_H__
#include "../Model/Rect.h"
class Shape
{
public:
static bool CheckPointInRect(Point& point, Rect& rect);
static bool CheckIntersect(Rect& rectA, Rect& rectB);
};
#endif
Shape是一个静态类,里面有两个静态成员函数。
CheckPointInRect函数负责判断一个点是否在一个矩形的范围内。CheckInterSect函数负责判断两个矩形是否重合。
Shape.cpp
#include "Shape.h"
bool Shape::CheckPointInRect(Point& point, Rect& rect)
{
if (point.GetX() < rect.GetStartPoint().GetX() || point.GetX() > rect.GetEndPoint().GetX() ||
point.GetY() < rect.GetStartPoint().GetY() || point.GetY() > rect.GetEndPoint().GetY())
{
return false;
}
else
{
return true;
}
}
bool Shape::CheckIntersect(Rect& rectA, Rect& rectB)
{
if (CheckPointInRect(rectA.GetStartPoint(), rectB) ||
CheckPointInRect(rectA.GetEndPoint(), rectB) ||
CheckPointInRect(rectA.GetTRPoint(), rectB) ||
CheckPointInRect(rectA.GetBLPoint(), rectB))
{
return true;
}
else
{
return false;
}
}
判断两个矩形是否重合的方法很简单,只要判断第一个矩形的四个顶点是否在第二个矩形的范围内即可。
功能实现
在main.cpp中,添加一个函数来实时判断是否有炮弹击中坦克的情况。
void CheckCrash()
{
for (list<Object*>::iterator it = lstMainTankBullets.begin(); it != lstMainTankBullets.end(); it++)
{
for (list<Tank*>::iterator itt = lstTanks.begin(); itt != lstTanks.end(); itt++)
{
if (Shape::CheckIntersect((*it)->GetSphere(), (*itt)->GetSphere()))
{
(*itt)->SetDisappear();
(*it)->SetDisappear();
}
}
}
}
分别遍历主战坦克的炮弹list和坦克list,两两进行碰撞检测,发现有相交的就通过接口把两个元素都设置为消失。后面的工作交给展示阶段完成。
炮弹失效的动作我们已经在上一篇实现了,在这里只需要添加坦克销售的动作。
for (list<Tank*>::iterator it = lstTanks.begin(); it != lstTanks.end();)
{
(*it)->Move();
if ((*it)->IsDisappear())
{
// Add a bomb
(*it)->Boom(lstBombs);
// Delete the tank
delete *it;
it = lstTanks.erase(it);
continue;
}
(*it)->Display();
it++;
}
绘制坦克时,发现失效后添加一个爆炸的对象,之后删除这个坦克。
需要注意的是,在实现坦克的Boom接口时,我们创建的是一个大的爆炸。
void Boom(list<Object*>& lstBombs)
{
lstBombs.push_back(new Bomb(m_pos, LARGE));
}
一看代码大家就应该明白了。
好了,运行一下程序看看效果是不是和文章开头相同呢?
项目源码托管在GitHub上,请大家自行下载。
我是天花板,让我们一起在软件开发中自我迭代。
如有任何问题,欢迎与我联系。
上一篇:C++代码训练营 | 坦克大战(5)
下一篇:C++代码训练营 | 坦克大战(7)