坦克大战设计与实现

J2SE是近年来随着各种不同技术的发展,尤其是编程语言飞速发展而诞生的一项新的开发语言。随着信息技术的飞速发展,计算机的使用也日渐普及,本文从实际应用出发,向大家介绍坦克大战游戏的设计与实现。Eclipse平台模拟器开发调试。玩家坦克的运行是通过键盘响应事件控制,敌方坦克则是自动运行并具有了一定的智能性。程序进行了精简和优化,游戏运行流畅,具有一定的吸引力。

论文中介绍了游戏的总体设计思路和开发游戏的多线程、高级、低级图形界面的使用。本论文介绍了坦克大战的历史、java的相关技术及本程序的结构分析和具体功能的实现。游戏是在Eclipse3.5.1环境下编程。它的主要功能有:

能够四处移动、能够发射子弹打击敌人、敌人能够移动、 能够模拟爆炸、能够产生障碍、能够增长生命。

关键词游戏 Java  坦克  智能

ABSTRACT

J2SEis a kind of fast developing technology implemented on various devices especially equipments.With the rapid development of information technology, computer use has become more popular, the practical application of this paper to introduce the design and  implementation of the game Battle City .It transports the classical product to computer, offering corresponding software for such more popular hardware platform. Eclipse platform simulator. The player’s tank is controlled by keyboard response events, while the enemy’s tank is operated automatically and is of intelligence. Having been streamlined and optimized, the game system can run fluently and attract the players.

This paper describes the history of Battle City, java related technologies and the procedures of structural analysis and the realization of specific functions. Game programming environment in Eclipse3.5.1. Its main features are:

to move around. capable of firing bullets against enemy. the enemy can move. to simulate the explosion. to create an obstacle. to increase the life

Key wordsgame Java tank Smart

1 前  言

1.1 开发背景

1.2 国内外研究现状

1.3 系统设计目标

1.4 研究的意义

1.5 一个成功的游戏大多具有以下特征

 系统开发所采用的技术

2.1 开发环境

2.2 Java语言的特点

2.3 关于Eclipse

3 坦克大战需求分析

3.1 可行性分析

3.2 游戏规则和系统功能介绍

3.3 程序流程图

3.3.1 程序中炮弹的运行和流程

3.3.2 敌方坦克运行流程

 系统总体设计

4.1 TankClient.java

4.2 Missile.java

4.3 Tank.java

4.4 Wall.java

4.5 Direction.java

4.6 PropertyMgr.java

4.7 Blood.java

4.8 Explode.java

5 系统详细设计与实现

5.1 游戏窗口与我方坦克

5.2 子弹与一辆敌人坦克

5.3 爆炸与多辆敌人坦克

5.4 墙、坦克互相穿越、超级炮弹和生命值

5.5 血块与加入图片

6 坦克大战打炮弹功能系统测试

6.1 CTRL打炮弹的测试要点

6.2 坦克大战系统采用的测试方法

6.3 功能测试用例

6.3.1 坦克打炮弹

6.3.2 坦克打炮弹功能测试图

参考文献

附  录

1 前  言

1.1 开发背景

如果要从精品云集的FC游戏中找一款经典之作,《坦克大战》绝对是FC上所有经典中的经典,当年NAMCO的《Battle City》在国内又称《坦克大战》,以其极为出色的游戏性令全球众多玩家为之疯狂。

该游戏是一款以红白机为平台操作射击游戏,1-2名玩家可控制自己的坦克进行移动和射击,以保护总部为前提,歼灭对方为胜利目标。每个关卡的地形不同,玩家可利用各种地形对敌人进行打击,并会得到补给。两人配合游戏时,第一关都有不一样的战术,以便能顺利完成关卡目标,对于玩家来说大大加强了游戏乐趣。其操作简单且娱乐性强,是一款男女老少皆宜的游戏。游戏附带地图编辑器,玩家可自行编辑地图进行游戏。游戏中的众多经典关卡至今还让许多玩家记忆犹新,在80年代时曾经在无数家庭中带来了无尽的欢乐。

JAVA语言是当今最为流行的程序设计语言之一,作为非常优秀和极为健壮的编程语言,它同时有的面向对象,与平台无关,分布式应用,安全,稳定和多线程等优良的特征,使用JAVA语言,不仅可以开发出功能强大的大型应用程序,而且JAVA语言本身突出的跨平台的特性也使得它特别适合于Internet上的应用开发,可以这样说,JAVA的出现使得所开发的应用程序“一次编写,处处可用”的实现成为了可能。

1.2 国内外研究现状

从1980年第一款坦克大战游戏Battle City诞生至今已有29年的历史了,坦克大战也发展出了很多版本,从最初的红白游戏机到现在的网络游戏,从最开始只支持双人游戏到现在支持多人网络对战,从当初的平面2D到现在的立体3D游戏,坦克大战趋势是网络化,立体化和多样化。

网络化的市场让游戏变成人和人的交战,而不是人和电脑的对战,这样可以增加游戏的难度,游戏的时间和游戏的寿命。立体化是为了适应现在越来越3D的趋势,游戏有必要做的更加的完善,趋向3D并且加重环境描写。多样化则是增加游戏的任务,游戏的耐玩性而在坦克大战里增加更多的内容加大其可玩性,比如:装备,对战方式,任务合作模式等等。可以想像,如果坦克大战还是人机对战的模式,并且协作性不提高的化,玩家对该游戏的选择性将大大降低,最终成为历史。

1.3 系统设计目标

能够四处移动;能够发射子弹打击敌人;敌人能够移动;能够模拟爆炸;能够产生障碍;能够增长生命

1.4 研究的意义

本课题就是想对该游戏进行分析研究,借助于它的游戏规则,用JAVA语言模拟开发一个类似的,功能相对简单的小游戏。从而使自己更清楚的认识小游戏开发需要的知识和开发的环节和涉及到的领域。

1.5 一个成功的游戏大多具有以下特征

1.易于学习

既然游戏面向的是普通消费者而不是计算机专家,那么他们不可能深入的学习游戏技巧。消费者不会花几天去研究游戏。保持游戏的简单是最基本的要求。 

