本篇文章总结一下我在网上看到的一个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();
}

 

这样,整个程序就书写完毕了,效果图如下(蓝色食物为冰冻,白色为治愈)

 

C++练习实例———控制台实现坦克大战小游戏-LMLPHP

谢谢观看:)
 

07-14 10:36