基于Java实现的潜艇大战游戏

server/2024/11/30 0:11:51/

基于Java实现的潜艇大战游戏

一.需求分析

1.1 设计任务

本次游戏课程设计小组成员团队合作的方式,通过游戏总体分析设计,场景画面的绘制,游戏事件的处理,游戏核心算法的分析实现,游戏的碰撞检测,游戏的反复测试,游戏的打包运行等一个完整的游戏设计编码实现的整个过程。进一步提高编写结构清晰,扩展性好,风格良好的应用程序,进一步提高思考解决实际问题的工程能力。

1.2 功能需求

  • 玩家进入游戏,退出游戏的功能,没有过关重玩的功能
  • 玩家开始游戏,暂停游戏,查看游戏规则
  • 玩家键盘A,D(快捷键)鼠标操作实现战舰的移动和投导弹攻击潜艇
  • 潜艇左面出,向右移动,每一个时间间隔向上投鱼雷攻击战舰
  • 战舰和潜艇被攻击后有相应的爆炸特效和对应的音效
  • 玩家的得分统计,进入前十就提示输入姓名,当前最高分的查看
  • 玩家通过的关口统计,下一关口难度增加的设置

1.3 用例模型

  • Actor主要为玩家和游戏系统
  • 玩家 user case 包括 开始游戏,操作战舰,退出游戏,暂停游戏,得分关卡,进入排行榜
  • 游戏系统 user case 潜艇出击,爆炸效果,积分榜重置,结束游戏,场景画面

1.4 性能需求

技术可行性

Windows8.1下的开发平台,Windows 8.1的运行平台,使用Java Swing+AWT图形界面制作客户端程序的技术,界面友好,功能齐全,音效动感,操作简单,的一款潜艇大战游戏.

经济可行性

游戏的关卡设计,使游戏的趣味性高,音效设计,都是基于素材网上的免费资源,单机游戏,开发平台在本地,一台装有JAVA的PC即可提供硬件环境,成本极低。

操作可性性

键盘操作,加快捷键和鼠标结合,提供完整游戏规则,查看即可学习,立刻就可以玩起来,操作非常简单。

1.5 系统结构图

1.6 系统流程图

二.游戏功能内容设计

2.1 工程结构图

2.2 游戏界面绘制

2.2.1 进入游戏界面绘制

首先使用Swing组件的JFrame完成窗口的绘制,包括大小,位置,背景图片,按钮的排放,布局采用BorderLayout的边框布局方式.窗口图标采用Toolkit.getDefaultToolkit().getImage(imgUrl)

2.2.2 游戏主界面的绘制

上面的进入游戏的界面点击进入游戏后切换至主界面里面后,完成主界面的颜色背景绘制,主要使用Graphics2D设置相应的颜色代码完成实现。上面的菜单栏采用JMenuBar,JMenu,JMenuItem来完成绘制。至于得分,关数是在指定的位置绘制的。那个游戏的最高分是从游戏玩家排行榜里面读取出来然后绘制的,命数是自定义的3条。在程序里面设置好的。然后就是战舰和导弹的绘制了的.战舰封装一个战舰对象然后结合Image对象加载图片设置位置来实现的.导弹是初始设置五个,通过线程的方式指定的时间内绘制即可。最多为5个,最小为0个.绘制的时候要注意判断当前的导弹数量来绘制的。

2.2.3 游戏对话框的绘制

实现的方法就是继承JDialog对话框+Graphics2D设置宽高和位置就可以设置的,例如帮助对话框的实现。

java">private static final long serialVersionUID = 1L;
protected JFrame    frame1;
private MyPanel panel;
private boolean   flag = false;
private boolean   isDraw = false;
private boolean   isOutDraw = false;
/** 设置对话框为圆角矩形*/
public void setVisiableRigeon(int width,int height)
{Shape shape = new RoundRectangle2D.Float(20,20,this.getWidth() - 40,this.getHeight()-40,15.0f,15.0f);Shape shape2 = new Ellipse2D.Double(this.getWidth() - 40, 10,30,30);Area area = new Area(shape);Area area2 = new Area(shape2);area.add(area2);AWTUtilities.setWindowShape(this,area);
}

2.3 游戏音效制作

JAVA的AWT下面的AudioClip类播放音乐.wav格式的音乐,音乐文件使用的地方有两个:一个是战舰发射导弹命中潜艇,潜艇发射鱼雷命中战舰的,先展示爆炸图片然后在播放背景音效。

2.4 游戏爆炸场景的设计