2.可中断性

多任务处理是基本特征。电脑用户常常在任务之间有一小段时间。而游戏、日历管理、通讯和工作数据访问使用的是同一个电脑。所以一个好的电脑游戏应该提供短时间的娱乐功能,并且允许用户在游戏和工作模式之间顺利切换。 

3.丰富的社会交互

不管一个游戏设计得多好,只要玩家找到了它的根本模式或者玩完了所有的游戏路径很快就会厌烦这个游戏。对于一个游戏,重要的是与别的玩家合作以增强所玩游戏的智力和随机性。在今天纷繁复杂的游戏中具有丰富社会交互的游戏证明是成功的。

4.无违法内容

既然所有年龄性别的人群都玩手机游戏并且常常在公共/工作场合,就应该避免明显的暴力或者色情内容。

系统开发所采用的技术

2.1 开发环境

操作系统:Microsoft Windows XP

程序语言:Java

开发工具:Eclipse

2.2 Java语言的特点

1.平台无关性

平台无关性是指Java能运行于不同的平台。Java引进虚拟机 原理,并运行于虚拟机,实现不同平台的Java接口之间。使用Java编写的程序能在世界范围内共享。Java的数据类型与 机器无关,Java虚拟机(Java Virtual Machine)是建立在硬件和操作系统之上,实现Java二进制代码的解释执行功能, 提供于不同平台的接口的。

2.安全性

Java的编程类似C++,学习过C++的读者将很快掌握Java的精髓。Java舍弃了C++的指针对存储器地址的直接操作,程序运行时,内存由操作系统分配,这样可以避免病毒通过指针侵入系统。Java对程序提供了安全管理器,防止程序的非法访问。

3.面向对象

Java吸取了C++面向对象的概念,将数据封装于类中,利用类的优点,实现了程序的简洁性和便于维护性。类的封装性、继承性等有关对象的特性,使程序代码只需一次编译,然后通过上述特性反复利用。程序员只需把主要精力用在类和接口的设计和应用上。Java提供了众多的一般对象的类,通过继承即可使用父类的方法。在Java中,类的继承关系是单一的非多重的,一个子类只有一个父类,子类的父类又有一个父类。Java提供的Object类及其子类的继承关系如同一棵倒立的树形,根类为Object类,Object类功能强大,经常会使用到它及其它派生的子类。

4.分布式

Java建立在扩展TCP/IP网络平台上。库函数提供了用HTTP和FTP协议传送和接受信息的方法。这使得程序员使用网络上的文件和使用本机文件一样容易, 使用其相关技术可以十分方便的构建分布式应用系统。

5.健壮性

Java致力于检查程序在编译和运行时的错误。类型检查帮助检查出许多开发早期出现的错误。Java自己操纵内存减少了内存出错的可能性。Java还实现了真数组,避免了覆盖数据的可能,这些功能特征大大提高了开发Java应用程序的周期。并且Java还提供了Null指针检测、数组边界检测、异常出口、Byte code校验等功能。Java取消了C语言的结构、指针、#define语句、多重继承、goto语句、操作符、重载等不易被掌握的特性,提供垃圾收集器自动回收不用的内存空间。

6.简单性

Java删除了许多极少被使用、不容易理解和容易令人混淆的C++功能。剔除的功能主要包括运算符重载、多重继承以及广泛的自动强迫同型。与C++相比,Java语言也提供重载函数,不过它重载的对象是函数而非变量或是运算符。

Java还增加了自动内存收集功能,从而简化了Java程序的工作,不过同时也让系统变得稍微复杂了一些。

Java的目标之一是要协助开发能独立地在小型机器上顺利执行的软件。Java的基本解释器和对象类支持约占40KB空间,而若增加基本的标准程序库和线程支持则需增加约175KB。体积小对于在嵌入式系统中的使用是很重要的,正因为这样,Java才能够通过网络轻易下载。

7.中立性

Java的设计目标是要支持网络应用程序。一般而言,网络是由许多不同的平台系统构成,包括各种CPU与操作系统结构。为了让Java应用程序能够在网络上任何地方执行,其编译器将会生成一种具备结构中立性的目标文件格式。编译后的程序码可以在提供Java运行系统的多种不同处理器上面执行。

Java的这种中立性结构不仅对网络应用很有帮助,而且也很适合单一的系统软件流通。

Java编译器是通过生成与某一特定电脑结构无关的字节代码指令,以达到上述功能的。这些字节代码指令能很容易在任何机器上解释执行。

  结构中立性是确保程序可移植的最重要部份,不过除此之外还需很多必须配合的条件和C与C++不同的是,Java规范中并无任何与机器结构相依存的陈述存在。它指定了基本数据类型的大小,及其算术运算元的执行行为。例如:“int”代表一个有符号的二进制补码32Bit整数,而“float”代表一个32BitIEEE754浮点数。这些选择在今天的环境很适用,因为几乎所有CPU都具备这些特性。

  程序运行库属于系统的一部份,它定义了一些可移植的程序接口。例如,它包括一个抽象的Windows类,并且提供了该类在Unix、Windows和Macintosh平台上的实现。

8.解释执行性

Java解决器可以直接在任何已移植该解释器的机器上解释、执行Java字节代码。再者,由于其链接过程比较倾向于逐步增量与轻量过程,因此程序开发更快、更精密。

9.高效能性

虽然解决过的字节代码性能已相当不错,不过有些情形下还是要求程序达到更高执行效能。字节代码可以动态地解释为执行应用程序特定CPU的机器码。这对于习惯使用一般编译器与动态载人器的程序设计者而言,有点类似将最终的机器码生成器放到动态载入器之内。

  字节代码格式在设计上即已考虑了机器码的产生,因此实际的机器码生成程序相当简单。其生成的机器码是有效的,编译器自动分配寄存器,而在生成字节代码期间也会进行一些优化。

  我们以解决码在一台Sun Microsystem SPARC Station10上执行时,达到每秒三十万个函数调用速率。字节代码转换至机器码的速度性能,几乎和直接生成机器目标代码的C或C++没有什么两样。

