Java坦克大战06

8.IO流应用01

坦克大战6.0版

增加功能:

  1. 防止敌人坦克重叠运动
  2. 记录玩家的成绩(累计击毁坦克数),存盘退出
  3. 记录当时的敌人坦克坐标,存盘退出
  4. 玩游戏时,可以选择是开新游戏还是继续上局游戏

8.1防止敌人坦克重叠运动

8.1.1思路分析

按照目标坦克的向右下左四种情况分析,每一种情况又分为两种小情况,一种八种情况。

8.1.2代码实现

8.1.2.1修改处1

在EnemyTank类中:

  1. 增加了增加一个成员属性,EnemyTank 可以得到敌人坦克成员的 Vector,用于循环比较是否重叠
  2. 新增一个方法setEnemyTanks,可以将MyPanel的成员 Vector enemyTanks = new Vector<>() 设置到Enemy 的成员enemyTank
  3. 编写方法isTouchEnemyTank(),判断当前敌人坦克是否和enemyTanks中的其他坦克发生了重叠或碰撞
  4. 在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行后:

day06-IO流应用01的更多相关文章

  1. Java基础知识强化之IO流笔记01:异常的概述和分类

     IO流操作的时候会出现很多问题,java中叫作异常,所以我们先介绍一下异常: 1. 程序的异常:Throwable(Throwable类是java中所有异常或错误的超类) (1)严重问题:Error ...

  2. java——IO流01

    移动文件有一种简单方法,不需要复制文件再删除文件. package com.unir.test01; import java.io.File; import java.io.IOException; ...

  3. IO流(字节流复制)01

    package ioDemo; import java.io.*; /** * IO流(字节流复制) * Created by lcj on 2017/11/2. */ public class bu ...

  4. 01 语言基础+高级:1-8 File类与IO流_day10【缓冲流、转换流、序列化流】

    day10[缓冲流.转换流.序列化流] 主要内容 缓冲流 转换流 序列化流 打印流 教学目标 能够使用字节缓冲流读取数据到程序 能够使用字节缓冲流写出数据到文件 能够明确字符缓冲流的作用和基本用法 能 ...

  5. 01 语言基础+高级:1-8 File类与IO流_day09【字节流、字符流】

    day09[字节流.字符流] 主要内容 IO流 字节流 字符流 异常处理 Properties 教学目标 能够说出IO流的分类和功能 能够使用字节输出流写出数据到文件 能够使用字节输入流读取数据到程序 ...

  6. java IO流详解

    流的概念和作用 学习Java IO,不得不提到的就是JavaIO流. 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

  7. IO流01--毕向东JAVA基础教程视频学习笔记

    提要 01 IO流(BufferedWriter)02 IO流(BufferedReader)03 IO流(通过缓冲区复制文本文件)04 IO流(readLine的原理)05 IO流(MyBuffer ...

  8. Java IO流详尽解析

    流的概念和作用 学习Java IO,不得不提到的就是JavaIO流. 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

  9. 【转载】JAVA IO 流的总结

    来自http://www.cnblogs.com/oubo/archive/2012/01/06/2394638.html,写的很详细 Java流操作有关的类或接口: Java流类图结构: 流的概念和 ...

  10. JavaSE_ IO流 总目录(19~22)

    JavaSE学习总结第19天_IO流119.01 集合的特点和数据结构总结19.02 如何选择使用哪种集合19.03 集合常见功能和遍历方式总结19.04 异常的概述和分类19.05 JVM默认处理异 ...

随机推荐

  1. 【STL源码剖析】vector类模拟实现 了解底层-走进底层-掌握底层【超详细的注释和解释】

    今天博主继续带来STL源码剖析专栏的第二篇博客了! 今天带来vector的模拟实现! 其实在很多人学习C++过程中,都是只学习一些STL的使用方式,并不了解底层的实现.博主本人认为,这样的学习这样的技 ...

  2. 【可观测性系列】 OpenTelemetry Collector的部署模式分析

    作者简介:大家好,我是蓝胖子 ️博客首页:主页蓝胖子的编程梦 ️热门专题:我的服务监控实践 ,500行代码手写Docker **每日一句:白日莫闲过,青春不再来 大家好,我是蓝胖子,在前面我介绍了下O ...

  3. 洛谷P1308统计单词数,strtok函数的使用以及对于单词分割的一些思考

    [NOIP2011 普及组] 统计单词数 题目描述 一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数. 现在,请你编程实现这一功能 ...

  4. MySQL-面试知识点汇总

    1. DQL相关 2. DDL.DML.DCL相关 3.架构相关 3.1 MySQL的复制原理以及流程 主从复制:将主数据库中的DDL和DML操作通过二进制日志(BINLOG)传输到从数据库上,然后将 ...

  5. 迟来的HIT2024和realworld2024体验赛WP

    目录 前言碎语 2024.2.14 中午 rwctf2024 体验赛 vision 哈工大青训营2024 结营赛 计算器 小技巧 神奇玩意 gdb! 再也不用苦哈哈往回翻 跟踪fork 赛后复现rw ...

  6. SSD 简介—— NAND 芯片介绍

    制作 存储芯片的制作和其他芯片制作大致相同,从沙子中提取单晶硅制作晶圆再封装芯片. 闪存芯片从架构上分为NOR和NAND NOR Flash的source line把每个cell都并联起来,而NAND ...

  7. Encrypt or Decrypt sensitive data using PLSQL - DBMS_CRYPTO

    Oracle 10g introduced Transparent Data Encryption, which is about storing data physically as encrypt ...

  8. Java集合框架学习(十一) Hashtable详解

    Hashtable介绍 1. 继承自Dictionary; 2. 线程安全: 3. 支持Iterator和Enumeration: 4. key和value都不可为null; 5. 一般用于多线程环境 ...

  9. win32 - 虚拟内存的一些介绍

    对32位Windows来说,其虚拟地址空间总数就是2的32次方,即4GB. 如果没有在引导时加上/3GB或/BOOTVA选项,Windows默认最大会分2GB给内核模式程序使用,2GB给用户模式程序. ...

  10. 前后端分离解决跨域cors问题

    修改windows的hosts文件 vim C:\Windows\System32\drivers\etc\hosts 添加域名 前端:www.luffycity.cn 后端:api.luffycit ...