在游戏设计过程中,需要记录大量的位置信息,如果仅仅使用(x,y)坐标很容易出错。这一篇中,我们先定义两个简单的类用来保存点和矩形的信息
创建下面四个文件:
Point.h
#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);
void SetX(int x);
void SetY(int y);
int GetX();
int GetY();
private:
int m_x;
int m_y;
};
#endif
Point.cpp
#include "Point.h"
void Point::Set(int x, int y)
{
m_x = x;
m_y = y;
}
void Point::SetX(int x)
{
m_x = x;
}
void Point::SetY(int y)
{
m_y = y;
}
int Point::GetX()
{
return m_x;
}
int Point::GetY()
{
return m_y;
}
Rect.h
#ifndef __RECTANGLE_H__
#define __RECTANGLE_H__
#include "Point.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(){};
Rect& operator=(const Rect &rect)
{
m_startPoint = rect.GetStartPoint();
m_endPoint = rect.GetEndPoint();
return *this;
}
void Set(const Point pStart, const Point pEnd);
void Set(int x1, int y1, int x2, int y2);
void SetStartPoint(const Point p);
void SetEndPoint(const Point p);
Point GetStartPoint() const;
Point GetEndPoint() const;
int GetWidth();
int GetHeight();
private:
void Check();
//m_startPoint表示左上角,m_endPoint表示右下角
Point m_startPoint;`在这里插入代码片`
Point m_endPoint;
};
#endif
Rect.cpp
#include "Rect.h"
void Rect::Set(Point pStart, Point pEnd)
{
m_startPoint = pStart;
m_endPoint = pEnd;
}
void Rect::Set(int x1, int y1, int x2, int y2)
{
m_startPoint.Set(x1, y1); //矩形左上角坐标
m_endPoint.Set(x2, y2); //矩形右下角坐标
}
void Rect::SetStartPoint(Point p)
{
m_startPoint = p;
}
void Rect::SetEndPoint(Point p)
{
m_endPoint = p;
}
Point Rect::GetStartPoint() const
{
return m_startPoint;
}
Point Rect::GetEndPoint() const
{
return m_endPoint;
}
int Rect::GetWidth()
{
return m_endPoint.GetX() - m_startPoint.GetX();
}
int Rect::GetHeight()
{
return m_endPoint.GetY() - m_startPoint.GetY();
}
void Rect::Check()
{
if (m_startPoint.GetX() > m_endPoint.GetX() || m_startPoint.GetY() > m_endPoint.GetY())
{
Point p = m_startPoint;
m_startPoint = m_endPoint;
m_endPoint = p;
}
}
Tank.h
首先,我们对Tank类进行修改,新增一部分功能,代码如下:
#ifndef __TANK_H__
#define __TANK_H__
#include "Graphic.h"
#include "model\Point.h"
#include "model\Rect.h"
enum Dir { UP, DOWN, LEFT, RIGHT }; //定义了一个枚举类型,表示方向
class Tank
{
public:
virtual void Display() = 0; //定义显示纯虚函数
virtual void Move() = 0;//定义移动纯虚函数
protected:
Point m_pos; //m_pos对象表示坐标
COLORREF m_color; //使用 RGB 函数来初始化 COLORREF
Rect m_rectSphere; // 用来记录坦克的形状范围
Dir m_dir; //m_dir保存坦克的行驶方向
int m_step;
};
#endif
MainTank.h
/*
战坦克就是玩家控制的坦克,所有的坦克中,只有这个一个是可以控制的
*/
#ifndef __MAIN_TANK__
#define __MAIN_TANK__
#include "Tank.h"
class MainTank : public Tank //公有继承Tank基类
{
public:
MainTank()
{ //初始化主坦克的位置,颜色,移动方向,移动距离
m_pos.Set(300, 300);
this->CalculateSphere(); //初始化玩家坦克位置坐标
m_color = YELLOW;
m_dir = Dir::UP;
m_step = 2;
}
~MainTank(){}
// 设置行驶方向
void SetDir(Dir dir);
void Display();//重写基类的纯虚函数
void Move();
protected:
// 绘制坦克主体外观
void DrawTankBody();
//计算玩家坦克两条履带的位置坐标范围
void CalculateSphere();
};
#endif
MainTank.cpp
#include "MainTank.h"
void MainTank::SetDir(Dir dir)
{
m_dir = dir;
}
void MainTank::DrawTankBody()
{
fillrectangle(m_pos.GetX() - 6, m_pos.GetY() - 6, m_pos.GetX() + 6, m_pos.GetY() + 6);
switch (m_dir)
{
case UP:
case DOWN:
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());
break;
case LEFT:
case RIGHT:
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());
break;
default:
break;
}
}
void MainTank::Display()
{
COLORREF fill_color_save = getfillcolor();
COLORREF color_save = getcolor();
setfillcolor(m_color);
setcolor(m_color);
DrawTankBody();
switch (m_dir)
{
case UP:
line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX(), m_pos.GetY() - 15);
break;
case DOWN:
line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX(), m_pos.GetY() + 15);
break;
case LEFT:
line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX() - 15, m_pos.GetY());
break;
case RIGHT:
line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX() + 15, m_pos.GetY());
break;
default:
break;
}
setcolor(color_save);
setfillcolor(fill_color_save);
}
void MainTank::Move()
{
switch (m_dir)
{
case UP:
m_pos.SetY(m_pos.GetY() - m_step); //向上移动,更新y坐标
//如果小于对战区域左上角y坐标,即超出对战区域,就从另外一边出现
if (m_pos.GetY() < Graphic::GetBattleGround().GetStartPoint().GetY())
m_pos.SetY(Graphic::GetBattleGround().GetEndPoint().GetY() - 1);
break;
case DOWN:
m_pos.SetY(m_pos.GetY() + m_step);
if (m_pos.GetY() > Graphic::GetBattleGround().GetEndPoint().GetY())
m_pos.SetY(Graphic::GetBattleGround().GetStartPoint().GetY() + 1);
break;
case LEFT:
m_pos.SetX(m_pos.GetX() - m_step);
if (m_pos.GetX() < Graphic::GetBattleGround().GetStartPoint().GetX())
m_pos.SetX(Graphic::GetBattleGround().GetEndPoint().GetX() - 1);
break;
case RIGHT:
m_pos.SetX(m_pos.GetX() + m_step);
if (m_pos.GetX() > Graphic::GetBattleGround().GetEndPoint().GetX())
m_pos.SetX(Graphic::GetBattleGround().GetStartPoint().GetX() + 1);
break;
default:
break;
}
CalculateSphere();
}
void MainTank::CalculateSphere() //计算玩家坦克两条履带的位置坐标范围
{
switch (m_dir)
{
case UP:
case DOWN:
m_rectSphere.Set(m_pos.GetX() - 13, m_pos.GetY() - 10, m_pos.GetX() + 13, m_pos.GetY() + 10);
break;
case LEFT:
case RIGHT:
m_rectSphere.Set(m_pos.GetX() - 10, m_pos.GetY() - 13, m_pos.GetX() + 10, m_pos.GetY() + 13);
break;
default:
break;
}
}
Graphic.h
#ifndef __GRAPHIC_H__
#define __GRAPHIC_H__
#include
#include "model/Rect.h"
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
//一组宏来定义战场区域的位置
#define BATTLE_GROUND_X1 5
#define BATTLE_GROUND_Y1 5
#define BATTLE_GROUND_X2 600
#define BATTLE_GROUND_Y2 (SCREEN_HEIGHT - BATTLE_GROUND_Y1)
class Graphic
{
public:
static void Create();
static void Destroy();
static void DrawBattleGround();
static int GetScreenWidth();
static int GetScreenHeight();
static Rect GetBattleGround();
private:
static Rect m_rectScreen;
static Rect m_rectBattleGround; //m_rectBattleGround这个Rect对象来保存战区位置
};
#endif
Graphic.cpp
#include "Graphic.h"
//类外初始化静态成员变量
Rect Graphic::m_rectScreen;
Rect Graphic::m_rectBattleGround;
void Graphic::Create()
{
m_rectScreen.Set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
initgraph(SCREEN_WIDTH, SCREEN_HEIGHT);
setbkcolor(DARKGRAY); //设置当前设备绘图背景色
m_rectBattleGround.Set(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());
}
int Graphic::GetScreenWidth()
{
return SCREEN_WIDTH;
}
int Graphic::GetScreenHeight()
{
return SCREEN_HEIGHT;
}
Rect Graphic::GetBattleGround()
{
return m_rectBattleGround;//返回Rect对象的对战区区域
}
新建文件EnemyTank.h和EnemyTank.cpp。实现一个敌人坦克类
EnemyTank.h
#ifndef __ENEMY_TANK__
#define __ENEMY_TANK__
#include "Tank.h"
class EnemyTank : public Tank
{
public:
EnemyTank()
{
RandomTank();
}
~EnemyTank(){}
void Display();
void Move();
protected:
void CalculateSphere();
void RandomTank(); //随机地在战场区域生成一个坦克
};
#endif
EnemyTank.cpp
#include "EnemyTank.h"
void EnemyTank::RandomTank()
{
//随机生成敌方坦克中心坐标
m_pos.SetX(rand() % Graphic::GetBattleGround().GetWidth()); //随机数为0-对战区的宽度
m_pos.SetY(rand() % Graphic::GetBattleGround().GetHeight());
m_color = WHITE;
//随机生成敌方坦克方向
m_dir = (Dir)(Dir::UP + (rand() % 4)); //随机数0-3
m_step = 2;
}
void EnemyTank::Display()
{
COLORREF fill_color_save = getfillcolor();
COLORREF color_save = getcolor();
setfillcolor(m_color);
setcolor(m_color);
fillrectangle(m_pos.GetX() - 6, m_pos.GetY() - 6, m_pos.GetX() + 6, m_pos.GetY() + 6);
fillrectangle(m_rectSphere.GetStartPoint().GetX(), m_rectSphere.GetStartPoint().GetY(),
m_rectSphere.GetStartPoint().GetX() + 4, m_rectSphere.GetStartPoint().GetY() + 4);
fillrectangle(m_rectSphere.GetEndPoint().GetX() - 4, 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.GetStartPoint().GetX() + 4, m_rectSphere.GetEndPoint().GetY());
fillrectangle(m_rectSphere.GetEndPoint().GetX() - 4, m_rectSphere.GetEndPoint().GetY() - 4,
m_rectSphere.GetEndPoint().GetX(), m_rectSphere.GetEndPoint().GetY());
switch (m_dir)
{
case UP:
line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX(), m_pos.GetY() - 15);
break;
case DOWN:
line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX(), m_pos.GetY() + 15);
break;
case LEFT:
line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX() - 15, m_pos.GetY());
break;
case RIGHT:
line(m_pos.GetX(), m_pos.GetY(), m_pos.GetX() + 15, m_pos.GetY());
break;
default:
break;
}
setcolor(color_save);
setfillcolor(fill_color_save);
}
void EnemyTank::Move()
{
switch (m_dir)
{
case UP:
m_pos.SetY(m_pos.GetY() - m_step);
if (m_pos.GetY() < Graphic::GetBattleGround().GetStartPoint().GetY())
m_pos.SetY(Graphic::GetBattleGround().GetEndPoint().GetY() - 1);
break;
case DOWN:
m_pos.SetY(m_pos.GetY() + m_step);
if (m_pos.GetY() > Graphic::GetBattleGround().GetEndPoint().GetY())
m_pos.SetY(Graphic::GetBattleGround().GetStartPoint().GetY() + 1);
break;
case LEFT:
m_pos.SetX(m_pos.GetX() - m_step);
if (m_pos.GetX() < Graphic::GetBattleGround().GetStartPoint().GetX())
m_pos.SetX(Graphic::GetBattleGround().GetEndPoint().GetX() - 1);
break;
case RIGHT:
m_pos.SetX(m_pos.GetX() + m_step);
if (m_pos.GetX() > Graphic::GetBattleGround().GetEndPoint().GetX())
m_pos.SetX(Graphic::GetBattleGround().GetStartPoint().GetX() + 1);
break;
default:
break;
}
CalculateSphere();
}
void EnemyTank::CalculateSphere()
{
switch (m_dir)
{
case UP:
case DOWN:
m_rectSphere.Set(m_pos.GetX() - 13, m_pos.GetY() - 10, m_pos.GetX() + 13, m_pos.GetY() + 10);
break;
case LEFT:
case RIGHT:
m_rectSphere.Set(m_pos.GetX() - 10, m_pos.GetY() - 13, m_pos.GetX() + 10, m_pos.GetY() + 13);
break;
default:
break;
}
}
main.cpp
#pragma warning(disable:4996)
#include
#include
#include
#include "Graphic.h"
#include "MainTank.h"
#include "EnemyTank.h"
using namespace std;
#define MAX_TANKS 10
void main()
{
srand((unsigned)time(NULL));//时间种子,time(NULL)返回当前的时间
Graphic::Create(); //创建画布
MainTank mainTank; //创建玩家坦克对象
Tank* pTank[MAX_TANKS];//指针数组保存每个坦克的指针
for (int i = 0; i < MAX_TANKS; i++)
{
pTank[i] = new EnemyTank(); //循环创建坦克,这里用了new的方法把坦克对象创建在堆空间中
//父类指针指向子类对象
}
bool loop = true;
bool skip = false;
while (loop)
{
/*
通过kbhit()捕捉键盘动作,该函数是一个非阻塞函数,不管有没有按键被按下,该函数都会立即返回
之后再通过getch()得到按下键的码值
使用_getch读取字符时,读取一次就行;在读取方向键和功能键的时候,需要读取两次
*/
if (kbhit()) //检测到用户按下某键时就会返回真
{
int key = _getch(); //返回用户按键
switch (key)
{
// Up
case 72:
mainTank.SetDir(Dir::UP);
break;
// Down
case 80:
mainTank.SetDir(Dir::DOWN);
break;
// Left
case 75:
mainTank.SetDir(Dir::LEFT);
break;
// Right
case 77:
mainTank.SetDir(Dir::RIGHT);
break;
case 224: // 方向键高8位
break;
// Esc
case 27: //退出
loop = false;
break;
// Space
case 32:
break;
// Enter
case 13: //暂停功能
if (skip)
skip = false;
else
skip = true;
break;
default:
break;
}
}
if (!skip) //默认为flase
{
cleardevice(); //清空绘图设备
Graphic::DrawBattleGround();
mainTank.Display();
mainTank.Move();
//每次擦屏之后,遍历指针数组,绘制出每个坦克。调用的是Move()和Display()方法
for (int i = 0; i < MAX_TANKS; i++)
{
pTank[i]->Move();
pTank[i]->Display();
}
}
Sleep(200); //休眠时间,相当于设置坦克的速度
}
//退出程序前,释放每一个坦克所占的堆空间
for (int i = 0; i < MAX_TANKS; i++)
{
delete pTank[i];
}
Graphic::Destroy();
}