day07-IO流应用02
Java坦克大战07
8.IO流应用02
8.3记录退出游戏时敌人坦克坐标/方向,存盘退出
8.3.1思路分析
在Recorder类中,增加一个Vector集合,用来接收从MyPanel类中传入的enemyTanks集合,在记录时遍历集合,将还存活的敌人坦克的方向和坐标逐一取出并保存
8.3.2代码实现
修改处1
Recorder类:增加属性enemyTanks、增加方法setEnemyTanks、修改keepRecord方法:
//定义Vector,指向MyPanel对象的敌人坦克的Vector
private static Vector<EnemyTank> enemyTanks = null;
public static void setEnemyTanks(Vector<EnemyTank> enemyTanks) {
Recorder.enemyTanks = enemyTanks;
}
public static void keepRecord() {
try {
bw = new BufferedWriter(new FileWriter(recordFile));
bw.write(allEnemyTankNum + "
");
//遍历敌人坦克的Vector,根据情况保存即可
for (int i = 0; i < enemyTanks.size(); i++) {
//取出敌人坦克
EnemyTank enemyTank = enemyTanks.get(i);
if (enemyTank.isLive) {//虽然被击中的坦克对象已经被删除了,但是还是建议判断一下
//保存该enemyTank信息
String record = enemyTank.getX()+" "+enemyTank.getY()+" "+enemyTank.getDirect();
//写入到文件中
bw.write(record+"
");
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bw != null) {
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
修改处2
在MyPanel类中的MyPanel方法,调用Recorder的静态方法setEnemyTanks,将敌人坦克的集合enemyTanks传到Recorder类中
ps:引用类型传递的是地址,地址不会变化,因此可以获取到集合的最新的状态
关闭时:
记录状态:
8.4玩游戏时可以选择是开新游戏还是继续上局游戏
8.4.1思路分析
将每个敌人信息恢复(读取)成Node对象,再放到vector里面去,通过Node的vector去初始化敌人坦克的位置和方向
8.4.2代码实现
修改处1
新建一个Node类:
package li.TankGame.version06;
/**
* @author 李
* @version 6.0
* 一个Node对象表示一个敌人坦克的信息
*/
public class Node {
private int x ;
private int y ;
private int direct ;
public Node(int x, int y, int direct) {
this.x = x;
this.y = y;
this.direct = direct;
}
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 getDirect() {
return direct;
}
public void setDirect(int direct) {
this.direct = direct;
}
}
修改处2
在Recorder类中:
创建一个BufferedReader对象:
private static BufferedReader br = null;//输入处理流
定义一个Node的Vector对象,用于保存敌人的信息node:
//定义一个Node的Vector对象,用于保存敌人的信息node
private static Vector<Node> nodes = new Vector<>();
增加getNodesAndEnemyTankRec方法,用于读取recordFile,恢复相关信息:
//增加一个方法,用于读取recordFile,恢复相关信息
//该方法在点击继续上局的时候调用
public static Vector<Node> getNodesAndEnemyTankRec() {
try {
br = new BufferedReader(new FileReader(recordFile));
//读取上局击毁坦克数量
allEnemyTankNum = Integer.parseInt(br.readLine());
//循环读取文件,生成nodes集合
String line = "";
while ((line = br.readLine()) != null) {
//用空格将每一行的数据分割,分割完的字符数组里面存储了一组敌方坦克的x,y,direct
String[] xyd = line.split(" ");
//将字符串转为int类型,赋值给node对象
Node node = new Node(Integer.parseInt(xyd[0]),
Integer.parseInt(xyd[1]), Integer.parseInt(xyd[2]));
//将该node对象放到nodes集合中
nodes.add(node);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return nodes;
}
修改处3
在MyPanel类中:
定义一个存放Node对象的Vector,用于恢复敌人坦克的坐标和方向:
//定义一个存放Node对象的Vector,用于恢复敌人坦克的坐标和方向
Vector<Node> nodes = new Vector<>();
修改MyPanel方法,修改了方法传入的参数String key;
在方法里面调用了getNodesAndEnemyTankRec,将其返回的nodes集合传给MyPanel类的nodes的集合;
使用switch,根据输入的key判断是新开一局还是接着上一局游戏;
public MyPanel(String key) {
nodes = Recorder.getNodesAndEnemyTankRec();
//将MyPanel对象的enemyTanks 设置给Recorder的enemyTanks
Recorder.setEnemyTanks(enemyTanks);
hero = new Hero(400, 200);//初始化自己的坦克
//hero.setSpeed(5); //改变坦克的速度
switch (key){
case "1": //开新游戏
Recorder.setAllEnemyTankNum(0);//重设击毁敌方坦克数目
//初始化敌人的坦克
for (int i = 0; i < enemyTankNum; i++) {
//创建一个敌人的坦克
EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0);
//将enemyTanks集合设置给 enemyTank
enemyTank.setEnemyTanks(enemyTanks);
//初始化敌人坦克方向向下
enemyTank.setDirect(2);
//启动敌人坦克线程,让他动起来
new Thread(enemyTank).start();
//给该enemyTank加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());
//将该子弹加入到enemyTank的Vector集合中
enemyTank.shots.add(shot);
//启动 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());
//将enemyTanks集合设置给 enemyTank
enemyTank.setEnemyTanks(enemyTanks);
//初始化敌人坦克方向向下
enemyTank.setDirect(node.getDirect());
//启动敌人坦克线程,让他动起来
new Thread(enemyTank).start();
//给该enemyTank加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());
//将该子弹加入到enemyTank的Vector集合中
enemyTank.shots.add(shot);
//启动 shot对象
new Thread(shot).start();
//将设置好的的敌人坦克放入到集合中
enemyTanks.add(enemyTank);
}
break;
default:
System.out.println("输入有误");
}
//初始化图片对象
image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb1.png"));
image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb2.png"));
image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb3.png"));
}
修改处4
在TankGame06类中新建一个Scanner对象:
static Scanner scanner = new Scanner(System.in);
修改TankGame06构造器,是在控制台输入的选择传到MyPanel构造器中:
public TankGame06() {
System.out.println("请输入选择:
" + "1:新游戏 2:继续上局");
String key = scanner.next();
mp = new MyPanel(key);
//将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:这里截图不及时,实际上恢复的位置与上局一致
坦克大战7.0版
增加功能:
- 游戏开始时,播放音乐
- 修正下文件存储位置
- 处理文件相关异常
8.5游戏开始时,播放音乐
思路
新建一个播放音乐的类
修改处1
在网上找到相关音乐的.wav文件(注意一定要是wav,自己改的缀有可能会出现不能播放的情况),将其粘贴到项目的src文件的根目录下面
修改处2
新建一个播放音乐的类
package li.TankGame.version07;
import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
public class AePlayWave extends Thread {
private String filename;
public AePlayWave(String wavfile) { //构造器 , 指定文件
filename = wavfile;
}
public void run() {
File soundFile = new File(filename);
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
} 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();
}
}
}
修改处3
在MyPanel类中的构造器MyPanel中,在最后一行启动线程
//这里播放指定的音乐
new AePlayWave("src\111.wav").start();
8.6修正下文件存储位置
把Recorder类中的记录文件修改为:保存到src目录下
8.7处理文件相关异常
8.7.1异常情况
在还没有文件记录的时候,如果我们选择“继续上局”游戏的话,就会出现异常。因为文件中没有记录,MyPanel中的nodes集合也就得不到数据,会出现一个敌人坦克都没有的情况:
8.7.2修改方法
修改处1
在Recorder类中增加getRecordFile方法:
//返回记录文件的路径
public static String getRecordFile() {
return recordFile;
}
修改处2
在MyPanel类中的MyPanel方法的最开始,添加判断方法:
先判断记录文件是否存在,如果存在就正常执行,若不存在,就提示只能开新新游戏,将 key 置为 1
//先判断记录文件是否存在,如果存在就正常执行,若不存在,就提示只能开新新游戏,将 key 置为 1
File file = new File(Recorder.getRecordFile());
if (file.exists()) {
nodes = Recorder.getNodesAndEnemyTankRec();
} else {
System.out.println("没有存档记录,只能开启新游戏!");
key = "1";
}
修改处3
顺便把子弹类中子弹位置的提示信息删除:
运行截图: