本文共 7330 字,大约阅读时间需要 24 分钟。
在使用C#做的面向对象程序设计,做的虽然是坦克大战小游戏,结合上课内容,理解类图以及对应的语句,完成作业,也是一种收获,现在记录一下,也分享一下,共勉!有错误的话欢迎大家指出~
通过该实验,掌握windows下C++程序设计的基本方法。掌握类图、代码图的分析方法。通过处理过程对计算机软件系统工作原理的进一步理解,促进对面向对象概念的系统理解以及面向对象方法的应用。
(1)完成代码编写设计;
(2)编译通过整个程序,并运行相应小游戏; (3)界面成功显示,可以对坦克进行相应操作; (4)能够正常更新己方坦克和敌方坦克的HP值; (5)可以实现两个己方坦克的同时操作; (6)在双人模式下,保证界面的正确运行; (7)对最后的积分结果有着正确的显示。(1)可以任意改变墙的形状;
(2)长按空格键可以无限发射子弹; (3)可以任意改变坦克的颜色大小; (4)可以显示在线帮助和设置; (5)其它自行设计的功能。本设计在于用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动态模型图,也就无法表示系统的动态行为。类图也是面向对象编程的起点和依据。
类图用于描述系统中所包含的类以及它们之间的相互关系,帮助我们简化对系统的理解,是系统分析和设计阶段的重要产物,也是系统编码和测试的重要模型依据。 类图如下:CData这个类的类图如下图所示:
这个类的作用是获得和重置坦克大战地图的高和宽,下面给出一个40*45的地图程序实现,由于这个程序过长,故截图显示:“
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; } } } }}
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; } }}
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(); }}
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);}
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();}
#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类里面的成员变量进行初始化,然后进入循环,进入玩家选择界面,确定参与人数,是否需要编辑地图,是否需要进行设置以及游戏帮助和是否退出游戏。
Codemap可以更加清楚的看清程序之间的关系,可以通过创建代码图来可视化代码中的依赖项。 代码图可帮助查看代码如何相互配合,而无需读取文件和代码行。
可见,六个类之间的聚合链路都很广,它们之间的依赖关系都很强。可见,发射子弹,移动、HP、记分板等功能正常
在运行过程中,功能也都按照最初想法实现了
点击设置之后,可以调整敌方坦克数量、敌方坦克攻击力、敌方坦克血量、敌方坦克移动速度、敌方坦克自动寻路等
点击帮助之后,可以查看玩家1和玩家2的玩法操作。综上,经过测试,最初的设想均已实现,坦克大战V1.0设计成功。
1)做大程序前,要学会做程序框架图。
以前,编写的都是小程序,可能不需要做框架图就可以在脑子中构思好,但是一旦程序量大了之后,脑子是运转不过来的,很容易混乱,这时候,做一个总的程序框架图是很有必要的,不用非常细腻,具体到每一个函数,只需要将程序的大体结构画出来,这样,你才知道具体的路该怎么走,从哪起步,接下来该干什么,对自己的行动有一个规划,这样做,可以使程序的结构很清晰,又能节省很多时间去思考接下来该干什么,这是一种很好的思想。 2)学会借鉴别人的程序 小程序看别人的,可能会被称为抄袭,但是如果大程序的每个语句都由自己来编写,在我看来是一种很愚蠢的方法,有个名人曾说过,天下文章一大抄,牛顿也说过,只有站在巨人的肩膀上,才能看的更远,所以对于大程序而言,有些可以借鉴别人的,没有必要全部自己来编写,可以在别人的基础上进一步来改进,这样既节省时间,又可以使自己的程序到达一个更高的水平。 3)学会用调试去发现和解决问题 程序量较小的时候,可能用脑子去模拟程序的运作就可以想出来错误在哪里,但是如果程序量大,不可能再去用脑子模拟,这时候就要用调试去解决,首先,需要找到错误的大概位置,设置断点,运行调试,一步一步,查看关键变量的数值,或者在关键位置设一个标志位,看标志位的值,同时看窗口的运行结果。这样,就能发现问题,解决问题。 4)写程序日志 每天对这天遇到的问题和解决方法,以及做的工作都记录下来,对编写程序能力的提升会有很大的帮助,这样做,解决问题的方法越来越多,遇到的问题也不会慌张,这样做,积累的是解决办法,而不是积累错误。转载地址:http://ghtki.baihongyu.com/