类如下

关键方法

java">private MyPanel panel;
private Image   image;
/** 爆炸效果显示的时间默认为700毫秒*/
private int    liveTime = 700;
/** 位置 x y*/
private int    beginX  = 0;
private int    beginY= 0;
/** 游戏是否正在运行标志*/
private boolean isRunning = false;/*绘制爆炸效果图片*/
public void drawHitting(Graphics2D g)
{g.drawImage(this.image, this.beginX, this.beginY, this.panel);MusicUtil musicUtil = new MusicUtil();musicUtil.playMissile();
}/**圆的半径集合**/
private int[] r = {2,3,5,8,12,15,18,20,25,30,33,25,17,15,13,9,5};
/**集合中圆的位置起始标记**/
private int step = 0;

鱼雷命中潜艇是通过一个动态设置圆半径来实现先从小到大在到小的过程结合音效,效果还是不错的。

2.5 碰撞检测分析实现

碰撞的对象有两对的,分别是潜艇和导弹,鱼雷和战舰。通过位置坐标来判断(核心代码如下)当前战舰的位置坐标和窗体的高度来做一个判断后就取其一半,添加到爆炸绘制数组里面的。绘制的时候都设置和相应的位置坐标的。

java">/** 判断是否击中战舰 **/
public void hitting()
{if(this.X > (this.ship.getBeginX() - this.weight) && this.X < (this.ship.getBeginX() + this.ship.getWidth()-this.getWeight())){int num = this.panel.getLiveNum();blast  = new Blast(this.ship.getBeginX() + this.ship.getWidth() /2, this.ship.getBeginY() + this.ship.getHeight() / 2);Thread t = new Thread(blast);this.panel.getBlastArray().add(blast);t.start();num --;this.panel.setLiveNum(num);this.panel.loseGmae();}
}/** 根据集合中对象的位置参数判断潜艇和战舰是否被击中*/
public void explode()
{// this.updateScreen();if(!this.bumbArray.isEmpty() && !this.submarineArray.isEmpty()){for(int i = 0; i < this.bumbArray.size(); i ++){if(((Bumb)this.bumbArray.get(i)).flag == false){for(int j = 0; j < this.submarineArray.size(); j ++){if(((Submarine)this.submarineArray.get(j)).flag == false){int by =((Bumb)this.bumbArray.get(i)).getBeginY();int syStart = ((Submarine)this.submarineArray.get(j)).getY() - ((Bumb)this.bumbArray.get(i)).getHeight();int syEnd   = ((Submarine)this.submarineArray.get(j)).getY() + ((Submarine)this.submarineArray.get(j)).getHeight();int bx = ((Bumb)this.bumbArray.get(i)).getBeginX();int sxStart =   ((Submarine)this.submarineArray.get(j)).getX() - ((Bumb)this.bumbArray.get(i)).getWidth();int sxEnd   =   ((Submarine)this.submarineArray.get(j)).getX() + ((Submarine)this.submarineArray.get(j)).getWeight();if( by >= syStart && by <= syEnd && bx >= sxStart && bx <= sxEnd){this.bumbArray.get(i).flag = true;this.submarineArray.get(j).flag = true;this.hitX   = this.bumbArray.get(i).getBeginX() + 10;this.hitY   = this.bumbArray.get(i).getBeginY() + 30;//this.hitFlag  = true;Hit hit = new Hit(this.hitX,this.hitY,this);this.hitArray.add(hit);Thread  t = new Thread(hit);t.start();this.score += 10;this.addPass(this.score);}}}}}}
}

2.6 游戏对象绘制

2.6.1 潜艇对象绘制

通过随机产生随机数,0,1,2分别对应不同的潜艇图片,方向是自左向右的(使用0和1代表)。移动的步长是一个1个单位(初始),还有一个是否运行的标记的,后面线程绘制暂停要使用的。使用Timer计时器来每隔一段时间产生潜艇来不断绘制。

