基于Java的坦克大战游戏的详细设计与实现-建议学生收藏

566 阅读18分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

1.引言

Java是一种面向对象的编程语言,它不仅包含了C语言的所有优点,而且具有面向对象、跨平台、安全性等特点。 现在在程序设计中,它是一种更常用的程序设计语言。 Java具有“一次编译,到处运行”的特点,很好地体现了它的跨平台和面向对象的特点,使程序员可以用感性思维进行复杂的编程

Graphics类是软件包java.awt(其全部类都用来用户界面的创建和图形图像的绘制)下的类,它同意一个应用程序绘制到组件,以及在屏幕图像上进行绘制。Graphics 对象封装了 Java 支持的基本呈现操作所需的状态信息。Graphics()方法构造了一个新的 Graphics 对象, 由于 Graphics类是抽象类,因此Graphics()方法不可以被直接调用,此构造方法是图形上下文的默认构造方法,通过在组件上调用 getGraphics() 来创建图形上下文,或者从其他图形上下文获取。

2.系统分析

2.1 需求和技术分析

今天的游戏已经成为世界上最大的娱乐休闲项目之一,游戏市场规模不断增长,潜力巨大,中国政府一直特别鼓励游戏产业的发展,尤其是我国本土游戏产业,多年来的扶持力度不断加大,因此,游戏产业在中国的重要性日益凸显。 坦克大战游戏是经典的90坦克大战红白机的延续。 对于80后和90后来说,他们是他们童年最珍贵的记忆,而80后和90后只是占据了今天游戏人群的大多数。 对于他们来说,坦克战争游戏不仅可以缓解人们的社交压力,放松自己,你也可以回忆起你小时候玩红白电脑游戏的疯狂时光,但你不会沉迷于游戏。 适合所有年龄段,能更好地体验游戏的乐趣。

(1)要对所有坦克打出的子弹进行实时监测并判断它打到了什么物体对象,因此需要开辟一个独立的线程来处理子弹,还需要控制好所有的物体对象。在同一时候,在JVM虚拟机上保持运行这么多的线程,可能会造成程序的迟钝,甚至瘫痪。

(2)由于游戏界面中物体对象繁多,为了避免重叠运行,玩家坦克在前进时需要时刻地扫描周围环境。

(3)游戏的动态画面是一个优秀程序不可缺少的组成成分,精美的用户界面是引起玩家兴趣的关键,相关的构图美化技术也需要有所考虑。

(4)在游戏地图中会有许多各种不同的物体对象,这不是绘图方法能够解决的,而且过多的大型photo会束缚程序的大小,所以要准确掌握Graphcis()方法的使用。同时使用读取外部文件的方法来加载游戏关卡,因为内存有限,不适合用于存储地图关卡。

(5)游戏要对玩家的分数进行记录,这就需要对其功能和属性进行妥善的策划,还需要制作良好的解决方案来处理其存储方式。

2.2 功能分析

坦克战斗选项使用了之前的游戏规则。 服务器端创建并设置主机,客户端申请连接加入,如果其IP输入判断正确,加载地图等级并开始游戏,在游戏界面中将实时显示敌方坦克数量以及玩家坦克寿命和得分。 敌人坦克移动和子弹,玩家通过键盘控制自己的行动坦克和子弹,一颗子弹击中同一派系的坦克,当双方的十字路口将相互抵消的子弹,油罐爆炸效应时相互碰撞,中途停顿,可以发送消息,之后玩家坦克吃超级武器会给其特殊的功能,还有一个通信功能在游戏中地图界面。 如果你赢了,上面写着“你通过了!” 如果失败了,系统就会给出“GAME OEVR!” 你想再玩一次吗? (y / n) ,如果双方玩家都选择继续游戏,游戏将重新加载并开始,否则将结束并退出游戏界面。

3.总体设计

3.1 总体功能

游戏由服务器端和客户端两部分组成。

在服务器端,ServerModel类主要用来创建主机,ServerView类主要负责服务器端图形界面的面板信息的设置,ServerControler类处理来自服务器视图框架的输入,包括创立通信与帮助信息等,enemy类主要负责敌方坦克的创建,player类主要用来设置玩家的得分及其显示位置等信息,drawingPanel类主要负责服务器端界面窗口的创建和设置,powerUp类主要用来设置子弹属性,例如加快速度、提升火力等,feedbackHandler类主要用来解码从客户端发来的指令字符串,再将其转换成指令来判断游戏失败后玩家是否继续游戏的问题。

在客户端,ClientModel类主要用来设置与服务器的连接,ClientView类主要负责客户端端图形界面的面板信息,ClientControler类主要负责处理来自客户端视图框架的输入和创立通信与帮助信息等,drawingPanel主要用来设置客户端窗口界面,instructionHandler类主要用来解码从服务器端发来的指令字符串,再将其转换成指令来判断游戏失败后玩家是否继续游戏的问题,shield类主要负责设置坦克吃掉头盔图标获得保护时的状态,normalObject类主要用来创建和描绘其他物体对象。

在服务器端和客户端中都存在的类中,Actor类主要用来创建接口,base类主要用来创建基地并设置属性,bullet类主要用来创建子弹并设置属性,Ticker类主要用来创建时间信息,bomb类主要用来创建子弹打出后产生的爆炸效果,river类主要用来创建河道并设置属性,grass类主要负责创建草坪并设置属性,Steelwall类主要用来创建铁墙并设置属性,wall类主要用来创建和设置普通墙及其属性,level类负责创建关卡。如表1,表2所示。

其总体功能如图1所示。

3.2 坦克大战总体流程图

图2 总体流程图

4.详细设计

4.1 面板功能设计

4.1.1 基地的设计

base类定义了几个变量,通过构造器初始化了基地的变量参数,使用g.drawImage()方法设置一幅图片来代表基地,通过public void move(){}来处理基地受到铁墙防护时的变化,使用if()判断语句判断铁墙的保护时间。游戏的核心重点自然就是基地了,一旦基地被毁,则玩家失败,游戏结束。游戏面板是游戏程序的关键,这里使用了方法paintComponent(Graphics g)来进行建立并设置其属性,具体实现代码如下:

4.1.2敌方坦克的设计

enemy类设置了敌方坦克的共同属性,并根据不同外形的坦克设置了不同的属性,如椭圆形的坦克的移动速度会比较快,拥有多层颜色坦克的生命力与其拥有的颜色数量一样,利用随机函数Math.random()*4随机生成一个随机值,并转换成整形数值,将此值赋给变量direction来作为敌方坦克生成时的方向,再将Math.random()*200生成的随机值转换成整形数值,赋予变量interval来作为生成时的时间间隔,并且设置了地方坦克的移动周期,在一个周期内将会朝着相同的方向继续移动,并设置了其发射子弹的遂进行。而在特殊属性中,分别设置了敌方坦克的的火力、速度、图标等属性,再用方法draw(Graphics g){}向地图中加入敌方坦克。具体实现代码如下:

interval = (int)(Math.random()*200);

direction = (int)(Math.random()*4);

if(type ==args1 ){

firePosibility = args2;

speed = args3;

textures = new Image[args4];}

public void draw(Graphics g){

if(flashing && gameModel.gameFlow%10 > 4)

g.drawImage(textures[textures.length-4+direction], xPos - size, yPos - size, null);

else

g.drawImage(textures[direction], xPos - size, yPos - size, null);}

public void draw(Graphics g){

g.drawImage(base, xPos - 12, yPos - 12, null );}