10.多线程性

Java拥有一组复杂的同步化基本单元,它们是以广泛使用的C.A.R.Hoare监视器与条件变量图为基础的。将这些概念融合到语言中之后,它们就变得更容易使用且更为健壮。这种融合方式大部分来自Xerox的Cedar/Mesa系统。

  多线程所带来的其它好处包括更好的交互式回应能力与实时运行能力。然而这会受到底层平台的限制:独立执行的Java运行环境有着很好的实时执行能力,而若在其他系统例如Unix、Windows、Macintosh或WindowsNT等平台上执行时,则会由于底层平台的原因而使实时响应能力受到影响。

11.动态特性

就各方面而言,Java是一种比C或C++更具动态特性的语言。它在设计上强调为不断发展的运算环境提供支持。 Java能了解由Objective C引用过来的接口概念。简单地说,接口就是规范一组与对象相对应的成员函数,但对象如何实现这些成员函数则亟待解决。一个类实现一个接口是要提供这个接口所包含的所有成员函数的实现,与此相反,派生子类则从父类继承了一组成员函数以及它们的实现。一个Java类可以实行多个接口,但只能从一个父类继承。接口告诉连接对象它可以做什么而不是怎么做,使得它在代码上更具有灵活性和可复用性。在C或C++程序中,如果你有一个指针指向一个对象,但你不知道该对象的类型是什么,那么你将没有办法找出它。然而,在Java中根据运行类型信息进行查找是很直截了当的,因为在编译阶段和运行阶段都会检查数据类型转换。所以你在Java中完全可以信任这种转换,而C与C++的编译器则只是相信你自己已做了正确的处理。

12.有关绘图的一些技术

Graphics类提供了简单的2D绘图功能。它具有24位深度色彩的绘制能力,以三原色分别各占一个字节表示其颜色。程序只能在paint()函数中使用Graphics绘制,GameCanvas可调用getGraphics()函数直接绘制在缓冲区上,可以在任何时间请求传输到前台。其对象会被传给Canvas的paint()函数,以便最终显示。

在没有MIDP2.0前,进行游戏绘图一般需要手动编程使用双缓冲。需要在paint()方法内所想要画的图形画在一张预先准备好的背景,等所有绘图操作都完成后再将背景的数据拷贝到实际的屏幕上。Image类提供了一个建立背景的静态方法createImage(int width, int height),再利用getGraphics()方法取得属于这个背景的Graphics对象,所进行的绘图操作都会作用在背景上,等到全部的绘图操作完成后,再调用drawImage()方法将背景的数据复制到实际显示的屏幕上。

这样的技术在绘制动画时特别有用。绘制动画时经常需要不断地更新画面,而更新画面的操作就是先将屏幕以fillRect()的方式清除,再将下一张图片画在屏幕上,然而反复的清除及重绘会造成屏幕的闪烁现象(flicker),因此使用双重缓冲的好处就是在背景进行这个清除及重绘的操作,再将完成的绘图拷贝到屏幕上,由于用户看不到清除的操作,因此就不会出现闪烁的现象了。不过在某些MIDP的实现上已经加上了双重缓冲的支持,因此在处理前应先利用Canvas类的isDoubleBuffer()方法来判断。

2.3 关于Eclipse

Eclipse最初是由IBM公司开发的替代商业软件Visual Age for Java的下一代IDE开发环境,2001年11月贡献给开源社区,现在它由非营利软件供应商联盟Eclipse基金会(Eclipse Foundation)管理。 2003年,Eclipse 3.0选择OSGi服务平台规范为运行时架构。 2007年6月,稳定版3.3发布。2008年6月发布代号为Ganymede的3.4版。2009年7月发布代号为GALILEO的3.5版。2010年6月发布代号为Helios的3.6版。

Eclipse是著名的跨平台的自由集成开发环境(IDE)。最初主要用来Java语言开发,但是目前亦有人通过插件使其作为其他计算机语言比如C++和Python的开发工具。Eclipse的本身只是一个框架平台,但是众多插件的支持使得Eclipse拥有其他功能相对固定的IDE软件很难具有的灵活性。许多软件开发商以Eclipse为框架开发自己的IDE。

Eclipse 最初由OTI和IBM两家公司的IDE产品开发组创建,起始于1999年4月。IBM提供了最初的Eclipse代码基础,包括Platform、JDT 和PDE。目前由IBM牵头,围绕着Eclipse项目已经发展成为了一个庞大的Eclipse联盟,有150多家软件公司参与到Eclipse项目中,其中包括Borland、Rational Software、Red Hat及Sybase等。Eclipse是一个开发源码项目,它其实是 Visual Age for Java的替代品,其界面跟先前的Visual Age for Java差不多,但由于其开放源码,任何人都可以免费得到,并可以在此基础上开发各自的插件,因此越来越受人们关注。近期还有包括Oracle在内的许多大公司也纷纷加入了该项目,并宣称Eclipse将来能成为可进行任何语言开发的IDE集大成者,使用者只需下载各种语言的插件即可。

虽然大多数用户很乐于将 Eclipse 当作 Java IDE 来使用,但 Eclipse 的目标不仅限于此。Eclipse 还包括插件开发环境(Plug-in Development Environment,PDE),这个组件主要针对希望扩展 Eclipse 的软件开发人员,因为它允许他们构建与 Eclipse 环境无缝集成的工具。由于 Eclipse 中的每样东西都是插件,对于给 Eclipse 提供插件,以及给用户提供一致和统一的集成开发环境而言,所有工具开发人员都具有同等的发挥场所。

基于Eclipse的应用程序的突出例子是IBM的WebSphereStudioWorkbench,它构成了IBM Java开发工具系列的基础。例如,WebSphere Studio Application Developer 添加了对 JSP、servlet、EJB、XML、Web 服务和数据库访问的支持。

