zl程序教程

您现在的位置是:首页 >  APP

当前栏目

day07-IO流应用02

2023-03-31 10:52:25 时间

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类中

image-20220915160450882

ps:引用类型传递的是地址,地址不会变化,因此可以获取到集合的最新的状态

关闭时:

image-20220915160844262

记录状态:

image-20220915160923441

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);
        }
    });

}

退出时:

继续上局:

image-20220915172402336 image-20220915172437378

ps:这里截图不及时,实际上恢复的位置与上局一致

坦克大战7.0版

增加功能:

  1. 游戏开始时,播放音乐
  2. 修正下文件存储位置
  3. 处理文件相关异常

8.5游戏开始时,播放音乐

思路

新建一个播放音乐的类

修改处1

在网上找到相关音乐的.wav文件(注意一定要是wav,自己改的缀有可能会出现不能播放的情况),将其粘贴到项目的src文件的根目录下面

image-20220915181105962
修改处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目录下

image-20220915190120400

image-20220915190312656

8.7处理文件相关异常

8.7.1异常情况

在还没有文件记录的时候,如果我们选择“继续上局”游戏的话,就会出现异常。因为文件中没有记录,MyPanel中的nodes集合也就得不到数据,会出现一个敌人坦克都没有的情况:

image-20220915190911880 image-20220915191115844

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";
}

image-20220915191927638

修改处3

顺便把子弹类中子弹位置的提示信息删除:

image-20220915192601634

运行截图:

image-20220915192438694 image-20220915192459030