day07-IO流應用02

来源:https://www.cnblogs.com/liyuelian/archive/2022/09/15/16697374.html
-Advertisement-
Play Games

Java坦克大戰07 8.IO流應用02 8.3記錄退出游戲時敵人坦克坐標/方向,存檔退出 8.3.1思路分析 在Recorder類中,增加一個Vector集合,用來接收從MyPanel類中傳入的enemyTanks集合,在記錄時遍歷集合,將還存活的敵人坦克的方向和坐標逐一取出並保存 8.3.2代碼 ...


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 + "\n");
        //遍歷敵人坦克的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+"\n");
            }
        }
    } 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("請輸入選擇:\n" + "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
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 安全是產品的底座,是體驗的基礎,也是企業的一項核心競爭力。安全生產是一項系統性的工作,同時也是一件比較瑣碎的事,需要做方方面面的考慮盡一切可能保障系統安全穩定運行。個人之前一直負責商品的穩定性工作,在這方面有比較多的經歷和實踐。 記得在18年的時候,我們做商品發佈的組件化改造,當時正好碰上網站剛開... ...
  • 我的設計模式之旅,使用 Golang 實現。本節實現策略模式與簡單工廠。編程旅途是漫長遙遠的,在不同時刻有不同的感悟,本文會一直更新下去。 ...
  • 有時候,我們明明在類或者方法上添加了@Transactional註解,卻發現方法並沒有按事務處理。其實,以下場景會導致Spring的@Transactional事務失效。 1、事務方法所在的類沒有載入到Spring IOC容器中。 @Transactional是Spring的註解,未被Spring管 ...
  • 個人博客:槿蘇的知識鋪 一、什麼是自動裝配 SpringBoot 定義了一套介面規範,這套規範規定:SpringBoot在啟動時會掃描外部引用jar包中的META-INF/spring.factories文件,將文件中配置的類型信息載入到Spring容器,並執行類中定義的各種操作。對於外部jar包來 ...
  • 真是一本透著編程思想的書。 上面的書讓你從微觀角度瞭解 Java,而這本書則可以讓你從一個巨集觀角度瞭解 Java。 這本書和 Java 核心技術的厚度差不多,但這本書的信息密度比較大。 所以,讀起來是非常耗大腦的,因為它會讓你不斷地思考。 對於想學好 Java 的程式員來說,這是一本必讀的書。 -... ...
  • 上一篇我們學習了多對一的處理,這次我們來學習一對多的處理。 一對多的處理與多對一的處理差別不大,只是有一些細微的地方需要註意。 我們還是先做準備工作,其他部分與多對一的準備工作相同,僅實體類構建需要做出改變。 一、修改實體類 Student類: package com.jms.pojo; publi ...
  • 前因後果 公司新來的小姐姐,超級喜歡看漫畫,天天給我介紹,好煩~ 現在是2022年9月15日16點30,於是我決定, 五點下班前寫個代碼把她說的漫畫全部爬下來,應付一下~ 再發篇文章揭露她的罪惡,嘿嘿~ 準備事項 環境使用 Python 3.8 Pycharm 2021.2版本 模塊使用 impor ...
  • 現在我們就開始更加深入的學習了,今天我們要學習的是多對一的處理。 在正式開始之前我們需要做一些準備工作。 一、在資料庫建立兩張新的表並插入數據 CREATE TABLE `teacher` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NUL ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...