C++程序设计作业--坦克大战[分享]

在使用C#做的面向对象程序设计,做的虽然是坦克大战小游戏,结合上课内容,理解类图以及对应的语句,完成作业,也是一种收获,现在记录一下,也分享一下,共勉!有错误的话欢迎大家指出~

1.设计目的

通过该实验,掌握windows下C++程序设计的基本方法。掌握类图、代码图的分析方法。通过处理过程对计算机软件系统工作原理的进一步理解,促进对面向对象概念的系统理解以及面向对象方法的应用。

2.设计目标

1、基本目标

(1)完成代码编写设计;
(2)编译通过整个程序,并运行相应小游戏;
(3)界面成功显示,可以对坦克进行相应操作;
(4)能够正常更新己方坦克和敌方坦克的HP值;
(5)可以实现两个己方坦克的同时操作;
(6)在双人模式下,保证界面的正确运行;
(7)对最后的积分结果有着正确的显示。

2、额外目标

(1)可以任意改变墙的形状;
(2)长按空格键可以无限发射子弹;
(3)可以任意改变坦克的颜色大小;
(4)可以显示在线帮助和设置;
(5)其它自行设计的功能。

3 程序设计思路

3.1 类的设计

本设计在于用C++实现一个小游戏—坦克大战,经考虑可以用六个类来实现:

Class1:class Ctank:virtual public Draw

Class2:class Control : virtual public CTank, virtual public CBullet

Class3:class CBullet : virtual public Draw

Class4:class Draw : public CData

Class5:class CUnion

Class6:class CData

先给出整个程序的类图,类图(Class diagram)由许多(静态)说明性的模型元素(例如类、包和它们之间的关系,这些元素和它们的内容互相连接)组成。类图可以组织在(并且属于)包中,仅显示特定包中的相关内容。类图(Class diagram)是最常用的UML图,显示出类、接口以及它们之间的静态结构和关系;它用于描述系统的结构化设计。类图(Class diagram)最基本的元素是类或者接口。 类图主要用在面向对象软件开发的分析和设计阶段,描述系统的静态结构。类图图示了所构建系统的所有实体、实体的内部结构以及实体之间的关系。即类图中包含从用户的客观世界模型中抽象出来的类、类的内部结构和类与类之间的关系。它是构建其他设计模型的基础,没有类图,就没有对象图、状态图、协作图等其他UMI动态模型图,也就无法表示系统的动态行为。类图也是面向对象编程的起点和依据。
类图用于描述系统中所包含的类以及它们之间的相互关系,帮助我们简化对系统的理解,是系统分析和设计阶段的重要产物,也是系统编码和测试的重要模型依据。
类图如下:
在这里插入图片描述

3.1.1 CData

CData这个类的类图如下图所示:
在这里插入图片描述

这个类的作用是获得和重置坦克大战地图的高和宽,下面给出一个40*45的地图程序实现,由于这个程序过长,故截图显示:“
在这里插入图片描述

3.1.2 class Draw

class Draw派生于class Cdata,Draw的类图是:
在这里插入图片描述

这个类旨在实现设置控制台窗口,如打印地图、打印字符、在一个界面显示主菜单(清屏、处理按上下键之后的显示)、设置界面、显示选项、显示选项后的参数、接受按键(如果退出、将设置更新、清除箭头、防止选项超、检测左右键、防止参数超出范围、处理指定行的内容)、清除指定行的内容、打印帮助……
下面给出显示主菜单的源码:

int Draw::showOption()
{
	//清屏
	Draw::clearConsole(8);
	Draw::writeChar(15, 11, "坦克大战 v1.0", FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);

	char  ch;
	int option = 0;
	
	writeChar(13, 13 , "→", FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);

	char * options[] = { "单人游戏", "双人游戏", "编辑地图", "设    置", "游戏帮助", "退    出" };
	for (int i = 0; i < 6; i++)
	{
		writeChar(15, 13 + i, options[i], FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
	}


	while (true)
	{
		if (_kbhit())
		{
			ch = _getch();

			if (ch == 27)	//esc
			{
				return -1;
			}
			if (ch == 72 || ch == 80||ch == '\r')  //只检测上下键
			{

				if (ch == 72)	//UP
				{
					writeChar(12, 13 + option, "    ", 0);
					option--;
				}
				else if (ch == 80)	//DOWN
				{
					writeChar(12, 13 + option, "    ",0);
					option++;
				}
				if (option < 0)
				{
					option = 0;
				}
				else if (option >= 6)
				{
					option--;
				}
				//处理按上下键之后的显示
				writeChar(13, 13 + option, "                        ",0);
				Sleep(100);
				writeChar(13, 13 + option, "→", FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
				writeChar(15, 13 + option, options[option], FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
				
				if (ch == '\r')
				{
					return option;
				}
			}
		
		}
	}
}

3.1.3 class CBullet

class CBullet虛继承于class Draw,CBullet的类图如下:
在这里插入图片描述

class CBullet实现的功能是:清除子弹、子弹向着指定的方向移动一步、打印子弹如果是空道才打印返回true,否则返回false,然后调用attackSomeThing()等功能。
下面给出子弹移动的代码:

void CBullet::move(int direction)
{
	this->clearBullet();
	if (this->printBullet())
	{
		this->oldBulletY = BulletY;
		this->oldBulletX = BulletX;
		switch (direction)
		{
		case UP:
			--BulletY;
			break;
		case DOWN:
			++BulletY;
			break;
		case LEFT:
			--BulletX;
			break;
		case RIGHT:
			++BulletX;
			break;
		}
	}
}

3.1.4 class Ctank:virtual public Draw

class Ctank虛继承于class Draw,Ctank实现的功能是:打印坦克、将玩家1(2)写入地图、将坦克坐标写到地图上、对坦克进行碰壁检测、清除坦克、移动坦克等。
它的类图是:
在这里插入图片描述

下面给出移动坦克的源码:

void CTank::move(int direction)
{
	round(time(NULL));
	//判断坦克是否可以通过
	if (tankIsThrough(this->TankX,this->TankY, direction))
	{
		switch (direction)
		{
		case UP:
			this->clearTank();
			this->TankY--;
			this->Direction = UP;
			this->printTank();
			break;
		case DOWN:
			this->clearTank();
			this->TankY++;
			this->Direction = DOWN;
			this->printTank();
			break;
		case LEFT:
			this->clearTank();
			this->TankX--;
			this->Direction = LEFT;
			this->printTank();
			break;
		case RIGHT:
			this->clearTank();
			this->TankX++;
			this->Direction = RIGHT;
			this->printTank();
			break;
		}
	}
	else    //不能通过就只打印
	{
		if (TankID >= 2)   //用于敌方坦克在碰到障碍物时改变方向
		{
			switch (this->Direction)
			{
			case UP:
				 this->Direction = (int)rand() % 4; break;
			case DOWN:
				this->Direction = (int)rand() % 4; break;
			case LEFT:
				this->Direction = (int)rand() % 4; break;
			case RIGHT:
				this->Direction = (int)rand() % 4; break;
			}
		}
		else
		{
			this->Direction = direction;
		}
		
		this->printTank();
	}
}

3.1.5 class Control : virtual public CTank, virtual public CBullet

class Control虛继承于CTank和CBullet,Control的类图如下:
在这里插入图片描述

class Control实现的功能是逻辑算法,控制坦克和子弹,检测坦克是否可以通过、循环输出子弹夹里面的子弹、开始游戏(游戏人数)、画记分板、更新我方和敌方记分板、打印初始化坦克等功能。下面给出生成第一个子弹的源码:

void Control::sendBullet(CTank& tank)
{
	//生成一个子弹
	clock_t firstTime = clock();
	switch (tank.Direction)
	{
	case UP:
		bullet.InitBullet( tank.TankID, tank.TankX + 1, tank.TankY - 1, tank.Direction, true, firstTime, tank.TankX + 1, tank.TankY - 1 );
		break;
	case DOWN:
		bullet.InitBullet(tank.TankID, tank.TankX + 1, tank.TankY + 3, tank.Direction, true, firstTime, tank.TankX + 1, tank.TankY + 3);
		break;
	case LEFT:
		bullet.InitBullet(tank.TankID, tank.TankX - 1, tank.TankY + 1, tank.Direction, true, firstTime, tank.TankX - 1, tank.TankY + 1);
		break;
	case RIGHT:
		bullet.InitBullet(tank.TankID, tank.TankX + 3, tank.TankY + 1, tank.Direction, true, firstTime, tank.TankX + 3, tank.TankY + 1 );
		break;
	}
	//将子弹存入VBullet容器
	CUnion::ListBullet.push_back(bullet);
	//移动第一步
	bullet.move(tank.Direction);
}

3.1.6 class CUnion

class CUnion的类图如图所示:
在这里插入图片描述

这个类所要实现的功能是敌军被子弹击中 更新HP、更新计分板、处理子弹碰到障碍物等问题,下面给出击中后更新HP和记分板的源码:

void CUnion::enemyBeAttack(int posX, int posY)
{
	int tank = CData::GetTypeOfMap(posX, posY);
	VTank[tank + 2].clearTank();
	VTank[tank + 2].setnMaxHP(VTank[tank + 2].getMaxHP() - 1);
	Control::updateTableau();
}

3.2主程序

#include "stdafx.h"
#include "Control.h"


int _tmain(int argc, _TCHAR* argv[])
{
	Control con;
	con.init();
	while (true)
	{
		int option = Draw::showOption();
		switch (option)
		{
		case 0:con.gameStart(1);			 break;	 //单人游戏
		case 1:con.gameStart(2);			 break;	 //双人游戏
		case 2:Draw::editMap();      	 break;	 //编辑地图
		case 3:Draw::showConfig();	     break;	 //设置
		case 4:Draw::help();		     break;	 //游戏帮助
		case 5:return 0;			     break;	 //退出游戏
		}
	}
	return 0;
}
}

可见,主程序先对Control类里面的成员变量进行初始化,然后进入循环,进入玩家选择界面,确定参与人数,是否需要编辑地图,是否需要进行设置以及游戏帮助和是否退出游戏。

3.3 Codemap:

在这里插入图片描述

Codemap可以更加清楚的看清程序之间的关系,可以通过创建代码图来可视化代码中的依赖项。 代码图可帮助查看代码如何相互配合,而无需读取文件和代码行。
可见,六个类之间的聚合链路都很广,它们之间的依赖关系都很强。

4 测试过程

4.1 进入游戏开始界面

在这里插入图片描述

4.2 选择单人模式,进入游戏

在这里插入图片描述

4.3 单人操作

在这里插入图片描述

可见,发射子弹,移动、HP、记分板等功能正常

4.4 双人模式

在这里插入图片描述

在运行过程中,功能也都按照最初想法实现了

4.5 运行设置和帮助

在这里插入图片描述

点击设置之后,可以调整敌方坦克数量、敌方坦克攻击力、敌方坦克血量、敌方坦克移动速度、敌方坦克自动寻路等
在这里插入图片描述
点击帮助之后,可以查看玩家1和玩家2的玩法操作。

综上,经过测试,最初的设想均已实现,坦克大战V1.0设计成功。

5.设计感想与总结

1)做大程序前,要学会做程序框架图。
以前,编写的都是小程序,可能不需要做框架图就可以在脑子中构思好,但是一旦程序量大了之后,脑子是运转不过来的,很容易混乱,这时候,做一个总的程序框架图是很有必要的,不用非常细腻,具体到每一个函数,只需要将程序的大体结构画出来,这样,你才知道具体的路该怎么走,从哪起步,接下来该干什么,对自己的行动有一个规划,这样做,可以使程序的结构很清晰,又能节省很多时间去思考接下来该干什么,这是一种很好的思想。
2)学会借鉴别人的程序
小程序看别人的,可能会被称为抄袭,但是如果大程序的每个语句都由自己来编写,在我看来是一种很愚蠢的方法,有个名人曾说过,天下文章一大抄,牛顿也说过,只有站在巨人的肩膀上,才能看的更远,所以对于大程序而言,有些可以借鉴别人的,没有必要全部自己来编写,可以在别人的基础上进一步来改进,这样既节省时间,又可以使自己的程序到达一个更高的水平。
3)学会用调试去发现和解决问题
程序量较小的时候,可能用脑子去模拟程序的运作就可以想出来错误在哪里,但是如果程序量大,不可能再去用脑子模拟,这时候就要用调试去解决,首先,需要找到错误的大概位置,设置断点,运行调试,一步一步,查看关键变量的数值,或者在关键位置设一个标志位,看标志位的值,同时看窗口的运行结果。这样,就能发现问题,解决问题。
4)写程序日志
每天对这天遇到的问题和解决方法,以及做的工作都记录下来,对编写程序能力的提升会有很大的帮助,这样做,解决问题的方法越来越多,遇到的问题也不会慌张,这样做,积累的是解决办法,而不是积累错误。

  • 8
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值