韩顺平--Java坦克大战
小开开 2022-05-15 09:56:27 2022-05-15 40 0
1、说明
写了14个版本,终于完成了,项目结构如下
2、游戏效果
(1)击中敌人
(2)被敌人击中
3、代码如下
(1)TankGame 主方法
package cn.bloghut;import cn.bloghut.component.MyPanel;import cn.bloghut.service.Recorder;import javax.swing.*;import java.awt.*;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.util.Scanner;/** * @Classname TankGame * @Description TODO * @Date 2022/5/8 8:25 * @Created by 闲言 */public class TankGame extends JFrame { private MyPanel panel; public static void main(String[] args) { new TankGame(); } public TankGame(){ System.out.println("请输入选择: 1 表示新游戏,2是继续上局"); Scanner scanner = new Scanner(System.in); //将panel放入到thread中 this.panel = new MyPanel(scanner.next()); scanner.close(); new Thread(panel).start(); //设置绑定监听器 this.addKeyListener(panel); this.add(panel); //设置窗体关闭 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setTitle("闲言-坦克大战"); //获取当前屏幕分辨率 int width = Toolkit.getDefaultToolkit().getScreenSize().width; int height = Toolkit.getDefaultToolkit().getScreenSize().height; //设置窗体大小不可变 this.setResizable(false); //设置窗体位置 width = (width - 1300) / 2; height = (height - 750) / 2; this.setBounds(width,height,1300,750); this.setBackground(Color.black); //设置窗体显示 this.setVisible(true); //在JFrame 中增加相关关闭窗口的处理 this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { Recorder.keepRecord(); System.exit(0); } }); }}
(2)MyPanel 视图
package cn.bloghut.component;import cn.bloghut.pojo.*;import cn.bloghut.service.PlayAudio;import cn.bloghut.service.Recorder;import cn.bloghut.service.Shot;import javax.swing.*;import java.awt.*;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.io.File;import java.util.Vector;/** * @Classname MyPanel * @Description 坦克大战绘图区域 * 为了让panel 不停的重绘子弹,需要将MyPanel * @Date 2022/5/8 8:44 * @Created by 闲言 */public class MyPanel extends JPanel implements KeyListener, Runnable { //定义我的坦克 Hero hero = null; //定义敌人坦克 Vector<EnemyTank> enemyTanks = new Vector<>(); //用于存放炸弹 //当子弹击中坦克时,就加入一个bomb 对象 Vector<Bomb> bombs = new Vector<>(); //定义三种图片,用于显示炸弹 Image img1 = null; Image img2 = null; Image img3 = null; //定义一个存放Node 对象的Vector,用于恢复敌人坦克的坐标和放向 Vector<Node> nodes = null; //敌人坦克数量 int enemyTankSize = 3; public MyPanel(String type) { //判断记录文件是否存在 //如果存在就正常执行,如果文件不存在,提示只能开启新的游戏 File file = new File(Recorder.getRecordFile()); if (!file.exists()){ type = "1"; System.out.println("文件不存在只能开启新游戏"); }else { nodes = Recorder.getNodesAndTanks(); } //初始化我的坦克 hero = new Hero(150, 150); //设置坦克速度 hero.setSpeed(5); switch (type) { case "1": //初始化敌人坦克 for (int i = 0; i < enemyTankSize; i++) { EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0); enemyTank.setDirection(2); //启动敌人坦克线程 new Thread(enemyTank).start(); //给该enemyTank 加入一颗子弹 Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirection()); //加入到 enemyTank.getShots().add(shot); //为每个坦克,放入所有的敌人对象 enemyTank.setEnemyTanks(enemyTanks); //启动shot new Thread(shot).start(); enemyTanks.add(enemyTank); } break; case "2": //继续上局游戏 //初始化敌人坦克 for (int i = 0;i < nodes.size();i++){ Node node = nodes.get(i); EnemyTank enemyTank = new EnemyTank(node.getX(),node.getY()); enemyTank.setDirection(node.getDirection()); //启动敌人坦克线程 new Thread(enemyTank).start(); //给该enemyTank 加入一颗子弹 Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirection()); //加入到 enemyTank.getShots().add(shot); //为每个坦克,放入所有的敌人对象 enemyTank.setEnemyTanks(enemyTanks); //启动shot new Thread(shot).start(); enemyTanks.add(enemyTank); } break; default: System.out.println("你的输入有误"); } //获取项目路径 //1.获取图片资源 String parent = System.getProperty("user.dir"); parent += "\\"; //2.初始化图片 img1 = Toolkit.getDefaultToolkit().getImage(parent + "bomb_1.gif"); img2 = Toolkit.getDefaultToolkit().getImage(parent + "bomb_2.gif"); img3 = Toolkit.getDefaultToolkit().getImage(parent + "bomb_3.gif"); //存放敌人坦克信息 Recorder.setEnemyTanks(enemyTanks); //播放指定的音乐 new PlayAudio("bg.wav").start(); } @Override public void paint(Graphics g) { super.paint(g); //填充矩形,默认黑色 g.fillRect(0, 0, 1000, 750); //画出游戏信息 showInfo(g); //画自己坦克 if (hero != null && hero.isLive()) { drawTank(hero.getX(), hero.getY(), g, hero.getDirection(), 1); } //画出自己射击的子弹 //不为空,且处于发射状态 if (hero.getShot() != null && hero.getShot().isLive()) { g.draw3DRect(hero.getShot().getX(), hero.getShot().getY(), 2, 2, false); } if (hero.getShots().size() > 0) { for (int i = 0; i < hero.getShots().size(); i++) { Shot shot = hero.getShots().get(i); if (shot != null && shot.isLive()) { g.draw3DRect(shot.getX(), shot.getY(), 2, 2, false); } else { //如果shot对象已经无效,就从子弹集合中移除 hero.getShots().remove(shot); } } } //画敌人坦克 for (int i = 0; i < enemyTanks.size(); i++) { EnemyTank enemyTank = enemyTanks.get(i); //判断当前坦克是否还存活,只有存活的时候才去画 if (enemyTank.isLive()) { drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 0); //绘制敌人坦克子弹 Vector<Shot> shots = enemyTank.getShots(); for (int j = 0; j < shots.size(); j++) { Shot shot = shots.get(j); //子弹存活的时候才绘制 if (shot.isLive()) { g.draw3DRect(shot.getX(), shot.getY(), 1, 1, false); } else { //子弹出边界了,移除子弹 enemyTank.getShots().remove(shot); } } } } //如果bombs 集合中有对象,就画出 for (int i = 0; i < bombs.size(); i++) { Bomb bomb = bombs.get(i); //根据当前炸弹的life值画出对应的图片 if (bomb.getLife() > 6) { g.drawImage(img1, bomb.getX(), bomb.getY(), 60, 60, this); } else if (bomb.getLife() > 3) { g.drawImage(img1, bomb.getX(), bomb.getY(), 60, 60, this); } else { g.drawImage(img1, bomb.getX(), bomb.getY(), 60, 60, this); } //让这个炸弹的生命值减少 bomb.lifeDown(); //如果bomb 生命值为0,就从集合中删除 if (bomb.getLife() == 0) { bombs.remove(bomb); } } } /** * 显示 击毁敌方的坦克信息 * * @param g 画图 */ public void showInfo(Graphics g) { g.setColor(Color.BLACK); Font font = new Font("宋体", Font.BOLD, 25); g.setFont(font); g.drawString("您累计击毁敌方坦克", 1020, 30); drawTank(1020, 60, g, 0, 0); g.setColor(Color.BLACK); g.drawString(Recorder.getAllEnemyTankTotal() + "", 1080, 100); } /** * 绘制坦克 * * @param x 坦克的左上角x坐标 * @param y 坦克的左上角y坐标 * @param g 画笔 * @param direction 坦克方向(上下左右) * @param type 坦克类型 */ public void drawTank(int x, int y, Graphics g, int direction, int type) { //根据不同类型的坦克设置不同颜色 switch (type) { case 0://0 敌人的坦克 g.setColor(Color.cyan); break; // case 1://1 我们的坦克 g.setColor(Color.yellow); break; } //根据坦克的方向来绘制坦克 //direction 0-向上,1-向右,2-向下,3-向左 switch (direction) { case 0: //左右两边轮子 g.fill3DRect(x, y, 10, 60, false); g.fill3DRect(x + 30, y, 10, 60, false); //画矩形 g.fill3DRect(x + 10, y + 10, 20, 40, false); //画圆 g.fillOval(x + 10, y + 20, 20, 20); //画炮杆--起点终点 g.drawLine(x + 20, y + 30, x + 20, y); break; case 1: g.fill3DRect(x, y, 60, 10, false);//上边轮子 g.fill3DRect(x, y + 30, 60, 10, false);//下边轮子 //画矩形 g.fill3DRect(x + 10, y + 10, 40, 20, false); //画圆 g.fillOval(x + 20, y + 10, 20, 20); //画炮杆--起点终点 g.drawLine(x + 30, y + 20, x + 60, y + 20); break; case 2: //两边轮子 g.fill3DRect(x, y, 10, 60, false); g.fill3DRect(x + 30, y, 10, 60, false); //画矩形 g.fill3DRect(x + 10, y + 10, 20, 40, false); //画圆 g.fillOval(x + 10, y + 20, 20, 20); //画炮杆--起点终点 g.drawLine(x + 20, y + 30, x + 20, y + 60); break; case 3: g.fill3DRect(x, y, 60, 10, false);//上边轮子 g.fill3DRect(x, y + 30, 60, 10, false);//下边轮子 //画矩形 g.fill3DRect(x + 10, y + 10, 40, 20, false); //画圆 g.fillOval(x + 20, y + 10, 20, 20); //画炮杆--起点终点 g.drawLine(x + 30, y + 20, x, y + 20); break; default: System.out.println("暂时没有处理"); } } @Override public void keyTyped(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { int code = e.getKeyCode(); //判断用户按下的按键 if (KeyEvent.VK_W == code) { //向上 //移动时判断不能重叠 hero.setDirection(0); hero.moveUp(); } else if (KeyEvent.VK_D == code) { //向右 hero.setDirection(1); hero.moveRight(); } else if (KeyEvent.VK_S == code) { //向下 hero.setDirection(2); hero.moveDown(); } else if (KeyEvent.VK_A == code) { //向左 hero.setDirection(3); hero.moveLeft(); } //判断是否是射击 if (KeyEvent.VK_J == code) { //如果为空,说明是第一次发射 //发送一颗子弹// if (hero.getShot() == null || !(hero.getShot().isLive())){// hero.shotEnemyTank();// } //发射多颗子弹 hero.shotEnemyTank(); } //重绘 this.repaint(); } //如果我们的坦克可以发射多发子弹 //在判断我方子弹是否击中敌人坦克时,就需要把我们的子弹集合所有的子弹取出,都取出和敌人的所有坦克进行判断 //编写方法,判断我方的子弹是否击中敌人坦克 /** * 判断子弹和坦克 * * @param shots 子弹集合 * @param tank 坦克 */ public void hitTank(Vector<Shot> shots, Tank tank) { for (int i = 0; i < shots.size(); i++) { hitTank(shots.get(i), tank); } } /** * 判断子弹和坦克 * * @param s 子弹 * @param tank 坦克 */ public void hitTank(Shot s, Tank tank) { switch (tank.getDirection()) { case 0://坦克向上 case 2://坦克向下 if (s.getX() > tank.getX() && s.getX() < tank.getX() + 40 && s.getY() > tank.getY() && s.getY() < tank.getY() + 60) { s.setLive(false); tank.setLive(false); //当我的子弹击中敌人坦克后,将enemyTank 坦克从集合中去掉 enemyTanks.remove(tank); //当我方击毁一辆敌方坦克时,就对一个值++(前提不是我放坦克) if (!(tank instanceof Hero)) { Recorder.addlEnemyTankTotal(); } //创建bomb对象,加入到bombs集合 Bomb bomb = new Bomb(tank.getX(), tank.getY()); bombs.add(bomb); } break; case 1://坦克向右 case 3://坦克向左 if (s.getX() > tank.getX() && s.getX() < tank.getX() + 60 && s.getY() > tank.getY() && s.getY() < tank.getY() + 40) { s.setLive(false); tank.setLive(false); //打我的子弹击中敌人坦克后,将enemyTank 坦克从集合中去掉 enemyTanks.remove(tank); //当我方击毁一辆敌方坦克时,就对一个值++(前提不是我放坦克) if (!(tank instanceof Hero)) { Recorder.addlEnemyTankTotal(); } //创建bomb对象,加入到bombs集合 Bomb bomb = new Bomb(tank.getX(), tank.getY()); bombs.add(bomb); } break; } } @Override public void keyReleased(KeyEvent e) { } @Override public void run() { //每隔100毫秒,重绘区域 while (true) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //判断是否击中敌人坦克 hitEnemy(); //判断敌人坦克是否击中我们 hitHero(); this.repaint(); } } /** * 判断是否击中敌人坦克 */ public void hitEnemy() { if (hero.getShot() != null && hero.getShot().isLive()) { //遍历敌人所有的坦克 for (int i = 0; i < enemyTanks.size(); i++) { EnemyTank enemyTank = enemyTanks.get(i); //发射单个子弹// hitTank(hero.getShot(),enemyTank); //发射多颗子弹 hitTank(hero.getShots(), enemyTank); } } } /** * 判断敌人坦克是否击中 */ public void hitHero() { if (enemyTanks.size() > 0) { //拿到所有敌人的坦克-下的子弹,判断是否重合如果重合则显示爆炸 for (int i = 0; i < enemyTanks.size(); i++) { EnemyTank enemyTank = enemyTanks.get(i); //判断是否击中 //敌人的子弹 Vector<Shot> shots = enemyTank.getShots(); if (shots.size() > 0) { for (int j = 0; j < shots.size(); j++) { Shot shot = shots.get(j); if (hero.isLive() && shot.isLive()) { //判断子弹和坦克是否重合 hitTank(shots, hero); } } } } } }}
(3)Bomb 爆炸类
package cn.bloghut.pojo;/** * @Classname Bomb * @Description TODO * @Date 2022/5/8 16:18 * @Created by 闲言 */public class Bomb { private int x,y;//炸弹坐标 private int life = 9;//炸弹生命周期 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 void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getLife() { return life; } public void setLife(int life) { this.life = life; } public boolean isLive() { return isLive; } public void setLive(boolean live) { isLive = live; }}
(4)EnemyTank 敌方坦克类
package cn.bloghut.pojo;import cn.bloghut.service.Shot;import java.util.Vector;/** * @Classname EnemyTank * @Description TODO * @Date 2022/5/8 11:06 * @Created by 闲言 */@SuppressWarnings("All")public class EnemyTank extends Tank implements Runnable { //在敌人坦克类,保存多个shot private Vector<Shot> shots = new Vector<>(); //敌人坦克 可以得到敌人坦克的Vector //1.EnemyTank 在mypanel里面 private Vector<EnemyTank> enemyTanks = new Vector<>(); public EnemyTank(int x, int y) { super(x, y); } public Vector<Shot> getShots() { return shots; } public void setShots(Vector<Shot> shots) { this.shots = shots; } public Vector<EnemyTank> getEnemyTanks() { return enemyTanks; } /** * 提供一个方法,可以将mypanel 的成员 Vector<EnemyTank> enemyTanks = new Vector<>(); * 设置到 private Vector<EnemyTank> enemyTanks = new Vector<>(); * @param enemyTanks 敌人坦克集合 */ public void setEnemyTanks(Vector<EnemyTank> enemyTanks) { this.enemyTanks = enemyTanks; } //编写方法,判断当前的敌人坦克,是否和 enemyTanks 中2的 其他坦克发生了重叠 或者是碰撞 public boolean isTouchEnemyTank(){ //判断当前敌人坦克(this) 方向 switch (this.getDirection()){ case 0: //上 //让当前的this 敌人坦克 和 其他所有的敌人坦克比较 for (int i = 0;i< enemyTanks.size();i++){ //从vector中取出一辆敌人的坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和自己比较 if (this != enemyTank){ /* 1.如果敌人坦克是上/下方向 x的范围是什么【enemyTank.getX() ,enemyTank.getX() + 40】 y的范围是什么【enemyTank.getY() ,enemyTank.getY() + 60】 */ if (enemyTank.getDirection() == 0 || enemyTank.getDirection() == 2){ //2.当前坦克的左上角坐标【this.getX(),this.getY()】 if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 40 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 60){ return true; } //3.当前坦克的右上角坐标【this.getX() + 40,this.getY()】 if (this.getX() + 40 >= enemyTank.getX() && this.getX() + 40 <= enemyTank.getX() + 60 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 40){ return true; } } /* 1.如果敌人坦克是左/右方向 x的范围是什么【enemyTank.getX() ,enemyTank.getX() + 60】 y的范围是什么【enemyTank.getY() ,enemyTank.getY() + 40】 */ //如果敌人坦克是左/右方向 if (enemyTank.getDirection() == 1 || enemyTank.getDirection() == 3){ //2.当前坦克的左上角坐标【this.getX(),this.getY()】 if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 60 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 40){ return true; } //3.当前坦克的右上角坐标【this.getX() + 40,this.getY()】 if (this.getX() + 40 >= enemyTank.getX() && this.getX() + 40 <= enemyTank.getX() + 60 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 40){ return true; } } } } break; case 1: //右 //让当前的this 敌人坦克 和 其他所有的敌人坦克比较 for (int i = 0;i< enemyTanks.size();i++){ //从vector中取出一辆敌人的坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和自己比较 if (this != enemyTank){ /* 1.如果敌人坦克是上/下方向 x的范围是什么【enemyTank.getX() ,enemyTank.getX() + 40】 y的范围是什么【enemyTank.getY(),enemyTank.getY() + 40】 */ if (enemyTank.getDirection() == 0 || enemyTank.getDirection() == 2){ //2.当前坦克的右上角坐标【this.getX() + 60,this.getY()】 if (this.getX() + 60 >= enemyTank.getX() && this.getX() + 60 <= enemyTank.getX() + 40 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 60){ return true; } //3.当前坦克的右下角坐标【this.getX() + 60,this.getY() + 40】 if (this.getX() + 60 >= enemyTank.getX() && this.getX() + 60 <= enemyTank.getX() + 40 && this.getY() + 40 >= enemyTank.getY() && this.getY() + 40 <= enemyTank.getY() + 60){ return true; } } /* 1.如果敌人坦克是左/右方向 x的范围是什么【enemyTank.getX(),enemyTank.getX() + 60】 y的范围是什么【enemyTank.getY(),enemyTank.getX() + 40】 */ //如果敌人坦克是左/右方向 if (enemyTank.getDirection() == 1 || enemyTank.getDirection() == 3){ //2.当前坦克的右上角坐标【this.getX() + 60,this.getY()】 if (this.getX() + 60 >= enemyTank.getX() && this.getX() + 60 <= enemyTank.getX() + 60 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 40){ return true; } //3.当前坦克的右下角坐标【this.getX() + 60,this.getY() + 40】 if (this.getX() + 60 >= enemyTank.getX() && this.getX() + 60 <= enemyTank.getX() + 60 && this.getY() + 40 >= enemyTank.getY() && this.getY() + 40 <= enemyTank.getY() + 40){ return true; } } } } break; case 2: //下 //让当前的this 敌人坦克 和 其他所有的敌人坦克比较 for (int i = 0;i< enemyTanks.size();i++){ //从vector中取出一辆敌人的坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和自己比较 if (this != enemyTank){ /* 1.如果敌人坦克是上/下方向 x的范围是什么【enemyTank.getX() ,enemyTank.getX() + 40】 y的范围是什么【enemyTank.getY(),enemyTank.getY() + 60】 */ if (enemyTank.getDirection() == 0 || enemyTank.getDirection() == 2){ //2.当前坦克的左下角坐标【this.getX(),this.getY() + 60】 if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 40 && this.getY() + 60 >= enemyTank.getY() && this.getY() + 60 <= enemyTank.getY() + 60){ return true; } //3.当前坦克的右下角坐标【this.getX() + 40,this.getY() + 60】 if (this.getX() + 40 >= enemyTank.getX() && this.getX() + 40 <= enemyTank.getX() + 40 && this.getY() + 60 >= enemyTank.getY() && this.getY() + 60 <= enemyTank.getY() + 60){ return true; } } /* 1.如果敌人坦克是左/右方向 x的范围是什么【enemyTank.getX(),enemyTank.getX() + 60】 y的范围是什么【enemyTank.getY(),enemyTank.getX() + 40】 */ //如果敌人坦克是左/右方向 if (enemyTank.getDirection() == 1 || enemyTank.getDirection() == 3){ //2.当前坦克的左下角坐标【this.getX(),this.getY() + 60】 if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 60 && this.getY() + 60 >= enemyTank.getY() && this.getY() + 60 <= enemyTank.getY() + 40){ return true; } //3.当前坦克的右下角坐标【this.getX() + 40,this.getY() + 60】 if (this.getX() + 40 >= enemyTank.getX() && this.getX() + 40 <= enemyTank.getX() + 60 && this.getY() + 60 >= enemyTank.getY() && this.getY() + 60 <= enemyTank.getY() + 40){ return true; } } } } break; case 3: //左 //让当前的this 敌人坦克 和 其他所有的敌人坦克比较 for (int i = 0;i< enemyTanks.size();i++){ //从vector中取出一辆敌人的坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和自己比较 if (this != enemyTank){ /* 1.如果敌人坦克是上/下方向 x的范围是什么【enemyTank.getX() ,enemyTank.getX() + 40】 y的范围是什么【enemyTank.getY(),enemyTank.getY() + 60】 */ if (enemyTank.getDirection() == 0 || enemyTank.getDirection() == 2){ //2.当前坦克的左上角坐标【this.getX(),this.getY()】 if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 40 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 60){ return true; } //3.当前坦克的左下角坐标【this.getX(),this.getY() + 40】 if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 40 && this.getY() + 40 >= enemyTank.getY() && this.getY() + 40 <= enemyTank.getY() + 60){ return true; } } /* 1.如果敌人坦克是左/右方向 x的范围是什么【enemyTank.getX(),enemyTank.getX() + 60】 y的范围是什么【enemyTank.getY(),enemyTank.getX() + 40】 */ //如果敌人坦克是左/右方向 if (enemyTank.getDirection() == 1 || enemyTank.getDirection() == 3){ //2.当前坦克的左上角坐标【this.getX(),this.getY()】 if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 60 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 40){ return true; } //3.当前坦克的左下角坐标【this.getX(),this.getY() + 40】 if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 60 && this.getY() + 40 >= enemyTank.getY() && this.getY() + 40 <= enemyTank.getY() + 40){ return true; } } } } break; } return false; } @Override public void run() { while (true) { //这里我们判断如果shots size() == 0,创建一颗子弹,放入到shots集合,并启动 if (isLive() && shots.size() < 3) { Shot s = null; //判断坦克的方向,创建对应的子弹 switch (getDirection()) { case 0: s = new Shot(getX() + 20, getY(), 0); break; case 1: s = new Shot(getX() + 60, getY() + 20, 1); break; case 2: s = new Shot(getX() + 20, getY() + 60, 2); break; case 3: s = new Shot(getX(), getY() + 20, 3); break; } //添加到容器 shots.add(s); //启动线程 new Thread(s).start(); } //当前坦克的方向来继续移动 for (int i = 0; i < 30; i++) { switch (getDirection()) { case 0://向上 if (getY() > 0 && !isTouchEnemyTank()){ moveUp(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } break; case 1://向右 if (getX() + 60 < 1000 && !isTouchEnemyTank()){ moveRight(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } break; case 2://向下 if (getY() + 60 < 722 && !isTouchEnemyTank()){ moveDown(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } break; case 3://向左 if (getX() > 0 && !isTouchEnemyTank()){ moveLeft(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } break; } } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } //然后随机的改变方向 int direction = (int) (Math.random() * 4); setDirection(direction); //一旦写并发程序,一定要考虑清楚该线程什么时候结束 //被子弹打中了,结束线程 if (!isLive()) { break; } } }}
(5)Hero 我方坦克类
package cn.bloghut.pojo;import cn.bloghut.service.Shot;import java.util.Vector;/** * @Classname Hero * @Description 自己坦克 * @Date 2022/5/8 8:43 * @Created by 闲言 */public class Hero extends Tank{ //定义一个shot对象,表示射击行为 private Shot shot = null; //可以发射多颗子弹 private Vector<Shot> shots = new Vector<>(); public Hero(int x, int y) { super(x, y); } //射击 public void shotEnemyTank(){ //控制面板上子弹数量 if (shots.size() >= 5){ return; } //创建short对象,根据当前hero对象的位置方向来创建shot对象 switch (getDirection()){ case 0: shot = new Shot(getX() + 20,getY(),0); break; case 1: shot = new Shot(getX() + 60,getY() +20,1); break; case 2: shot = new Shot(getX() + 20,getY() + 60,2); break; case 3: shot = new Shot(getX(),getY() + 20,3); break; } //把进创建的short放到集合中 shots.add(shot); //启动shot线程 new Thread(shot).start(); } public Vector<Shot> getShots() { return shots; } public void setShots(Vector<Shot> shots) { this.shots = shots; } public Shot getShot() { return shot; } public void setShot(Shot shot) { this.shot = shot; }}
(6)Node 记录敌方坦克类(继续上局游戏用)
package cn.bloghut.pojo;/** * @Classname Node * @Description 一个node 对象,表示一个敌人坦克的信息 * @Date 2022/5/9 10:47 * @Created by 闲言 */public class Node { private int x; private int y; private int direction; public Node(int x, int y, int direction) { this.x = x; this.y = y; this.direction = direction; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getDirection() { return direction; } public void setDirection(int direction) { this.direction = direction; }}
(7)Tank 坦克父类
package cn.bloghut.pojo;/** * @Classname Tank * @Description 坦克实体 * @Date 2022/5/8 8:43 * @Created by 闲言 */public class Tank { private int x; private int y; private int direction;///坦克方向0-上 1-右 2-下 3-左 private int speed = 1;//坦克速度 private boolean isLive = true; public Tank(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getDirection() { return direction; } public void setDirection(int direction) { this.direction = direction; } //上下左右移动方法 public void moveUp(){ //坦克不能出边界 if (y > 0){ y-=speed; } } public void moveRight(){ if (x + 60 < 1000){ x+=speed; } } public void moveDown(){ if (y + 60 < 722){ y+=speed; } } public void moveLeft(){ if (x > 0){ x-=speed; } } public boolean isLive() { return isLive; } public void setLive(boolean live) { isLive = live; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; }}
(8)PlayAudio 播放背景音乐类
package cn.bloghut.service;import javax.sound.sampled.*;import java.io.*;/** * @author ajun * Date 2021/7/16 * @version 1.0 * 播放音乐 */public class PlayAudio extends Thread { private String filename; public PlayAudio(String wavfile) { String path = System.getProperty("user.dir"); filename = path+"\\"+wavfile; } @Override public void run() { // 从项目资源目录下加载背景音乐 AudioInputStream audioInputStream = null; try { audioInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream(filename))); } catch (Exception e1) { e1.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(); return; } 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(); return; } finally { auline.drain(); auline.close(); } }}
(9)Recorder 记录游戏数据类
package cn.bloghut.service;import cn.bloghut.pojo.EnemyTank;import cn.bloghut.pojo.Node;import java.io.*;import java.util.Vector;/** * @Classname Recorder * @Description 用于记录 * @Date 2022/5/9 10:00 * @Created by 闲言 */public class Recorder { //定义变量,记录我方击毁敌人坦克数量 private static int allEnemyTankTotal = 0; //定义IO 对象,准备写数据到文件中 private static BufferedWriter bufferedWriter = null; private static BufferedReader bufferedReader = null; private static String recordFile; //存储敌人坦克 private static Vector<EnemyTank> enemyTanks = null; //定义一个Node 的Vector ,用于保存敌人的信息node private static Vector<Node> nodes = new Vector<>(); static { String parentPath = System.getProperty("user.dir"); recordFile = parentPath + "\\record.txt"; } /** * 该方法在继续上局时调用 * 用于读取record.txt 文件,恢复相关信息 * @return 敌人坦克信息 */ public static Vector<Node> getNodesAndTanks(){ try { bufferedReader = new BufferedReader(new FileReader(recordFile)); //击毁数量 allEnemyTankTotal = Integer.parseInt(bufferedReader.readLine()); //获取敌人坦克信息 String dataLine = ""; while ((dataLine = bufferedReader.readLine()) != null){ String[] split = dataLine.split(" "); if (split.length == 3){ Node node = new Node(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2])); nodes.add(node); } } }catch (Exception e){ e.printStackTrace(); }finally { if (bufferedReader != null){ try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } return nodes; } public static void setEnemyTanks(Vector<EnemyTank> enemyTanks) { Recorder.enemyTanks = enemyTanks; } public static int getAllEnemyTankTotal() { return allEnemyTankTotal; } public static void setAllEnemyTankTotal(int allEnemyTankTotal) { Recorder.allEnemyTankTotal = allEnemyTankTotal; } /** * 当我方坦克击毁一辆敌人坦克,就应当对这个值++ */ public static void addlEnemyTankTotal(){ allEnemyTankTotal++ ; } /** * 当游戏退出时,我们将 allEnemyTankTotal 保存到 */ public static void keepRecord(){ try { bufferedWriter = new BufferedWriter(new FileWriter(recordFile)); bufferedWriter.write(allEnemyTankTotal+"\r\n"); //变量敌人坦克集合,然后根据情况保存 //OOP 编程思想,定义一个属性,通过SET 方法得到敌人的坦克集合 if (enemyTanks.size() > 0){ for (int i = 0; i < enemyTanks.size(); i++) { EnemyTank enemyTank = enemyTanks.get(i); //为了保险,判断一下是否存活 if (enemyTank.isLive()){ //保存enemy坦克信息 String record = enemyTank.getX() + " " + enemyTank.getY() + " " + enemyTank.getDirection(); bufferedWriter.write(record); bufferedWriter.newLine(); } } } }catch (Exception e){ e.printStackTrace(); }finally { if (bufferedWriter != null){ try { bufferedWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static String getRecordFile() { return recordFile; } public static void setRecordFile(String recordFile) { Recorder.recordFile = recordFile; }}
(10)Shot 子弹类
package cn.bloghut.service;/** * @Classname Shot * @Description TODO * @Date 2022/5/8 14:15 * @Created by 闲言 */public class Shot implements Runnable{ private int x;//子弹x坐标 private int y;//子弹x坐标 private int direction = 0;//子弹方向 private int speed = 2;//子弹速度 private boolean isLive = true; //子弹是否还存活 @Override public void run() {//射击行为 while (true){ //线程休眠 50 毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } //根据方向来改变x ,y坐标 switch (direction){ case 0: //向上 y -= speed; break; case 1: //向右 x += speed; break; case 2://向下 y += speed; break; case 3://向左 x -= speed; break; } System.out.println("x==>"+x+" y===>"+y); //当子弹碰到敌人坦克时,也应该结束线程 //当子弹移动到面板的边界时,销毁子弹 if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750 && isLive)){ //设置子弹是否存活状态 isLive = false; System.out.println("子弹线程退出"); break; } } } //构造子弹 public Shot(int x, int y, int direction) { this.x = x; this.y = y; this.direction = direction; } public boolean isLive() { return isLive; } public void setLive(boolean live) { isLive = live; } public int getDirection() { return direction; } public void setDirection(int direction) { this.direction = direction; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } @Override public String toString() { return "Shot{" + "x=" + x + ", y=" + y + ", direction=" + direction + ", speed=" + speed + ", isLive=" + isLive + '}'; }}
注意:本工程是模块化进行开发的,所以一些文件( 图片、存档路径、音乐)的路径需要改成自己对应。