Eclipse是一个开放源代码的软件开发项目,专注于为高度集成的工具开发提供一个全功能的、具有商业品质的工业平台。它主要由Eclipse项目、Eclipse工具项目和Eclipse技术项目三个项目组成,具体包括四个部分组成——Eclipse Platform、JDT、CDT和PDE。JDT支持Java开发、CDT支持C开发、PDE用来支持插件开发,Eclipse Platform则是一个开放的可扩展IDE,提供了一个通用的开发平台。它提供建造块和构造并运行集成软件开发工具的基础。Eclipse Platform允许工具建造者独立开发与他人工具无缝集成的工具从而无须分辨一个工具功能在哪里结束,而另一个工具功能在哪里开始。

Eclipse SDK(软件开发者包)是Eclipse Platform、JDT和PDE所生产的组件合并,它们可以一次下载。这些部分在一起提供了一个具有丰富特性的开发环境,允许开发者有效地建造可以无缝集成到Eclipse Platform中的工具。Eclipse SDK由Eclipse项目生产的工具和来自其它开放源代码的第三方软件组合而成。Eclipse项目生产的软件以 GPL发布,第三方组件有各自自身的许可协议。

3 坦克大战需求分析

3.1 可行性分析

1.游戏程序是一项精度要求很高的程序系统,因为其代码利用率很高。一个实时运行的最终作品,每秒都会运行成千上万行程序,绘图事件、健盘事件都会以极高的频率在后台等待循环。因此,其逻辑设计应当相当严谨,需将所有可能发生的事件各意外情考虑在设计中。

2.游戏中为了美观,适用性强,可能需要采用外部文件引入的图片贴图,屏幕刷新的双缓冲等都有较好的解决方案。

3.我方的坦克的运行可以通过键盘响应事件控制,但敌方则因为是自动运行,就需要有一定的智能性;同时,出现在屏幕上的敌方可能性会有较多的数量,这需要为每个敌方开辟一个线程以便能让其独立运行。Java的多线程能力为实现这样的游戏提供了可能。

4.对于双方坦克发出的子弹的控制也需要对其跟踪控制,子弹也需要处在独立的线程中。敌方子弹仅需要扫描用户坦克,而用户坦克需要在每一步扫描所有的敌方坦克。这需要对所有的对象有较好的控制。另外,子弹在运行过程中也需要实时扫描是否碰撞到了相关障碍物或屏幕边界。如此过多的线程同时在本来效率就不高的机器上运行,也许会导致程序的缓慢。

5.双方坦克在前进时也考虑到是否碰撞到相关物体或对方坦克,以免重叠运行,造成许多物理上不可能的情况,缺乏真实感。

6.是基于虚拟机的半解释型编译系统,其执行效率较C++等完全编译后的程序会低很多,程序如果不进行精简和优化,将可能导致运行的不流畅。开发过程中要对结构的控制、注意变量的使用。

3.2 游戏规则和系统功能介绍

游戏中坦克能向八个方向移动,和友方坦克碰到后不能穿越。坦克能向八个方向发射子弹,子弹击中坦克产生爆炸,玩家坦克还能发射同时向八个方向发射集束炸弹。地图上有障碍物,坦克和子弹都不能穿越障碍物。玩家坦克有生命数,还有生命值,被敌人子弹击中后生命值减少,生命值为0后坦克爆炸,开始新坦克的操作按F2。地图上有随机出现的加生命值的血块,吃恢复生命值满,游戏的目的是操作自己的坦克消灭敌人坦克,消灭所有敌人坦克后游戏结束。

基于Java语言的各种特性,本软件使用Java语言进行开发,并最终完成当初的设计要求,所有的程序以及程序功能如表3-1所示

3-1程序功能表

程序名

程序功能

TankClient.java

游戏的主窗口

Tank.java

坦克的数据和方法

Missile.java

炮弹的数据和方法

Explore.java

爆炸的数据和方法

Wall.java

墙的数据和方法

Blood.java

血块的数据和方法

Direction.java

单独定义方向的类

PropertyMgr.java

载入图片管理

3.3 程序流程图

3.3.1 程序中炮弹的运行和流程

炮弹继承了Missile,运行在独立的线程中,它拥有一个很重要的变量,good。它标识了子弹是属于玩家的还是敌人的,这样可以控制子弹再脱离坦克后的运行状态中的行为,其中主要功能流程图见图3-1流程图

当击中了需要做出反应的物体时,就分别采取措施:击中墙时,子弹生命就结束,不再继续画.如果没有击中物体,就继续检测是否击中了坦克,这根据子弹的来源分为两种情况.当来自玩家时,检测是否与敌人碰撞,发生碰撞时,将敌人从List中移走,并置为空,产生爆炸效果,敌人数量减少一位,敌人屏幕上数量减少一位。如果是来自敌人的子弹,将同样检测与玩家坦克的碰撞,如有碰撞,玩家生命就减少,当生命值为0时,就死亡了。

下图为炮弹运行的主要流程: 

图3-1流程图

3.3.2 敌方坦克运行流程

图3-2流程图

4 系统总体设计

本系统共包括8个java源文件

4.1 TankClient.java

该文件为主类,是各个类的主管,负责调用各个类。创建游戏窗口。

表4-1成员变量表

成员变量描述

变量类型

名称

整个游戏的宽度

int

GAME_WIDTH

整个游戏的高度

int

GAME_HEIGHT

表4-2方法表

方法

功能

备注

lauchFrame()

显示坦克主窗口

无备注

paint(Graphics  g)

重画时调用

无备注

update(Graphics g)

使用双缓冲消除闪烁现象

无备注

4.2 Missile.java

该文件是描述子弹的类,实现子弹的各个功能,实现打坦克,实现子弹是否活着的功能

表4-3成员变量表

成员变量描述

变量类型

名称

子弹在X方向上的速度

int

XSPEED

子弹在Y方向上的速度

int

YSPEED

子弹的高度

int

HEIGHT

表4-4方法表

方法名

功能

备注

hitTank(Tank t)

实现子弹撞坦克的功能

无备注

Islive()

实现判断子弹是否活着的功能

无备注

4.3 Tank.java

该文件是描述坦克的类,创建坦克,打子弹,打多发子弹。处理坦克和墙相撞的问题。坦克不能相互穿越。加入主战坦克的生命值。击毙敌人坦克。让坦克更加智能

表4-5成员变量表

成员变量描述

变量类型

名称

坦克在X方向上的速度

int

XSPEED

坦克在Y方向上的速度

int

YSPEED

坦克的高度

int

HEIGHT

表4-6方法表

方法名

功能

备注

collidesWithWall(Wall w)

处理坦克和墙相撞的问题

无备注

Eat(Blood b)

坦克吃血,增长生命

无备注

4.4 Wall.java

该文件是描述墙的类,添加两堵墙,处理坦克和墙相撞的问题

表4-7成员变量表

成员变量描述

变量类型

名称

坦克在X方向上的速度

int

XSPEED

坦克在Y方向上的速度

int

YSPEED

坦克的高度

int

HEIGHT

表4-8方法表

方法名

功能

备注

collidesWithWall(Wall w)

处理坦克和墙相撞的问题

无备注

4.5 Direction.java

该文件是描述该软件所需静态成员变量的类。

表4-9成员变量表

成员变量描述

变量类型

名称

向左的方向

static

L

向左上的方向

static

LU

向上的方向

static

U

4.6 PropertyMgr.java

该文件是描述配置文件管理者的类。

表4-10成员变量表

成员变量描述

变量类型

名称

程序配置文件

Properties

Props

表4-11方法表

方法名

功能

备注

getProperty(String key)

获得配置文件

无备注

4.7 Blood.java

该文件是描述给坦克加血的类,添加必要的方法,让血块按照一定轨迹运动,并在一定时间消失。

表4-12成员变量表

成员变量描述

变量类型

名称

血块在X轴的坐标

Int

X

血块在X轴的坐标

Int

Y

血块的宽度

Int

W

血块的高度

Int

H

表4-13方法表

方法名

功能

备注

Move()

处理血块移动的问题

无备注

islive()

判断血块是否活着

无备注

4.8 Explode.java

该文件是描述坦克被击中后爆炸的类,击毙一辆坦克后产生爆炸。

表4-14成员变量表

成员变量描述

变量类型

名称

坦克生死的描述

boolean

Live

主类的变量

TankClient

Tc

表4-15方法表

方法名

功能

备注

GetDefaultToolkit()

拿到的工具包

无备注

Draw(Graphics)

画爆炸现象

无备注

图4-1 程序功能结构图

5 系统详细设计与实现

5.1 游戏窗口与我方坦克

1.窗口

产生一个窗口,添加关闭窗口的事件处理,不允许窗口的大小改动,通过Eclipse建立新的项目,为新的项目指定不同的源代码和输出目录,指定项目所用的JDK版本,通过Eclipse建立新的类,匿名类的用法。

注意:类名和方法名的命名,见名知意,类名首字母大写,方法名、变量名首字母小写,应用驼峰标识。

图5-1功能图

import java.awt.*;

import java.awt.event.*;

public class TankClient extends Frame {

public void lauchFrame() {

this.setLocation(400, 300);

this.setSize(800, 600);

this.setTitle("TankWar");

this.addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) {

System.exit(0);

}

});

this.setResizable(false);

setVisible(true);

}

public static void main(String[] args) {

TankClient tc = new TankClient();

tc.lauchFrame();

}

}

2.画出代表坦克的实心圆

重写paint方法。注意:不要改变原来的前景色

图5-2功能图

  public void paint(Graphics g) {

Color c = g.getColor();

g.setColor(Color.RED);

g.fillOval(50, 50, 30, 30);

g.setColor(c);

}

3.让坦克动起来

将位置改变为变量,启动线程不断重画,线程重画更均匀,更能控制重画的速度。按键重画不能解决子弹自动飞行的问题。每次重画改变Tank位置。

注意:x, y值得含义,指的是小方块的左上角点。

图5-3功能图

