1. 程式人生 > >C++練習例項———控制檯程式碼實現坦克大戰小遊戲

C++練習例項———控制檯程式碼實現坦克大戰小遊戲

    坦克大戰是一款經典的遊戲,今天我來介紹一個在vs中僅用控制檯程式碼實現的坦克大戰小遊戲,在很多學校裡作為一個面對物件程式設計的大作業,對於學習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();
}

這樣,整個程式就書寫完畢了,效果圖如下(藍色食物為冰凍,白色為治癒)

謝謝觀看:)