1. 成果演示

成果演示视频

2. 实现了哪些功能?

  1. 按键WDSA是己方坦克方向键,J键负责发射子弹。己方一次最多可以发射5颗子弹。子弹走到边界或者击中敌方坦克时,生命周期结束。
  2. 当我方子弹击中敌方或者敌人子弹打中我方时,会有爆炸效果
  3. 敌人坦克随机移动,随机发射子弹。坦克与坦克的位置不能重合,且不能移动到边界之外
  4. 我方击杀敌人数量动态更新在界面右上角
  5. 游戏关闭后默认存档,保存了关闭前敌方坦克的位置、方向以及我方击杀的坦克数。选择继续游戏的话,会还原场景,不过当时发射的子弹会被清屏
  6. 关闭游戏时,若我方坦克的阵亡情况不会被存档,也就是说当游戏再次运行时,我方坦克会恢复生命。
  7. 开启游戏时会播放事先准备好的音乐

3. 需要哪些基础?

线程、向上/下转型、子类父类、事件监听、I/O

4. 设计思路

设计思路配合源码食用!!

①绘制坦克

绘图基础

Java的图形化设计以JFrame类为框架【可以理解为画板】,JPanel类为面板【理解为画纸】。

X-Y坐标轴以左上角为原点,右边是X轴正向;Y轴下方是正向

  • JPanel上的画图由paint(Graphics g)方法实现,其中g可以理解为画笔Graphics是一个接口,在JPanel中实现了初始化,我们重写的时候收到的实际是一个上转型对象,所以可以直接调用方法使用。【简单来说就是JDK已经帮忙把活干好了,直接拿来用就行】

  • 以下情况会自动调用paint()方法

    1. 窗口最小化再最大化
    2. 窗口大小发生变化
    3. 调用repaint函数【刷新组件外观方法】被调用
  • 使用画笔进行画图时经常需要标明坐标位置以及图像的高宽,高宽是在坐标位置基础上计算的,坐标位置如箭头所示

坦克绘制

不难发现,这辆坦克是由一个长方形1号+长方形2号+长方形1号构成的主体
炮台部分是一个圆形,炮筒部分则是一条直线

各位根据自己的审美喜好,进行绘画拼接即可。我这里的坦克整体宽度是40像素,长度是60像素

//提供几个画图方法,其中g是画笔(paint方法中传入的参数),具体使用参考JDK文档
g.fill3DRect  //画具有3D效果的矩形
g.fillOval //画椭圆(长轴与短轴相同时就是圆)
g.drawLine //画直线
g.fillRect //用画笔颜色填充矩形,可以用来画面板

经过这些步骤,相信各位已经可以完成坦克以及面板的绘制了~

②我方坦克动起来

其实原理很简单,每个坦克都有自己的坐标【因此将坦克封装成一个对象,拥有自己属性】。而我们对己方坦克进行键盘事件监听,当按下方向键的时,就改变自己的坐标,接着马上调用repaint()方法这个方法的作用的调用paint()方法,即重新绘图。又因为我们的坐标改变了,所以画出来的就是一幅新的图片。

同时我们知道,方向不同时,构成坦克的各图形坐标位置会有所变化【大小不变】。因此绘图时还要根据方向进行调节。建议将画根据方向画坦克封装成一个drawTank方法,后边只需要传入坐标和方向即可完成绘制

CPU的运行速度很快,当一直按着方向键的时,就会绘制一张张不同的新图片,连起来看就像动起来了一样。

③我方发射子弹

子弹的本质是什么?

根据常识,子弹发射出去后移动与原坦克没关系了,看作是独立个体,因此要单独封装成一个类,享有自己的属性,同时子弹的移动也体现在坐标的变化上

因此想要让子弹有规律的动起来,一个想法是让子弹成为一个线程。按下发射键时,子弹线程就启动,并且有规律的改变自己的坐标。当子弹坐标与敌方坦克重合或者走到边界时,生命周期结束。

子弹的移动要满足什么规律?

