day06-IO流应用01
Java坦克大战06
8.IO流应用01
坦克大战6.0版
增加功能:
- 防止敌人坦克重叠运动
- 记录玩家的成绩(累计击毁坦克数),存盘退出
- 记录当时的敌人坦克坐标,存盘退出
- 玩游戏时,可以选择是开新游戏还是继续上局游戏
8.1防止敌人坦克重叠运动
8.1.1思路分析
按照目标坦克的向右下左四种情况分析,每一种情况又分为两种小情况,一种八种情况。
8.1.2代码实现
8.1.2.1修改处1
在EnemyTank类中:
- 增加了增加一个成员属性,EnemyTank 可以得到敌人坦克成员的 Vector,用于循环比较是否重叠
- 新增一个方法setEnemyTanks,可以将MyPanel的成员 Vector
enemyTanks = new Vector<>() 设置到Enemy 的成员enemyTank - 编写方法isTouchEnemyTank(),判断当前敌人坦克是否和enemyTanks中的其他坦克发生了重叠或碰撞
- 在run方法中,在根据坦克的方法来继续移动的判断条件中,调用isTouchEnemyTank方法,如果返回值不为true,则可以继续运行
package li.TankGame.version06; import java.util.Vector; public class EnemyTank extends Tank implements Runnable { //在敌人坦克类使用Vector保存多个shot Vector<Shot> shots = new Vector<>(); boolean isLive = true; //增加一个成员,EnemyTank 可以得到敌人坦克成员的Vector,用于循环比较是否重叠 Vector<EnemyTank> enemyTanks = new Vector<>(); public EnemyTank(int x, int y) { super(x, y); } //这里提供一个方法,可以将MyPanel的成员 Vector<EnemyTank> enemyTanks = new Vector<>(); // 设置到Enemy 的成员enemyTank public void setEnemyTanks(Vector<EnemyTank> enemyTanks) { this.enemyTanks = enemyTanks; } /** * 编写方法,判断当前敌人坦克是否和enemyTanks中的其他坦克发生了重叠或碰撞 * 思路:读取一个坦克的坐标,在前进之前先将该坦克坐标与当前所有坦克的坐标依次比较, * 如果到达某个坦克的边缘则不能再前进,则更改方向前进 */ public boolean isTouchEnemyTank() { //判断当前敌人坦克(this)方向 switch (this.getDirect()) { case 0://向上 //让当前的this敌人坦克和其他所有的敌人坦克比较 for (int i = 0; i < enemyTanks.size(); i++) { //Vector 中取出一个敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和自己比较 if (enemyTank != this) { //如果敌人坦克是上/下方向 /** * 情况1.this坦克向上,如果敌人的坦克是上/下方向 * 那么敌人坦克x的范围是[enemyTank.getX(),enemyTank.getX()+40] * 敌人坦克y的范围是[enemyTank.getY(),enemyTank.getY()+60] * */ if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) { //this坦克的左上角的坐标(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;//如果进入左上角了该范围,说明发生了碰撞,返回true } //this坦克的右上角的坐标(this.getX()+40,this.getY()) if (this.getX() + 40 >= enemyTank.getX() && this.getX() + 40 <= enemyTank.getX() + 40 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 60) { return true;//如果右上角进入了该范围,说明发生了碰撞,返回true } } //如果敌人坦克是右/左方向 /** * 情况2.this坦克向上,如果敌人的坦克是右/左方向 * 那么敌人坦克x的范围是[enemyTank.getX(),enemyTank.getX()+60] * 敌人坦克y的范围是[enemyTank.getY(),enemyTank.getY()+40] * */ if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) { //this坦克的左上角的坐标(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;//如果进入左上角了该范围,说明发生了碰撞,返回true } //this坦克的右上角的坐标(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;//如果右上角进入了该范围,说明发生了碰撞,返回true } } } } break; case 1://向右 //让当前的this敌人坦克和其他所有的敌人坦克比较 for (int i = 0; i < enemyTanks.size(); i++) { //Vector 中取出一个敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和自己比较 if (enemyTank != this) { //如果敌人坦克是上/下方向 /** * 情况3.this坦克向右,如果敌人的坦克是上/下方向 * 那么敌人坦克x的范围是[enemyTank.getX(),enemyTank.getX()+40] * 敌人坦克y的范围是[enemyTank.getY(),enemyTank.getY()+60] * */ if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) { //this坦克的右上角的坐标(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;//如果进入右上角了该范围,说明发生了碰撞,返回true } //this坦克的右下角的坐标(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;//如果右下角进入了该范围,说明发生了碰撞,返回true } } //如果敌人坦克是右/左方向 /** * 情况4.this坦克向右,如果敌人的坦克是右/左方向 * 那么敌人坦克x的范围是[enemyTank.getX(),enemyTank.getX()+60] * 敌人坦克y的范围是[enemyTank.getY(),enemyTank.getY()+40] * */ if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) { //this坦克的右上角的坐标(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;//如果进入右上角了该范围,说明发生了碰撞,返回true } //this坦克的右下角的坐标(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;//如果右下角进入了该范围,说明发生了碰撞,返回true } } } } break; case 2://向下 for (int i = 0; i < enemyTanks.size(); i++) { //Vector 中取出一个敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和自己比较 if (enemyTank != this) { //如果敌人坦克是上/下方向 /** * 情况5.this坦克向下,如果敌人的坦克是上/下方向 * 那么敌人坦克x的范围是[enemyTank.getX(),enemyTank.getX()+40] * 敌人坦克y的范围是[enemyTank.getY(),enemyTank.getY()+60] * */ if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) { //this坦克的左下角的坐标(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;//如果进入左下角了该范围,说明发生了碰撞,返回true } //this坦克的右下角的坐标(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;//如果右下角进入了该范围,说明发生了碰撞,返回true } } //如果敌人坦克是右/左方向 /** * 情况6.this坦克向下,如果敌人的坦克是右/左方向 * 那么敌人坦克x的范围是[enemyTank.getX(),enemyTank.getX()+60] * 敌人坦克y的范围是[enemyTank.getY(),enemyTank.getY()+40] * */ if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) { //this坦克的左下角的坐标(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;//如果进入左下角了该范围,说明发生了碰撞,返回true } //this坦克的右下角的坐标(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;//如果右下角进入了该范围,说明发生了碰撞,返回true } } } } break; case 3://向左 for (int i = 0; i < enemyTanks.size(); i++) { //Vector 中取出一个敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和自己比较 if (enemyTank != this) { //如果敌人坦克是上/下方向 /** * 情况7.this坦克向左,如果敌人的坦克是上/下方向 * 那么敌人坦克x的范围是[enemyTank.getX(),enemyTank.getX()+40] * 敌人坦克y的范围是[enemyTank.getY(),enemyTank.getY()+60] * */ if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) { //this坦克的左上角的坐标(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;//如果进入左上角了该范围,说明发生了碰撞,返回true } //this坦克的左下角的坐标(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;//如果左下角进入了该范围,说明发生了碰撞,返回true } } //如果敌人坦克是右/左方向 /** * 情况8.this坦克向左,如果敌人的坦克是右/左方向 * 那么敌人坦克x的范围是[enemyTank.getX(),enemyTank.getX()+60] * 敌人坦克y的范围是[enemyTank.getY(),enemyTank.getY()+40] * */ if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) { //this坦克的左上角的坐标(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;//如果进入左下角了该范围,说明发生了碰撞,返回true } //this坦克的左下角的坐标(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;//如果右下角进入了该范围,说明发生了碰撞,返回true } } } } break; } return false; } @Override public void run() { while (true) { //这我们先判断当前的坦克是否存活 // 在判断shots.size<3是否真,为真,说明当前的3颗子弹已经消亡了, // 就创建一颗子弹,放到shots集合中,并启动线程 if (isLive && (shots.size() < 3)) {//可以通过控制数字来修改敌人坦克一次发射几颗子弹 Shot s = null; //判断坦克的方创建对应的子弹 switch (getDirect()) { 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(); } //根据坦克的方法来继续移动 switch (getDirect()) { case 0://上 //让坦克保持一个方向走50步 for (int i = 0; i < 50; i++) { if (getY() > 0 && !isTouchEnemyTank()) { moveUp(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 1://右 //让坦克保持一个方向走50步 for (int i = 0; i < 50; i++) { if (getX() + 60 < 700 && !isTouchEnemyTank()) {//700为面板宽度 moveRight();//走一步 } try { Thread.sleep(50);//每走一步就休眠50毫秒 } catch (InterruptedException e) { e.printStackTrace(); } } break; case 2://下 for (int i = 0; i < 50; i++) { if (getY() + 60 < 550 && !isTouchEnemyTank()) {//550为面板宽度 moveDown(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 3://左 for (int i = 0; i < 50; i++) { if (getX() > 0 && !isTouchEnemyTank()) { moveLeft(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; } //随机地改变坦克的方向 0-3 setDirect((int) (Math.random() * 4));//[0,4)的取整 //如果被击中了,就退出线程 if (!isLive) { break;//退出线程 } } } }
8.1.2.2修改处2
在MyPanel类中的MyPanel()方法,初始化敌人坦克时,将enemyTanks集合设置给 enemyTank
//将enemyTanks集合设置给 enemyTank enemyTank.setEnemyTanks(enemyTanks);
可以看到,两个坦克移动到相邻的时候不再发生碰撞/重叠:

8.2记录玩家的成绩,存盘退出
8.2.1思路
创建Recorder类,记录我方击毁敌方坦克的数量。

8.2.2代码实现
8.2.2.1修改处1
在MyPanel类中的paint方法前增加一个方法,用来显示信息
/** * 编写方法,显示我方击毁敌方坦克的信息 */ public void showInfo(Graphics g) { //画出 玩家的总成绩 g.setColor(Color.BLACK);//设置画笔颜色 Font font = new Font("宋体", Font.BOLD, 20); g.setFont(font); g.drawString("您累计击毁敌方坦克", 730, 30);//画出“您累计击毁敌方坦克”的字样 drawTank(760, 55, g, 0, 0);//画一个敌方坦克图案 g.setColor(Color.BLACK);//重新设置画笔颜色 g.drawString(Recorder.getAllEnemyTankNum()+"", 850, 95); }
8.2.2.2修改处2
在paint方法中调用showInfo方法:

8.2.2.3修改处3
新增Recorder类:
package li.TankGame.version06; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; /** * @author 李 * @version 6.0 * 该类用于记录相关信息,和文件交互 */ public class Recorder { //定义变量,记录我方击毁敌人坦克数 private static int allEnemyTankNum = 0; //定义IO对象,用于写入到文件中 private static BufferedWriter bw = null;//处理流 private static String recordFile = "d:\\myRecord.txt";//记录文件的路径 public static int getAllEnemyTankNum() { return allEnemyTankNum; } public static void setAllEnemyTankNum(int allEnemyTankNum) { Recorder.allEnemyTankNum = allEnemyTankNum; } //当我方击毁一辆敌人坦克时,就应该allEnemyTankNum++ public static void addAllEnemyTankNum() { Recorder.allEnemyTankNum++; } //增加一个方法,当游戏退出时,将allEnemyTankNum保存到myRecord.txt文件中 public static void keepRecord() { try { bw = new BufferedWriter(new FileWriter(recordFile)); bw.write(allEnemyTankNum + "\n"); } catch (IOException e) { e.printStackTrace(); } finally { try { if (bw != null) { bw.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
8.2.2.4修改处4
在TankGame06类中的构造器TankGame06方法中,增加相应关闭窗口的处理(在关闭窗口时进行数据存储)
public TankGame06(){ mp = new MyPanel(); //将mp放入到Thread,并启动 Thread thread = new Thread(mp); thread.start(); this.add(mp);//把面板(就是游戏的绘图区域)添加进来 this.setSize(950,600);//设置大小 this.addKeyListener(mp);//让JFrame监听mp的键盘事件 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击窗口的叉时停止运行 this.setVisible(true);//设置显示 //在JFrame中增加相应关闭窗口的处理 this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { Recorder.keepRecord(); System.exit(0); } }); }


PS:这里在MyPanel类中的hitTank方法中,发现之前子弹击中敌坦克的判断条件有误,产生了坦克向左边打其他坦克会瞬间爆炸的bug
修正250行、268行后:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!