private class PaintThread implements Runnable {

public void run() {

while(true) {

repaint();

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

4.使用双缓冲消除闪烁现象

原因:刷心重画频率太快,paint方法还没有完成,逐条显示。

解决:将所有东西画在虚拟图片上,一次性显示出来。

图5-4功能图

public void update(Graphics g) {

if(offScreenImage == null) {

offScreenImage = this.createImage(800, 600);

}

Graphics gOffScreen = offScreenImage.getGraphics();

Color c = gOffScreen.getColor();

gOffScreen.setColor(Color.GREEN);

gOffScreen.fillRect(0, 0, 800, 600);

gOffScreen.setColor(c);

paint(gOffScreen);

g.drawImage(offScreenImage, 0, 0, null);

}

5.让坦克听从我们的指挥

添加键盘监听类KeyMonitor,TankClient添加键盘监听器,针对不同的键改变坦克位置,与重画线程结合产生不同方向运动。

注意:switch case语句中break语句的运用

图5-5功能图

private class KeyMonitor extends KeyAdapter {

public void keyPressed(KeyEvent e) {

int key = e.getKeyCode();

switch(key) {

case KeyEvent.VK_LEFT :

x -= 5;

break;

case KeyEvent.VK_UP :

y -= 5;

break;

case KeyEvent.VK_RIGHT :

x += 5;

break;

case KeyEvent.VK_DOWN :

y += 5;

break;

}

}

}

6.将坦克单独包装成类

建立Tank类,为Tank类添加成员变量x y,添加draw方法,使Tank类独立,制自己的画法,添加Tank类处理按键的方法,根据Tank类修改TankClient类。

7.让主战坦克向8个方向行走

添加记录按键状态的布尔量,添加代表方向的量(使用枚举),根据按键状态确定Tank方向,根据方向进行下一步的移动(move)。处理键抬起的消息修改TankClient相关代码。

图5-6功能图

public void keyPressed(KeyEvent e) {

int key = e.getKeyCode();

switch(key) {

case KeyEvent.VK_LEFT :

bL = true;

break;

case KeyEvent.VK_UP :

bU = true;

break;

case KeyEvent.VK_RIGHT :

bR = true;

break;

case KeyEvent.VK_DOWN :

bD = true;

break;

}

locateDirection();

}

5.2 子弹与一辆敌人坦克

1.添加子弹类

添加Missile类,添加x,y,dir等属性以及常量.添加构造方法, draw方法等必要方法,根据不同方向,进行不同的运动,在TankClient中模拟一颗子弹

注意:不一定一次写到位。

图5-7功能图

import java.awt.*;

public class Missile {

public static final int XSPEED = 10;

public static final int YSPEED = 10;

int x, y;

Tank.Direction dir;

public Missile(int x, int y, Tank.Direction dir) {

this.x = x;

this.y = y;

this.dir = dir;

}

public void draw(Graphics g) {

Color c = g.getColor();

g.setColor(Color.BLACK);

g.fillOval(x, y, 10, 10);

g.setColor(c);

move();

}

private void move() {

switch(dir) {

case L:

x -= XSPEED;

break;

case LU:

x -= XSPEED;

y -= YSPEED;

break;

case U:

y -= YSPEED;

break;

case RU:

x += XSPEED;

y -= YSPEED;

break;

case R:

x += XSPEED;

break;

case RD:

x += XSPEED;

y += YSPEED;

break;

case D:

y += YSPEED;

break;

case LD:

x -= XSPEED;

y += YSPEED;

break;

} }

}

2.根据主战坦克的方向和位置,打出子弹

增加对Ctrl键的按键处理,根据“坦克打出一发子弹”这句话,来确定Tank中的方法fire,其返回值为Missle,根据Tank方向和位置设定子弹的方向和位置并new出来,然后返回(fire方法的实现)

注意:掌握面向对象的思维方式来确定类应该具有的方法。

图5-8功能图

3.为了解决坦克停下也能打出炮弹的问题—画出炮筒

Tank类增加新的属性ptDir,每次move后根据Tank新的方向确定炮筒的方向,将炮筒用直线的形式表现出来。

4.打出多发炮弹

使用容器装炮弹,每当抬起Ctrl键就往容器中加入新的炮弹,逐一画出每一发炮弹.

注意:泛型的使用。

图5-9功能图

5.解决炮弹不消亡的问题,解决坦克出界的问题

加入控制炮弹生死的量bLive(Missle),当炮弹已经死去就不需要对其重,当炮弹飞出边界就死亡,当炮弹死亡就从容器中去除.

图5-10功能图

6.画一辆敌人的坦克

加入区别敌我的量good,根据敌我的不同设置不同的颜色,更新Tank的构造函数,加入good,TankClient中new 出敌人的坦克并画出.

图5-11功能图

public Tank(int x, int y, boolean good) {

this.x = x;

this.y = y;

this.good = good;

}

public Tank(int x, int y, boolean good, TankClient tc) {

this(x, y, good);

this.tc = tc;

}

7.将敌人坦克击毙

Missle中加入hitTank(Tank)方法返回布尔类型,碰撞检测的辅助类Rectangle,为Tank和Missle都加入getRect方法,当击中敌人坦克时,坦克被打死,子弹也死,增加控制Tank生死的量live,如果死去就不画了。

图5-12功能图

5.3 爆炸与多辆敌人坦克

1.加入爆炸

添加爆炸类,用不同直径的圆模拟爆炸,加入live,加入位置属性,加入draw方法。爆炸应该存在于集合类中,TankClient加入集合

将集合中的爆炸逐一画出(如果死去就去除),击毙一辆坦克后应产生爆炸.

hitTank时应产生爆炸

图5-13功能图

2.添加多辆坦克

用容器来装敌人的Tank向容器中装入多辆敌人Tank,画出来,运行,不能打掉,添加hitTanks方法,打一系列Tank;TankClient里面每发子弹都打tanks

3.让敌军坦克更加智能

让敌军坦克动起来,构造函数中可以指定方向,new敌军坦克的时候指定敌军坦克的方向;让敌军坦克向随机方向移动,(Tank)静态的,添加随机数产生器 java.util.Random,move完成后,如果是敌军坦克的,随机产生一个数,来设定坦克下一个方向,Direction.values();让敌军坦克向随机方向移动随机的步骤,添加变量,记录随机步骤,当==0时,改变方向,否则,只是随机步骤递减

;让敌军坦克发射炮弹,本军炮弹不打本军,炮弹添加好坏bGood,根据好坏画不同颜色,修改炮弹的构造方法,修改Tank的fire方法,修改hitTank方法,好不能打好,坏不能打坏;敌军炮火不能太猛烈。

图5-14功能图

private static Random r = new Random();

if(!good) {

Direction[] dirs = Direction.values();

if(step == 0) {

step = r.nextInt(12) + 3;

int rn = r.nextInt(dirs.length);

dir = dirs[rn];

}

step --;

if(r.nextInt(40) > 38) this.fire();

}

public Missile fire() {

if(!live) return null;

int x = this.x + Tank.WIDTH/2 - Missile.WIDTH/2;

int y = this.y + Tank.HEIGHT/2 - Missile.HEIGHT/2;

Missile m = new Missile(x, y, good, ptDir, this.tc);

tc.missiles.add(m);

return m;

}

5.4 墙、坦克互相穿越、超级炮弹和生命值

1.添加两堵墙

建Wall类、建立Wall对象、画出来;让每一颗子弹打击每一堵墙.hitWall()方法

注意:子弹速度不能太快,否则很容易穿过墙;让坦克不能穿过墙.

要求:记录上一次的位置oldX, oldY;修改构造函数;每次move之前纪录上一次位置;添加stay方法;记录移动前的位置;当撞到时回到移动前的位置;当碰到墙的时候stay。

图5-15功能图

import java.awt.*;

public class Wall {

int x, y, w, h;

TankClient tc ;

public Wall(int x, int y, int w, int h, TankClient tc) {

this.x = x;

this.y = y;

this.w = w;

this.h = h;

this.tc = tc;

}

public void draw(Graphics g) {

g.fillRect(x, y, w, h);

}

public Rectangle getRect() {

return new Rectangle(x, y, w, h);

}

}

2.坦克不能互相穿越

当坦克撞到Tank时stay

public boolean collidesWithTanks(java.util.List<Tank> tanks) {

for(int i=0; i<tanks.size(); i++) {

Tank t = tanks.get(i);

if(this != t) {

if(this.live && t.isLive() && this.getRect().intersects(t.getRect())) {

this.stay();

t.stay();

return true;

}

}

}

return false;

} 

3.超级炮弹

处理按键A。

图5-16功能图

private void superFire() {

Direction[] dirs = Direction.values();

for(int i=0; i<8; i++) {

fire(dirs[i]);

}

}

 public Missile fire(Direction dir) {

if(!live) return null;

int x = this.x + Tank.WIDTH/2 - Missile.WIDTH/2;

int y = this.y + Tank.HEIGHT/2 - Missile.HEIGHT/2;

Missile m = new Missile(x, y, good, dir, this.tc);

tc.missiles.add(m);

return m;

} 

4.主战坦克的生命值.

加入life变量;在窗口显示生命值

5.图形化表示主战坦克的生命值

根据不同的life值进行的不同的显示。

图5-17功能图

public boolean hitTank(Tank t) {

if(this.live && this.getRect().intersects(t.getRect()) && t.isLive() && this.good != t.isGood()) {

if(t.isGood()) {

t.setLife(t.getLife()-20);

if(t.getLife() <= 0) t.setLive(false);

} else {

t.setLive(false);

}

this.live = false;

Explode e = new Explode(x, y, tc);

tc.explodes.add(e);

return true;

}

return false;

}

5.5 血块与加入图片

1.添加“血块”

添加blood类;添加必要的方法;让blood对象固定轨迹运动, 并在一定时间后消失。

图5-18功能图

private class BloodBar {

public void draw(Graphics g) {

Color c = g.getColor();

g.setColor(Color.RED);

g.drawRect(x, y-10, WIDTH, 10);

int w = WIDTH * life/100 ;

g.fillRect(x, y-10, w, 10);

g.setColor(c);

}

}

2.加入图片

在classpath中添加资源,反射的初步概念;对于classloader, 每一个.class实际就是一个Class对象;Class是对类信息的表述, 是类的metainfo / metadata。

图5-19功能图

3.最后的修正

敌人死光了重新加入;我军死掉了F2开始。

 if(tanks.size() <= 0) {

for(int i=0; i<Integer.parseInt(PropertyMgr.getProperty("reProduceTankCount")); i++) {

tanks.add(new Tank(50 + 40*(i+1), 50, false, Direction.D, this));

}

} 

case KeyEvent.VK_F2 :

if(!this.live) {

this.live = true;

this.life = 100;

}

break;

6 坦克大战打炮弹功能系统测试

6.1 抬CTRL打炮弹的测试要点

测试要点:抬CTRL打炮弹

测试路径:抬CTRL是否打炮弹

测试结果:打炮弹,游戏能实现打炮弹功能;不打炮弹,游戏不能实现打炮弹功能

6.2 坦克大战系统采用的测试方法

白盒测试

6.3 功能测试用例

6.3.1 坦克打炮弹

表6-1坦克打炮弹测试用例

编号

测试项

操作步骤

预期结果

输入数据

实际结果

结果比较说明

001

坦克打炮弹

TankClient类,KeyReleased()方法上设置断点后,用Debug运行程序,按Ctrl键

按Ctrl键正常打一发炮弹;按Ctrl键不大炮弹。

Ctrl

不符合按Ctrl键打炮弹

不符合

002

坦克打炮弹

TankClient类,KeyReleased()方法上设置断点后,用Debug运行程序,按Ctrl键

按Ctrl键正常打一发炮弹;按Ctrl键不大炮弹。

Alt

不符合按Ctrl键打炮弹

符合

6.3.2 坦克打炮弹功能测试图

图6-1功能图

图6-2功能图

几乎每一个计算机程序都会有这样那样的不足,尤其是未经过精心维护的非商业软件。即使是作为操作系统的各种版本的Windows也时常会发生许多类型的错误和漏洞.

这次毕设,我学到了很多很多,让自己的技术更进步了,java技术在做游戏时,非常的轻松,它的功能很强大。

首先感谢我的指导老师,他在我的毕业设计过程中提出了指导性的方案和架构,并指引我阅读相关的资料和书籍,使我在不熟悉的领域中仍能迅速掌握新兴的技术。

感谢答辩组对本毕业设计的考核,如果可以得到专家的认可将对我的学习和工作给予极大的鼓励。你们客观的评价和建议我将牢记在心,在今后的发展中扬长避短,更加努力的严格要求自己。

感谢父亲在设计中对我的督促、鼓励和母亲为我创造的安静环境,家人的支持永远是个人发展的基石。

本科学习中的各科老师对提高我的编程素质有很大的帮助,我的同学在设计完成后对程序的测试,没有他们,也许就难以发现一些潜在的错误,在此一并表示感谢。

参考文献

[1] 王森(台湾).手机程序设计入门.中国铁道出版社.2003.02

[2] Rogers Cadenhead. 淡文刚 于红梅 译.循序渐进Java2教程(第三版).人民邮电出版社.2005.07

[3] Mokiden.MIDP图形编程简介.Nokia电子文档.2001.09

[4] 飞思科技产品研发中心.精通Jbuilder9.电子工业出版社2008.01

[5] 微型爪哇人.Java手机程序开发.中国铁道出版社.2007.09

[6] Kim Topley.J2ME in a Nutshell.O'Reilly publish. 2006.03

[7] MIDP 2.0 profile .Wireless Tool Kit.Sun Micro.2001.09

[8] Borland corporation .Borland Jbuilder9 Help documents.2003.04

[9] Jonathan Knudsen .Creating 2D Action Games with the Game API. Sun公司网页中的Technical Articles and Tips. 及其源代码 2006.08

[10] Eric Giguere .Record Management System Basics. Sun Micro.2007.07

附  录

1.TankClient主类源代码:

package com.dljdxxxy.tank;

import java.awt.Color;

import java.awt.Frame;

import java.awt.Graphics;

import java.awt.Image;

import java.awt.event.KeyAdapter;

import java.awt.event.KeyEvent;

import java.awt.event.WindowAdapter;

import java.awt.event.WindowEvent;

import java.io.IOException;

import java.util.ArrayList;

import java.util.List;

import java.util.Properties;

/**

 * 这个类的作用是坦克游戏的主窗口

 * @author 尹长伟

 *

 */

public class TankClient extends Frame {

/**

 * 整个坦克游戏的宽度

 */

public static final int GAME_WIDTH = 800;

public static final int GAME_HEIGHT = 600;

Tank myTank = new Tank(50, 50, true, Direction.STOP, this);

Wall w1 = new Wall(100, 200, 20, 150, this), w2 = new Wall(300, 100, 300, 20, this);

List<Explode> explodes = new ArrayList<Explode>();

List<Missile> missiles = new ArrayList<Missile>();

List<Tank> tanks = new ArrayList<Tank>();

Image offScreenImage = null;

Blood b = new Blood();

public void paint(Graphics g) {

/*

 * 指明子弹-爆炸-坦克的数量

 * 以及坦克的生命值

 */

g.drawString("missiles count:" + missiles.size(), 10, 50);

g.drawString("explodes count:" + explodes.size(), 10, 70);

g.drawString("tanks    count:" + tanks.size(), 10, 90);

g.drawString("tanks     life:" + myTank.getLife(), 10, 110);

/**

 * 指明重新产生的坦克数量

 ×

 */

if(tanks.size() <= 0) {

for(int i=0; i<Integer.parseInt(PropertyMgr.getProperty("reProduceTankCount")); i++) {

tanks.add(new Tank(50 + 40*(i+1), 50, false, Direction.D, this));

}

}

for(int i=0; i<missiles.size(); i++) {

Missile m = missiles.get(i);

m.hitTanks(tanks);

m.hitTank(myTank);

m.hitWall(w1);

m.hitWall(w2);

m.draw(g);

//if(!m.isLive()) missiles.remove(m);

//else m.draw(g);

}

for(int i=0; i<explodes.size(); i++) {

Explode e = explodes.get(i);

e.draw(g);

}

for(int i=0; i<tanks.size(); i++) {

Tank t = tanks.get(i);

t.collidesWithWall(w1);

t.collidesWithWall(w2);

t.collidesWithTanks(tanks);

t.draw(g);

}

myTank.draw(g);

myTank.eat(b);

w1.draw(g);

w2.draw(g);

b.draw(g);

}

public void update(Graphics g) {

if(offScreenImage == null) {

offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);

}

Graphics gOffScreen = offScreenImage.getGraphics();

Color c = gOffScreen.getColor();

gOffScreen.setColor(Color.GREEN);

gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);

gOffScreen.setColor(c);

paint(gOffScreen);

g.drawImage(offScreenImage, 0, 0, null);

}

/**

 * 本方法显示坦克主窗口

 *

 */

public void lauchFrame() {

int initTankCount = Integer.parseInt(PropertyMgr.getProperty("initTankCount"));

for(int i=0; i<initTankCount; i++) {

tanks.add(new Tank(50 + 40*(i+1), 50, false, Direction.D, this));

}

//this.setLocation(400, 300);

this.setSize(GAME_WIDTH, GAME_HEIGHT);

this.setTitle("TankWar");

this.addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) {

System.exit(0);

}

});