当我方坦克向上,发射的子弹自然也向上……因此子弹坐标的改变需要根据发射时坦克的方向进行确定,所以像前边举的例子,子弹向上走,只需要改变Y轴即可

如何控制最多发射5颗子弹?

子弹虽然自己是一个个体,但是实际上还是和原来的坦克有关系的【不然就不知道是谁发射的子弹】,因此我们可以用一个Vector来装当前发射出去的子弹,每发射一颗子弹就将其加入Vector,当数量达到5时不允许再发射;当子弹生命周期结束时就将其从Vector移除

④我方子弹击中敌人坦克

此时敌方坦克应该消失,我们发出的子弹也应该消失

所有敌人坦克可以放在一个Vector中,每次调用paint()方法时,遍历敌人坦克集合,接着在这个过程中再遍历我方发射出的子弹,将这些子弹的坐标与当前遍历到的坦克坐标进行对比,如果子弹进入了坦克范围就代表击中了坦克。我们将被击中的坦克移出坦克集合同时将这颗子弹移出己方子弹集合

⑤我方子弹击中敌方产生爆炸效果

爆炸效果如何做?

其实就是提前准备好几张图片,当子弹击中坦克时,在击中的位置绘制这几张图片,从而达到爆炸的效果

每个爆炸有自己的生命值,如生命值为3绘制第一张图片;生命值为2绘制第二张图片……从而达到动态效果

由于爆炸的效果与子弹移动、坦克移动等都是互不干扰的,且其是一个动态变化过程,所以爆炸也要单独封装成一个类,享有自己的属性

//得到图片对象
Image image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/相对路径"));
g.drawImage(image1, X, Y, 宽, 高, this); //画图方法

爆炸实现具体思路

爆炸可以多处一起发生,因此我们也将爆炸对象Bomb存放到Vector中。每次绘图的时候对这个Vector进行遍历,得到其中的Bomb对象根据不同生命值进行爆炸效果绘制。当其生命值为0时将其移除,移除后自然不会再绘制。

⑥敌方坦克自动移动

所谓自动移动,其实就是定时改个方向和坐标,接着再对页面进行刷新即可。由此我们不难想到将敌人坦克设置为线程,隔一段时间就随机设置一个方向以及这个方向上正向坐标。

但是重绘怎么要怎么触发呢?毕竟只有在JPanel类以及子类才会有这个方法。

解决办法是将整个面板也设置为一个线程,隔一段时间就进行重绘

⑦敌方坦克自动发射子弹

隔一段时间就得到一个随机数,如果随机数大于某个值就允许其产生一个子弹对象【这是为了控制子弹发射速率】,子弹线程启动,并且将子弹放入自己的子弹集合中

⑧如何防止敌我坦克重叠?

我的作法是在setX以及setY时对新位置进行一个判断,遍历当前面板上存在的所有坦克,如果我的新位置没有进入其他坦克的范围,才被认为是合法的

原理比较简单,但是实现起来需要一些耐心,具体可以参考韩老师给的图↑

⑨如何存档

我利用的方法是将需要记录的数据通通放到一个Recorder类中【比如当前敌人坦克集合、我击杀的敌方坦克数量】,当关闭窗口事件发生时,就将这个类写入文件中。

当选择继续游戏时,输入流会从文件中读取这个类,并将其中的信息进行对应赋值

⑩如何添加背景音乐?

利用JDK自带的音乐播放工具进行播放即可,大致也是I/O流原理,参考以下代码

AutoMusic.java

package TankGame1;import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;public class AutoMusic extends Thread {String path;public AutoMusic(String path) {this.path = path;}@Overridepublic void run() {File file = new File(path);AudioInputStream audioInputStream = null;try {audioInputStream = AudioSystem.getAudioInputStream(file);} catch (Exception e) {e.printStackTrace();return;}AudioFormat format = audioInputStream.getFormat();SourceDataLine auline = null;DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);try {auline = (SourceDataLine) AudioSystem.getLine(info);auline.open(format);} catch (Exception e) {e.printStackTrace();}auline.start();int nBytesRead = 0;//这是缓冲byte[] abData = new byte[512];try {while (nBytesRead != -1) {nBytesRead = audioInputStream.read(abData, 0, abData.length);if (nBytesRead >= 0) {auline.write(abData, 0, nBytesRead);}}} catch (IOException e) {e.printStackTrace();} finally {auline.drain();auline.close();}}
}