java">// 计时器方法
public TimeManager(WarShip ship, MyPanel panel)
{this.ship = ship;this.panel    = panel;
}
@Override
public void run()
{Random r = new Random();while(this.panel.isRunning()){if(this.panel.isStop() == true){synchronized(MyPanel.subLock){try{MyPanel.subLock.wait();}catch(Exception e){e.printStackTrace();//this.flag = true;this.panel.endGame();}}}Submarine sm = new Submarine(this.ship,this.panel);this.panel.getSubmarineArray().add(sm);Thread t = new Thread(sm);t.start();try{Thread.sleep(this.speed + r.nextInt(this.speed * 3));}catch(Exception e){e.printStackTrace();}}
}// 计时器每隔一段时间产生鱼雷对象
TimeManager2 tm2 = new TimeManager2(this,this.panel,this.ship,this.panel.getTorpedoArray());
Thread  t = new Thread(tm2);
t.start();// 绘制方法
public void drawSubmarine(Graphics2D g)
{g.drawImage(image,this.X, this.Y, panel);
}

向左向右移动就是增加减少当前的坐标X值.然后重新绘制面板即可判断一下X值的范围。

java">public void moveLeft()
{this.X -= dx;this.panel.repaint();if(this.X < 0){this.flag = true;}
}

2.6.2 战舰对象绘制

首先是封装一个战舰对象类。

java">/** 战舰初始位置和高度,宽度*/
private int beginX ;
private int beginY ;
private int width ;
private int height;
/** 初始炸弹数量*/
private int bombNum  = 5;
private MyPanel panel;
/** 图片对象*/
private Image image;

就一句话,使用给出初始时刻的位置坐标X,Y,然后就是加载战舰的图片类,X值为相应窗体宽度的一半,高度就是指定一定的高度。同样也有相应的左移和右移的方法实现,指定每次一定的步长为5个单位。

java">/** 绘制战舰*/
public void drawShip(Graphics2D g)
{   g.drawImage(image,this.beginX ,this.beginY,this.panel);
}
/** 战舰右移动,步长为五*/
public void moveRight()
{this.beginX +=5;if((this.beginX + this.width) > this.panel.getWidth()){System.out.println("panel:"+this.panel.getWidth());this.beginX = this.panel.getWidth() - this.width;}
}
/** 战舰左移动,步长为五*/
public void moveLeft()
{this.beginX -= 5;if(this.beginX < 0){this.beginX = 0;}
}

2.6.3 鱼雷对象绘制

原来和前面相似就不做介绍了的,关键方法如下,移动的距离,鱼雷向上移动。

java">/** 潜艇对象*/
private WarShip ship;
/** 鱼雷的坐标和宽高*/
private int  beginX;
private int  beginY;
private int  width = 5;
private int  height = 15;
public  boolean flag = false;
private MyPanel panel;
private Image image;public Bumb(MyPanel p, WarShip ship)
{this.beginX= ship.getBeginX()+20;this.beginY = ship.getBeginY()+20;this.panel = p;this.ship  = ship;URL imgUrl=this.getClass().getResource("/images/炸弹.png");image = Toolkit.getDefaultToolkit().getImage(imgUrl);image = new ImageIcon(image).getImage();this.width = image.getWidth(panel);this.height = image.getHeight(panel);
}
/** 绘制鱼雷*/
public void drawBumb(Graphics2D g)
{g.drawImage(image, this.beginX, this.beginY,  this.panel);
}
/** 鱼雷向下移动*/
public void moveDown()
{this.beginY += 1;if(this.beginY > (this.panel.getHeight() - this.height2)){flag = true;}// this.panel.repaint();
}
// 鱼雷向上移动
public void upMove()
{this.Y -= this.dy;if(this.Y <= 150){this.hitting();this.flag = true;}if(this.sm.flag == true){if(this.Y < 150){this.flag = true;}}
}

2.6.4 炸弹对象绘制

java">/** 绘制炸弹*/
public void drawBombNum(Graphics2D g)
{for(int i = 0;i < this.ship.getBombNum();i ++){g.drawImage(this.bumbImage,this.getWidth()/4 + 25*i + 70, 0,this);}
}

2.7 战舰装弹实现

java">/** 装弹保持炸弹数量大于零小于五 并每隔一定时间装弹*/
public void addBomb() {int bn = this.ship.getBombNum();if(bn >= 0 && bn < 5){bn ++;this.ship.setBombNum(bn);}if(bn < 0){bn =0;this.ship.setBombNum(bn);}if(bn >= 5){bn = 5;this.ship.setBombNum(bn);}
}

2.8 面板绘制

主面板里面绘制的对象非常多连接游戏各个组件的枢纽。

java">/** 战舰*/
private WarShip ship;
/** 命数默认为3*/
private int       liveNum= 3;
/** 通关数默认为0*/
private int       pass= 0;
/** 最高分默认为0*/
private int       higncore  = 0;
/** 玩家得分 默认为0*/
private int       score = 0;
/** 战舰装弹速度,默认为3000毫秒*/
private int       delay  = 3000;
/** 暂停标记*/
private boolean   suspendFlag = false;
/** 判断游戏是否正在运行标记*/
private boolean   isRunning   = false;
/** 潜艇是否被击中标记*/
private boolean   hitFlag  = false;
/** 击中位置潜艇x坐标*/
private int       hitX  = 0;
/** 击中位置潜艇y坐标*/
private int       hitY  = 0;
/** 炸弹图片*/
private Image     bumbImage;
/** 击中潜艇效果图片*/
private Image     hitImage;
/** 暂停游戏所有线程用到的*/
public static final Object subLock = new Object();
/** 用来不停绘制玩家剩余可用炸弹的计时器*/
private Timer timer;
/** 用来不停重绘面板和判断移动元素位置的计时器*/
private Timer timer3;
/** 用来管理产生潜艇对象的线程*/
private TimeManager tm = null;
/** 炸弹对象集合*/
private ArrayList<Bumb> bumbArray = new ArrayList<Bumb>();
/** 潜水艇对象集合*/
private ArrayList<Submarine> submarineArray  = new ArrayList<Submarine>();
/** 鱼雷对象集合*/
private ArrayList<Torpedo> torpedoArray = new ArrayList<Torpedo>();
/** 潜艇爆炸对象集合*/
private ArrayList<Hit> hitArray = new ArrayList<Hit>();
/** 战舰爆炸对象集合*/
private ArrayList<Blast> blastArray = new ArrayList<Blast>();

三.游戏场景创意相关设计

3.1 游戏关卡分数设计

具有挑战创意的分数关卡等你来战,每次多通过一关后,潜艇的速度会变快,就是绘制潜艇的速度会提高的,同时计时器的间隔会减低的。

java">/** 判断通关数量*/
public void addPass(int score)
{if(score < 100){this.pass = 0;}else if(score >= 100 && score < 200){this.pass = 1;this.tm.setSpeed(800);timer.setDelay(2800);}else if(score >= 200 && score < 300){this.pass = 2;this.tm.setSpeed(600);timer.setDelay(2500);}else if(score >= 300 && score < 500){this.pass = 3;this.tm.setSpeed(500);timer.setDelay(2300);}else if(score >= 500 && score < 800){this.pass = 4;this.tm.setSpeed(400);timer.setDelay(2000);}else if(score >= 800 && score < 1000){this.pass = 5;this.tm.setSpeed(300);timer.setDelay(1800);}else if(score >= 1000 && score < 1500){this.pass = 6;this.tm.setSpeed(200);timer.setDelay(1500);}else if(score >= 1500 && score < 2000){this.pass = 7;this.tm.setSpeed(100);timer.setDelay(1200);}else if(score >= 2000){this.pass = 8;this.tm.setSpeed(50);timer.setDelay(1000);}
}

还算不错的完成了功能.

3.2 读取游戏最高分

设计通过后判断是否进入前十了的。

java">/** 得到玩家的最高分*/
public int getHigeScore(InputStream inputStream)
{BufferedReader bf = null;int    tempNum = 0;try{bf = new BufferedReader(new InputStreamReader(inputStream));String temp = null;while((temp = bf.readLine()) != null){String[] info = temp.split(" ");int    num    = Integer.valueOf(info[1]);if(num > tempNum){tempNum = num;}}bf.close();}catch(IOException e){e.printStackTrace();}finally{if(bf != null){try{bf.close();}catch(IOException e){e.printStackTrace();}}}return tempNum;
}

游戏界面绘制时要读取出当前的最高分数的,从玩家的排行榜里面读取出来的userInfo/user.txt文件读取。

java">计算机网络 630 4 2018-06-09
归来 570 3 2018-06-08
时间刺客 480 2 2018-06-07
图像处理 410 2 2018-06-05
深入浅出 390 3 2018-06-04
游戏设计 330 2 2018-06-03
数据挖掘 270 2 2018-06-03
多媒体技术 260 2 2018-06-07
数据结构 250 2 2018-06-10
集合对象 190 1 2018-06-09

下面是一些关键的代码.按照分数的降序来完成的。

java">{// 得到用户信息String name = InputDialog.this.field.getText().trim();String score = Integer.toString(InputDialog.this.panel.getScore());String pass  = Integer.toString(InputDialog.this.panel.getPass());SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String date = df.format(new Date());String args[] = date.split(" ");String time  = args[0];String user = name + " " + score + " " + pass + " " + time;//将数据文件中的用户信息取出,去掉最后一名,加入玩家信息,并按降序排序ArrayList<String> userList = new ArrayList<String>();BufferedReader br = null;BufferedWriter bw = null;

游戏结束执行isRecord方法来判断玩家是否进入前十来实现荣誉榜。

java">/** 判断玩家成绩是否进入前十名,是的话返回true*/
public boolean isRecord()
{boolean isRecord = false;InputStream inputStream=MyPanel.class.getClassLoader().getResourceAsStream("userInfo/user.txt");BufferedReader bf = null;int    num = 0;String temp = null;

四.游戏相关测试

4.1 功能测试

4.1.1 游戏测试

测试游戏结束中玩家3条命结束后游戏是否按照预期的就结束了的。正确的应该是弹出一个对话框提示重玩还是退出.错误的就是各种报错了的可以在控制台查看的。

这个测试的过程也同时测试了潜艇的正确运行,战舰的键盘事件,投弹爆炸,音效的测试.和一些功能按钮的测试的.

4.1.2 暂停游戏的测试

鼠标左键实现暂停游戏的功能正确的弹出串口的,查看控制台看是否报错的。

4.2 性能测试

4.2.1 测试战舰装弹的性能来实现

正确进入断点处执行,单步调试

4.2.2 测试战舰键盘事件是否向右移动正确

Debug运行单步调试

五.软件的运行的方式

直接运行

基于jar包的运行,我们使用项目构建工具Maven来快速打成jar包。

进入控制台

使用exe4J将jar打成了可执行文件.exe格式的

点击直接可以运行的。


http://www.ppmy.cn/server/146027.html

相关文章

【GPT】力量训练是什么,必要吗,有可以替代的方式吗

什么是力量训练&#xff1f; 力量训练是一种通过抵抗力&#xff08;如重量、阻力带、自身体重等&#xff09;来刺激肌肉收缩&#xff0c;从而提高肌肉力量、耐力和体积的运动形式。它包括以下常见形式&#xff1a; 自由重量训练&#xff1a;使用哑铃、杠铃、壶铃等。固定器械…

【HarmonyOS开发模板/组件分享 – 用户隐私政策弹窗】

介绍 元服务或应用在上架应用市场时&#xff0c;审核要求在启动界面或登录界面以显著的方式展示隐私政策&#xff0c;提示用户阅读隐私政策。 本篇Codelab介绍了如何使用自定义弹窗设计用户隐私政策弹窗&#xff0c;方便开发者快速集成到自己的工程项目中&#xff0c;满足审…

R-Meta分析

原文&#xff1a;R-Meta分析https://mp.weixin.qq.com/s/9ziVzSNRsgUbV9hTtXz5_g?token340211611&langzh_CN 一&#xff1a;AIMeta分析检索 1、AI大模型助力Meta分析的选题与文献检索 1)什么是Meta分析 2)Meta分析的选题策略 3)精确检索策略&#xff0c;如何检索全、检索…

【JAVA】IntelliJ IDEA 如何创建一个 Java 项目

链接: IntelliJ IDEA 如何创建一个 Java 项目

2024下半年——【寒假】自学黑客计划(网络安全)

CSDN大礼包&#xff1a;&#x1f449;基于入门网络安全/黑客打造的&#xff1a;&#x1f449;黑客&网络安全入门&进阶学习资源包 前言 什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&a…

ensp配置静态路由与RIP协议

一、简介 RIP是典型的基于矢量计算路由的动态路由协议 RIP会选择经过路由器少的那条&#xff0c;超过15个路由器之后将不会再进行路由传递 RIP适合小型、中型的网络结构&#xff0c;有两个版本&#xff0c;一种不会对ip地址的网段进行特殊的子网掩码设置&#xff0c;只能理解…

【C++】入门【三】

本节目标 一、类的6个默认成员函数 二、 构造函数 三、析构函数 四、拷贝构造函数 五、赋值运算符重载 六、const成员函数 七、取地址及const取地址操作符重载 一、类的6个默认成员函数 如果类里一个成员都没有&#xff0c;简称空类空类中真的什么都没有吗&#xff1f;并不不是…

Linux自动化构建-make/Makefile

目录 1. 背景2. 基本使用3. 推导过程4. 好用的操作5. 拓展语法 1. 背景 会不会写makefile&#xff0c;从⼀个侧⾯说明了⼀个⼈是否具备完成⼤型⼯程的能⼒⼀个⼯程中的源⽂件不计数&#xff0c;其按类型、功能、模块分别放在若⼲个⽬录中&#xff0c;makefile定义了⼀系列的规…