Java坦克大戰06 8.IO流應用01 坦克大戰6.0版 增加功能: 防止敵人坦克重疊運動 記錄玩家的成績(累計擊毀坦克數),存檔退出 記錄當時的敵人坦克坐標,存檔退出 玩游戲時,可以選擇是開新游戲還是繼續上局游戲 8.1防止敵人坦克重疊運動 8.1.1思路分析 按照目標坦克的向右下左四種情況分析 ...
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:這裡發現之前子彈擊中敵坦克的判斷條件有誤,修正了坦克向左邊打其他坦克會瞬間爆炸的bug
修正250行、268行後: