1、坦克大战项目文档- 1 -坦克大战一、需求分析坦克大战中有我方坦克和敌方坦克,我方坦克有一个,敌方坦克有多个。坦克可以移动,也可以发射子弹。我方坦克可以通过上下左右键来控制方向,敌方坦克自动改变方向。在游戏窗体中有障碍物,包括砖墙、钢墙、水墙和草地。坦克可以穿过草地,在遇到其他障碍物时,我方坦克停止移动,并通过操纵改变方向再移动,敌方坦克遇到其他障碍物时可以自动的改变方向。我方坦克有三次生命值,当与敌方坦克或敌方子弹相撞时,生命值减一,当生命值是零时,敌方胜利,游戏结束。敌方坦克的生命值为一,当敌方坦克与我方坦克或者我方子弹相遇就消失,同时我方子弹也消失,并产生爆炸。当我方坦克把敌方坦克消灭完
2、后,我方胜利,游戏结束。敌方子弹或我方子弹击中老巢,游戏结束。二、程序设计采用面向对象的设计方法,项目中有以下类:主窗体:GameForm,游戏的主窗体。坦克:父类坦克(Tank) 、我方坦克(MyTank) 、敌方坦克(EnemyTank) 。墙:父类墙(Wall) 、砖墙(BrickWall) 、刚墙(SteelWall ) 、水墙(WaterWall) 、草地(MeadoeWall ) 。子弹:父类子弹(Bullet) 、我方子弹(MyBullet ) 、敌方坦克(EnemyBullet) 。老巢:Home火花:Spark,子弹与墙碰撞产生火花。爆炸:Blast,子弹与坦克和老巢碰撞产生
3、爆炸。项目类图如下:坦克大战项目文档- 2 -三、编码实现1、新建一个控制台项目。创建一个游戏窗体(GameForm) ,并设置其宽和高,并让其在屏幕上显示出来。代码如下:static void Main(string args)GameForm gameForm = new GameForm();/创建游?戏窗体?gameForm.ShowDialog();/让?窗体?显?示?出?来2、定义一个接口。因为所有的类在窗体的的实现都要绘制,所以设置一个接口。接口只管定义方法而不实现。代码如下:interface IDrwvoid Draw(Graphics g); /接口里?面?只?定义?Dra
4、w方?法3、坦克类(Tank) 。坦克的属性有宽、高、X 坐标、Y 坐标、速度、外观、方向。坦克的方法有实现接口 IDraw 的虚方法,有抽象方法移动(Move)和返回值为子弹的发弹方法(ShootBullet) 。坦克可以向不同方向移动,因此用一个枚举表示不同的方向。坦克大战项目文档- 3 -4、我方坦克(MyTank )我方坦克的属性可以继承父类坦克,但加一个生命值的属性。在构造函数中分别初始化我方坦克的属性,重写 Move 方法和 ShootBullet 方法。在 Move方法中可以向四种不同的方向移动,发弹的时候子弹也要随着坦克的方向而改变,都以坦克向上移动为例,代码为:public
5、override void Move()MyTank myTank = new MyTank();if (_Direction = DirectionValue.Up)_Shape = Resource.myup1;_Width = 34;_Height = 60;_Y = _Y - _Speed;if (_Y _ListEnemyTank = new List();/创建一?个?放?敌D方?坦1克?的?集合?Thread _threadCreatEnemyTank = null;/声明一?个?创建敌D方?坦1克?的?进?程Thread _threadMoveEnemyTank = null;
6、/声明一?个?对?方?坦1克?移?动的?进?程两个线程启动后后分别调用产生敌方坦克和移动敌方坦克的方法。void CreatEnemyTank() /创建敌D方?坦1克?for (int i = 0; i _ListBullet = new List();/创建一?个?放?子弹的?集合子弹的移动也是一直在进行,因此也要一个线程来控制子弹的移动,Thread _threadMoveBullet = null;/声明一?个?子弹移?动的?进?程线程启动后子弹的Move方法也被调用, void MoveBullet() /子弹移?动的?方?法Bullet bullet = null;坦克大战项目文档
7、- 6 -while (1 = 1)for (int i = 0; i _ListBullet.Count; i+) /遍历子弹集合?中D的?每?一?个?元a素?bullet = _ListBulleti;if (bullet != null)bullet.Move(); /子弹移?动6、墙(Wall)墙的属性有宽、高、X 坐标、Y 坐标、外观,类型。墙的方法只有绘制,因此继承IDraw接口的方法。在主窗体绘制墙的时候,如果单个画很费力,而且程序代码比较繁琐,所以采用二维数组的方法来绘制。根据窗体的大小来定义一个二维数组int, _WallArray=new int18,12,遍历数组的每一个
8、元素值来确定绘制那种墙,这里1代表砖墙,2代表刚墙,3代表水墙,4代表草地,采用这种方法方便快捷,容易修改。public void CreateWall() /创建墙?的?方?法_WallArray = new int14, 180,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,2,4,4,4,4,2,4,4,4,4,2,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,3
9、,3,0,0,0,1,1,1,1,1,0,0,3,3,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,0,2,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,1,1,1,4,0,0,0,0,0,0,4,4,0,0,0,0,0,1,1,1,1,1,0,0,0,0,4,4,3,4,0,0,0,0,0,1,1
10、,0,1,1,0,0,0,0,4,3,;for (int i = 0; i 14; i+)for (int j = 0; j 18; j+)Point p = new Point(i * 50, j * 50); /墙?的?位?置?switch (_WallArrayi, j) case 0:坦克大战项目文档- 7 -break; /0 无T作为acase 1:brickWall = new BrickWall(p);/1 砖?墙?_ListWall.Add(brickWall);break;case 2:steelWall = new SteelWall(p); /2 钢?墙?_ListWa
11、ll.Add(steelWall);break;case 3:waterWall = new WaterWall(p);/ 3 河流_ListWall.Add(waterWall);break;case 4:meadoWall = new MeadoeWall(p); / 草Y地?_ListWall.Add(meadoWall);break;default:break;7、碰撞检测碰撞检测贯穿整个游戏中,在游戏中要进行我方坦克与墙的碰撞检测、敌方坦克与墙的碰撞检测、我方子弹与敌方坦克的碰撞检测、敌方子弹与我方坦克的碰撞检测、子弹与墙的碰撞检测、子弹与老巢的碰撞检测、我方坦克与敌方坦克的碰撞检测
12、。子弹与墙碰撞产生火花,子弹与坦克碰撞产生爆炸。进行碰撞检测的原理就是各对象在窗体上绘制出来都占有一个矩形,我们获取到这个矩形,当不同对象的矩形相交就认为是碰撞了。然后我们根据具体的对象碰撞分别写各自的方法。我们在坦克父类、子弹父类、墙的父类和老巢的方法中写一个获得所在矩形的方法,public Rectangle GetRectangle()return new Rectangle(_X, _Y, _Width, _Height);1) 我方坦克与墙的碰撞检测我方坦克在移动的过程中就进行碰撞检测,当碰到草地时,方向不变,当碰到其他墙时,就停止前进,由键盘操作改变方向继续前进。我方坦克的移动是在
13、KeyDown事件下写的,因此碰撞检测也写在KeyDown事件下,而且在Move方法后面。我方坦克一移动就检测是否发生碰撞,以向上为例,代码如下:if (e.KeyCode = Keys.Up) /按向上?键坦克大战项目文档- 8 -_MyTank.Direction = DirectionValue.Up; /我方?坦1克?向上?移?动_MyTank.Move();for (int i = 0; i _ListWall.Count; i+)Wall wall = _ListWalli;bool isMyTankHitWall = _MyTank.GetRectangle().Intersec
14、tsWith(_ListWalli.GetRectangle(); /上?衣?过y程中D与?墙?进?行D碰?撞2检测aif (isMyTankHitWall)if (wall is SteelWall | wall is WaterWall | wall is BrickWall)_MyTank.Y = _ListWalli.Y + _ListWalli.Height;else_MyTank.Y = _MyTank.Y - _MyTank.Speed; 2) .敌方坦克与墙的碰撞检测敌方坦克与墙的碰撞检测原理一样,都是在移动的过程中进行检测,只不过在碰撞之后方向是自动改变的,要实现自动改变,我
15、们采用随机数的方法来使方向随机改变。随机产生1-4这4个数,1表示向上,2表示向下,3表示向左,4表示向右。敌方坦克与墙的碰撞可以仿照我方坦克与墙的碰撞,在这里就不多说了。3)我方子弹与墙的碰撞检测我方子弹与墙的碰撞检测原理跟坦克与墙的碰撞检测一样,方法写在子弹移动的方法MoveBullet中。子弹与墙碰撞产生火花,并且子弹消失,如果是砖墙,撞墙也消失,跟草地没有任何反应。代码片段:for (int j = 0; j _ListWall.Count; j+)Wall wall = _ListWallj;bool isBulletHitWall = bullet.GetRectangle().I
16、ntersectsWith(wall.GetRectangle(); /子弹与?墙?进?行D碰?撞2检测aif (isBulletHitWall)if (wall is SteelWall) /子弹与?钢?墙?碰?撞2产生火e花,?并把?子弹移?除y坦克大战项目文档- 9 -Spark spark = new Spark(bullet.X, bullet.Y);_ListSpark.Add(spark);_ListBullet.Remove(bullet);if (wall is BrickWall) /子弹与?砖?墙?碰?撞2产生火e花,?并把?砖?墙?,?子弹移?除ySpark spark
17、 = new Spark(bullet.X, bullet.Y);_ListSpark.Add(spark);_ListWall.Remove(wall);_ListBullet.Remove(bullet);4)敌方坦克与墙的碰撞检测、我方子弹与敌方坦克相撞、敌方子弹与我放坦克相撞、我方坦克与敌方坦克相撞、子弹击中老巢的方法都可以仿照我方子弹与墙的碰撞检测进行编写,这样就可以举一反三。8、边界判断在游戏中,子弹出边界后就从集合中移除子弹,我方坦克移动到边界后就停止移动,敌方坦克移动到边界就自动改变方向,这些都要进行判断,代码写在各自的Move方法里。9、窗体刷新所有的对象都在主窗体中绘制,要
18、显示出效果就要不断地刷新主窗体,因此设置一个线程来刷新主窗体。10、游戏效果图图1:游戏刚开始坦克大战项目文档- 10 -图2:我方胜利后在窗体上显示消息框图3:敌方胜利胜利后在窗体显示消息框坦克大战项目文档- 11 -图4:老巢被攻破四、心得体会坦克大战中我遇到的第一个问题是子弹的发射,当时我完全没有思路,老师也讲了子弹发射的思路,可我就是不会写,来回纠结了一天,直到同桌作出了我方子弹的发射,我看了代码才慢慢开始写,最后也算写出来了。我觉得这就是我的问题,遇到问题还是比较毛躁,不敢下手去写。老师教导我们遇到问坦克大战项目文档- 12 -题时把问题下下来然后分析,这种方法很好,但由于我的毛躁,这种方法这次没能得到很好的实施,下回一定注意,心态一定要平和。第一次写坦克大战还有一个致命错误就是笔误。我记得这次遇到好几次,其中就像敌方坦克发不出子弹,草地和水墙画不出来都是笔误的问题,这是一个细节问题,也是一个细心问题,要引起高度的重视。项目中团队合作很重要,就像项目类图的绘制就是充分的证明,两人一组进行讨论,最后定出最终类图。在遇到问题自己想不出解决办法时一定要向他人请教,不能不问,敢问才能解决问题,才能进步。坦克大战是在赛车项目后做的第一个项目,总得还感觉不错,以后要更加努力,做的更好!