this.setResizable(false);

this.setBackground(Color.GREEN);

this.addKeyListener(new KeyMonitor());

setVisible(true);

new Thread(new PaintThread()).start();

}

public static void main(String[] args) {

TankClient tc = new TankClient();

tc.lauchFrame();

}

private class PaintThread implements Runnable {

public void run() {

while(true) {

repaint();

try {

Thread.sleep(50);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

private class KeyMonitor extends KeyAdapter {

public void keyReleased(KeyEvent e) {

myTank.keyReleased(e);

}

public void keyPressed(KeyEvent e) {

myTank.keyPressed(e);

}

}

}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Funcode编程C++实现坦克大战游戏课程设计 1.内容 在一个战场上,玩家控制坦克,消灭敌方坦克,并防止敌方坦克摧毁我方基地。 2.任务和要求 1).游戏有一个初始游戏页面。 2).按下任意键开始游戏,玩家控制坦克在战场上穿梭,碰到墙时,不能通过。 3).敌方坦克自由移动,每隔2秒改变一个方向,每隔3秒发射一发子弹。 4).敌方坦克共有20辆,每隔5秒,从屏幕上方的左、中、右三个位置依次出现。 5).当玩家被消灭或者我方基地被摧毁时,游戏结束。 二、设计思路 1.数据结构 CSprite* m_pSplash;//开始的静态图 CSprite* m_pStart;//开始的静态图画 CTankPlayer* m_pTankplayer;//指向我方坦克的指针 CBullet* m_pBullet;//指向子弹的指针 CWeapon* m_pAim_nor;//家 流程图: case 2 case1 case3 (此处略去部分文件内容) 2.算法 定义了四个类,他们分别是子弹类(用来描述子弹的移动,碰撞等),敌方坦克类(用来描述敌方坦克的移动,碰撞等),我方坦克类(用来描述我方坦克的移动,碰撞等),武器类(作为子弹类,敌方坦克类,我方坦克类的基类。) CGameMain::CGameMain()构造函数: 完成初始化工作: 1、为指针数据分配内存空间,普通数据设置初值。 2、初始化精灵类start,对应“空格开始”精灵。 CGameMain::GameMainLoop(float fDeltaTime ) 游戏的主循环,根据游戏的状态切换逻辑 1、当游戏状态为0时,主循环一直循环,但不做处理,而是等待空格键按下,开始游戏。 2、当游戏状态为1时,调用GameInit()方法初始化游戏,然后置游戏状态为2。 3、当游戏状态为2时: 1.调游戏进行中,处理各种游戏逻辑 2.修改游戏结束状态为结束 (查看超全完整文件请下载观看,良心文档谢谢您的支持!)

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

等天晴i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值