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();
}
這樣,整個程式就書寫完畢了,效果圖如下(藍色食物為冰凍,白色為治癒)
謝謝觀看:)