视频URL:【零基础 快速学Java】韩顺平 零基础30天学会Java_哔哩哔哩_bilibili
AePlayWave(添加了判断音乐是否播放完毕)
package com.sunl19ht.tankgame6;
import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
public class AePlayWave extends Thread {
private String filename;
private volatile boolean stopFlag; // 添加一个标志来表示是否停止播放
public AePlayWave(String wavfile) { //构造器 , 指定文件
filename = wavfile;
stopFlag = false; // 初始化为false,表示没有停止播放
}
private LineListener lineListener; // 添加一个LineListener成员变量
public void setLineListener(LineListener listener) {
this.lineListener = listener;
}
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();
}
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();
if (lineListener != null) {
// 触发LineListener的update()方法,通知音频播放完毕
LineEvent event = new LineEvent(auline, LineEvent.Type.STOP, auline.getFramePosition());
lineListener.update(event);
}
}
}
// 新增的停止音乐的方法
public void stopMusic() {
stopFlag = true; // 设置停止标志为true,让音乐播放线程结束
}
}
Bomb(爆炸效果)
package com.sunl19ht.tankgame6;
public class Bomb {
int x, y;
int left = 9; // 炸弹的生命周期
boolean isLive = true; // 是否存活
public Bomb(int x, int y) {
this.x = x;
this.y = y;
}
// 减少生命值
public void liftDown() {
if (left > 0) {
left--;
} else {
isLive = false;
}
}
}
EnemyTank敌人坦克
package com.sunl19ht.tankgame6;
import java.util.Vector;
public class EnemyTank extends Tank implements Runnable{
// 在敌人坦克类使用Vector 保存多个Shot
Vector<Shot> shots = new Vector<>();
// 增加成员 敌人坦克可以得到敌人坦克的Vector
// 1. Vector<EnemyTank>
Vector<EnemyTank> enemyTanks = new Vector<>();
boolean isLive = true;
public EnemyTank(int x, int y) {
super(x, y);
}
// 提供一个方法 可以将MyPanel的成员Vector<EnemyTank> enemyTanks = new Vector<>();
// 设置到EnemyTank的一个属性enemyTanks
public void setEnemyTanks(Vector<EnemyTank> enemyTanks) {
this.enemyTanks = enemyTanks;
}
// 编写方法 判断当前的敌人坦克是否和enemyTank中的其他坦克是否发生重叠
public boolean isTouchEnemyTank() {
// 判断当前敌人坦克方向
switch (this.getDirect()) {
case 0: // 上
// 让当前的敌人坦克和其他所有的敌人坦克进行比较
for (int i = 0; i < enemyTanks.size(); i++) {
// 从Vector中取出一个坦克
EnemyTank enemyTank = enemyTanks.get(i);
// 不和自己比较
if (enemyTank != this) {
// 如果敌人坦克是 上/下
// 1. 如果敌人坦克是上下 x[enemyTank.getX(), enemyTank.getX() + 40]
// y[enemyTank.getY(), enemyTank.getY() + 60]
if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) {
// 2. 当前坦克 左上角坐标[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;
}
// 3. 当前坦克 右上角坐标[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;
}
}
// 如果敌人坦克是 右/左
// 1. 如果敌人坦克是上左右 x[enemyTank.getX(), enemyTank.getX() + 60]
// y[enemyTank.getY(), enemyTank.getY() + 40]
if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) {
// 2. 当前坦克 左上角坐标[this.getX(), this.getY()]
// 3. 当前坦克 右上角坐标[this.getX() + 40, this.getY()]
if (this.getX() >= enemyTank.getX()
&& this.getX() <= enemyTank.getX() + 60
&& this.getY() >= enemyTank.getY()
&& this.getY() <= enemyTank.getY() + 40) {
return true;
}
if (this.getX() + 40 >= enemyTank.getX()
&& this.getX() + 40 <= enemyTank.getX() + 60
&& this.getY() >= enemyTank.getY()
&& this.getY() <= enemyTank.getY() + 40) {
return true;
}
}
}
}
break;
case 1: // 右
// 让当前的敌人坦克和其他所有的敌人坦克进行比较
for (int i = 0; i < enemyTanks.size(); i++) {
// 从Vector中取出一个坦克
EnemyTank enemyTank = enemyTanks.get(i);
// 不和自己比较
if (enemyTank != this) {
// 如果敌人坦克是 上/下
// 1. 如果敌人坦克是上下 x[enemyTank.getX(), enemyTank.getX() + 40]
// y[enemyTank.getY(), enemyTank.getY() + 60]
if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) {
// 2. 当前坦克 右上角坐标[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;
}
// 3. 当前坦克 右下角坐标[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;
}
}
// 如果敌人坦克是 右/左
// 1. 如果敌人坦克是上左右 x[enemyTank.getX(), enemyTank.getX() + 60]
// y[enemyTank.getY(), enemyTank.getY() + 40]
if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) {
// 2. 当前坦克 右上角坐标[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;
}
// 3. 当前坦克 右下角坐标[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;
}
}
}
}
break;
case 2: // 下
// 让当前的敌人坦克和其他所有的敌人坦克进行比较
for (int i = 0; i < enemyTanks.size(); i++) {
// 从Vector中取出一个坦克
EnemyTank enemyTank = enemyTanks.get(i);
// 不和自己比较
if (enemyTank != this) {
// 如果敌人坦克是 上/下
// 1. 如果敌人坦克是上下 x[enemyTank.getX(), enemyTank.getX() + 40]
// y[enemyTank.getY(), enemyTank.getY() + 60]
if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) {
// 2. 当前坦克 左下角坐标[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;
}
// 3. 当前坦克 右下角坐标[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;
}
}
// 如果敌人坦克是 右/左
// 1. 如果敌人坦克是上左右 x[enemyTank.getX(), enemyTank.getX() + 60]
// y[enemyTank.getY(), enemyTank.getY() + 40]
if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) {
// 2. 当前坦克 右上角坐标[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;
}
// 3. 当前坦克 右下角坐标[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;
}
}
}
}
break;
case 3: // 左
// 让当前的敌人坦克和其他所有的敌人坦克进行比较
for (int i = 0; i < enemyTanks.size(); i++) {
// 从Vector中取出一个坦克
EnemyTank enemyTank = enemyTanks.get(i);
// 不和自己比较
if (enemyTank != this) {
// 如果敌人坦克是 上/下
// 1. 如果敌人坦克是上下 x[enemyTank.getX(), enemyTank.getX() + 40]
// y[enemyTank.getY(), enemyTank.getY() + 60]
if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) {
// 2. 当前坦克 左上角坐标[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;
}
// 3. 当前坦克 左下角坐标[this.getX() + 40, this.getY() + 60]
if (this.getX() >= enemyTank.getX()
&& this.getX() <= enemyTank.getX() + 40
&& this.getY() + 40 >= enemyTank.getY()
&& this.getY() + 40 <= enemyTank.getY() + 60) {
return true;
}
}
// 如果敌人坦克是 右/左
// 1. 如果敌人坦克是上左右 x[enemyTank.getX(), enemyTank.getX() + 60]
// y[enemyTank.getY(), enemyTank.getY() + 40]
if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) {
// 2. 当前坦克 右上角坐标[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;
}
// 3. 当前坦克 右下角坐标[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;
}
}
}
}
break;
}
return false;
}
@Override
public void run() {
while (true) {
// 如果shots size() == 0
// 如果集合里的子弹没有了 那么就重新创建一个子弹
if(isLive && shots.size() == 0) {
// 判断坦克方向 创建对应子弹
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: // 上
// 让坦克保持一个方向走30步
for (int i = 0; i < 30; i++) {
if (getY() > 0 && !isTouchEnemyTank()) {
moveUp();
}
// 休眠50毫米
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
case 1: // 右
for (int i = 0; i < 30; i++) {
if (getX() + 60 < 1000 && !isTouchEnemyTank()) {
moveRight();
}
// 休眠50毫米
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
case 2: // 下
for (int i = 0; i < 30; i++) {
if (getY() + 60 < 750 && !isTouchEnemyTank()) {
moveDown();
}
// 休眠50毫米
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
case 3: // 左
for (int i = 0; i < 30; i++) {
if (getX() > 0 && !isTouchEnemyTank()) {
moveLeft();
}
// 休眠50毫米
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
}
// 然后随机改变坦克方向
setDirect((int) (Math.random() * 4)); // [0 - 4)
// 一旦写并发程序 一定要考虑清楚该线程什么时候结束
if (!isLive) {
break; // 退出线程
}
}
}
}
MyPanel主界面
package com.sunl19ht.tankgame6;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.util.Vector;
// 为了监听键盘事件 要实现接口KeyListener
// 为了让Panel不停重绘子弹 需要将MyPanel实现Runnable 当作线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable{
// 坦克大战的绘图区
MyTank myTank = null;
// 定义敌人坦克 放入到Vector 因为后期要使用多线程 所以放入Vector集合
Vector<EnemyTank> enemyTanks = new Vector<>();
// 定义一个存放Node对象的Vector 用于恢复敌人坦克坐标和方向
Vector<Node> nodes = new Vector<>();
// 定义Vector 用与存放炸弹
// 当子弹击中坦克时 就加入一个Bomb对象到bombs
Vector<Bomb> bombs = new Vector<>();
int enemyTankSize = 3;
// 定义三张图片 用于显示爆炸效果
Image image1 = null;
Image image2 = null;
Image image3 = null;
// 定义背景音乐
AePlayWave sandinist = null;
public MyPanel(String key) {
// 先判断记录文件是否存在 如果存在就正常执行 如果不存在提示只能开新游戏 将key设置为1
File file = new File(Recorder.getRecordFile());
if (file.exists()) {
nodes = Recorder.getNodesAndEnemyTankNumRec();
} else {
System.out.println("文件不存在 只能开启新游戏");
key = "1";
}
// 将MyPanel对象的enemyTanks设置给Recorder对象的enemyTanks
Recorder.setEnemyTanks(enemyTanks);
myTank = new MyTank(500, 100); // 初始化自己的坦克
switch (key) {
case "1":
// 初始化敌人坦克
for (int i = 0; i < enemyTankSize; 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); // 控制每个坦克的间隔 100 像素分开
}
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); // 控制每个坦克的间隔 100 像素分开
}
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"));
// 播放指定音乐
AePlayWave BattleCity = new AePlayWave("src\\Battle City.wav");
BattleCity.start();
BattleCity.setLineListener(new LineListener() {
@Override
public void update(LineEvent event) {
if (event.getType() == LineEvent.Type.STOP) {
// 音乐播放完毕,执行相关操作
System.out.println("音乐播放完毕");
sandinist = new AePlayWave("src\\SANDINIST.wav");
sandinist.start();
}
}
});
}
// 编写方法 显示我击毁敌方坦克的信息
public void showInfo(Graphics g) {
// 画出玩家总成绩
g.setColor(Color.BLACK);
Font font = new Font("微软雅黑", Font.BOLD, 25);
g.setFont(font);
g.drawString("您累计击毁敌方坦克", 1020, 30);
drawTank(1020, 60, g, 0, 0); // 画出一个敌方坦克
g.setColor(Color.BLACK);
g.drawString(Recorder.getAllEnemyTankNum() + "", 1080, 100);
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 1000, 750);
showInfo(g);
if (myTank != null && myTank.isLive) {
// 填充矩形 默认黑色
// 绘制自己的坦克--> 方法
drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirect(), 1);
}
// 绘制MyTank发出射击的子弹
// if (myTank.shot != null && myTank.shot.isLive == true) {
// // g.fill3DRect(myTank.shot.x, myTank.shot.y, 10, 10, false);
// g.draw3DRect(myTank.shot.x, myTank.shot.y, 1, 1, false);
// }
for (int i = 0; i < myTank.shots.size(); i++) {
Shot shot = myTank.shots.get(i);
if (shot != null && shot.isLive == true) {
g.draw3DRect(shot.x, shot.y, 1, 1, false);
} else {
// 如果该shot对象已经无效 就从集合中去除这个对象
myTank.shots.remove(shot);
}
}
// 如果bombs集合中有对象 就绘制
for (int i = 0; i < bombs.size(); i++) {
// 取出炸弹
Bomb bomb = bombs.get(i);
// 根据当前这个bomb对象的炸弹值lift画出对应的图片
if (bomb.left > 6) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
g.drawImage(image1, bomb.x, bomb.y, 60, 60, this);
} else if(bomb.left > 3) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
g.drawImage(image2, bomb.x, bomb.y, 60, 60, this);
} else {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
g.drawImage(image3, bomb.x, bomb.y, 60, 60, this);
}
// 让炸弹生命值减少
bomb.liftDown();
// 如果bomb life为0 那就从bombs集合中删除
if (bomb.left == 0) {
bombs.remove(bomb);
}
}
// 画出敌人的坦克 遍历Vector
// enemyTanks.size() 因为坦克会被销毁
for (int i = 0; i < enemyTanks.size(); i++) {
// 取出坦克
EnemyTank enemyTank = enemyTanks.get(i);
// 判断当前坦克是否存活
if (enemyTank.isLive) { // 当敌人坦克存活才绘制坦克
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
// 画出enemyTank的子弹
for (int j = 0; j < enemyTank.shots.size(); j++) {
// 取出子弹
Shot shot = enemyTank.shots.get(j);
// 绘制
// 先判断子弹是否存活
if (shot.isLive) {
g.draw3DRect(shot.x, shot.y, 1, 1, false);
} else { // 子弹死亡 销毁子弹
// 移除子弹
enemyTank.shots.remove(shot);
}
}
}
}
}
// x y 坐标
// g画笔
// direct 方向
// type 类型
public void drawTank(int x, int y, Graphics g, int direct, int type) {
// 给不同类型的坦克设置颜色
switch (type) {
case 0: // 敌人的坦克
g.setColor(Color.cyan);
break;
case 1: // 我们的坦克
g.setColor(Color.yellow);
break;
}
// 绘制对应形状坦克
// 根据坦克的方向来绘制坦克
switch (direct) { // 0向上 1向右 2向下 3向左
case 0:
g.fill3DRect(x, y, 10, 60, false); // 坦克左边轮子
g.fill3DRect(x + 30, y, 10, 60, false); // 绘制右边的轮子
g.fill3DRect(x + 10, y + 10, 20, 40, false); // 中间驾驶舱
g.fillOval(x + 10, y + 20, 20, 20); // 画出驾驶舱上的圆型盖子
g.drawLine(x + 20, y + 30, x + 20, y); // 绘制炮筒
break;
case 1: // 向右
g.fill3DRect(x, y, 60, 10, false); // 坦克上边轮子
g.fill3DRect(x, y + 30, 60, 10, false); // 绘制下边的轮子
g.fill3DRect(x + 10, y + 10, 40, 20, false); // 中间驾驶舱
g.fillOval(x + 20, y + 10, 20, 20); // 画出驾驶舱上的圆型盖子
g.drawLine(x + 30, y + 20, x + 60, y + 20); // 绘制炮筒
break;
case 2:
g.fill3DRect(x, y, 10, 60, false); // 坦克左边轮子
g.fill3DRect(x + 30, y, 10, 60, false); // 绘制右边的轮子
g.fill3DRect(x + 10, y + 10, 20, 40, false); // 中间驾驶舱
g.fillOval(x + 10, y + 20, 20, 20); // 画出驾驶舱上的圆型盖子
g.drawLine(x + 20, y + 30, x + 20, y + 60); // 绘制炮筒
break;
case 3:
g.fill3DRect(x, y, 60, 10, false); // 坦克上边轮子
g.fill3DRect(x, y + 30, 60, 10, false); // 绘制下边的轮子
g.fill3DRect(x + 10, y + 10, 40, 20, false); // 中间驾驶舱
g.fillOval(x + 20, y + 10, 20, 20); // 画出驾驶舱上的圆型盖子
g.drawLine(x + 30, y + 20, x, y + 20); // 绘制炮筒
break;
default:
System.out.println("暂时没有处理");
}
}
// 如果我们的坦克可以发射多颗子弹
// 在判断我方子弹是否击中敌人坦克时 就需要把我们的子弹集合中所有的子弹都取出来和敌人的所有坦克进行判断
public void hitEnemyTank() {
// // 遍历我们的子弹
// for (int j = 0; j < myTank.shots.size(); j++) {
// Shot shot = myTank.shots.get(j);
// // 判断是否击中了敌人坦克
// if (shot != null && shot.isLive) { // 自己的子弹存活
// // 遍历敌人所有坦克
// for (int i = 0; i < enemyTanks.size(); i++) {
// EnemyTank enemyTank = enemyTanks.get(i);
// hitTank(shot, enemyTank);
// }
// }
// }
// 判断单颗子弹是否击中敌人坦克
if (myTank.shot != null && myTank.shot.isLive) {
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);
hitTank(myTank.shot, enemyTank);
}
}
}
// 编写方法 判断敌人坦克是否击中自己的坦克
public void hitMyTank() {
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);
for (int j = 0; j < enemyTank.shots.size(); j++) {
Shot shot = enemyTank.shots.get(j);
if (myTank.isLive && shot.isLive) {
hitTank(shot, myTank);
}
}
}
}
// 判断我方子弹是否击中
public void hitTank(Shot s, Tank enemyTank) {
// 判断击中坦克
switch (enemyTank.getDirect()) {
case 0: // 坦克向上的方向
case 2: // 坦克向下
if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 40 &&
s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60) {
s.isLive = false; // 子弹销毁
enemyTank.isLive = false; // 坦克销毁
// 我们的子弹击中敌人坦克后 将enemyTank从Vector中去掉
enemyTanks.remove(enemyTank);
// 当我方坦克击毁敌方坦克就对allEnemyTankNum++
// enemyTank 可以是我方坦克 也可是敌方坦克 所以需要进行判断
if (enemyTank instanceof EnemyTank) {
Recorder.addAllEnemyTankNum();
}
if (enemyTank instanceof MyTank) {
sandinist.stopMusic();
new AePlayWave("src\\MyTank.wav").start();
}
// 创建Bomb对象加入到bombs集合
Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());
bombs.add(bomb);
}
break;
case 1: // 坦克向右
break;
case 3: // 坦克向左
if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 60 &&
s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40) {
s.isLive = false; // 子弹销毁
enemyTank.isLive = false; // 坦克销毁
// 我们的子弹击中敌人坦克后 将enemyTank从Vector中去掉
enemyTanks.remove(enemyTank);
// 创建Bomb对象加入到bombs集合
Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());
bombs.add(bomb);
}
break;
}
}
@Override
public void keyTyped(KeyEvent e) {
}
// 处理WASD按键按下的情况
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {
// 按下W键
myTank.setDirect(0);
// 修改坦克坐标
if (myTank.getY() > 0) {
myTank.moveUp();
}
} else if (e.getKeyCode() == KeyEvent.VK_D) {
myTank.setDirect(1);
if (myTank.getX() + 60 < 1000) {
myTank.moveRight();
}
} else if (e.getKeyCode() == KeyEvent.VK_S) {
myTank.setDirect(2);
if (myTank.getY() + 60 < 750) {
myTank.moveDown();
}
} else if (e.getKeyCode() == KeyEvent.VK_A) {
myTank.setDirect(3);
if (myTank.getX() > 0) {
myTank.moveLeft();
}
}
AePlayWave shot = new AePlayWave("src\\Shot.wav");
// 如果用户按下J 就发射子弹
if (e.getKeyCode() == KeyEvent.VK_F) {
// if (myTank.shot == null || !myTank.shot.isLive) {
// myTank.shotEnemyTank();
// }
// 发射多颗子弹
myTank.shotEnemyTank();
shot.start();
// if (myTank.shots.size() < 5) {
// myTank.shotEnemyTank();
// }
}
this.repaint(); // 重绘面板
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void run() { // 每隔100毫秒 重绘区域 子弹就被重绘
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
hitEnemyTank();
hitMyTank();
this.repaint();
}
}
}
MyTank(Hero)我方坦克
package com.sunl19ht.tankgame6;
import java.util.Vector;
public class MyTank extends Tank {
// 定义Shor对象
Shot shot = null; // 表示射击行为
Vector<Shot> shots = new Vector<>();
public MyTank(int x, int y) {
super(x, y);
}
// 射击
public void shotEnemyTank() {
if (shots.size() == 5) {
return;
}
// 创建Shot对象 根据当前MyTank对象的位置来创建Shot对象
switch (getDirect()) { // 得到MyTank对象方向
case 0:
shot = new Shot(getX() + 20, getY(),0);
break;
case 1:
shot = new Shot(getX() + 60, getY() + 20,1);
break;
case 2:
shot = new Shot(getX() + 20, getY() + 60,2);
break;
case 3:
shot = new Shot(getX(), getY() + 20,3);
break;
}
// 把新创建的shot放入到集合中
shots.add(shot);
// 启动Shot线程
new Thread(shot).start();
}
}
MyTankGame06运行main方法
package com.sunl19ht.tankgame6;
import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Scanner;
public class MyTankGame06 extends JFrame {
// 定义MyPanel
MyPanel mp = null;
static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
MyTankGame06 myTankGame06 = new MyTankGame06();
}
public MyTankGame06() {
System.out.println("请输入选择(1新游戏 2继续上局):");
String key = scanner.next();
mp = new MyPanel(key);
// 将mp放入到Thread 并且启动
new Thread(mp).start();
this.add(mp); // 把面板(就是游戏的绘图区)
this.setSize(1280, 750);
this.addKeyListener(mp); // 增加键盘监听事件
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
// 在JFrame中增加监听关闭窗口事件
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("监听到用户关闭窗口");
Recorder.keepRecord();
System.exit(0);
}
});
}
}
Node节点敌人对象
package com.sunl19ht.tankgame6;
public class Node {
// 一个 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;
}
}
Recorder存档文件
package com.sunl19ht.tankgame6;
import java.io.*;
import java.util.Vector;
public class Recorder {
// 记录相关信息 和文件交互
// 定义变量 记录我方击毁的坦克数量
private static int allEnemyTankNum = 0;
// 定义IO对象 用于写数据到文件中
private static BufferedWriter bw = null;
private static BufferedReader br = null;
private static String recordFile = "src\\myRecord.txt";
// 定义Node的Vector 用于保存敌人信息的node
private static Vector<Node> nodes = new Vector<>();
// 定义Vector 执行MyPanel对象的敌人坦克Vector
private static Vector<EnemyTank> enemyTanks = null;
public static void setEnemyTanks(Vector<EnemyTank> enemyTanks) {
Recorder.enemyTanks = enemyTanks;
}
// 返回记录文件的路径
public static String getRecordFile() {
return recordFile;
}
// 增加方法用于读取recordFile恢复相关信息
// 该方法在继续上局的时候调用
public static Vector<Node> getNodesAndEnemyTankNumRec() {
try {
br = new BufferedReader(new FileReader(recordFile));
allEnemyTankNum = Integer.parseInt(br.readLine());
// 循环读取文件 生成nodes集合
String line = "";
while ((line = br.readLine()) != null) {
String[] xyd = line.split(" ");
Node node = new Node(Integer.parseInt(xyd[0]), Integer.parseInt(xyd[1]), Integer.parseInt(xyd[2]));
nodes.add(node);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return nodes;
}
// 当游戏退出时 将 allEnemyTankNum 保存到文件中 recordFile
// 对keepRecord升级 保存敌人坦克的坐标和方向
public static void keepRecord() {
try {
bw = new BufferedWriter(new FileWriter(recordFile));
bw.write(allEnemyTankNum + "\r\n");
// 遍历敌人坦克的Vector 然后根据情况保存
// OOP 定义一个属性 通过set方法得到enemyTanks
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 + "\r\n");
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (bw != null) {
bw.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public static int getAllEnemyTankNum() {
return allEnemyTankNum;
}
public static void setAllEnemyTankNum(int allEnemyTankNum) {
Recorder.allEnemyTankNum = allEnemyTankNum;
}
// 当我方坦克击毁一个敌人坦克就应该对allEnemyTankNum++
public static void addAllEnemyTankNum() {
Recorder.allEnemyTankNum++;
}
}
Shot子弹
package com.sunl19ht.tankgame6;
public class Shot implements Runnable{
int x; // 子弹x坐标
int y; // 子弹y坐标
int direct = 0; // 子弹方向
int speed = 6; // 子弹速度
boolean isLive = true; // 子弹是否还存活
public Shot(int x, int y, int direct) {
this.x = x;
this.y = y;
this.direct = direct;
}
@Override
public void run() { // 射击行为
while (true) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 根据方向改变x 和 y坐标
switch (direct) {
case 0: // 上
y -= speed;
break;
case 1:
x += speed;
break;
case 2:
y += speed;
break;
case 3:
x -= speed;
break;
}
// 测试 输出x和y坐标
// System.out.println("子弹 x" + x + " y=" + y);
// 子弹碰到面板边界
// 当子弹碰到敌人坦克时也应该销毁
if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750) && isLive) {
System.out.println("子弹线程退出");
isLive = false; // 销毁子弹
break;
}
}
}
}
Tank坦克
package com.sunl19ht.tankgame6;
public class Tank {
private int x;
private int y;
private int direct = 0; // 坦克方向 0上 1右 2下 3左
private int speed = 5;
boolean isLive = true;
public Tank(int x, int y) {
this.x = x;
this.y = y;
}
public void moveUp() {
y-=speed;
}
public void moveRight() {
x+=speed;
}
public void moveDown() {
y+=speed;
}
public void moveLeft() {
x-=speed;
}
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;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
}