public void paintComponent(Graphics g) {

Graphics offScreenGraphics;

if (offScreenImage == null) {

offScreenImage = createImage(640, 550);

}

offScreenGraphics = offScreenImage.getGraphics();

myPaint(offScreenGraphics);

4.1.3河道、草坪的设计

grass类继承了Actor接口,通过grass类构造器定义了草坪的x,y坐标和矩形边界以及图标,通过public void draw(Graphics g) {}方法绘制了草坪。游戏双方坦克及其子弹可以自由通过草坪。具体实现代码如下:


```java
public grass(int a, int b){

xPos = a;

yPos = b;

border = new Rectangle(0,0,0,0);

}

public void draw(Graphics g) {

g.setColor(new Color(0, 225, 0));

for(int i = yPos - 11; i <= yPos + 12; i+=5)

g.drawLine(xPos - 12, i, xPos + 12, i);

for(int i = xPos - 11; i <= xPos + 12; i+=5)

g.drawLine(i, yPos - 12, i, yPos + 12);

g.setColor(new Color(0, 128, 0));

for(int i = yPos - 10; i <= yPos + 12; i+=5)

g.drawLine(xPos - 12, i, xPos + 12, i);

for(int i = xPos - 10; i <= xPos + 12; i+=5)

g.drawLine( i, yPos - 12, i, yPos + 12);

}

river类继承了Actor接口,用river类构造器定义了河道的x,y坐标和矩形边界以及图标,使用g.drawImage(Image,int,int, ImageObserver)方法设置一幅图片来代表河道。以前各个经典版本的坦克大战游戏都将河道设置为不允许坦克经过,但子弹可以自由通过,本程序为了遵循经典,也如此设计。具体实现代码如下:

public river(int a, int b, ServerModel gameModel){

this.gameModel = gameModel;

river = gameModel.textures[71];

xPos = a;

yPos = b;

Border = new Rectangle(xPos - 12, yPos - 12, 25, 25);}

g.drawImage(river, xPos - 12, yPos - 12, null)}

4.1.4墙与铁墙的设计

该模块通过new Rectangle(int x,int y,int width,int height)完成了一个矩形的创建,然后将其具体值赋给变量数组border[数组索引]生成了墙。坐标x和y为绘制矩形时的起始点,width,height分别为矩形绘制的宽和高。通过构造器定义普通墙和铁墙的矩形边界和图标。具体实现代码如下:

generalBorder = new Rectangle(xPos - 12, yPos - 12, 25, 25);

border[0] = new Rectangle(xPos - 11, yPos - 11, 11, 11);

border[1] = new Rectangle(xPos + 1, yPos - 11, 11, 11);

border[2] = new Rectangle(xPos - 11, yPos + 1, 11, 11);

border[3] = new Rectangle(xPos + 1, yPos + 1, 11, 11);

public wall(int a, int b, int orientation, ServerModel gameModel){

xPos = a;

yPos = b;

this.gameModel = gameModel;

wall = gameModel.textures[70];

generalBorder = new Rectangle(xPos - 12, yPos - 12, 25, 25);}

4.1.5界面窗口的创建

再该模块中,在ServerView类的构造器中用super()方法调用父类构造器设置窗口题目为“坦克大战”,使用JButton(String)定义了六个(退出、帮助、发送、暂停/继续、建立主机和隐藏)鼠标点击事件(即所谓的按钮),JTextField定义文本框,并将其设置为不可编辑。使用new drawingPanel()和setBackground(Color c)创建一个面板并设置背景颜色,再通过setBounds()和setLayout()方法定义了页面的大小并将页面布局定义为空。通过add()方法向页面添加按钮和文本框。使用setColor(Color c)方法设置面板颜色为指定颜色,再通过继承自java.awt.Graphics类中的drawString(String,int,int)方法向面板添加并输出当前关数及剩余敌人数,通过if判断语句进行判断,如果消灭坦克数量大于敌方坦克数量则输出胜利场景。

4.2子弹功能设计

该模块用setColor()方法设置子弹颜色为浅灰色,使用g.fillRect(int,int,int,int)方法设置子弹水平或垂直发射时的形状,子弹和什么东西相交通过if()判断语句和equals()方法来鉴定,然后再处理相交时子弹和其他对象的属性变化。具体代码如下:

public void draw(Graphics g) {

g.setColor(Color.lightGray);

if(direction == 0 || direction == 1)

g.fillRect(border.x + 1, border.y +1, 3, 9);

if(direction == 2 || direction == 3)

g.fillRect(border.x +1, border.y + 1, 9, 3);

}

if(!border.intersects(map)){

gameModel.removeActor(this);

notifiyOwner();

makeBomb();

writeToOutputLine();

return;

if(gameModel.actors[i].getType().equals("steelWall")){

Steelwall temp =(Steelwall)gameModel.actors[i];

if(!temp.walldestoried){

temp.damageWall(border,bulletpower,direction);

if(temp.bulletdestoried)

hitTarget = true;

}

}else if(gameModel.actors[i].getType().equals("wall")){

wall temp = (wall)gameModel.actors[i];

if(!temp.walldestoried){

temp.damageWall(border,bulletpower,direction);

if(temp.bulletdestoried)

hitTarget = true;

}

}else if(gameModel.actors[i].getType().equals("bullet")){

bullet temp = (bullet)gameModel.actors[i];

if(temp.owner.getType().equals("Player")){

hitTarget = true;

gameModel.removeActor(gameModel.actors[i]);

temp.notifiyOwner();

}

}else if(gameModel.actors[i].getType().equals("Player")){

if(owner.getType().equals("enemy")){

player temp = (player)gameModel.actors[i];

temp.hurt();

}else{

}

hitTarget = true;

}else if

(gameModel.actors[i].getType().equals("enemy") && owner.getType().equals("Player")){

enemy temp = (enemy)gameModel.actors[i];

player tempe = (player)owner;

if(temp.health == 0)

tempe.scores+=temp.type*100;

temp.hurt();

hitTarget = true;

}else if(gameModel.actors[i].getType().equals("base")){

base temp = (base)gameModel.actors[i];

temp.doom();

hitTarget = true;

gameModel.gameOver = true;

}

}

}

}

}

if(hitTarget){

gameModel.removeActor(this);

notifiyOwner();

makeBomb();

writeToOutputLine();

return;

}

4.3坦克功能设计

坦克吃掉了什么特殊武器图标通过if()判断语句和equals()方法来鉴定,如果吃到TNT图标,则已经出现了的敌方坦克全部被炸毁;如果吃到坦克图标,玩家坦克加一条生命;如果吃到五角星图标,则提升玩家坦克子弹火力并且速度加快,可以打破铁墙;若是头盔图标,则玩家坦克获得防护盾且免疫敌方子弹;如果吃到铁墙图标,则基地由普通墙变为铁墙,如果吃到钟表图标,则时间停止,所有敌方坦克静止。具体代码如下:

if(gameModel.actors[i].getType().equals("powerUp")){

scores+=50powerUp temp = (powerUp)gameModel.actors[i];

int function = temp.function;

if(function == 0){ upgrade();

}else if(function == 1){ base tempe = (base)gameModel.actors[4];tempe.steelWallTime = 600;

}else if(function == 2){

for(int j = 0; j < gameModel.actors.length; j++)

if(gameModel.actors[j] != null)

if(gameModel.actors[j].getType().equals("enemy")){

enemy tempe = (enemy)gameModel.actors[j];

gameModel.addActor(new bomb(tempe.xPos, tempe.yPos, "big", gameModel));

gameModel.removeActor(gameModel.actors[j]);}

level.NoOfEnemy = 0;level.deathCount = 20 - level.enemyLeft;}else if(function == 3){

InvulnerableTime = 300 + (int)Math.random()*400;

}else if(function == 4){

enemy.freezedTime = 300 + (int)Math.random()*400;

enemy.freezedMoment = ServerModel.gameFlow;

}else if(function == 5){

if(status < 3)numberOfBullet++;

status =4;health = 2;if(type.equals("1P"))

for(int j = 0; j < 4; j ++)textures[j] = gameModel.textures[66+j];

else

for(int j = 0; j < 4; j ++)

textures[j] = gameModel.textures[84+j];

}else if(function == 6){ life++;}

4.4服务器设计

4.4.1 ServerModel类

在ServerModel类,实现了ActionListener接口,具备了监听功能。创建了一些连接变量和游戏变量,设置了布尔类型的服务器状态变量,使用构造器完成了消息队列信息的设置,使用createServer(){}方法建立主机,当布尔变量serverCreated的值为true时,主机创建成功,还设置了一个端口号,用try{}catch{}语句处理代码执行时发生的异常,并给出错误提示,服务器通过accept()方法与客户端建立连接,当端口号没有被其他应用程序占用并IP地址正确,则成功完成连接,载入游戏,如若不然,就会显示相应的错误提示。使用addMessage()在屏幕上显示消息,使用removeMessage()方法删除屏幕上的消息,通过addActor()和remove()方法完成了向地图中增添新的物体对象和清除已经不存在了的物体对象的操作。具体代码如下:

public void createServer(){

addMessage("正在建立主机(端口9999)");

try {

serverSocket = new ServerSocket(9999);

serverCreated = true;

} catch (Exception e) {

addMessage("无法建立主机,请确认端口9999没有被别的程序使用");

System.out.println(e);

t.stop();

return;

}

addMessage("建立完成,等待玩家连接");

try {

clientSocket = serverSocket.accept();

clientConnected = true;

out = new PrintWriter(clientSocket.getOutputStream(), true);

in = new BufferedReader(new InputStreamReader(

clientSocket.getInputStream()));

} catch (Exception e) {

addMessage("连接中出现错误,请重新建立主机");

serverCreated = false;

clientConnected = false;

t.stop();

try{

serverSocket.close();

clientSocket.close();

out.close();

in.close();

}catch(Exception ex){}

return;

}

view.messageField.setEnabled(true);

addMessage("玩家已连接上,开始载入游戏");

out.println("L1;");

textures = new Image[88];

for(int i = 1; i < textures.length+1; i++)

textures[i-1] = Toolkit.getDefaultToolkit().getImage("image\\" + i + ".jpg");

actors = new Actor[400];

level.loadLevel(this);

P1 = new player("1P", this);

addActor(P1);

P2 = new player("2P", this);

addActor(P2);

gameStarted = true;

view.mainPanel.actors = actors;

view.mainPanel.gameStarted = true;

addMessage("载入完毕,游戏开始了!");

}

public void removeActor(Actor actor){

for(int i = 0; i < actors.length; i ++ )

if(actors[i] == actor){

actors[i] = null;

break;

}

}

public void addMessage(String message){

if(messageIndex < 8){

messageQueue[messageIndex] = message;

messageIndex++;

}

else{

for(int i = 0; i < 7; i++)

messageQueue[i] = messageQueue[i+1];

messageQueue[7] = message;

}

public void removeMessage(){

if(messageIndex == 0)

return;

messageIndex--;

for(int i = 0; i < messageIndex; i++)

messageQueue[i] = messageQueue[i+1];

messageQueue[messageIndex] = null;

if(!gameStarted)

view.mainPanel.repaint();

}

4.5.3 其他各类的实现

在shield类中,使用了构造器,并实现了玩家坦克吃掉头盔图标后获得的防护盾的功能,通过draw(Graphics g){}方法绘制防护盾,用方法setColor(Color c)设置其颜色,drawRect()设置防护盾的x,y坐标和高度、宽度,当护盾时间结束时,通过removeActor()方法去除护盾。在level类中,定义了游戏正在玩的关数,设置了不同的关卡,通过if(1+ (levelIndex-1)%8 == num){}判断语句来加入关卡,在进入下一关卡时,上一个关卡的所有东西都会被系统清理,并且增加了游戏难度。此类只有一层对象,所以是一个静态变量。normalObject类代表所有其他对象。ClientControler类功能与ServerControler相同,都是实现按钮的功能和处理键盘输入操作。

5.2 系统测试

5.2.1 游戏启动测试

运行Eclipse中的server项目和client项目或双击server文件夹和client文件夹下的Play.BAT文件来运行游戏,游戏启动成功,看到了游戏界面。如图3所示。

图3 启动测试图

5.2.2 页面按钮测试

(1)在服务器端,点击“建立主机”按钮,成功建立主机,并给出了提示。如图4所示。

图4 建立主机按钮测试图

(2)在客户端,在页面上方的文本框内输入IP地址:127.0.0.1,然后点击“连接主机”按钮。连接成功并给出提示,进入游戏。如图5所示。

图5 连接主机按钮测试图

(3)点击帮助按钮,成功在页面上显示游戏的方法。如图6所示。

图6 帮助按钮侧视图

(4)点击页面上方的暂停/继续按钮,若游戏正在进行,点击这个按钮就会暂停游戏,然后再点击,就会取消暂停,并且在页面上给出提示。在页面下方的文本框内输入对话消息,点击发送按钮,成功发送消息,并且在页面上显示了通话内容。如图7所示。

图7 暂停/继续与发送按钮测试图

5.2.3 玩家坦克测试

敲击方向键,坦克也成功向着相同的方向移动,点击“s”键,成功发射子弹。如图8所示。

图8 坦克测试图

5.2.4 超级武器测试

玩家坦克子弹打中敌方红色坦克,在地图上随机的位置生成了随机的图标,玩家坦克吃到各种图标后成功地获得了该图标所对应的功能。

5.2.5 胜利与失败测试

在游戏中击毁了所有敌方坦克,成功地显示了“过关了!”界面。

玩家坦克数量为0或基地被敌方坦克攻破了,成功地在页面上显示了“GAME OVER!想再玩一次吗(y/n)?”的消息。若游戏双方都选择都输入“y”,则游戏重新开始,并给出了提示。

6.结论

该游戏是基于Java语言,使用Eclipse软件开发的一款坦克大战游戏, 该游戏包括对面板功能、坦克功能、子弹功能的设计,在面板功能中对双方坦克、基地、河道、草坪、普通墙与铁墙等地图元素进行创建并设置其属性,还实现了页面按钮功能,玩家可以点击按钮来实现相应的功能。在坦克功能中,设计了操作玩家坦克的方法,还设置了超级武器,玩家吃掉后会获得特殊技能。在子弹功能中,设置了子弹打中不同物体对象产生的不同效果。另外,还实现了服务器与客户端的连接,加载关卡等功能,玩家再游戏面板中可以实时查看自己坦克的生命数量和分数以及敌方坦克的数量,基本上完成了设计任务。总体来说,本游戏有一定的逻辑性和复杂性,对玩家有一定的吸引力。

在设计与实现游戏的过程中,遇到一些逻辑问题和技术故障都是在所难免的,例如如何加载地图关卡和物体对象等、监探坦克与地图元素是否碰撞等,都是需要完全克服的。该游戏还需要进一步的优化,需要在更大的程度上提升敌方坦克的智能化、在地图中添加物体对象来增强可玩性等等。