C++开发游戏——坦克大战(2)


在这里插入图片描述

位置信息类

在游戏设计过程中,需要记录大量的位置信息,如果仅仅使用(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 <graphics.h>

#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 <iostream>
#include <conio.h>
#include <time.h>

#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();
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值