在面板构造函数处将此线程启动即可播放事先准备好的音乐

记住!!文件一定是.wav,否则会报异常。直接将mp3文件修改后缀名是行不通的【好像是内部格式啥的】。酷狗可以将mp3文件转换成wav文件,前提是mp3文件是无损音质【若不知道去哪里搞可在如下地址下载,我提供了样本】
链接:https://pan.baidu.com/s/1Al5a_r-QOntlug3FmiGRgQ 提取码:8p9l

坦克大战爆炸图可在此处下载↓
爆炸效果图

5. 源码

1.MyFrame.java(主函数所在)

package TankGame1;import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Vector;/*** 面板启动*/
public class MyFrame extends JFrame {private MyPanel myPanel;private ObjectOutputStream oos;public static void main(String[] args) throws Exception {new MyFrame();}public MyFrame() throws Exception {int i=modelChoose();if (i==-1){System.exit(0); //程序退出}myPanel = new MyPanel(i);new Thread(myPanel).start();this.setSize(1300, 820);this.add(myPanel);this.addKeyListener(myPanel);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);//监听关闭窗口事件this.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {try {Vector<EnemyTank> tanks = myPanel.getEnemyTanks();for (int i = 0; i < tanks.size(); i++) {tanks.get(i).getShots().clear();}//对象输出流oos = new ObjectOutputStream(new FileOutputStream("src\\recorder1.txt"));//将记录对象Recorder写到文件oos.writeObject(myPanel.getRecorder());} catch (Exception e1) {e1.printStackTrace();} finally {if (oos != null) {try {oos.close();} catch (IOException ioException) {ioException.printStackTrace();}}}}});}public int modelChoose() {// 设置按钮字体大小UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("楷体", Font.BOLD, 25)));// 设置文本字体大小UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("楷体", Font.BOLD, 30)));Object[] options = {"新游戏", "继续上局游戏"};int i = JOptionPane.showOptionDialog(null, "请选择开局方式?", "选择", JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE, null, options, null);return i;}
}

2. MyPanel.java(绘图面板)

package TankGame1;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Vector;/*** 面板设计*/
public class MyPanel extends JPanel implements KeyListener, Runnable {private MyTank myTank; //自己的坦克private int initialTankCount = 8; //初始坦克数量private Vector<EnemyTank> EnemyTanks = new Vector<>(); //保存敌方坦克private Vector<Bomb> bombs = new Vector<>(); //爆炸效果private Recorder recorder;private ObjectInputStream ois;Image image1 = null; //爆炸图1Image image2 = null; //爆炸图2Image image3 = null; //爆炸图3public MyPanel(int j) throws Exception {myTank = new MyTank(400, 400, 0, EnemyTanks);if (j == 0) {//开始新游戏startNewGame();} else {//继续上一局游戏File file = new File(MyFrame.class.getResource("../recorder1.txt").toURI());if (!file.exists()) {Object[] options = {"是", "否"};int res = JOptionPane.showOptionDialog(null, "当前无存档,是否开启新游戏?", "选择", JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE, null, options, null);if (res == 0) {startNewGame();} else {System.exit(0);}} else {ois = new ObjectInputStream(new FileInputStream(file));Object object = ois.readObject();if (object != null) {recorder = (Recorder) object;EnemyTanks = new Vector<>(recorder.getEnemyTanks());for (int i = 0; i < EnemyTanks.size(); i++) {new Thread(EnemyTanks.get(i)).start();}}ois.close();}}image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.png"));image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.png"));image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.png"));new AutoMusic(MyFrame.class.getResource("/nightSong.wav").getPath()).start();}public void showInfo(Graphics g) {g.setColor(Color.black);Font font = new Font("楷体", Font.BOLD, 25);g.setFont(font);g.drawString("您累积击毁敌方坦克", 1020, 30);g.drawString(recorder.getKillNums() + "", 1080, 100);drawTank(1020, 60, 0, 0, g);}@Overridepublic void paint(Graphics g) {super.paint(g);g.fillRect(0, 0, 1000, 750);showInfo(g);if (myTank.isLive())drawTank(myTank.getX(), myTank.getY(), 0, myTank.getDirection(), g);Vector<Shot> shotsVector = myTank.getShots(); //己方子弹库for (int i = 0; i < shotsVector.size(); i++) {Shot shot = shotsVector.get(i);g.fill3DRect(shot.getX(), shot.getY(), 3, 3, false);}for (int i = 0; i < bombs.size(); i++) {Bomb bomb = bombs.get(i);if (bomb.getLife() > 12) {g.drawImage(image1, bomb.getX(), bomb.getY(), 60, 60, this);} else if (bomb.getLife() > 6) {g.drawImage(image2, bomb.getX(), bomb.getY(), 60, 60, this);} else {g.drawImage(image3, bomb.getX(), bomb.getY(), 60, 60, this);}bomb.lifeDown(); //炸弹效果if (!bomb.isLive()) {bombs.remove(bomb);i--;}}for (int i = 0; i < EnemyTanks.size(); i++) {EnemyTank tank = EnemyTanks.get(i);if (tank.isLive()) { //敌人坦克还活着就要画drawTank(tank.getX(), tank.getY(), 1, tank.getDirection(), g);}for (int j = 0; j < tank.getShots().size(); j++) { //查看敌人发射子弹情况Shot shot = tank.getShots().get(j); //得到子弹之一g.setColor(Color.yellow);g.draw3DRect(shot.getX(), shot.getY(), 3, 3, false);}if (tank.getShots().size() == 0 && !tank.isLive()) { //坦克当前被消灭且子弹生命期都结束EnemyTanks.remove(tank);i--;}}}@Overridepublic void run() {while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//遍历敌人坦克,看看子弹能不能打到for (int i = 0; i < EnemyTanks.size(); i++) {EnemyTank enemyTank = EnemyTanks.get(i); //敌人坦克shotsTank(myTank.getShots(), enemyTank);//我打敌人shotsTank(enemyTank.getShots(), myTank);//敌人打我}repaint();}}/*** @param shot1 对方子弹* @param tank  坦克*/public boolean hitTank(Shot shot1, TankModel tank) {int direction = tank.getDirection(); //坦克方向int x = shot1.getX();int y = shot1.getY();int tankX = tank.getX();int tankY = tank.getY();switch (direction) {case 0://上case 2:if (x > tankX && x < tankX + 40 && y > tankY && y < tankY + 60) {//击中坦克tank.setLive(false);Bomb bomb = new Bomb(tank.getX(), tank.getY());//在被子弹击中处设置一个爆炸效果bombs.add(bomb);return true;}break;case 1:case 3:if (x > tankX && x < tankX + 60 && y > tankY && y < tankY + 40) {//击中坦克tank.setLive(false);Bomb bomb = new Bomb(tank.getX(), tank.getY());bombs.add(bomb);return true;}break;}return false;}/*** 子弹集合是否打得到目标坦克** @param shots 子弹集合* @param tank  目标坦克*/public void shotsTank(Vector<Shot> shots, TankModel tank) {for (int j = 0; j < shots.size(); j++) {Shot shot = shots.get(j);if (tank.isLive()) { //坦克活着才有被打的机会if (shot.isLive()) { //子弹活着boolean flag = hitTank(shot, tank);if (flag) { //子弹命中坦克shots.remove(shot);if (tank instanceof EnemyTank) {recorder.setKillNums(recorder.getKillNums() + 1);}j--;}} else {shots.remove(shot);j--;}} else {if (!shot.isLive()) {shots.remove(shot);j--;}break;}}}/*** 绘制坦克方法** @param x         坦克左上角x坐标* @param y         坦克左上角y坐标* @param type      //0为己方坦克,1为对方坦克* @param direction //方向,0上,1右,2下,3左* @param g         //画笔*/public void drawTank(int x, int y, int type, int direction, Graphics g) {//设置坦克颜色switch (type) {case 0:g.setColor(Color.cyan);break;case 1:g.setColor(Color.yellow);break;}//根据方向进行绘制switch (direction) {case 0:case 2:g.fill3DRect(x, y, 10, 60, false);g.fill3DRect(x + 10, y + 10, 20, 40, false);g.fill3DRect(x + 30, y, 10, 60, false);g.fillOval(x + 10, y + 20, 20, 20);if (direction == 0) {g.drawLine(x + 20, y, x + 20, y + 30);//炮筒} else {g.drawLine(x + 20, y + 60, x + 20, y + 30);}break;case 1:case 3:g.fill3DRect(x, y, 60, 10, false);g.fill3DRect(x + 10, y + 10, 40, 20, false);g.fill3DRect(x, y + 30, 60, 10, false);g.fillOval(x + 20, y + 10, 20, 20);if (direction == 1) {g.drawLine(x + 30, y + 20, x + 60, y + 20);} else {g.drawLine(x + 30, y + 20, x, y + 20);}break;}}public void startNewGame() {for (int i = 0; i < initialTankCount; i++) {EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 10, 2, EnemyTanks, myTank);EnemyTanks.add(enemyTank); //将坦克加入数组中new Thread(enemyTank).start();}recorder = new Recorder();recorder.setEnemyTanks(EnemyTanks);}@Overridepublic void keyPressed(KeyEvent e) {switch (e.getKeyCode()) {case 87://W键myTank.setDirection(0);myTank.setY(myTank.getY() - 10);repaint();break;case 83://S键myTank.setDirection(2);myTank.setY(myTank.getY() + 10);repaint();break;case 65://A键myTank.setDirection(3);myTank.setX(myTank.getX() - 10);repaint();break;case 68://D键myTank.setDirection(1);myTank.setX(myTank.getX() + 10);repaint();break;case 74://J键if (myTank.getShots().size() < 5 && myTank.isLive())myTank.Shooting();break;}}@Overridepublic void keyTyped(KeyEvent e) {}@Overridepublic void keyReleased(KeyEvent e) {}public Recorder getRecorder() {return recorder;}public Vector<EnemyTank> getEnemyTanks() {return EnemyTanks;}
}

3.TankModel.java(坦克模型)

package TankGame1;import java.io.Serializable;
import java.util.Vector;/*** 坦克的父类*/
public class TankModel implements Serializable {private int x;//x坐标private int y;//y坐标private int direction; //坦克方向private boolean isLive = true;private Vector<Shot> shots = new Vector<>();private Vector<EnemyTank> enemyTanks = null;public TankModel(int x, int y, int direction, Vector<EnemyTank> enemyTanks) {this.x = x;this.y = y;this.direction = direction;this.enemyTanks = enemyTanks;}public void setX(int x) {if (x >= 0 && x <= 940) {if (xCollide(x))this.x = x;}}public void setY(int y) {if (y >= 0 && y <= 690) {if (yCollide(y))this.y = y;}}//往x方向移动是否合法public boolean xCollide(int x) {//当前坦克往左边走for (int i = 0; i < getEnemyTanks().size(); i++) {EnemyTank tank = getEnemyTanks().get(i);int x1;int y1;int direction;if (tank != this) {x1 = tank.getX();y1 = tank.getY();direction = tank.getDirection();} else {//敌人坦克和我的位置是否冲突MyTank myTank = tank.getMyTank();if (myTank == null || !myTank.isLive()) {continue;}x1 = myTank.getX();y1 = myTank.getY();direction = myTank.getDirection();}switch (direction) {case 0:case 2://对方坦克上/下if (getY() + 40 <= y1 || getY() >= y1 + 60) {continue;} else {if (x1 + 40 <= x || x + 60 <= x1) {continue;}return false;}case 1:case 3://对方坦克左/右if (getY() + 40 <= y1 || getY() >= y1 + 40) {continue;} else {if (x1 + 60 <= x || x + 60 <= x1) {continue;}return false;}}}return true;}//往y方向移动是否合法public boolean yCollide(int y) {//当前坦克往上或下int x1;int y1;int direction;for (int i = 0; i < enemyTanks.size(); i++) {EnemyTank tank = enemyTanks.get(i);if (tank != this) {x1 = tank.getX();y1 = tank.getY();direction = tank.getDirection();} else {//敌人坦克和我的位置是否冲突MyTank myTank = tank.getMyTank();if (myTank == null || !myTank.isLive()) {continue;}x1 = myTank.getX();y1 = myTank.getY();direction = myTank.getDirection();}//自己和自己不用比较switch (direction) {case 0:case 2://对方坦克上/下if (getX() + 40 <= x1 || getX() >= x1 + 40) {continue;} else {if (y1 + 60 <= y || y + 60 <= y1) {continue;}return false;}case 1:case 3://对方坦克左/右if (getX() + 40 <= x1 || getX() >= x1 + 60) {continue;} else {if (y1 + 40 <= y || y + 60 <= y1) {continue;}return false;}}}return true;}public void setDirection(int direction) {this.direction = direction;}public int getX() {return x;}public int getY() {return y;}public int getDirection() {return direction;}public Vector<EnemyTank> getEnemyTanks() {return enemyTanks;}public Vector<Shot> getShots() {return shots;}public void setLive(boolean live) {isLive = live;}public boolean isLive() {return isLive;}
}

4.EnemyTank.java(敌方坦克)

package TankGame1;import java.util.Vector;/*** 敌方坦克*/
public class EnemyTank extends TankModel implements Runnable {private MyTank myTank;public EnemyTank(int x, int y, int direction, Vector<EnemyTank> enemyTanks, MyTank myTank) {super(x, y, direction, enemyTanks);this.myTank = myTank;}@Overridepublic void run() {int flag = 20; //同一个方向走20次double direction = 0.65;while (isLive()) {double count = Math.random() * 15; //在当前方向上移动的距离if (flag-- == 0) {direction = Math.random();flag = 5;}moveByDirection((int) count, direction);if (Math.random() > 0.9) //发子弹概率Shotting(Math.random());try {Thread.sleep(150);} catch (InterruptedException e) {e.printStackTrace();}}}public void moveByDirection(int count, double direction) {if (direction < 0.25) {//向上发射子弹int res = getY() - (int) count;setY(res);setDirection(0);} else if (direction < 0.5) {//向右发射子弹int res = getX() + (int) count;setX(res);setDirection(1);} else if (direction < 0.75) {//向下发射子弹int res = getY() + (int) count;setY(res);setDirection(2);} else {//向左发射子弹int res = getX() - (int) count;setX(res);setDirection(3);}}public void Shotting(double flag) {if (flag > 0.5 && getShots().size() <= 5) {Shot shot = null;//根据方向移动int dir = getDirection();switch (dir) {case 0:shot = new Shot(getX() + 20, getY(), 5, dir);break;case 2:shot = new Shot(getX() + 20, getY() + 60, 5, dir);break;case 1:shot = new Shot(getX() + 60, getY() + 20, 5, dir);break;case 3:shot = new Shot(getX(), getY() + 20, 5, dir);}getShots().add(shot);new Thread(shot).start();}}public MyTank getMyTank() {return myTank;}
}

5.MyTank.java(我方坦克)

package TankGame1;import java.util.Vector;/*** 自己坦克*/
public class MyTank extends TankModel {public MyTank(int x, int y, int direction, Vector<EnemyTank> enemyTanks) {super(x, y, direction, enemyTanks);}/*** 射击方法*/public void Shooting() {int direction = getDirection(); //当前坦克方向Shot temp = null;switch (direction) {case 0:temp = new Shot(getX() + 20, getY(), 20, 0);break;case 1:temp = new Shot(getX() + 60, getY() + 20, 20, 1);break;case 2:temp = new Shot(getX() + 20, getY() + 60, 20, 2);break;case 3:temp = new Shot(getX(), getY() + 20, 20, 3);break;}getShots().add(temp);Thread thread = new Thread(temp);thread.start(); //启动子弹线程}
}

6.Bomb.java(爆炸效果)

package TankGame1;public class Bomb {private int x;private int y;private int life = 18;private boolean isLive=true;public Bomb(int x, int y) {this.x = x;this.y = y;}public void lifeDown() {if (life>0){life--;}else{isLive=false;}}public int getX() {return x;}public int getY() {return y;}public int getLife() {return life;}public boolean isLive() {return isLive;}
}

7.Shot.java(子弹)

package TankGame1;/*** 子弹*/
public class Shot implements Runnable {private int x; //子弹X位置private int y; //子弹Y位置private int speed; //子弹速度private int direction; //子弹方向private boolean isLive=true; //子弹是否存活public Shot(int x, int y, int speed, int direction) {this.x = x;this.y = y;this.speed = speed;this.direction = direction;}@Overridepublic void run() {while(true){ //子弹活着才刷新子弹状态//每隔0.1秒子弹就刷新位置switch (direction){case 0:y-=speed; //Y变小就是往上走break;case 1:x+=speed; //X变大就是往右break;case 2:y+=speed; //Y变大就是往下break;case 3:x-=speed; //X变小时往左break;}if(x<0||x>1000||y<0||y>750){isLive=false;//此时说明子弹不在范围内,线程可以结束break;}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public int getX() {return x;}public int getY() {return y;}public int getDirection() {return direction;}public void setLive(boolean live) {isLive = live;}public boolean isLive() {return isLive;}
}

8.Recorder.java(记录类)

package TankGame1;import java.io.Serializable;
import java.util.Vector;public class Recorder implements Serializable {//敌人坦克情况private Vector<EnemyTank> EnemyTanks = new Vector<>();//杀了几个敌人private int killNums = 0;//有存档时使用public Recorder(Vector<EnemyTank> enemyTanks, int killNums) {EnemyTanks = enemyTanks;this.killNums = killNums;}//新游戏时使用public Recorder() {}public Vector<EnemyTank> getEnemyTanks() {return EnemyTanks;}public void setEnemyTanks(Vector<EnemyTank> enemyTanks) {EnemyTanks = enemyTanks;}public int getKillNums() {return killNums;}public void setKillNums(int killNums) {this.killNums = killNums;}
}

9.AutoMusic.java

上边给出过


懒得复制的朋友也可百度云自取源码

Java实现基础坦克大战【含源码】相关推荐

