本篇文章总结一下我在网上看到的一个C++坦克大战小游戏,我在原有的基础上加入了技能功能,并对碰撞代码进行了改进,然后将代码进行了整理,感觉很有意思。游戏中用到了EasyX图形库,这个库非常小巧轻便,下载地址:https://www.easyx.cn/downloads/
现在正文开始,首先,创建一个简单的坐标类
#ifndef POINT_H
#define POINT_H
//坐标类
class Point
{
public:
Point(int x = 0, int y = 0) : m_x(x), m_y(y) {};
~Point() {};
Point& operator=(const Point &p)
{
m_x = p.m_x;
m_y = p.m_y;
return *this;
}
void Set(int x, int y){
m_x = x;
m_y = y;
}
void SetX(int x) { m_x = x; }
void SetY(int y) { m_y = y; }
int GetX() const{ return m_x; }
int GetY()const {return m_y;}
private:
int m_x;
int m_y;
};
#endif
然后为了便于画图,创建一个Rect类
#include "Point.h"
#ifndef RECT_H
#define RECT_H
//矩形类,便于画图
class Rect
{
public:
Rect(int x1 = 0, int y1 = 0, int x2 = 0, int y2 = 0) :
m_StartPoint(x1, y1), m_EndPoint(x2, y2)
{}
Rect(const Point p1, const Point p2) :
m_StartPoint(p1), m_EndPoint(p2)
{}
Rect(const Rect& r1):
m_StartPoint(r1.GetStartPoint()), m_EndPoint(r1.GetEndPoint())
{}
~Rect() {};
void SetRect(const Point pStart, const Point pEnd);
void SetRect(int x1, int y1, int x2, int y2);
void SetStartPoint(const Point p){ m_StartPoint = p; }
void SetEndPoint(const Point p){ m_EndPoint = p; }
Point GetStartPoint() const{ return m_StartPoint; }
Point GetEndPoint() const{ return m_EndPoint; }
Point GetRightPoint() const;
Point GetLeftPoint() const;
Rect& operator=(const Rect &rect)
{
m_StartPoint = rect.GetStartPoint();
m_EndPoint = rect.GetEndPoint();
return *this;
}
int GetWidth();
int GetHeight();
private:
Point m_StartPoint;//矩形左上角的坐标点
Point m_EndPoint;//矩形右下角的坐标点
};
#endif
#include "Rect.h"
void Rect::SetRect(Point pStart, Point pEnd)
{
m_StartPoint = pStart;
m_EndPoint = pEnd;
}
void Rect::SetRect(int x1, int y1, int x2, int y2)
{
m_StartPoint.Set(x1, y1);
m_EndPoint.Set(x2, y2);
}
Point Rect::GetRightPoint() const
{
Point p = m_StartPoint;
p.SetX(m_EndPoint.GetX());
return p;
}
Point Rect::GetLeftPoint() const
{
Point p = m_StartPoint;
p.SetY(m_EndPoint.GetY());
return p;
}
int Rect::GetWidth()
{
return m_EndPoint.GetX() - m_StartPoint.GetX();
}
int Rect::GetHeight()
{
return m_EndPoint.GetY() - m_StartPoint.GetY();
}
游戏对象之间会发生碰撞,需要写一个用于碰撞检测的类Shape
#include "Rect.h"
#ifndef SHAPE_H
#define SHAPE_H
//该类用于碰撞检测
class Shape
{
public:
static bool CheckPointInRect(Point& point, Rect& rect);//判断第一个参数点point是否在第二个参数代表的矩形内
static bool CheckIntersect(Rect& rectA, Rect& rectB);//判断两个矩形是否碰撞
};
#endif
#include "Shape.h"
bool Shape::CheckPointInRect(Point& point, Rect& rect)
{
if (point.GetX() < rect.GetStartPoint().GetX()-1 || point.GetX() >rect.GetEndPoint().GetX()+1 ||
point.GetY() <rect.GetStartPoint().GetY()-1 || point.GetY() >rect.GetEndPoint().GetY()+1)
return false;
else return true;
}
bool Shape::CheckIntersect(Rect& rectA, Rect& rectB)
{
if (CheckPointInRect(rectA.GetStartPoint(), rectB) ||
CheckPointInRect(rectA.GetEndPoint(), rectB) ||
CheckPointInRect(rectA.GetRightPoint(), rectB) ||
CheckPointInRect(rectA.GetLeftPoint(), rectB))
return true;
else return false;
}
好的,现在需要一个对游戏数据进行统计处理的类,因为游戏中的数据是伴随整个游戏过程的,区别与游戏中具体的对象,所以把数据成员都设置为静态
#include <list>
#ifndef SETTING_H
#define SETTING_H
using namespace std;
//数值设定类,用于计算分数和关卡等
class Setting
{
public:
static bool NewLev;
static int GetGameLevel() { return m_GameLevel; }
static int GetTankNum() { return m_TankNum; }
static int GetHp(){return m_Hp;}
static void SetHp(int hp) { m_Hp = hp; }
static int GetSumScore(){ return m_SumScore; }
static int GetKillNum() { return m_KillNum; }
static void NewGameLevel();
static void Damaged();
static int GetGameSpeed() { return m_GameSpeed; }
private:
static int m_GameSpeed; //游戏速度
static int m_Hp; // 生命值
static int m_GameLevel; // 关卡等级
static int m_TankNum; // 当前坦克数
static int m_SumScore; // 总分
static int m_KillScore; // 击毁坦克得分
static int m_KillNum; // 共击毁坦克数
};
#endif
#include "Setting.h"
int Setting::m_GameSpeed = 60;
bool Setting::NewLev = true;
int Setting::m_Hp = 20;
int Setting::m_GameLevel = 0;
int Setting::m_TankNum = 5;
int Setting::m_SumScore = 0;
int Setting::m_KillScore = 5;
int Setting::m_KillNum = 0;
void Setting::NewGameLevel()
{
m_GameLevel++;
m_TankNum = 10 + 5 * (m_GameLevel - 1);
m_KillScore += 5;
}
void Setting::Damaged()
{
m_TankNum--;
m_SumScore += m_KillScore;
m_KillNum++;
if (m_TankNum == 0) NewLev = true;
}
有了以上的基础后,还需要创建一个画图类,进行游戏基础界面的展示,注意这个类需要包含graphics.h,用到了一些画图接 口,需要查文档
#include <graphics.h>
#include "Rect.h"
#ifndef GRAPHIC_H
#define GRAPHIC_H
enum
{
SCREEN_WIDTH = 1024,
SCREEN_HEIGHT = 568
};
enum
{
BATTLE_GROUND_X1 = 5,
BATTLE_GROUND_Y1 = 5,
BATTLE_GROUND_X2 = 800,
BATTLE_GROUND_Y2 = (SCREEN_HEIGHT - BATTLE_GROUND_Y1)
};
class Graphic
{
public:
static void DrawBattleGround();
static void Create();
static void Destroy();
static void ShowScore();
static int GetScreenWidth(){ return SCREEN_WIDTH; }
static int GetScreenHeight(){ return SCREEN_HEIGHT; }
static Rect GetBattleGround(){ return m_RectBattleGround; }
private:
static Rect m_RectBattleGround;
static Rect m_RectScreen;
static char m_pArray[100];
};
#endif
#include "Graphic.h"
#include "Setting.h"
enum { SCORE_LEFT = 810, SCORE_TOP = 5 };
Rect Graphic::m_RectScreen;
Rect Graphic::m_RectBattleGround;
char Graphic::m_pArray[100];
void Graphic::ShowScore()
{
COLORREF fill_color_save = getfillcolor();
COLORREF color_save = getcolor();
rectangle(SCORE_LEFT, SCORE_TOP, SCORE_LEFT + 200, SCORE_TOP + 40);
RECT r = { SCORE_LEFT, SCORE_TOP, SCORE_LEFT + 200, SCORE_TOP + 40 };
wsprintf((LPSTR)m_pArray, _T("第 %d 关"), Setting::GetGameLevel());
drawtext((LPSTR)m_pArray, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
r.top += 50; r.bottom += 50;
wsprintf((LPSTR)m_pArray, _T("分 数 : %d"), Setting::GetSumScore());
drawtext((LPSTR)m_pArray, &r, DT_VCENTER | DT_SINGLELINE);
r.top += 50; r.bottom += 50;
wsprintf((LPSTR)m_pArray, _T("生 命 : %d"), Setting::GetHp());
drawtext((LPSTR)m_pArray, &r, DT_VCENTER | DT_SINGLELINE);
r.top += 50; r.bottom += 50;
wsprintf((LPSTR)m_pArray, _T("敌人数 : %d"), Setting::GetTankNum());
drawtext((LPSTR)m_pArray, &r, (DT_VCENTER | DT_SINGLELINE));
r.top += 50; r.bottom += 50;
wsprintf((LPSTR)m_pArray, _T("击毁敌人数 : %d"), Setting::GetKillNum());
drawtext((LPSTR)m_pArray, &r, DT_VCENTER | DT_SINGLELINE);
setcolor(color_save);
setfillcolor(fill_color_save);
}
void Graphic::Create()
{
m_RectScreen.SetRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
initgraph(SCREEN_WIDTH, SCREEN_HEIGHT);
setbkcolor(CYAN);
m_RectBattleGround.SetRect(BATTLE_GROUND_X1, BATTLE_GROUND_Y1, BATTLE_GROUND_X2, BATTLE_GROUND_Y2);
}
void Graphic::Destroy(){closegraph();}
void Graphic::DrawBattleGround()
{
rectangle(m_RectBattleGround.GetStartPoint().GetX(), m_RectBattleGround.GetStartPoint().GetY(),
m_RectBattleGround.GetEndPoint().GetX(), m_RectBattleGround.GetEndPoint().GetY());
}
现在我们可以开始创造游戏中的对象了,首先创建一个Object类,他是游戏中所有对象的共同父类,因为游戏中的每个对象都需要有展示、判断或设置状态和计算碰撞范围等功能,所以设置一些虚函数,以此对子类进行约束。该类同样需要包含Graphic.h
#ifndef OBJECT
#define OBJECT
#include <list>
#include "Graphic.h"
using namespace std;
enum Dir { UP, DOWN, LEFT, RIGHT };
//物体类,一切游戏对象(坦克、子弹、食物)的基类
class Object
{
public:
virtual void Display() = 0;
virtual void SetDisappear() = 0;
virtual bool IsDisappear() = 0;
virtual Rect GetRegion() = 0;
protected:
virtual void CalculateRegion() = 0; // 计算碰撞范围
Dir m_Direction;
Point m_Position;
Rect m_RectSphere;//碰撞范围
int m_Speed;
COLORREF m_Color;
bool m_IsDisappear;
};
#endif
可以说以上代码是在控制台下编写小游戏的基础代码,从这里开始,代码就可以灵活多变了,我们要做的是一个坦克大战小游戏,所以先创建一个爆炸类,继承自Object,用于物体被销毁时展现特效
#ifndef BOMB_H
#define BOMB_H
#include "Object.h"
enum BombType
{
LARGE,
SMALL
};
class Bomb : public Object
{
public:
Bomb();
Bomb(Point pos, BombType type);
~Bomb() {}
void Display();
void Move();
bool IsDisappear();
void SetDisappear() {}
Rect GetRegion(){return m_RectSphere;}
protected:
void CalculateRegion(){}
BombType m_type;//爆炸有两种类型 坦克的大爆炸和子弹的小爆炸
int m_timer;
};
#endif
#include "Bomb.h"
Bomb::Bomb()
{
this->m_IsDisappear = false;
this->m_Color = YELLOW;
this->m_Direction = UP;
}
Bomb::Bomb(Point pos, BombType type) : Bomb()
{
this->m_Position = pos;
this->m_type = type;
switch (m_type)
{
case LARGE:
m_timer = 8;
break;
case SMALL:
m_timer = 4;
break;
default:
break;
}
}
void Bomb::Display()
{
//保存当前颜色设置
COLORREF fill_color_save = getfillcolor();
COLORREF color_save = getcolor();
//画圆
setfillcolor(m_Color);
setcolor(RED);
fillcircle(m_Position.GetX(), m_Position.GetY(), 8 - m_timer);
//恢复之前的颜色设置
setcolor(color_save);
setfillcolor(fill_color_save);
}
void Bomb::Move()//每调用一次Move 爆炸圆就扩大
{
m_timer -= 2;
if (m_timer < 0)
m_IsDisappear = true;
}
bool Bomb:: IsDisappear()
{
return m_IsDisappear;
}
之后再构建子弹类
#ifndef BULLET_H
#define BULLET_H
#include "Object.h"
class Bomb;
//子弹类
class Bullet : public Object
{
public:
Bullet();
Bullet(Point pos, Dir dir, COLORREF color);
~Bullet();
void Display();
void Move();
void Boom(list<Bomb*>& listBombs);
bool IsDisappear(){ return m_IsDisappear;}
Rect GetRegion(){return m_RectSphere;}
void SetDisappear(){m_IsDisappear = true;}
protected:
void CalculateRegion();
};
#endif
#include "Bullet.h"
#include"Bomb.h"
Bullet::Bullet(){}
Bullet::Bullet(Point pos, Dir dir, COLORREF color)
{
m_Position = pos;
m_Direction = dir;
m_Color = color;
m_Speed = 20;
m_IsDisappear = false;
CalculateRegion();
}
Bullet::~Bullet(){}
void Bullet::Display()
{ //保存当前颜色设置
COLORREF fill_color_save = getfillcolor();
COLORREF color_save = getcolor();
//描绘子弹
setfillcolor(m_Color);
setcolor(m_Color);
fillcircle(m_Position.GetX(), m_Position.GetY(), 4);
//恢复颜色设置
setcolor(color_save);
setfillcolor(fill_color_save);
}
void Bullet::Move()
{
switch (m_Direction)
{
case UP:
m_Position.SetY(m_Position.GetY() - m_Speed);
CalculateRegion();
if (m_RectSphere.GetStartPoint().GetY() < Graphic::GetBattleGround().GetStartPoint().GetY())
{
m_Position.SetY(Graphic::GetBattleGround().GetStartPoint().GetY());
m_IsDisappear = true;
}
break;
case DOWN:
m_Position.SetY(m_Position.GetY() + m_Speed);
CalculateRegion();
if (m_RectSphere.GetEndPoint().GetY() > Graphic::GetBattleGround().GetEndPoint().GetY())
{
m_Position.SetY(Graphic::GetBattleGround().GetEndPoint().GetY());
m_IsDisappear = true;
}
break;
case LEFT:
m_Position.SetX(m_Position.GetX() - m_Speed);
CalculateRegion();
if (m_RectSphere.GetStartPoint().GetX() < Graphic::GetBattleGround().GetStartPoint().GetX())
{
m_Position.SetX(Graphic::GetBattleGround().GetStartPoint().GetX());
m_IsDisappear = true;
}
break;
case RIGHT:
m_Position.SetX(m_Position.GetX() + m_Speed);
CalculateRegion();
if (m_RectSphere.GetEndPoint().GetX() > Graphic::GetBattleGround().GetEndPoint().GetX())
{
m_Position.SetX(Graphic::GetBattleGround().GetEndPoint().GetX());
m_IsDisappear = true;
}
break;
default:
break;
}
}
void Bullet::Boom(list<Bomb*>& lstBombs)
{
lstBombs.push_back(new Bomb(m_Position, SMALL));
}
void Bullet::CalculateRegion()
{
m_RectSphere.SetRect(m_Position.GetX() - 2, m_Position.GetY() - 2,
m_Position.GetX() + 2, m_Position.GetY() + 2);
}
坦克类,继承于Object,敌人坦克和玩家坦克的父类
#include "Object.h"
#include "Bomb.h"
#include <list>
#ifndef TANK
#define TANK
using namespace std;
class Bullet;
class Tank : public Object
{
public:
Tank()
{
this->CalculateRegion();
m_IsDisappear = false;
m_IsNeedShoot = false;
}
~Tank() {}
void Display(){}
void Move(){}
void Boom(list<Bomb*>& listBombs)
{
listBombs.push_back(new Bomb(m_Position, LARGE));
}
void Shoot(list<Bullet*>& lstBullets){}
inline Rect GetRegion(){return m_RectSphere;}
inline bool IsNeedShoot(){return m_IsNeedShoot;}
inline void SetDisappear() { m_IsDisappear = true; }
inline bool IsDisappear() { return m_IsDisappear; }
protected:
void CalculateRegion(){}
bool m_IsNeedShoot;
};
#endif
玩家坦克类,为了便于实现技能效果,增添一个AddHp()
#ifndef MyTANK
#define MyTANK
#include "Tank.h"
//玩家坦克类
class MyTank : public Tank
{
public:
MyTank() : Tank()
{
m_Hp = 20;
m_Position.Set(300, 300);
this->CalculateRegion();
m_Color = YELLOW;
m_Direction = Dir::UP;
m_Speed = 2;
m_isCrashWithOther = false;
}
~MyTank() {}
void SetDir(Dir dir);
void Display();
void Move();
void Shoot(list<Bullet*>& lstBullets);
void Beaten() {
this->m_Hp--;
}
void AddHp();
int GetHp() { return m_Hp; }
int GetDir() { return m_Direction; }
void SetNowDir(int dir) { m_NextDir = dir; }
void CrashWithOther() { m_isCrashWithOther = true; }
void noCrashWithOther(){ m_isCrashWithOther = false; }
protected:
void CalculateRegion();
// 绘制坦克主体
void DrawTankBody();
private:
int m_NextDir;//接下来的方向,用于实现与其他坦克的碰撞效果,在move函数中应用
int m_Hp;
bool m_isCrashWithOther;//标记是否已经与其他坦克发生碰撞
};
#endif
#include "MyTank.h"
#include "Bullet.h"
void MyTank::SetDir(Dir dir) { m_Direction = dir; }
void MyTank::DrawTankBody()
{
fillrectangle(m_Position.GetX() - 6, m_Position.GetY() - 6, m_Position.GetX() + 6, m_Position.GetY() + 6);
//绘制矩形 组成坦克形状
switch (m_Direction)
{
case UP:
case DOWN:
fillrectangle(m_RectSphere.GetStartPoint().GetX(),
m_RectSphere.GetStartPoint().GetY(),
m_RectSphere.GetStartPoint().GetX() + 2,
m_RectSphere.GetEndPoint().GetY());
fillrectangle(m_RectSphere.GetEndPoint().GetX() - 2,
m_RectSphere.GetStartPoint().GetY(),
m_RectSphere.GetEndPoint().GetX(),
m_RectSphere.GetEndPoint().GetY());
break;
case LEFT:
case RIGHT:
fillrectangle(m_RectSphere.GetStartPoint().GetX(),
m_RectSphere.GetStartPoint().GetY(),
m_RectSphere.GetEndPoint().GetX(),
m_RectSphere.GetStartPoint().GetY() + 2);
fillrectangle(m_RectSphere.GetStartPoint().GetX(),
m_RectSphere.GetEndPoint().GetY() - 2,
m_RectSphere.GetEndPoint().GetX(),
m_RectSphere.GetEndPoint().GetY());
break;
default:
break;
}
}
void MyTank::Display()
{
COLORREF fill_color_save = getfillcolor();
COLORREF color_save = getcolor();
setfillcolor(m_Color);
setcolor(m_Color);
DrawTankBody();
switch (m_Direction)
{
case UP:
line(m_Position.GetX(), m_Position.GetY(), m_Position.GetX(), m_Position.GetY() - 15);
break;
case DOWN:
line(m_Position.GetX(), m_Position.GetY(), m_Position.GetX(), m_Position.GetY() + 15);
break;
case LEFT:
line(m_Position.GetX(), m_Position.GetY(), m_Position.GetX() - 15, m_Position.GetY());
break;
case RIGHT:
line(m_Position.GetX(), m_Position.GetY(), m_Position.GetX() + 15, m_Position.GetY());
break;
default:
break;
}
setcolor(color_save);
setfillcolor(fill_color_save);
}
void MyTank::Move()
{
if (m_isCrashWithOther) {//如果撞到其他坦克,无法保持原有方向移动
if (m_Direction == m_NextDir)return;
}
switch (m_Direction)
{
case UP:
if (m_isCrashWithOther)m_NextDir = UP;
else m_NextDir = 4;
if (m_RectSphere.GetStartPoint().GetY() > Graphic::GetBattleGround().GetStartPoint().GetY()+3)
m_Position.SetY(m_Position.GetY() - m_Speed); // 如果没到边界,移动
break;
case DOWN:
if (m_isCrashWithOther)m_NextDir = DOWN;
else m_NextDir = 4;
if (m_RectSphere.GetEndPoint().GetY()< Graphic::GetBattleGround().GetEndPoint().GetY()-3)
m_Position.SetY(m_Position.GetY() + m_Speed);
break;
case LEFT:
if (m_isCrashWithOther)m_NextDir = LEFT;
else m_NextDir = 4;
if (m_RectSphere.GetStartPoint().GetX() > Graphic::GetBattleGround().GetStartPoint().GetX()+3)
m_Position.SetX(m_Position.GetX() - m_Speed);
break;
case RIGHT:
if (m_isCrashWithOther)m_NextDir = RIGHT;
else m_NextDir = 4;
if (m_RectSphere.GetEndPoint().GetX() < Graphic::GetBattleGround().GetEndPoint().GetX()-3)
m_Position.SetX(m_Position.GetX() + m_Speed);
break;
default:
break;
}
CalculateRegion();
}
void MyTank::CalculateRegion()
{
switch (m_Direction)
{
case UP:
case DOWN:
m_RectSphere.SetRect(m_Position.GetX() - 13, m_Position.GetY() - 10,
m_Position.GetX() + 13, m_Position.GetY() + 10);
break;
case LEFT:
case RIGHT:
m_RectSphere.SetRect(m_Position.GetX() - 10, m_Position.GetY() - 13,
m_Position.GetX() + 10, m_Position.GetY() + 13);
break;
default:
break;
}
}
void MyTank::Shoot(list<Bullet*>& lstBullets)
{
Bullet* pBullet = new Bullet(m_Position, m_Direction, m_Color);//根据移动方向和位置发射子弹
lstBullets.push_back(pBullet);
}
void MyTank::AddHp()
{
m_Hp += 2;
if (m_Hp > 20) m_Hp=20;
}
敌人坦克类,此处为了实现之后的技能效果,数据成员中增加一个标志位,检测是否被群体冰冻
#include "Tank.h"
#ifndef ENEMYTANK_H
#define ENEMYTANK_H
enum { MAX_STEP_TURN=20, MAX_STEP_SHOOT=15 };
//敌方坦克类
class EnemyTank : public Tank
{
public:
EnemyTank()
{
RandomTank();
}
~EnemyTank() {}
void Display();
void Move();
void Shoot(list<Bullet*>& lstBullets);
void CrashWithOther() { m_isCrashWithOther = true; }
void noCrashWithOther() { m_isCrashWithOther = false; }
static void Cold() { m_IsCold = true;}
static bool IsCold() { return m_IsCold; }
static void Release() { m_IsCold = false; }
protected:
void CalculateRegion();
void RandomTank();//随机生成坦克信息
void RandomDir(int type); // 随机产生坦克方向
int m_StepCount;//记录步数,便于当坦克在一个方向走到一定步数时改变方向
private:
static bool m_IsCold;
bool m_isCrashWithOther;
};
#endif
#include "EnemyTank.h"
#include "Bullet.h"
bool EnemyTank::m_IsCold = false;
void EnemyTank::RandomTank()
{
m_Position.SetX(rand() % (Graphic::GetBattleGround().GetWidth() - 30) + 15);
m_Position.SetY(rand() % (Graphic::GetBattleGround().GetHeight() - 30) + 15);
m_Color = RED;
m_Direction = (Dir)(Dir::UP + (rand() % 4));
m_Speed = 2;
m_StepCount = rand();
m_isCrashWithOther = false;
}
void EnemyTank::RandomDir(int type)
{
if (type)
{
Dir dir;
while ((dir = (Dir)(Dir::UP + (rand() % 4))) == m_Direction){}
m_Direction = dir;
}
else m_Direction = (Dir)(Dir::UP + (rand() % 4));
}
void EnemyTank::Display()
{
COLORREF fill_color_save = getfillcolor();
COLORREF color_save = getcolor();
setfillcolor(m_Color);
setcolor(m_Color);
//绘制矩形 组成坦克形状
fillrectangle(m_Position.GetX() - 6, m_Position.GetY() - 6, m_Position.GetX() + 6, m_Position.GetY() + 6);
fillrectangle(m_RectSphere.GetStartPoint().GetX(),
m_RectSphere.GetStartPoint().GetY(),
m_RectSphere.GetEndPoint().GetX(),
m_RectSphere.GetStartPoint().GetY() + 4);
fillrectangle(m_RectSphere.GetStartPoint().GetX(),
m_RectSphere.GetEndPoint().GetY() - 4,
m_RectSphere.GetEndPoint().GetX(),
m_RectSphere.GetEndPoint().GetY());
fillrectangle(m_RectSphere.GetStartPoint().GetX(),
m_RectSphere.GetStartPoint().GetY(),
m_RectSphere.GetStartPoint().GetX() + 4,
m_RectSphere.GetEndPoint().GetY());
fillrectangle(m_RectSphere.GetEndPoint().GetX() - 4,
m_RectSphere.GetStartPoint().GetY(),
m_RectSphere.GetEndPoint().GetX(),
m_RectSphere.GetEndPoint().GetY());
switch (m_Direction)
{
case UP:
line(m_Position.GetX(), m_Position.GetY(), m_Position.GetX(), m_Position.GetY() - 15);
break;
case DOWN:
line(m_Position.GetX(), m_Position.GetY(), m_Position.GetX(), m_Position.GetY() + 15);
break;
case LEFT:
line(m_Position.GetX(), m_Position.GetY(), m_Position.GetX() - 15, m_Position.GetY());
break;
case RIGHT:
line(m_Position.GetX(), m_Position.GetY(), m_Position.GetX() + 15, m_Position.GetY());
break;
default:
break;
}
setcolor(color_save);
setfillcolor(fill_color_save);
}
void EnemyTank::Move()
{
if (m_isCrashWithOther)//如果撞到玩家,直接回头
{
if (m_Direction == UP)m_Direction = DOWN;
if (m_Direction == DOWN)m_Direction = UP;
if (m_Direction == LEFT)m_Direction = RIGHT;
if (m_Direction == RIGHT)m_Direction = LEFT;
}
switch (m_Direction)
{
case UP:
if (m_RectSphere.GetStartPoint().GetY() >Graphic::GetBattleGround().GetStartPoint().GetY()+3)
m_Position.SetY(m_Position.GetY() - m_Speed);
else this->RandomDir(1);
break;
case DOWN:
if (m_RectSphere.GetEndPoint().GetY()< Graphic::GetBattleGround().GetEndPoint().GetY()-3)
m_Position.SetY(m_Position.GetY() + m_Speed);
else this->RandomDir(1);
break;
case LEFT:
if (m_RectSphere.GetStartPoint().GetX() > Graphic::GetBattleGround().GetStartPoint().GetX()+3)
m_Position.SetX(m_Position.GetX() - m_Speed);
else this->RandomDir(1);
break;
case RIGHT:
if (m_RectSphere.GetEndPoint().GetX() < Graphic::GetBattleGround().GetEndPoint().GetX()-3)
m_Position.SetX(m_Position.GetX() + m_Speed);
else this->RandomDir(1);
break;
default:
break;
}
CalculateRegion();
m_StepCount++;
if (m_StepCount % MAX_STEP_TURN == 0)
{
this->RandomDir(0);
}
if (m_StepCount % MAX_STEP_SHOOT == 0)
{
m_IsNeedShoot = true;
}
}
void EnemyTank::CalculateRegion()
{
switch (m_Direction)
{
case UP:
case DOWN:
m_RectSphere.SetRect(m_Position.GetX() - 13, m_Position.GetY() - 10,
m_Position.GetX() + 13, m_Position.GetY() + 10);
break;
case LEFT:
case RIGHT:
m_RectSphere.SetRect(m_Position.GetX() - 10, m_Position.GetY() - 13,
m_Position.GetX() + 10, m_Position.GetY() + 13);
break;
default:
break;
}
}
void EnemyTank::Shoot(list<Bullet*>& lstBullets)
{
Bullet* pBullet = new Bullet(m_Position, m_Direction, m_Color);
lstBullets.push_back(pBullet);
m_IsNeedShoot = false;
}
为了让游戏中的元素更加丰富,再增添两个食物类,玩家吃掉食物可获得额外效果
冰冻食物类:可让所有敌人冻结8秒,具体实现冰冻效果需要借助坦克类中的冰冻标志位
#include "Object.h"
#include<time.h>
#include <list>
#ifndef COLDFOOD
#define COLDFOOD
using namespace std;
class ColdFood: public Object
{
public:
ColdFood()
{
m_Color = BLUE;
m_IsDisappear = false;
m_ColdTimeBegin = 0;
m_ColdTimeEnd = 0;
srand((unsigned)time(0));
m_Position.Set(rand() % 790, rand() % 550);
}
~ColdFood() {}
void Display();
void SetDisappear(){m_IsDisappear = true;}
bool IsDisappear(){return m_IsDisappear;}
Rect GetRegion(){return m_RectSphere;}
void Shoot(){}
void CalculateRegion();
void Fresh();
double GetDeltaTime() { return m_ColdTimeEnd - m_ColdTimeBegin; }
void ColdBegin() { m_ColdTimeBegin = time(0); }
int GetColdTime(){ return m_ColdTime; }
private:
double m_ColdTimeBegin ;//技能启动时间
double m_ColdTimeEnd;//当前时间
int m_ColdTime;
};
#endif
#include"ColdFood.h"
void ColdFood::Display()
{
COLORREF fill_color_save = getfillcolor();
COLORREF color_save = getcolor();
setcolor(m_Color);
setfillcolor(m_Color);
fillrectangle(BATTLE_GROUND_X1+m_Position.GetX()-7,
BATTLE_GROUND_Y1+ m_Position.GetY()-7,
BATTLE_GROUND_X1+ m_Position.GetX() + 7,
BATTLE_GROUND_Y1+ m_Position.GetY() + 7);
setcolor(color_save);
setfillcolor(fill_color_save);
m_ColdTimeEnd = time(0);//记录当前时间
}
void ColdFood::CalculateRegion()
{
m_RectSphere.SetRect(BATTLE_GROUND_X1 + m_Position.GetX() - 7,
BATTLE_GROUND_Y1 + m_Position.GetY() - 7,
BATTLE_GROUND_X1 + m_Position.GetX() + 7,
BATTLE_GROUND_Y1 + m_Position.GetY() + 7);
}
void ColdFood::Fresh() {
m_Position.Set(rand() % 790, rand() % 550);
CalculateRegion();
}
治愈食物类,吃掉可恢复两点生命,具体实现效果需要调用玩家坦克类中的AddHp()函数
#include "Object.h"
#include <list>
#include<time.h>
#ifndef CUREFOOD
#define CUREFOOD
using namespace std;
class CureFood : public Object
{
public:
CureFood()
{
m_Color = WHITE;
m_IsDisappear = false;
m_Position.Set(rand() % 790, rand() % 550);
}
~CureFood() {}
void Display();
void SetDisappear() { m_IsDisappear = true; }
bool IsDisappear() { return m_IsDisappear; }
Rect GetRegion() { return m_RectSphere; }
void CalculateRegion();
void Fresh();
};
#endif
#include"CureFood.h"
void CureFood::Display()
{
COLORREF fill_color_save = getfillcolor();
COLORREF color_save = getcolor();
setcolor(m_Color);
setfillcolor(m_Color);
fillrectangle(BATTLE_GROUND_X1 + m_Position.GetX() - 7,
BATTLE_GROUND_Y1 + m_Position.GetY() - 7,
BATTLE_GROUND_X1 + m_Position.GetX() + 7,
BATTLE_GROUND_Y1 + m_Position.GetY() + 7);
setcolor(color_save);
setfillcolor(fill_color_save);
}
void CureFood::CalculateRegion()
{
m_RectSphere.SetRect(BATTLE_GROUND_X1 + m_Position.GetX() - 7,
BATTLE_GROUND_Y1 + m_Position.GetY() - 7,
BATTLE_GROUND_X1 + m_Position.GetX() + 7,
BATTLE_GROUND_Y1 + m_Position.GetY() + 7);
}
void CureFood::Fresh()
{
m_Position.Set(rand() % 790, rand() % 550);
CalculateRegion();
}
好了 所有的类已经构造完毕,现在开始写主函数,整个主函数主要分为三部分:游戏对象和数据的初始化、游戏开始运行(进入循环)、游戏结束,清空内存
#include <iostream>
#include <conio.h>
#include <time.h>
#include <list>
#include "Graphic.h"
#include "MyTank.h"
#include "EnemyTank.h"
#include"Bullet.h"
#include"Bomb.h"
#include "Shape.h"
#include "Setting.h"
#include "ColdFood.h"
#include "CureFood.h"
using namespace std;
MyTank myTank;//玩家坦克
list<Bullet*> MyTankBullets;//存储玩家坦克的子弹
list<Bullet*> EnemyBullets;//存储敌人坦克的子弹
list<Bomb*> Bombs;//存储爆炸游戏对象
list<EnemyTank*> AllEnemyTanks;//存储敌方坦克
void CheckBCrashForEnemy();//检测是否有敌方坦克被子弹命中,是则销毁
void CheckBCrashForMe();//检测是否玩家坦克被子弹命中,是则损失生命值
void CheckCrashforTanks();//检测玩家是否与其他坦克相撞
int main(void)
{
//初始化数据和对象
Graphic::Create();
srand((unsigned)time(0));
ColdFood coldfood;
CureFood curefood;
MyTankBullets.clear();
EnemyBullets.clear();
Bombs.clear();
AllEnemyTanks.clear();
bool loop = true;
bool skip = false;//当为true时游戏暂停
//游戏开始运行
while (loop)
{
if (Setting::NewLev)//新的一关
{
Sleep(1000);
Setting::NewLev = false;
Setting::NewGameLevel();
for (int i = 0; i < Setting::GetTankNum(); i++)//新增敌方坦克
{
EnemyTank* p = new EnemyTank();
AllEnemyTanks.push_back(p);
}
}
if (_kbhit())//得到键盘相应
{
int key = _getch();
if (skip && key != 13)//游戏暂停,再按回车恢复
continue;
switch (key)
{
case 72:// 向上
myTank.SetDir(Dir::UP);break;
case 80:// 向下
myTank.SetDir(Dir::DOWN);break;
case 75:// 向左
myTank.SetDir(Dir::LEFT);break;
case 77:// 向右
myTank.SetDir(Dir::RIGHT);break;
case 27://Esc结束
loop = false;
break;
case 32:// Space发射
myTank.Shoot(MyTankBullets);
break;
case 13:// Enter暂停
if (skip)skip = false;
else skip = true;
break;
default:
break;
}
}
if (!skip)
{
cleardevice();//清屏
CheckBCrashForEnemy();
CheckBCrashForMe();
CheckCrashforTanks();
Setting::SetHp(myTank.GetHp());
Graphic::DrawBattleGround();
myTank.Move();
myTank.Display();
/* 绘画食物 */
coldfood.Display();
curefood.Display();
coldfood.CalculateRegion();
curefood.CalculateRegion();
if (Shape::CheckIntersect( coldfood.GetRegion(),myTank.GetRegion() ))//若冰冻食物被吃
{
EnemyTank::Cold();//冻结
coldfood.Fresh();//刷新食物位置
coldfood.ColdBegin();
coldfood.Display();
}
if (coldfood.GetDeltaTime() > 8) {//计时,大于八秒冻结释放
EnemyTank::Release();
}
if (Shape::CheckIntersect(curefood.GetRegion(), myTank.GetRegion()))//若加速食物被吃
{
myTank.AddHp();//加生命值
curefood.Fresh();//刷新食物位置
}
/* 绘画敌方坦克 */
for (list<EnemyTank*>::iterator it = AllEnemyTanks.begin(); it != AllEnemyTanks.end();)
{
if (Shape::CheckIntersect( myTank.GetRegion(), (*it)->GetRegion()))
(*it)->CrashWithOther();
else (*it)->noCrashWithOther();
if(!EnemyTank::IsCold())
(*it)->Move();
if ((*it)->IsDisappear())
{
Setting::Damaged();
(*it)->Boom(Bombs);
delete *it;
it = AllEnemyTanks.erase(it);
continue;
}
(*it)->Display();
if ((*it)->IsNeedShoot())
(*it)->Shoot(EnemyBullets);
it++;
}
/* 绘画子弹 */
for (list<Bullet*>::iterator it = MyTankBullets.begin(); it != MyTankBullets.end();)
{
(*it)->Move();
if ((*it)->IsDisappear())
{
(*it)->Boom(Bombs);
delete *it;
it = MyTankBullets.erase(it);
continue;
}
(*it)->Display();
it++;
}
for (list<Bullet*>::iterator it = EnemyBullets.begin(); it != EnemyBullets.end();)
{
(*it)->Move();
if ((*it)->IsDisappear())
{
(*it)->Boom(Bombs);
delete *it;
it = EnemyBullets.erase(it);
continue;
}
(*it)->Display();
it++;
}
/* 绘画爆炸效果 */
for (list<Bomb*>::iterator it = Bombs.begin(); it != Bombs.end();)
{
(*it)->Move();
if ((*it)->IsDisappear())
{
delete *it;
it = Bombs.erase(it);
continue;
}
(*it)->Display();
it++;
}
/*显示分数 */
Graphic::ShowScore();
}
Sleep(Setting::GetGameSpeed());//控制游戏速度
}
//游戏结束、释放内存
for (list<EnemyTank*>::iterator it = AllEnemyTanks.begin(); it != AllEnemyTanks.end(); it++)delete *it;
for (list<Bullet*>::iterator it = MyTankBullets.begin(); it != MyTankBullets.end(); it++)delete *it;
for (list<Bullet*>::iterator it = EnemyBullets.begin(); it != EnemyBullets.end(); it++)delete *it;
for (list<Bomb*>::iterator it = Bombs.begin(); it != Bombs.end(); it++)delete *it;
Graphic::Destroy();
}
void CheckBCrashForEnemy()
{
for (list<Bullet*>::iterator it = MyTankBullets.begin(); it != MyTankBullets.end(); it++)
{
for (list<EnemyTank*>::iterator IT = AllEnemyTanks.begin(); IT != AllEnemyTanks.end(); IT++)
{
if (Shape::CheckIntersect((*it)->GetRegion(), (*IT)->GetRegion()))
{
(*IT)->SetDisappear();
(*it)->SetDisappear();
}
}
}
}
void CheckBCrashForMe()
{
for (list<Bullet*>::iterator it = EnemyBullets.begin(); it != EnemyBullets.end(); it++)
{
if (Shape::CheckIntersect((*it)->GetRegion(), myTank.GetRegion()))
{
(*it)->SetDisappear();
myTank.Beaten();
if (myTank.GetHp() <= 0)exit(1);
}
}
}
void CheckCrashforTanks() {
for (list<EnemyTank*>::iterator it = AllEnemyTanks.begin(); it != AllEnemyTanks.end(); it++)
{
if (Shape::CheckIntersect((*it)->GetRegion(), myTank.GetRegion()))
{
myTank.CrashWithOther();
return;
}
}
myTank.noCrashWithOther();
}
这样,整个程序就书写完毕了,效果图如下(蓝色食物为冰冻,白色为治愈)
谢谢观看:)