增加功能[HspTankGame05java]
这里我们先以一辆敌人坦克跟其他敌人坦克进行对比:
当敌人坦克一方向向右的时候:其他敌人坦克有两种情况
当敌人坦克一方向向下的时候:其他敌人坦克有两种情况
当敌人坦克一方向向左的时候:其他敌人坦克有两种情况
至关重要的一步:在MyPanel中将enemyTanks 设置给 enemyTank,要不然不能实现防止敌人坦克重叠的优化
enemyTank.setEnemyTanks(enemyTanks);
一、新建一个Recorder类,用于记录相关的信息:
在类里面定义变量,记录我方击毁敌人坦克数量
private static int allEnemyTankNum = 0;
定义IO对象
private static FileWriter fw = null;
private static BufferedWriter bw = null;
private static String recordFile = "E:\\myRecord.txt";
添加allEnemyTankNum的构造器和set方法
public static int getAllEnemyTankNum() {
return allEnemyTankNum;
}
public static void setAllEnemyTankNum(int allEnemyTankNum) {
Recoder.allEnemyTankNum = allEnemyTankNum;
}
当我方坦克击毁一个敌人,就应当 allEnemyTankNum++;
public static void addAllEnemyTankNum() {
Recoder.allEnemyTankNum++;
}
增加一个方法,当游戏退出时,我们将 allEnemyTankNum 保存到 recordFile
public static void keepRecord() {
try {
bw = new BufferedWriter(new FileWriter(recordFile));
bw.write(allEnemyTankNum + "\r\n");//"\r\n" 换行
//或者用 bw.newLine();换行
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (bw!=null){
bw.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
二、在坦克显示的右边界面画出机会敌方坦克的情况,并且在IO文件中记录相应数据
在MyPanel中编写方法,显示我方击毁敌方坦克的信息
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(Recoder.getAllEnemyTankNum() + "", 1080, 100);
}
在击中敌人坦克的hitTank()方法中,当敌人击毁的时候,增加Recorder的addAllEnemyTankNum方法,记录击毁的数量
//当我方击毁敌人坦克时,就对数据allEnemyTankNum++
//因为 tank 可以是 MyTank,也可以是 EnemyTank
//所以我们这里要进行一个判断
if (tank instanceof EnemyTank) {
Recoder.addAllEnemyTankNum();
}
最后在 Jframe 中增加响应关闭窗口的处理,当我们关闭窗口的时候,记录Recorder的keepRecord()方法
//在 Jframe 中增加响应关闭窗口的处理
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
Recoder.keepRecord();
System.exit(0);
}
});
对 KeepRecord() 进行升级,保存敌人坦克的坐标和方向
//遍历敌人坦克的 Vector ,然后根据情况保存
//OOP ,定义一个属性,然后通过setXx 得到敌人的坦克 Vector
for (int i = 0; i < enemyTanks.size(); i++) {
//取出敌人坦克
EnemyTank enemyTank = enemyTanks.get(i);
if (enemyTank.isLive){//为了保险,建议判断
//保存坦克信息
String record = enemyTank.getX()+" "+enemyTank.getY()+" "+enemyTank.getDirection();
//写入到文件
bw.write(record+"\r\n");
//或者加一个
//bw.newLine();
}
}
接下来将 MyPanel 对象的 enemyTanks 设置给 Recorder 的 enemyTanks
//将 MyPanel 对象的 enemyTanks 设置给 Recorder 的 enemyTanks
Recoder.setEnemyTanks(enemyTanks);
一、将Recorder文件中每个敌人信息恢复成一个Node对象,再将其放入Vector
先在Recorder中定义一个Node的Vector,用于保存敌人坦克的信息
private static Vector<Node> nodes = new Vector<>();
在Recorder中定义一个输入流,用于读取recordFile文件
private static BufferedReader br = null;
然后增加一个方法,用于读取recordFile,恢复相关信息;该方法在继续上局游戏的时候调用
public static Vector<Node> getNodesAndEnemyTankRec() {
try {
br = new BufferedReader(new FileReader(recordFile));
//先恢复allEnemyTankNum的值
//1. 先读取第一行allEnemyTankNum
// 即读取击毁敌人坦克的数量
//因为 br.readLine 是一个字符串,所以我们用Integer.parseInt()进行一个转换
allEnemyTankNum = Integer.parseInt(br.readLine());
//2. 接下来循环读取文件,生成nodes集合
String Line = "";
//后面读取到的数据为 100 170 2类型
while ((Line = br.readLine()) != null) {
//所以这里用split分割String生成一个数组
String[] xyd = Line.split(" ");
//将读取到的数据生成Node对象
//因为这里的数据为String,所以用Integer.parseInt()进行一个转换
Node node = new Node(Integer.parseInt(xyd[0]), Integer.parseInt(xyd[1]),
Integer.parseInt(xyd[2]));
//将生成的Node对象node放入到Vector对象的nodes中
nodes.add(node);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return nodes;
}
二、通过Node的Vector去恢复敌人坦克的位置和方向
找到我们游戏启动的地方MyPanel,在游戏一启动就进行数据恢复;然后把相应的数据给下面定义的Node对象nodes接收
nodes = Recoder.getNodesAndEnemyTankRec();
然后在MyPanel中定义一个存放Node对象的Vector,用于恢复敌人坦克的坐标和方向;
Vector<Node> nodes = new Vector<>();
然后在MyPanel中增加一个参数,用于选择开始新游戏或者继续上局
public MyPanel(String key)
在主界面MyTankGame05中增加一个用户输入界面Scanner
static Scanner scanner = new Scanner(System.in);//添加static,可直接调用
在主界面MyTankGame05的构造器中添加供用户选择的代码:开始新游戏还是继续上局,再将key放入初始化中
//供用户选择:开始新游戏还是继续上局
System.out.println("请输入选择:1:新游戏 2:继续上局");
String key = scanner.next();
//初始化
mp = new MyPanel(key);
最后再在MyPanel中进行一个switch判断,进行新游戏和继续上局
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.setDirection(2);
//启动敌人坦克,让他动起来
new Thread(enemyTank).start();
//给该enemyTank对象加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20,
enemyTank.getY() + 60, enemyTank.getDirection());
//加入到enemyTank的Vector成员
enemyTank.shots.add(shot);
//立即启动
new Thread(shot).start();
enemyTanks.add(enemyTank);
}
break;
case "2":
for (int i = 0; i < nodes.size(); i++) {
//取出存放上局敌人数据的Node对象
Node node = nodes.get(i);
//恢复上局每个敌人坦克的坐标
EnemyTank enemyTank = new EnemyTank(node.getX(), node.getY());
//将enemyTanks 设置给 enemyTank !!!
//要不然不能完成重叠优化
enemyTank.setEnemyTanks(enemyTanks);
//恢复上局每个敌人坦克的方向
enemyTank.setDirection(node.getDirection());
//启动敌人坦克,让他动起来
new Thread(enemyTank).start();
//给该enemyTank对象加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20,
enemyTank.getY() + 60, enemyTank.getDirection());
//加入到enemyTank的Vector成员
enemyTank.shots.add(shot);
//立即启动
new Thread(shot).start();
enemyTanks.add(enemyTank);
}
break;
default:
System.out.println("选择有误");
}
增加功能[HspTankGame05java]
使用一个播放音乐的类AePlayWave
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();
}
}
}
找到一个自己想要的音乐,例如111.wav
在MyPanel的里面播放指定的音乐即可
//这里播放指定的音乐
new AePlayWave("src\\111.wav").start();
思路:
我们储存文件的位置一般跟着我们的项目走
但是我们之前储存IO流的文件位置定义在E盘,到时候不一定能够跟着项目一起走
会导致文件缺失,我们可以把记录文件保存到src下
//把记录文件的位置改为src下
//private static String recordFile = "E:\\myRecord.txt";
private static String recordFile = "src\\myRecord.txt";
一、我们前面在MyPanel中设置了在游戏一启动就进行数据恢复;
二、但是当我们还没有数据储存的时候运行继续上局时,会抛出异常
三、因此我们需要进行优化 => 提升代码的健壮性
先在Recorder类中添加一个getRecordFile()方法,用于返回记录文件的目录
//返回记录文件的目录
public static String getRecordFile() {
return recordFile;
}
在MyPanel中判断目录是否存在,如果存在,还是按照原来的执行,如果不存在只能开启新的游戏
//1. 我们先判断记录文件是否存在
//2. 如果存在,游戏正常执行,
// 如果文件不存在,提示只能开启新游戏, key = "1"
File file = new File(Recoder.getRecordFile());
if (file.exists()){
//当我们游戏启动的时候进行数据恢复
//然后用nodes接收相应数据
nodes = Recoder.getNodesAndEnemyTankRec();
}else {
System.out.println("文件不存在,只能开启新的游戏");
key = "1";
}
我们目前为止坦克大战0.6版的最终代码如下:
父类坦克Tank
public class Tank {
private int x;//坦克的横坐标
private int y;//坦克的纵坐标
boolean isLive = true;
//坦克的方向 0向上 1向右 2向下 3向左
private int direction;
//坦克的速度
private int speed = 2;
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
//添加上下左右移动方法
//向上
public void moveUp() {
y -= speed;
}
//向下
public void moveDown() {
y += speed;
}
//向左
public void moveLeft() {
x -= speed;
}
//向右
public void moveRight() {
x += speed;
}
public int getDirection() {
return direction;
}
public void setDirection(int direction) {
this.direction = direction;
}
public Tank(int x, int y) {
this.x = x;
this.y = y;
}
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;
}
}
敌人坦克 EnemyTank
public class EnemyTank extends Tank implements Runnable {
//在敌人坦克类,使用Vector保存多个shot
Vector<Shot> shots = new Vector<>();
//1. Vector 在 MyPanel
Vector<EnemyTank> enemyTanks = new Vector<>();
//这里提供一个方法,可以将 MyPanel 的成员 Vector enemyTanks = new Vector<>();
//设置到 EnemyTank 的成员 enemyTanks
public void setEnemyTanks(Vector<EnemyTank> enemyTanks) {
this.enemyTanks = enemyTanks;
}
//编写方法,判断当前的这个敌人坦克,是否和enemyTanks 中的其他坦克发生重叠或者碰撞
public boolean isTouchEnemyTank() {
//判断当前敌人坦克方向(this)
switch (this.getDirection()) {
case 0://向上
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);
if (enemyTank != this) {
//当敌人坦克是上/下
//1.如果敌人的坦克是上/下
// 敌人的 x 范围 [enemyTank.getX(),enemyTank.getX() + 40]
// 敌人的 y 范围 [enemyTank.getY(),enemyTank.getY() + 60]
if (enemyTank.getDirection() == 0 || enemyTank.getDirection() == 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.getDirection() == 1 || enemyTank.getDirection() == 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()+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;
}
}
}
}
break;
case 1://向右
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);
if (enemyTank != this) {
//当敌人坦克是上/下
//1.如果敌人的坦克是上/下
// 敌人的 x 范围 [enemyTank.getX(),enemyTank.getX() + 40]
// 敌人的 y 范围 [enemyTank.getY(),enemyTank.getY() + 60]
if (enemyTank.getDirection() == 0 || enemyTank.getDirection() == 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.getDirection() == 1 || enemyTank.getDirection() == 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++) {
EnemyTank enemyTank = enemyTanks.get(i);
if (enemyTank != this) {
//当敌人坦克是上/下
//1.如果敌人的坦克是上/下
// 敌人的 x 范围 [enemyTank.getX(),enemyTank.getX() + 40]
// 敌人的 y 范围 [enemyTank.getY(),enemyTank.getY() + 60]
if (enemyTank.getDirection() == 0 || enemyTank.getDirection() == 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.getDirection() == 1 || enemyTank.getDirection() == 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++) {
EnemyTank enemyTank = enemyTanks.get(i);
if (enemyTank != this) {
//当敌人坦克是上/下
//1.如果敌人的坦克是上/下
// 敌人的 x 范围 [enemyTank.getX(),enemyTank.getX() + 40]
// 敌人的 y 范围 [enemyTank.getY(),enemyTank.getY() + 60]
if (enemyTank.getDirection() == 0 || enemyTank.getDirection() == 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(),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;
}
}
//当敌人坦克是左/右
// 1.如果敌人的坦克是左/右
// 敌人的 x 范围 [enemyTank.getX(),enemyTank.getX() + 60]
// 敌人的 y 范围 [enemyTank.getY(),enemyTank.getY() + 40]
if (enemyTank.getDirection() == 1 || enemyTank.getDirection() == 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;
}
public EnemyTank(int x, int y) {
super(x, y);
}
@Override
public void run() {
while (true) {
//从这里我们判断如果shots.size()==0,
// 说明子弹已经销毁,再创建一颗子弹放入shots
//并启动
if (isLive && shots.size() < 3) {
//创建一个临时变量
Shot s = null;
//判断坦克的方向
//创建对应的子弹
switch (getDirection()) {
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 (getDirection()) {
case 0:
//让坦克保持一个方向走100步
for (int i = 0; i < 50; i++) {
if (getY() > 0 && !isTouchEnemyTank()) {
moveUp();
}
//休眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
case 1:
//让坦克保持一个方向走100步
for (int i = 0; i < 50; i++) {
if (getX() + 60 < 1000 && !isTouchEnemyTank()) {
moveRight();
}
//休眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
case 2:
//让坦克保持一个方向走100步
for (int i = 0; i < 50; i++) {
if (getY() + 60 < 750 && !isTouchEnemyTank()) {
moveDown();
}
//休眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
case 3:
//让坦克保持一个方向走100步
for (int i = 0; i < 50; i++) {
if (getX() > 0 && !isTouchEnemyTank()) {
moveLeft();
}
//休眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
}
//随机改变方向
setDirection((int) (Math.random() * 4));
//考虑线程什么时候退出
if (!isLive) {
break;
}
}
}
}
我们的坦克 MyTank
public class MyTank extends Tank {
//定义一个shot对象,表示一个射击行为(线程)
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对象
switch (getDirection()) {
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集合中
shots.add(shot);
//启动我们的shot线程
new Thread(shot).start();
}
}
子弹 Shot
public class Shot implements Runnable {//射击子弹
int x;//子弹x坐标
int y;//子弹y坐标
int direction;//子弹方向
int speed = 2;//子弹速度
boolean isLive = true;//子弹是否还存活
public Shot(int x, int y, int direction) {
this.x = x;
this.y = y;
this.direction = direction;
}
@Override
public void run() {//射击行为
while (true) {
try {//让子弹休眠一下
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//根据方向来改变x,y坐标
switch (direction) {
case 0://向上
y -= speed;
break;
case 1://向右
x += speed;
break;
case 2://向下
y += speed;
break;
case 3://向左
x -= speed;
break;
}
//这里用于调试,输入子弹坐标
//System.out.println("子弹x =" + x + " y =" + y);
//当子弹超出边界就销毁子弹
//当子弹碰到敌人坦克时,也应该结束线程
if (!(x > 0 && x < 1000 && y > 0 && y < 750 && isLive)) {
System.out.println("子弹消失");
isLive = false;
break;
}
}
}
}
爆炸效果 Boom
public class Boom {
int x, y;//炸弹的坐标
int life = 9;//炸弹的生命周期
boolean isLive = true;
public Boom(int x, int y) {
this.x = x;
this.y = y;
}
//减少生命值
public void lifeDown() {//配合图片爆炸效果
if (life > 0) {
life--;
} else {
isLive = false;
}
}
}
面板显示 MyPanel
//为了监听键盘事件,实现 KeyListener
//为了让panel不停的重绘,实现 Runnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable {
//定义我的坦克
MyTank myTank = null;
//定义敌人坦克,放入到 Vector
Vector<EnemyTank> enemyTanks = new Vector<>();
//定义一个存放Node对象的Vector,用于恢复敌人坦克的坐标和方向
Vector<Node> nodes = new Vector<>();
//定义一个Vector,用于存放炸弹
//当子弹击中坦克时,就加入一个Boom对象booms
Vector<Boom> booms = new Vector<>();
int enemyTanksize = 3;
//定义三张图片,用于显示爆炸效果
Image image1 = null;
Image image2 = null;
Image image3 = null;
public MyPanel(String key) {
//1. 我们先判断记录文件是否存在
//2. 如果存在,游戏正常执行,
// 如果文件不存在,提示只能开启新游戏, key = "1"
File file = new File(Recoder.getRecordFile());
if (file.exists()) {
//当我们游戏启动的时候进行数据恢复
//然后用nodes接收相应数据
nodes = Recoder.getNodesAndEnemyTankRec();
} else {
System.out.println("文件不存在,只能开启新的游戏");
key = "1";
}
//将 MyPanel 对象的 enemyTanks 设置给 Recorder 的 enemyTanks
Recoder.setEnemyTanks(enemyTanks);
//初始化自己的坦克
myTank = new MyTank(300, 600);
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.setDirection(2);
//启动敌人坦克,让他动起来
new Thread(enemyTank).start();
//给该enemyTank对象加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20,
enemyTank.getY() + 60, enemyTank.getDirection());
//加入到enemyTank的Vector成员
enemyTank.shots.add(shot);
//立即启动
new Thread(shot).start();
enemyTanks.add(enemyTank);
}
break;
case "2":
for (int i = 0; i < nodes.size(); i++) {
//取出存放上局敌人数据的Node对象
Node node = nodes.get(i);
//恢复上局每个敌人坦克的坐标
EnemyTank enemyTank = new EnemyTank(node.getX(), node.getY());
//将enemyTanks 设置给 enemyTank !!!
//要不然不能完成重叠优化
enemyTank.setEnemyTanks(enemyTanks);
//恢复上局每个敌人坦克的方向
enemyTank.setDirection(node.getDirection());
//启动敌人坦克,让他动起来
new Thread(enemyTank).start();
//给该enemyTank对象加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20,
enemyTank.getY() + 60, enemyTank.getDirection());
//加入到enemyTank的Vector成员
enemyTank.shots.add(shot);
//立即启动
new Thread(shot).start();
enemyTanks.add(enemyTank);
}
break;
default:
System.out.println("选择有误");
}
//初始化图片
image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif"));
image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));
image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));
//这里播放指定的音乐
new AePlayWave("src\\111.wav").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(Recoder.getAllEnemyTankNum() + "", 1080, 100);
}
@Override
public void paint(Graphics g) {
super.paint(g);
//设置填充矩形,默认黑色
g.fillRect(0, 0, 1000, 750);
//调用showInfo()方法
showInfo(g);
myTank.setSpeed(5);
//画出坦克-封装方法
//自己的坦克
//增加一个if判断,当自己不为空并且还存活的情况下才画坦克
if (myTank != null && myTank.isLive) {
drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 1);
}
//画出自己的子弹(一颗)
// if (myTank.shot != null && myTank.shot.isLive == true) {
// g.draw3DRect(myTank.shot.x, myTank.shot.y, 5, 5, true);
// }
//现在我们要画多颗子弹
//将myTank的子弹shots遍历取出
for (int i = 0; i < myTank.shots.size(); i++) {
//取出子弹
Shot shot = myTank.shots.get(i);
if (shot != null && shot.isLive) {
g.draw3DRect(shot.x, shot.y, 5, 5, true);
} else {//否则该shot对象无效,就从shots集合中拿掉
myTank.shots.remove(shot);
break;
}
}
//如果booms集合中有对象,就画出
for (int i = 0; i < booms.size(); i++) {
//取出炸弹
Boom boom = booms.get(i);
//根据当前这个boom对象的life值画出对应的图片
if (boom.life > 6) {
g.drawImage(image1, boom.x, boom.y, 80, 80, this);
} else if (boom.life > 30) {
g.drawImage(image2, boom.x, boom.y, 80, 80, this);
} else {
g.drawImage(image3, boom.x, boom.y, 80, 80, this);
}
//让炸弹的生命值减少
boom.lifeDown();
//如果boom.life为0,就从booms的集合中删除
if (boom.life == 0) {
booms.remove(boom);
}
}
//画出敌人的坦克,遍历Vector
for (int i = 0; i < enemyTanks.size(); i++) {
//从Vector取出坦克
EnemyTank enemyTank = enemyTanks.get(i);
//判断当前坦克是否还存活
if (enemyTank.isLive) {//如果敌人的坦克是存活的,才画出该坦克
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 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, 5, 5, true);
} else {
//从 Vector 移除
enemyTank.shots.remove(shot);
}
}
}
}
}
//编写方法,画出坦克
/**
* @param x 坦克的左上角x坐标
* @param y 坦克的左上角y坐标
* @param g 画笔
* @param direction 坦克的方向
* @param type 坦克的类型
*/
public void drawTank(int x, int y, Graphics g, int direction, int type) {
switch (type) {
case 0://敌人的坦克
g.setColor(Color.cyan);
break;
case 1://我们的坦克
g.setColor(Color.yellow);
break;
}
//根据坦克的方向,来绘制坦克
switch (direction) {//0向上 1向右 2向下 3向左
case 0://默认方向向上
// 先画第一个矩形 大小 10*60
//坦克左边轮子
// 定点(x,y)
g.fill3DRect(x, y, 10, 60, false);
// 第二个矩形 大小 20*40
//坦克身体
// 定点(x+10,y+10)
g.fill3DRect(x + 10, y + 10, 20, 40, false);
// 第三个矩形 大小 10*60
//坦克右边轮子
// 定点(x+30,y)
g.fill3DRect(x + 30, y, 10, 60, false);
// 上面的圆盖子 大小 (20,20)
// 定点(x+10,y+20)
g.fillOval(x + 10, y + 20, 20, 20);
// 最后的炮管
// 定点1 (x+20,y)
// 定点2 (x+20,y+30)
g.drawLine(x + 20, y, x + 20, y + 30);
// 画出子弹
g.drawLine(x + 20, y, x + 20, y + 5);
break;
case 1://默认方向向右
// 先画第一个矩形 大小 60*10
//坦克上边轮子
g.fill3DRect(x, y, 60, 10, false);
// 第二个矩形 大小 40*20
//坦克身体
g.fill3DRect(x + 10, y + 10, 40, 20, false);
// 第三个矩形 大小 10*60
//坦克下边轮子
g.fill3DRect(x, y + 30, 60, 10, false);
// 上面的圆盖子 大小 (20,20)
g.fillOval(x + 20, y + 10, 20, 20);
// 最后的炮管
g.drawLine(x + 60, y + 20, x + 30, y + 20);
// 画出子弹
g.drawLine(x + 60, y + 20, x + 55, y + 20);
break;
case 2://默认方向向下
// 先画第一个矩形 大小 10*60
//坦克左边轮子
g.fill3DRect(x, y, 10, 60, false);
// 第二个矩形 大小 20*40
//坦克身体
g.fill3DRect(x + 10, y + 10, 20, 40, false);
// 第三个矩形 大小 10*60
//坦克右边轮子
g.fill3DRect(x + 30, y, 10, 60, false);
// 上面的圆盖子 大小 (20,20)
g.fillOval(x + 10, y + 20, 20, 20);
// 最后的炮管
g.drawLine(x + 20, y + 60, x + 20, y + 30);
// 画出子弹
g.drawLine(x + 20, y + 60, x + 20, y + 55);
break;
case 3://默认方向向左
// 先画第一个矩形 大小 60*10
//坦克上边轮子
g.fill3DRect(x, y, 60, 10, false);
// 第二个矩形 大小 40*20
//坦克身体
g.fill3DRect(x + 10, y + 10, 40, 20, false);
// 第三个矩形 大小 10*60
//坦克下边轮子
g.fill3DRect(x, y + 30, 60, 10, false);
// 上面的圆盖子 大小 (20,20)
g.fillOval(x + 20, y + 10, 20, 20);
// 最后的炮管
g.drawLine(x, y + 20, x + 30, y + 20);
// 画出子弹
g.drawLine(x, y + 20, x + 5, y + 20);
break;
default:
System.out.println("暂时不作处理");
}
}
// 创建一个方法 hitMyTank
// 判断敌人的子弹是否打中我们
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);
//判断shot是否击中我们的坦克
if (myTank.isLive && shot.isLive) {
hitTank(shot, myTank);
}
}
}
}
// 现我们的坦克可以发射多个子弹
// 在判断我方子弹是否击中敌人坦克时,
// 就需要把我们的子弹集合中所有的子弹都取出,
// 和敌人的所有坦克进行判断
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);
}
}
}
}
// 编写方法,判断我方子弹是否击中敌人的坦克
// 什么时候判断我方子弹是否击中敌人?
// 在run方法里判断比较适合
// 因为现在这方法用于判断我们的坦克跟敌人的坦克了
// 所以这里把 EnemyTank enemyTank 改为 Tank tank
//改用两者的父类 Tank 对象
public void hitTank(Shot s, Tank tank) {
//判断击中坦克
switch (tank.getDirection()) {
case 0://向上
case 2://向下
//子弹的x坐标大于坦克的最左边x坐标
// 或者小于了坦克最右边x坐标(坦克宽40)
//子弹的y坐标大于坦克的最上边y坐标
// 或者小于了坦克最下边y坐标(坦克高60)
if (s.x > tank.getX() && s.x < tank.getX() + 40
&& s.y > tank.getY() && s.y < tank.getY() + 60) {
//子弹消失
s.isLive = false;
//敌人的坦克消失
tank.isLive = false;
//当我们的子弹击中敌人坦克时,将enemyTank从Vector拿掉
enemyTanks.remove(tank);
//当我方击毁敌人坦克时,就对数据allEnemyTankNum++
//因为 tank 可以是 MyTank,也可以是 EnemyTank
//所以我们这里要进行一个判断
if (tank instanceof EnemyTank) {
Recoder.addAllEnemyTankNum();
}
//创建Boom对象,加入到booms集合
Boom boom = new Boom(tank.getX(), tank.getY());
booms.add(boom);
break;
}
case 1://向右
case 3://向左
//子弹的x坐标大于坦克的最左边x坐标
// 或者小于了坦克最右边x坐标(坦克宽60)
//子弹的y坐标大于坦克的最上边y坐标
// 或者小于了坦克最下边y坐标(坦克高40)
if (s.x > tank.getX() && s.x < tank.getX() + 60
&& s.y > tank.getY() && s.y < tank.getY() + 40) {
//子弹消失
s.isLive = false;
//敌人的坦克消失
tank.isLive = false;
//当我们的子弹击中敌人坦克时,将enemyTank从Vector拿掉
enemyTanks.remove(tank);
//当我方击毁敌人坦克时,就对数据allEnemyTankNum++
//因为 tank 可以是 MyTank,也可以是 EnemyTank
//所以我们这里要进行一个判断
if (tank instanceof EnemyTank) {
Recoder.addAllEnemyTankNum();
}
//创建Boom对象,加入到booms集合
Boom boom = new Boom(tank.getX(), tank.getY());
booms.add(boom);
break;
}
}
}
@Override
public void keyTyped(KeyEvent e) {
}
//处理 wsad 按下的情况
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {
//改变坦克的方向
myTank.setDirection(0);
if (myTank.getY() > 0) {
myTank.moveUp();
}
} else if (e.getKeyCode() == KeyEvent.VK_S) {
myTank.setDirection(2);
if (myTank.getY() + 60 < 750) {
myTank.moveDown();
}
} else if (e.getKeyCode() == KeyEvent.VK_A) {
myTank.setDirection(3);
if (myTank.getX() > 0) {
myTank.moveLeft();
}
} else if (e.getKeyCode() == KeyEvent.VK_D) {
myTank.setDirection(1);
if (myTank.getX() + 60 < 1000) {
myTank.moveRight();
}
}
//如果用户按下J键,就是发射子弹
if (e.getKeyCode() == KeyEvent.VK_J) {
//判断当前myTank子弹是否已经销毁 发射一颗子弹
// if (myTank.shot == null || !myTank.shot.isLive) {
// myTank.shotEnemyTank();//发射子弹
// }
//判断当前myTank子弹是否已经销毁 发射duo颗子弹
myTank.shotEnemyTank();
}
this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
}
//让子弹不停的重绘
@Override
public void run() {
while (true) {
try {//每隔200毫秒,重绘
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//把下面注释的这段代码封装到上面的方法里面
//判断是否击中了敌人的坦克
// if (myTank.shot != null && myTank.shot.isLive) {//当前我的子弹还存活
// for (int i = 0; i < enemyTanks.size(); i++) {
// EnemyTank enemyTank = enemyTanks.get(i);
// hitTank(myTank.shot, enemyTank);
// }
// }
//判断敌人坦克是否击中我们
hitMyTank();
hitEnemyTank();
this.repaint();
}
}
}
用于记录相关的信息Recorder
//为了监听键盘事件,实现 KeyListener
//为了让panel不停的重绘,实现 Runnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable {
//定义我的坦克
MyTank myTank = null;
//定义敌人坦克,放入到 Vector
Vector<EnemyTank> enemyTanks = new Vector<>();
//定义一个存放Node对象的Vector,用于恢复敌人坦克的坐标和方向
Vector<Node> nodes = new Vector<>();
//定义一个Vector,用于存放炸弹
//当子弹击中坦克时,就加入一个Boom对象booms
Vector<Boom> booms = new Vector<>();
int enemyTanksize = 3;
//定义三张图片,用于显示爆炸效果
Image image1 = null;
Image image2 = null;
Image image3 = null;
public MyPanel(String key) {
//1. 我们先判断记录文件是否存在
//2. 如果存在,游戏正常执行,
// 如果文件不存在,提示只能开启新游戏, key = "1"
File file = new File(Recoder.getRecordFile());
if (file.exists()) {
//当我们游戏启动的时候进行数据恢复
//然后用nodes接收相应数据
nodes = Recoder.getNodesAndEnemyTankRec();
} else {
System.out.println("文件不存在,只能开启新的游戏");
key = "1";
}
//将 MyPanel 对象的 enemyTanks 设置给 Recorder 的 enemyTanks
Recoder.setEnemyTanks(enemyTanks);
//初始化自己的坦克
myTank = new MyTank(300, 600);
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.setDirection(2);
//启动敌人坦克,让他动起来
new Thread(enemyTank).start();
//给该enemyTank对象加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20,
enemyTank.getY() + 60, enemyTank.getDirection());
//加入到enemyTank的Vector成员
enemyTank.shots.add(shot);
//立即启动
new Thread(shot).start();
enemyTanks.add(enemyTank);
}
break;
case "2":
for (int i = 0; i < nodes.size(); i++) {
//取出存放上局敌人数据的Node对象
Node node = nodes.get(i);
//恢复上局每个敌人坦克的坐标
EnemyTank enemyTank = new EnemyTank(node.getX(), node.getY());
//将enemyTanks 设置给 enemyTank !!!
//要不然不能完成重叠优化
enemyTank.setEnemyTanks(enemyTanks);
//恢复上局每个敌人坦克的方向
enemyTank.setDirection(node.getDirection());
//启动敌人坦克,让他动起来
new Thread(enemyTank).start();
//给该enemyTank对象加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20,
enemyTank.getY() + 60, enemyTank.getDirection());
//加入到enemyTank的Vector成员
enemyTank.shots.add(shot);
//立即启动
new Thread(shot).start();
enemyTanks.add(enemyTank);
}
break;
default:
System.out.println("选择有误");
}
//初始化图片
image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif"));
image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));
image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));
//这里播放指定的音乐
new AePlayWave("src\\111.wav").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(Recoder.getAllEnemyTankNum() + "", 1080, 100);
}
@Override
public void paint(Graphics g) {
super.paint(g);
//设置填充矩形,默认黑色
g.fillRect(0, 0, 1000, 750);
//调用showInfo()方法
showInfo(g);
myTank.setSpeed(5);
//画出坦克-封装方法
//自己的坦克
//增加一个if判断,当自己不为空并且还存活的情况下才画坦克
if (myTank != null && myTank.isLive) {
drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 1);
}
//画出自己的子弹(一颗)
// if (myTank.shot != null && myTank.shot.isLive == true) {
// g.draw3DRect(myTank.shot.x, myTank.shot.y, 5, 5, true);
// }
//现在我们要画多颗子弹
//将myTank的子弹shots遍历取出
for (int i = 0; i < myTank.shots.size(); i++) {
//取出子弹
Shot shot = myTank.shots.get(i);
if (shot != null && shot.isLive) {
g.draw3DRect(shot.x, shot.y, 5, 5, true);
} else {//否则该shot对象无效,就从shots集合中拿掉
myTank.shots.remove(shot);
break;
}
}
//如果booms集合中有对象,就画出
for (int i = 0; i < booms.size(); i++) {
//取出炸弹
Boom boom = booms.get(i);
//根据当前这个boom对象的life值画出对应的图片
if (boom.life > 6) {
g.drawImage(image1, boom.x, boom.y, 80, 80, this);
} else if (boom.life > 30) {
g.drawImage(image2, boom.x, boom.y, 80, 80, this);
} else {
g.drawImage(image3, boom.x, boom.y, 80, 80, this);
}
//让炸弹的生命值减少
boom.lifeDown();
//如果boom.life为0,就从booms的集合中删除
if (boom.life == 0) {
booms.remove(boom);
}
}
//画出敌人的坦克,遍历Vector
for (int i = 0; i < enemyTanks.size(); i++) {
//从Vector取出坦克
EnemyTank enemyTank = enemyTanks.get(i);
//判断当前坦克是否还存活
if (enemyTank.isLive) {//如果敌人的坦克是存活的,才画出该坦克
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 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, 5, 5, true);
} else {
//从 Vector 移除
enemyTank.shots.remove(shot);
}
}
}
}
}
//编写方法,画出坦克
/**
* @param x 坦克的左上角x坐标
* @param y 坦克的左上角y坐标
* @param g 画笔
* @param direction 坦克的方向
* @param type 坦克的类型
*/
public void drawTank(int x, int y, Graphics g, int direction, int type) {
switch (type) {
case 0://敌人的坦克
g.setColor(Color.cyan);
break;
case 1://我们的坦克
g.setColor(Color.yellow);
break;
}
//根据坦克的方向,来绘制坦克
switch (direction) {//0向上 1向右 2向下 3向左
case 0://默认方向向上
// 先画第一个矩形 大小 10*60
//坦克左边轮子
// 定点(x,y)
g.fill3DRect(x, y, 10, 60, false);
// 第二个矩形 大小 20*40
//坦克身体
// 定点(x+10,y+10)
g.fill3DRect(x + 10, y + 10, 20, 40, false);
// 第三个矩形 大小 10*60
//坦克右边轮子
// 定点(x+30,y)
g.fill3DRect(x + 30, y, 10, 60, false);
// 上面的圆盖子 大小 (20,20)
// 定点(x+10,y+20)
g.fillOval(x + 10, y + 20, 20, 20);
// 最后的炮管
// 定点1 (x+20,y)
// 定点2 (x+20,y+30)
g.drawLine(x + 20, y, x + 20, y + 30);
// 画出子弹
g.drawLine(x + 20, y, x + 20, y + 5);
break;
case 1://默认方向向右
// 先画第一个矩形 大小 60*10
//坦克上边轮子
g.fill3DRect(x, y, 60, 10, false);
// 第二个矩形 大小 40*20
//坦克身体
g.fill3DRect(x + 10, y + 10, 40, 20, false);
// 第三个矩形 大小 10*60
//坦克下边轮子
g.fill3DRect(x, y + 30, 60, 10, false);
// 上面的圆盖子 大小 (20,20)
g.fillOval(x + 20, y + 10, 20, 20);
// 最后的炮管
g.drawLine(x + 60, y + 20, x + 30, y + 20);
// 画出子弹
g.drawLine(x + 60, y + 20, x + 55, y + 20);
break;
case 2://默认方向向下
// 先画第一个矩形 大小 10*60
//坦克左边轮子
g.fill3DRect(x, y, 10, 60, false);
// 第二个矩形 大小 20*40
//坦克身体
g.fill3DRect(x + 10, y + 10, 20, 40, false);
// 第三个矩形 大小 10*60
//坦克右边轮子
g.fill3DRect(x + 30, y, 10, 60, false);
// 上面的圆盖子 大小 (20,20)
g.fillOval(x + 10, y + 20, 20, 20);
// 最后的炮管
g.drawLine(x + 20, y + 60, x + 20, y + 30);
// 画出子弹
g.drawLine(x + 20, y + 60, x + 20, y + 55);
break;
case 3://默认方向向左
// 先画第一个矩形 大小 60*10
//坦克上边轮子
g.fill3DRect(x, y, 60, 10, false);
// 第二个矩形 大小 40*20
//坦克身体
g.fill3DRect(x + 10, y + 10, 40, 20, false);
// 第三个矩形 大小 10*60
//坦克下边轮子
g.fill3DRect(x, y + 30, 60, 10, false);
// 上面的圆盖子 大小 (20,20)
g.fillOval(x + 20, y + 10, 20, 20);
// 最后的炮管
g.drawLine(x, y + 20, x + 30, y + 20);
// 画出子弹
g.drawLine(x, y + 20, x + 5, y + 20);
break;
default:
System.out.println("暂时不作处理");
}
}
// 创建一个方法 hitMyTank
// 判断敌人的子弹是否打中我们
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);
//判断shot是否击中我们的坦克
if (myTank.isLive && shot.isLive) {
hitTank(shot, myTank);
}
}
}
}
// 现我们的坦克可以发射多个子弹
// 在判断我方子弹是否击中敌人坦克时,
// 就需要把我们的子弹集合中所有的子弹都取出,
// 和敌人的所有坦克进行判断
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);
}
}
}
}
// 编写方法,判断我方子弹是否击中敌人的坦克
// 什么时候判断我方子弹是否击中敌人?
// 在run方法里判断比较适合
// 因为现在这方法用于判断我们的坦克跟敌人的坦克了
// 所以这里把 EnemyTank enemyTank 改为 Tank tank
//改用两者的父类 Tank 对象
public void hitTank(Shot s, Tank tank) {
//判断击中坦克
switch (tank.getDirection()) {
case 0://向上
case 2://向下
//子弹的x坐标大于坦克的最左边x坐标
// 或者小于了坦克最右边x坐标(坦克宽40)
//子弹的y坐标大于坦克的最上边y坐标
// 或者小于了坦克最下边y坐标(坦克高60)
if (s.x > tank.getX() && s.x < tank.getX() + 40
&& s.y > tank.getY() && s.y < tank.getY() + 60) {
//子弹消失
s.isLive = false;
//敌人的坦克消失
tank.isLive = false;
//当我们的子弹击中敌人坦克时,将enemyTank从Vector拿掉
enemyTanks.remove(tank);
//当我方击毁敌人坦克时,就对数据allEnemyTankNum++
//因为 tank 可以是 MyTank,也可以是 EnemyTank
//所以我们这里要进行一个判断
if (tank instanceof EnemyTank) {
Recoder.addAllEnemyTankNum();
}
//创建Boom对象,加入到booms集合
Boom boom = new Boom(tank.getX(), tank.getY());
booms.add(boom);
break;
}
case 1://向右
case 3://向左
//子弹的x坐标大于坦克的最左边x坐标
// 或者小于了坦克最右边x坐标(坦克宽60)
//子弹的y坐标大于坦克的最上边y坐标
// 或者小于了坦克最下边y坐标(坦克高40)
if (s.x > tank.getX() && s.x < tank.getX() + 60
&& s.y > tank.getY() && s.y < tank.getY() + 40) {
//子弹消失
s.isLive = false;
//敌人的坦克消失
tank.isLive = false;
//当我们的子弹击中敌人坦克时,将enemyTank从Vector拿掉
enemyTanks.remove(tank);
//当我方击毁敌人坦克时,就对数据allEnemyTankNum++
//因为 tank 可以是 MyTank,也可以是 EnemyTank
//所以我们这里要进行一个判断
if (tank instanceof EnemyTank) {
Recoder.addAllEnemyTankNum();
}
//创建Boom对象,加入到booms集合
Boom boom = new Boom(tank.getX(), tank.getY());
booms.add(boom);
break;
}
}
}
@Override
public void keyTyped(KeyEvent e) {
}
//处理 wsad 按下的情况
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {
//改变坦克的方向
myTank.setDirection(0);
if (myTank.getY() > 0) {
myTank.moveUp();
}
} else if (e.getKeyCode() == KeyEvent.VK_S) {
myTank.setDirection(2);
if (myTank.getY() + 60 < 750) {
myTank.moveDown();
}
} else if (e.getKeyCode() == KeyEvent.VK_A) {
myTank.setDirection(3);
if (myTank.getX() > 0) {
myTank.moveLeft();
}
} else if (e.getKeyCode() == KeyEvent.VK_D) {
myTank.setDirection(1);
if (myTank.getX() + 60 < 1000) {
myTank.moveRight();
}
}
//如果用户按下J键,就是发射子弹
if (e.getKeyCode() == KeyEvent.VK_J) {
//判断当前myTank子弹是否已经销毁 发射一颗子弹
// if (myTank.shot == null || !myTank.shot.isLive) {
// myTank.shotEnemyTank();//发射子弹
// }
//判断当前myTank子弹是否已经销毁 发射duo颗子弹
myTank.shotEnemyTank();
}
this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
}
//让子弹不停的重绘
@Override
public void run() {
while (true) {
try {//每隔200毫秒,重绘
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//把下面注释的这段代码封装到上面的方法里面
//判断是否击中了敌人的坦克
// if (myTank.shot != null && myTank.shot.isLive) {//当前我的子弹还存活
// for (int i = 0; i < enemyTanks.size(); i++) {
// EnemyTank enemyTank = enemyTanks.get(i);
// hitTank(myTank.shot, enemyTank);
// }
// }
//判断敌人坦克是否击中我们
hitMyTank();
hitEnemyTank();
this.repaint();
}
}
}
保存敌人存活坦克的信息Node
//一个Node对象,表示一个敌人坦克的信息
public class Node {
private int x;
private int y;
private int direction;
public Node(int x, int y, int direction) {
this.x = x;
this.y = y;
this.direction = direction;
}
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 getDirection() {
return direction;
}
public void setDirection(int direction) {
this.direction = direction;
}
}
播放音乐的类AePlayWave
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();
}
}
}
主界面MyTankGame06
public class MyTankGame06 extends JFrame {
//定义 MyPanel
MyPanel mp = null;
//用户输入界面
static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
MyTankGame06 myTankGame01 = new MyTankGame06();
}
public MyTankGame06() {
//供用户选择:开始新游戏还是继续上局
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(1300, 750);
//添加键盘监听
this.addKeyListener(mp);
//当点击窗口的 × , 程序完全退出
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置显示
this.setVisible(true);
//在 Jframe 中增加响应关闭窗口的处理
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
Recoder.keepRecord();
System.exit(0);
}
});
}
}
这样一个简单版本的坦克大战就生成了。