  1. java完成经典坦克大战项目源码

    图片是类与类之间的包位置关系,与代码的导包操作相关 ​[点击并拖拽以移动] 图片是类之间的关系,以及工具类的包位置package com.tank.app;import com.tank.game.G ...

  2. Java坦克大战游戏源码(java坦克大战)

    Java坦克大战游戏源码(java坦克大战) public Swingtest002() {// 设置标题setTitle("请登陆");// 绝对布局setLayout(null ...

  3. 3D坦克大战游戏源码

    3D坦克大战游戏源码,该游戏是基于xcode 4.3,ios sdk 5.1开发.在xcode4.3.3上完美无报错.兼容ios4.3-ios6.0 ,一款ios平台上难得的3D坦克大战游戏源码,有2 ...

  4. 【180927】坦克大战游戏源码

    一.源码特点     采用winform进行开发,实现了坦克大战的游戏功能,非常简单,欢迎下载 二.功能介绍     本源码是一个坦克大战游戏源码,运行后,用键盘进行操作,击败对方坦克会随机出现武器加 ...

  5. 射击小游戏源码《90坦克大战》源码H5+安卓+IOS三端源码

    cocos creator2.2.2射击小游戏源码<90坦克大战>源码H5+安卓+IOS三端源码,开发脚本为javaScript方便扩展和阅读,支持cocos creator2.X版本,完 ...

  6. HTML5经典坦克大战及源码

    目录 一.简单介绍 二.具体界面 主界面 排行榜界面 组员信息界面 组员信息场景 三.部分代码 3.1 界面绘制的实现 6.2 坦克移动实现 3.3 发射炮弹 3.4声音处理 3.5计分 四.总结 一 ...

  7. java小游戏:蜡笔小新!!!基于JAVA面向对象实现的跑酷类游戏(小新跑酷)(简单小游戏,仅仅使用Java面向对象基础实现(附上源码))

    一起来看一下蜡笔小新,很好看的,解压!!!!!!!!!!!!!!! 由于Java面向对象基础的游戏,有一定的局限,但是我觉得这款的可玩性已经很高了,话不多说一起来看游戏: 游戏界面,点击空格跳跃来躲避 ...

  8. 17 - python——简单的坦克大战(附源码)

    基于对面向对象编程的思想完成简单的坦克大战游戏.主要目的锻炼面相对象编程思想 同样的在使用python进行游戏编写时需要安装pygame模块 安装方法: pycharm安装方式:File --> ...

  9. 坦克大战java_java版坦克大战游戏源码

    [java]代码库package tankwar; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; imp ...

  10. Java集合(超详细-含源码)

    一 集合体系结构 集合的体系结构分为单列集合和双列集合 二 Collection单列集合 Collection是单列集合的祖宗接口,他的功能是全部单列集合都可以继承使用的. 单列集合接口下又分为Lis ...

最新文章

  1. 如何给HTML添加事件?
  2. 每日一皮:给老板演示刚做好的功能...
  3. windows 下安装 scrapy报错:error: Unable to find vcvarsall.bat
  4. 全球及中国箱包市场需求前景与投资动态分析报告2022版
  5. python快速排序最简单写法_漫画:最最最最最简单的选择排序
  6. 自己的路 php,生活感悟的句子:走自己的路,做自己的事
  7. 大数据_Spark框架_快速上手_word count 案例-功能实现---Spark工作笔记0007
  8. mysql 5.5 外键_MySQL 5.5添加外键失败,错误[HY000] [150]和[HY000] [1005]
  9. iis服务器怎么限制运行asp文件,Win2008 r2 IIS7.5制定目录禁止执行脚本的方法
  10. AVI视频格式的Dvix和Xvid两种不同编码的区别
  11. 利用adsl拨号服务器VPS构建代理IP池
  12. 【皇室战争】使用Clash Royale API,构建你的皇室应用
  13. webflux之reactor-Subscriber
  14. android n 下载地址,android N镜像文件下载地址
  15. java抽象类重载_012 JAVA 抽象类、接口、String类的基础了解
  16. ElementUI 加载Loding组件
  17. Form(窗体)的FormBorderStyle属性的不同效果
  18. Failed to read schema document 'http://code.alibabatech.com/schema/dubbo/dubbo.xsd'问题解决方法
  19. 收货地址 (默认收货地址)
  20. 慧荣SM2246主控开卡一直处于Pretest状态解决方案

热门文章

  1. 一例智能网卡(mellanox)的网卡故障分析
  2. android stub,Android:ViewStub
  3. 雷诺方程、脉动运动方程及雷诺应力输运方程的推导
  4. 计算机网络(第七版)谢希仁
  5. MySQL 字符串拼接 - 多种字符串拼接实战案例
  6. 计量经济学第6章计算机c4,伍德里奇计量经济学第6章部分计算机习题详解STATA.pdf...
  7. URP管线理解(一)宏观入口
  8. Unity内置管线Projector原理分析
  9. tp5验证码详细代码
  10. 模块式工业机器人柔性自动化生产线实训系统(五站)