坦克大战(Tank Battalion)------Java代码实现

1.0版本

在这里插入图片描述

Java绘图

由于绘图功能比较强大实用,且内容较多,所以我决定采用链接方式。
链接地址:https://blog.csdn.net/qq_52934831/article/details/120782285
在这里插入图片描述

具体代码:

Tank类

package TankBattalion.TankGame01;

public class Tank {
    private int x;
    private int y;

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

MyTank类

package TankBattalion.TankGame01;

public class MyTank extends Tank{
    public MyTank(int x, int y) {
        super(x, y);
    }
}

MyPanel类

package TankBattalion.TankGame01;

import javax.swing.*;
import java.awt.*;

//绘制Tank活动的区域
public class MyPanel extends JPanel {
//定义我的Tank
    MyTank myTank=null;
    public MyPanel(){
        myTank=new MyTank(100,100);//初始化自己的Tank
    }
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //将画板填充,默认为黑色
        g.fillRect( 0,0,1000,750);
        drawTank(myTank.getX(),myTank.getY(), g,0,0);
    }
    //设置绘制Tank函数,需要获得Tank的初始位置,画笔g,direction坦克的方向,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){
            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.drawOval(x+10,y+20,20,20);//圆形炮台
                g.fillOval(x+10,y+20,20,20);//填补炮台
                g.drawLine(x+20,y,x+20,y+30);//炮管
        }
    }
}

TankGame01类

package TankBattalion.TankGame01;

import javax.swing.*;
//TankGame01为了绘制出Tank
public class TankGame01 extends JFrame {
    private MyPanel mp=null;
    public static void main(String[] args) {
        new TankGame01();
    }
    public TankGame01(){
        mp=new MyPanel();
        this.add(mp);
        this.setSize(1000,750);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }
}

2.0版本

Java事件处理机制(让Tank动起来)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

绘制敌人的Tank

在这里插入图片描述

代码实现

Tank类
p

ackage TankBattalion.TankGame02;

public class Tank {
    private int x;
    private int y;
    private int direction;//0:向上,1向右,2向下,3向左
    private int speed;//控制Tank移动速度

    public Tank(int x, int y) {
        this.x = x;
        this.y = y;
    }
    //根据自己定义的speed控制Tank移动方法
    public void MoveUp() {
        y -= speed;
    }

    public void MoveRight() {
        x += speed;
    }

    public void MoveDown() {
        y += speed;
    }

    public void MoveLeft() {
        x -= speed;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection(int direction) {
        this.direction = direction;
    }

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = 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;
    }
}

MyTank类

package TankBattalion.TankGame02;

public class MyTank extends Tank {
    public MyTank(int x, int y) {
        super(x, y);
    }
}

EnenemyTank类

package TankBattalion.TankGame02;

public class EnemyTank extends Tank{
    public EnemyTank(int x, int y) {
        super(x, y);
    }
}

MyPanel

package TankBattalion.TankGame02;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;

//绘制Tank活动的区域
//02版本让Tank动起来以及绘制敌人的Tank
public class MyPanel extends JPanel implements KeyListener {
    //定义我的Tank
    MyTank myTank = null;
    int EnemyTanknums=3;
    //为了确保线程安全使用Vector数组
    Vector<EnemyTank> enemyTanks=new Vector<>();
    public MyPanel() {
        myTank = new MyTank(100,100);//初始化自己的Tank
        //初始化Tank移动移动的速度
        myTank.setSpeed(3);
        //将敌人Tank放入vector数组
        for(int i=0;i<EnemyTanknums;i++){
            EnemyTank enemyTank=new EnemyTank(100*(i+1),0);
            enemyTank.setDirection(2);
            enemyTanks.add(enemyTank);
        }
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //将画板填充,默认为黑色
        g.fillRect(0, 0, 1000, 750);
        drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 0);
        for(int i=0;i<enemyTanks.size();i++){
            drawTank(enemyTanks.get(i).getX(),enemyTanks.get(i).getY(),g,enemyTanks.get(i).getDirection(),1);
        }
    }

    //设置绘制Tank函数,需要获得Tank的初始位置,画笔g,direction坦克的方向,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) {
            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;
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }
    //根据键盘输入的WASD移动Tank
    @Override
    public void keyPressed(KeyEvent e) {
    if(e.getKeyCode()==KeyEvent.VK_W){
        //Tank朝上
        myTank.setDirection(0);
        //Tank向上移动
        myTank.MoveUp();
    }else if(e.getKeyCode()==KeyEvent.VK_D){
        //Tank朝右
        myTank.setDirection(1);
        //Tank向右移动
        myTank.MoveRight();
    }else if(e.getKeyCode()==KeyEvent.VK_S){
        //Tank朝下
        myTank.setDirection(2);
        //Tank向下移动
        myTank.MoveDown();
    }else if(e.getKeyCode()==KeyEvent.VK_A){
        //Tank朝左
        myTank.setDirection(3);
        //Tank向左移动
        myTank.MoveLeft();
    }
    //repaint方法别忘了
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}

TankGame02类

package TankBattalion.TankGame02;

import javax.swing.*;

//TankGame02目的是为了让Tank实现朝向的改变以及移动
//以及绘制敌人的Tank
public class TankGame02 extends JFrame {
    private MyPanel mp = null;

    public static void main(String[] args) {
        new TankBattalion.TankGame02.TankGame02();
    }

    public TankGame02() {
        mp = new MyPanel();
        this.add(mp);
        this.addKeyListener(mp);//让JFrame监听mp
        this.setSize(1000, 750);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }
}

3.0版本

新增功能:如何实现按J使得我们的Tank发射子弹
1.当发射一颗子弹后,就相当于启动一个线程
2. MyTank类有子弹的对象,当按下J时,我们就启动一个发射行为(线程),让子弹不停的移动,形成一个射击的效果
3.我们MyPanel需要不停的重绘,才能出现该效果.
4.当子弹移动到面板的边界时,就应该销毁

下列代码只罗列出有新增代码的类

1、新增类Bullet

package TankBattalion.TankGame03;
//3.0新增的子弹类
//将子弹看成是一个线程
public class Bullet implements Runnable{
    int x;
    int y;
    int directon=0;//子弹的方向,根据不同的方向,移动模式不同
    int speed=5;//设置子弹的速度
    //判断子弹线程是否已经消亡
     boolean isLive=true;
    public Bullet(int x, int y, int directon) {
        this.x = x;
        this.y = y;
        this.directon = directon;
    }
    @Override
    public void run() {
        while(true){
            //子弹需要进行休眠,不然移动太快了,看不见
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //子弹不断地向规定的方向移动
            switch(directon){
                case 0://上
                    y-=speed;
                    break;
                case 1://右
                    x+=speed;
                    break;
                case 2://下
                    y+=speed;
                    break;
                case 3://左
                    x-=speed;
                    break;
            }
            //规定当子弹触碰到画板边界消亡
            if(!(x>=0&&x<=1000&&y>=0&&y<=750)){
                System.out.println("子弹越界消亡");
                isLive=false;
                break;//break,代表当前的子弹线程销毁
            }
        }
    }
}

2、MyTank类新增方法shotEnenmy()

package TankBattalion.TankGame03;

public class MyTank extends Tank {
    //定义一个Bullet对象, 表示一个子弹(线程)
    Bullet bullet=null;
    public MyTank(int x, int y) {
        super(x, y);
    }
    //3.0新增
    //为MyTank类增加射出子弹方法
    public void shotEnemy(){
        //首先子弹需要根据MyTank炮管的位置来生成,不能乱生成
        switch (getDirection()){
            case 0://Tank朝上
                bullet=new Bullet(getX()+20,getY(),0);
                break;
            case 1://Tank朝右
                bullet=new Bullet(getX()+60,getY()+20,1);
                break;
            case 2://Tank朝下
                bullet=new Bullet(getX()+20,getY()+60,2);
                break;
            case 3://Tank朝左
                bullet=new Bullet(getX(),getY()+20,3);
                break;
        }
        //在这里启动bullet线程
        new Thread(bullet).start();
    }
}

3、MyPanel类paint()方法新增绘制子弹方法,以及将MyPanel当成线程不断刷新

package TankBattalion.TankGame03;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;

//绘制Tank活动的区域
//02版本让Tank动起来以及绘制敌人的Tank
//03版本实现MyTank可以发射子弹,并需要让画板不断刷新,得把画板看成是一个线程,来实现能看到这个子弹
public class MyPanel extends JPanel implements KeyListener, Runnable {
    //定义我的Tank
    MyTank myTank = null;
    int EnemyTanknums = 3;
    //为了确保线程安全使用Vector数组
    Vector<EnemyTank> enemyTanks = new Vector<>();

    public MyPanel() {
        myTank = new MyTank(100, 100);//初始化自己的Tank
        //初始化Tank移动移动的速度
        myTank.setSpeed(3);
        //将敌人Tank放入vector数组
        for (int i = 0; i < EnemyTanknums; i++) {
            EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0);
            enemyTank.setDirection(2);
            enemyTanks.add(enemyTank);
        }
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //将画板填充,默认为黑色
        g.fillRect(0, 0, 1000, 750);
        drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 0);


        //3.0新增,画出MyTank射出的子弹
        //为什么if()判定条件调换就错了???
        if (myTank.bullet != null && myTank.bullet.isLive == true) {
            g.drawOval(myTank.bullet.x, myTank.bullet.y, 5, 5);
            g.fillOval(myTank.bullet.x, myTank.bullet.y, 5, 5);
        }


        for (int i = 0; i < enemyTanks.size(); i++) {
            drawTank(enemyTanks.get(i).getX(), enemyTanks.get(i).getY(), g, enemyTanks.get(i).getDirection(), 1);
        }
    }

    //设置绘制Tank函数,需要获得Tank的初始位置,画笔g,direction坦克的方向,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) {
            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;
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //根据键盘输入的WASD移动Tank
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W) {
            //Tank朝上
            myTank.setDirection(0);
            //Tank向上移动
            myTank.MoveUp();
        } else if (e.getKeyCode() == KeyEvent.VK_D) {
            //Tank朝右
            myTank.setDirection(1);
            //Tank向右移动
            myTank.MoveRight();
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            //Tank朝下
            myTank.setDirection(2);
            //Tank向下移动
            myTank.MoveDown();
        } else if (e.getKeyCode() == KeyEvent.VK_A) {
            //Tank朝左
            myTank.setDirection(3);
            //Tank向左移动
            myTank.MoveLeft();
        }

        //3.0新增用户按下J就发射子弹
        if (e.getKeyCode() == KeyEvent.VK_J) {
            //按下J键实现发射子弹
            myTank.shotEnemy();
        }
        //repaint方法别忘了
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
    //3.0新增每隔 100毫秒,重绘区域, 刷新绘图区域, 子弹呈现移动状态
    @Override
    public void run() {
        //每隔100ms刷新
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.repaint();
        }
    }
}

4、TankGame03类新增启动MyPanel线程的方法

package TankBattalion.TankGame03;

import javax.swing.*;

//TankGame02目的是为了让Tank实现朝向的改变以及移动
//以及绘制敌人的Tank
public class TankGame03 extends JFrame {
    private MyPanel mp = null;

    public static void main(String[] args) {
        new TankGame03();
    }

    public TankGame03() {
        mp = new MyPanel();
        //3.0新增,把画板当成线程不断刷新
        Thread thread=new Thread(mp);
        thread.start();
        this.add(mp);
        this.addKeyListener(mp);//让JFrame监听mp
        this.setSize(1000, 750);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }
}

4.0版本

实现新功能:
一、 让敌人的坦克也能够发射子弹(可以有多颗子弹)
1.在敌人坦克类,使用Vector保存多个Bullet
2.当每创建一个敌人坦克对象,给该敌人坦克对象初始化一个Bullet对象,同时启动Bullet
3.在绘制敌人坦克时,需要变量敌人坦克对象Vector,绘制所有的子弹,当子弹isLive == false时,就从Vector移除

二、当我方tank击中敌人tank时敌人tank消失(最好有爆炸效果)
三、 让敌人的Tank也可以只有移动
四、让Tank在指定的范围内运动

代码:

新增Bomb类

package TankBattalion.TankGame04;
//4.0新增实现Tank爆炸效果
public class Bomb {
    //将Bomb的vector数组放在MyPanel类中,因为只在画板上绘制它,它不属于EnemyTank或者MyTank
    int x;
    int y;
    int life=9;
    boolean isLive=true;
    public Bomb(int x, int y) {
        this.x = x;
        this.y = y;
    }
    //减少生命值
    public void lifeDown(){//配合出现炸弹爆炸的效果
        if(life>0){
            life--;
        }
        else {
            isLive=false;
        }
    }
}

Bullet类

package TankBattalion.TankGame04;
//3.0新增的子弹类
//将子弹看成是一个线程
public class Bullet implements Runnable{
    int x;
    int y;
    int directon=0;//子弹的方向,根据不同的方向,移动模式不同
    int speed=5;//设置子弹的速度
    //判断子弹线程是否已经消亡
     boolean isLive=true;
    public Bullet(int x, int y, int directon) {
        this.x = x;
        this.y = y;
        this.directon = directon;
    }
    @Override
    public void run() {
        while(true){
            //子弹需要进行休眠,不然移动太快了,看不见
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //子弹不断地向规定的方向移动
            switch(directon){
                case 0://上
                    y-=speed;
                    break;
                case 1://右
                    x+=speed;
                    break;
                case 2://下
                    y+=speed;
                    break;
                case 3://左
                    x-=speed;
                    break;
            }
            //规定当子弹触碰到画板边界消亡
            //4.0新增当子弹碰到敌人Tank死亡
            if(!(x>=0&&x<=1000&&y>=0&&y<=750&&isLive)){
                System.out.println("子弹越界消亡");
                isLive=false;
                break;//break,代表当前的子弹线程销毁
            }
        }
    }
}

EnemyTank新增移动逻辑

package TankBattalion.TankGame04;

import java.util.Vector;
//4.0新增为了让Tank随机地自由移动,需要将Tank当成一个线程
public class EnemyTank extends Tank implements Runnable{
    //4.0新增,在EnemyTank类中加入Vector 数组存放子弹,敌人Tank的寿命
    boolean isLive=true;
    public Vector<Bullet> bullets =new Vector<>();
    public EnemyTank(int x, int y) {
        super(x, y);
    }
//这里继承父类的初始速度没设置,导致Tank一直动不了
    @Override
    public void run() {
        while (true) {
            //自由移动逻辑语句
            switch (getDirection()) {
                case 0:
                    //沿着当前方向继续移动30步
                    //注意需要进行休眠,不然直接瞬移
                    for (int i = 0; i < 30; i++) {
                        if(getY()>0){
                            MoveUp();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 1:
                    for (int i = 0; i < 30; i++) {
                        if(getX()+60<1000)
                        {
                            MoveRight();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 2:
                    for (int i = 0; i < 30; i++) {
                        if(getY()+60<750)
                        {
                            MoveDown();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3:
                    for (int i = 0; i < 30; i++) {
                        if(getX()>0)
                        {
                            MoveLeft();
                        }                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
            //随机转变方法[0,4)
            setDirection((int) (Math.random() * 4));
            //优先就必须考虑什么时候退出问题
            if (!isLive) {
                break;
            }
        }
    }
}

MyPanel类

package TankBattalion.TankGame04;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;

//绘制Tank活动的区域
//02版本让Tank动起来以及绘制敌人的Tank
//03版本实现MyTank可以发射子弹,并需要让画板不断刷新,得把画板看成是一个线程,来实现能看到这个子弹
public class MyPanel extends JPanel implements KeyListener, Runnable {
    //定义我的Tank
    MyTank myTank = null;
    int EnemyTanknums = 3;
    //为了确保线程安全使用Vector数组
    Vector<EnemyTank> enemyTanks = new Vector<>();
    //4.0新增炸弹数组
    Vector <Bomb> bombs=new Vector<>();
    //爆炸的三张图片
    Image image1=null;
    Image image2=null;
    Image image3=null;

    public MyPanel() {
        myTank = new MyTank(100, 100);//初始化自己的Tank
        //初始化Tank移动移动的速度
        myTank.setSpeed(3);
        //将敌人Tank放入vector数组
        for (int i = 0; i < EnemyTanknums; i++) {
            EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0);
            enemyTank.setDirection(2);
            //4.0新增,在敌人Tank创建时,启动线程
            new Thread(enemyTank).start();
            //4.0新增,每绘制一个EnenmyTank就新增一个Bullet加入数组
            Bullet bullet = new Bullet(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirection());
            enemyTank.bullets.add(bullet);
            //启动bullet线程
            new Thread(bullet).start();
            enemyTanks.add(enemyTank);
        }
        //初始化图片对象
        image1 = Toolkit.getDefaultToolkit().getImage("E:\\Java\\notebook\\out\\production\\bomb_1.gif");
        image2 = Toolkit.getDefaultToolkit().getImage("E:\\Java\\notebook\\out\\production\\bomb_2.gif");
        image3 = Toolkit.getDefaultToolkit().getImage("E:\\Java\\notebook\\out\\production\\bomb_3.gif");

    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //将画板填充,默认为黑色
        g.fillRect(0, 0, 1000, 750);
        drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 0);


        //3.0新增,画出MyTank射出的子弹
        //按下J键才会初始化myTank的bullet,所以需要先判定它是否为空,不然一直是false
        //为什么if()判定条件调换就错了???
        if (myTank.bullet != null && myTank.bullet.isLive == true) {
            g.drawOval(myTank.bullet.x, myTank.bullet.y, 5, 5);
            g.fillOval(myTank.bullet.x, myTank.bullet.y, 5, 5);
        }

        //在绘制敌人坦克时,需要遍历敌人坦克对象Vector,绘制所有的子弹,当子弹isLive == false时,就从Vector移除
        for (int i = 0; i < enemyTanks.size(); i++) {
            //从Vector 取出坦克
            EnemyTank enemyTank = enemyTanks.get(i);

            //4.0新增,只有当enemyTank活着时,才绘制它
            if (enemyTank.isLive) {
                drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 1);
                //画出 enemyTank 所有子弹
                for (int j = 0; j < enemyTank.bullets.size(); j++) {
                    //取出子弹
                    Bullet bullet = enemyTank.bullets.get(j);
                    //绘制
                    if (bullet.isLive) { //isLive == true
                        g.draw3DRect(bullet.x, bullet.y, 1, 1, false);
                    } else {
                        //从Vector 移除
                        enemyTank.bullets.remove(bullet);
                    }
                }
            }
        }
        //4.0新增绘制爆炸效果
        for(int i=0;i<bombs.size();i++){
        //取出炸弹
            Bomb bomb=bombs.get(i);
            if(bomb.life>6){
                g.drawImage(image1, bomb.x, bomb.y, 60, 60, this);
            }else if(bomb.life>3){
                g.drawImage(image2, bomb.x, bomb.y, 60, 60, this);
            }else {
                g.drawImage(image3, bomb.x, bomb.y, 60, 60, this);
            }
            //  bomb的生命值不断减少
            bomb.lifeDown();
            if(bomb.life==0){
                bombs.remove(bomb);
            }
        }
    }

    //设置绘制Tank函数,需要获得Tank的初始位置,画笔g,direction坦克的方向,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) {
            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;
        }
    }

    //4.0新增,判断子弹是否击中敌方Tank
    public  void hitTank(Bullet bullet, EnemyTank enemyTank) {
        //根据敌人tank方向的不同,判断子弹是否在tank的范围内
        switch (enemyTank.getDirection()) {
            case 0:
            case 2://上下方向是一样的
                if (bullet.x > enemyTank.getX() && bullet.x < enemyTank.getX() + 40
                        && bullet.y > enemyTank.getY() && bullet.y < enemyTank.getY() + 60) {
                    enemyTank.isLive = false;
                    bullet.isLive = false;//子弹消亡

                    //别忘了将enemyTank从vector数组中拿出来,不然子弹命中Tank位置,虽然Tank消失,但是任出现爆炸效果
                    enemyTanks.remove(enemyTank);

                    //4.0新增根据死亡的Tank位置生成炸弹
                    Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());
                    bombs.add(bomb);
                }
                break;
            case 1:
            case 3://向左和向右
                if (bullet.x > enemyTank.getX() && bullet.x < enemyTank.getX() + 60
                        && bullet.y > enemyTank.getY() && bullet.y < enemyTank.getY() + 40) {
                    enemyTank.isLive = false;
                    bullet.isLive = false;
                    enemyTanks.remove(enemyTank);
                    Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());
                    bombs.add(bomb);
                }
                break;
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //根据键盘输入的WASD移动Tank
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W) {
            //Tank朝上
            myTank.setDirection(0);
            //Tank向上移动
            if(myTank.getY()>0)
            myTank.MoveUp();
        } else if (e.getKeyCode() == KeyEvent.VK_D) {
            //Tank朝右
            myTank.setDirection(1);
            //Tank向右移动
            if(myTank.getX()+60<1000)
            myTank.MoveRight();
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            //Tank朝下
            myTank.setDirection(2);
            //Tank向下移动
            if(myTank.getY()+60<750)
            myTank.MoveDown();
        } else if (e.getKeyCode() == KeyEvent.VK_A) {
            //Tank朝左
            myTank.setDirection(3);
            //Tank向左移动
            if(myTank.getX()>0)
            myTank.MoveLeft();
        }

        //3.0新增用户按下J就发射子弹
        if (e.getKeyCode() == KeyEvent.VK_J) {
            //按下J键实现发射子弹
            myTank.shotEnemy();
        }
        //repaint方法别忘了
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    //3.0新增每隔 100毫秒,重绘区域, 刷新绘图区域, 子弹呈现移动状态
    //4.0新增在刷新途中不断判断子弹是否命中敌人Tank
    @Override
    public void run() {
        //每隔100ms刷新
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //按下J键才会初始化myTank的bullet,所以需要先判定它是否为空,不然一直是false
            if (myTank.bullet != null && myTank.bullet.isLive) {
                for (int i = 0; i < enemyTanks.size(); i++) {
                    EnemyTank tank = enemyTanks.get(i);
                    //遍历Enemy数组,看子弹是否命中其中一只
                    hitTank(myTank.bullet, tank);
                }

            }
            this.repaint();
        }
    }
}

Tank、MyTank、TankGame04类未发生改动,此处不再赘述

5.0版本

一、首先实现MyTank在原有子弹消亡后才可以发射新子弹的功能
接着近一步实现可以发射多颗子弹的功能
线程消亡不代表MyTank.bullet就置空
在这里插入图片描述
二、让敌人Tank可以发射多枚子弹且子弹最多为5枚
注意点:这里一定要规定在EnemyTank存活的条件下才可以发射子弹,不然死了还能发射子弹

代码

EnemyTank新增发射多颗子弹功能

package TankBattalion.TankGame04;

import java.util.Vector;
//4.0新增为了让Tank随机地自由移动,需要将Tank当成一个线程
public class EnemyTank extends Tank implements Runnable{
    //4.0新增,在EnemyTank类中加入Vector 数组存放子弹,敌人Tank的寿命
    boolean isLive=true;
    Bullet bullet=null;
    public Vector<Bullet> bullets =new Vector<>();
    public EnemyTank(int x, int y) {
        super(x, y);
    }
//这里继承父类的初始速度没设置,导致Tank一直动不了
    @Override
    public void run() {
        while (true) {
            //5.0新增敌人Tank可以发射多枚子弹
            //注意一定是Tank存活的条件下
            if(isLive&&bullets.size()<5){
                switch (getDirection()){
                    case 0://Tank朝上
                        bullet=new Bullet(getX()+20,getY(),0);
                        break;
                    case 1://Tank朝右
                        bullet=new Bullet(getX()+60,getY()+20,1);
                        break;
                    case 2://Tank朝下
                        bullet=new Bullet(getX()+20,getY()+60,2);
                        break;
                    case 3://Tank朝左
                        bullet=new Bullet(getX(),getY()+20,3);
                        break;
                }
                //记得将子弹加入数组,并且启动子弹线程
                bullets.add(bullet);
                new Thread(bullet).start();
            }
            //自由移动逻辑语句
            switch (getDirection()) {
                case 0:
                    //沿着当前方向继续移动30步
                    //注意需要进行休眠,不然直接瞬移
                    for (int i = 0; i < 30; i++) {
                        if(getY()>0){
                            MoveUp();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 1:
                    for (int i = 0; i < 30; i++) {
                        if(getX()+60<1000)
                        {
                            MoveRight();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 2:
                    for (int i = 0; i < 30; i++) {
                        if(getY()+60<750)
                        {
                            MoveDown();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3:
                    for (int i = 0; i < 30; i++) {
                        if(getX()>0)
                        {
                            MoveLeft();
                        }                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
            //随机转变方法[0,4)
            setDirection((int) (Math.random() * 4));
            //优先就必须考虑什么时候退出问题
            if (!isLive) {
                break;
            }
        }
    }
}

MyPanel类

package TankBattalion.TankGame04;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;

//绘制Tank活动的区域
//02版本让Tank动起来以及绘制敌人的Tank
//03版本实现MyTank可以发射子弹,并需要让画板不断刷新,得把画板看成是一个线程,来实现能看到这个子弹
public class MyPanel extends JPanel implements KeyListener, Runnable {
    //定义我的Tank
    MyTank myTank = null;
    int EnemyTanknums = 3;
    //为了确保线程安全使用Vector数组
    Vector<EnemyTank> enemyTanks = new Vector<>();
    //4.0新增炸弹数组
    Vector<Bomb> bombs = new Vector<>();
    //爆炸的三张图片
    Image image1 = null;
    Image image2 = null;
    Image image3 = null;

    public MyPanel() {
        myTank = new MyTank(100, 100);//初始化自己的Tank
        //初始化Tank移动移动的速度
        myTank.setSpeed(3);
        //将敌人Tank放入vector数组
        for (int i = 0; i < EnemyTanknums; i++) {
            EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0);
            enemyTank.setDirection(2);
            //4.0新增,在敌人Tank创建时,启动线程
            new Thread(enemyTank).start();
            //5.0新增这里Tank设计逻辑太呆了出生时就发射子弹,且与Tank出生方向一致,应该做到随机
//            //4.0新增,每绘制一个EnenmyTank就新增一个Bullet加入数组
//            Bullet bullet = new Bullet(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirection());
//            enemyTank.bullets.add(bullet);
//            //启动bullet线程
//            new Thread(bullet).start();
            enemyTanks.add(enemyTank);
        }
        //初始化图片对象
        image1 = Toolkit.getDefaultToolkit().getImage("E:\\Java\\notebook\\out\\production\\bomb_1.gif");
        image2 = Toolkit.getDefaultToolkit().getImage("E:\\Java\\notebook\\out\\production\\bomb_2.gif");
        image3 = Toolkit.getDefaultToolkit().getImage("E:\\Java\\notebook\\out\\production\\bomb_3.gif");

    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //将画板填充,默认为黑色
        g.fillRect(0, 0, 1000, 750);
        if (myTank.isLive) {
            drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 0);
        }

        //3.0新增,画出MyTank射出的子弹
        //按下J键才会初始化myTank的bullet,所以需要先判定它是否为空,不然一直是false
        //为什么if()判定条件调换就错了???
        //5.0新增绘制多枚子弹
        for (int i = 0; i < myTank.bullets.size(); i++) {
            Bullet bullet = myTank.bullets.get(i);
            if (bullet != null && bullet.isLive == true) {
                g.drawOval(bullet.x, bullet.y, 5, 5);
                g.fillOval(bullet.x, bullet.y, 5, 5);
            }
            //别忘了将已经消亡的子弹从bullets数组中取出,不然一只Tank一辈子只能打5发子弹
            else {
                myTank.bullets.remove(bullet);
            }
        }
        //在绘制敌人坦克时,需要遍历敌人坦克对象Vector,绘制所有的子弹,当子弹isLive == false时,就从Vector移除
        for (int i = 0; i < enemyTanks.size(); i++) {
            //从Vector 取出坦克
            EnemyTank enemyTank = enemyTanks.get(i);

            //4.0新增,只有当enemyTank活着时,才绘制它
            if (enemyTank.isLive) {
                drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 1);
                //5.0新增,画出 enemyTank 所有子弹
                for (int j = 0; j < enemyTank.bullets.size(); j++) {
                    //取出子弹
                    Bullet bullet = enemyTank.bullets.get(j);
                    //绘制
                    if (bullet.isLive) { //isLive == true
                        g.draw3DRect(bullet.x, bullet.y, 1, 1, false);
                    } else {
                        //从Vector 移除
                        enemyTank.bullets.remove(bullet);
                    }
                }
            }
        }
        //4.0新增绘制爆炸效果
        for (int i = 0; i < bombs.size(); i++) {
            //取出炸弹
            Bomb bomb = bombs.get(i);
            if (bomb.life > 6) {
                g.drawImage(image1, bomb.x, bomb.y, 60, 60, this);
            } else if (bomb.life > 3) {
                g.drawImage(image2, bomb.x, bomb.y, 60, 60, this);
            } else {
                g.drawImage(image3, bomb.x, bomb.y, 60, 60, this);
            }
            //  bomb的生命值不断减少
            bomb.lifeDown();
            if (bomb.life == 0) {
                bombs.remove(bomb);
            }
        }
    }

    //设置绘制Tank函数,需要获得Tank的初始位置,画笔g,direction坦克的方向,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) {
            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;
        }
    }

    //4.0新增,判断子弹是否击中Tank
    //5.0将原本的参数EnemyTank enemyTank 改为Tank tank,这样就可以写hitMyTank和hitEnemyTank方法了
    public void hitTank(Bullet bullet, Tank tank) {
        //根据敌人tank方向的不同,判断子弹是否在tank的范围内
        switch (tank.getDirection()) {
            case 0:
            case 2://上下方向是一样的
                if (bullet.x > tank.getX() && bullet.x < tank.getX() + 40
                        && bullet.y > tank.getY() && bullet.y < tank.getY() + 60) {
                    tank.isLive = false;
                    bullet.isLive = false;//子弹消亡

                    //别忘了将enemyTank从vector数组中拿出来,不然子弹命中Tank位置,虽然Tank消失,但是任出现爆炸效果
                    enemyTanks.remove(tank);

                    //4.0新增根据死亡的Tank位置生成炸弹
                    Bomb bomb = new Bomb(tank.getX(), tank.getY());
                    bombs.add(bomb);
                }
                break;
            case 1:
            case 3://向左和向右
                if (bullet.x > tank.getX() && bullet.x < tank.getX() + 60
                        && bullet.y > tank.getY() && bullet.y < tank.getY() + 40) {
                    tank.isLive = false;
                    bullet.isLive = false;
                    enemyTanks.remove(tank);
                    Bomb bomb = new Bomb(tank.getX(), tank.getY());
                    bombs.add(bomb);
                }
                break;
        }
    }

    //5.0新增因为原方法是取出单独的子弹与enemyTank集合中的所有Tank一一比较,判断有没有命中
    //这样设计,现在可以发射多枚子弹,容易产生bullets集合中的一枚子弹击中了Tank,但没有爆炸的BUG
    //解决方案是将bullets集合中所有子弹取出与enemyTank中所有Tank一一比较
    public void hitEnemyTank() {
//        //单颗子弹
//        if (myTank.bullet != null && myTank.bullet.isLive) {
//            for (int i = 0; i < enemyTanks.size(); i++) {
//                EnemyTank tank = enemyTanks.get(i);
//                //遍历Enemy数组,看子弹是否命中其中一只
//                hitTank(myTank.bullet, tank);
//            }
//
//        }
        //多颗子弹
        for (int i = 0; i < myTank.bullets.size(); i++) {
            Bullet bullet = myTank.bullets.get(i);
            if (bullet != null && bullet.isLive) {
                for (int j = 0; j < enemyTanks.size(); j++) {
                    EnemyTank tank = enemyTanks.get(j);
                    //遍历Enemy数组,看子弹是否命中其中一只
                    hitTank(bullet, tank);
                }
            }
        }
    }

    public void hitMyTank() {
        for (int i = 0; i < enemyTanks.size(); i++) {
            EnemyTank enemyTank = enemyTanks.get(i);
            for (int j = 0; j < enemyTank.bullets.size(); j++) {
                Bullet bullet = enemyTank.bullets.get(j);
                if (myTank.isLive&&bullet != null && bullet.isLive) {
                    hitTank(bullet, myTank);
                }
            }
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //根据键盘输入的WASD移动Tank
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W) {
            //Tank朝上
            myTank.setDirection(0);
            //Tank向上移动
            if (myTank.getY() > 0)
                myTank.MoveUp();
        } else if (e.getKeyCode() == KeyEvent.VK_D) {
            //Tank朝右
            myTank.setDirection(1);
            //Tank向右移动
            if (myTank.getX() + 60 < 1000)
                myTank.MoveRight();
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            //Tank朝下
            myTank.setDirection(2);
            //Tank向下移动
            if (myTank.getY() + 60 < 750)
                myTank.MoveDown();
        } else if (e.getKeyCode() == KeyEvent.VK_A) {
            //Tank朝左
            myTank.setDirection(3);
            //Tank向左移动
            if (myTank.getX() > 0)
                myTank.MoveLeft();
        }

        //3.0新增用户按下J就发射子弹
        if (e.getKeyCode() == KeyEvent.VK_J) {
            //按下J键实现发射子弹
            myTank.shotEnemy();
        }
        //repaint方法别忘了
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    //3.0新增每隔 100毫秒,重绘区域, 刷新绘图区域, 子弹呈现移动状态
    //4.0新增在刷新途中不断判断子弹是否命中敌人Tank
    @Override
    public void run() {
        //每隔100ms刷新
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            hitEnemyTank();
            hitMyTank();
            this.repaint();
        }
    }
}

MyTank类新增发射多颗子弹功能

package TankBattalion.TankGame04;

import java.util.Vector;

public class MyTank extends Tank {
    //5.0新增功能让MyTank可以发射多枚子弹,并且限定为5颗
    Vector<Bullet> bullets= new Vector<>();
    //定义一个Bullet对象, 表示一个子弹(线程)
    Bullet bullet=null;
    public MyTank(int x, int y) {
        super(x, y);
    }
    //3.0新增
    //为MyTank类增加射出子弹方法
    public void shotEnemy(){
        //限定5颗子弹,子弹数目过5就不再new
        if(bullets.size()==5){
            return ;
        }
        //首先子弹需要根据MyTank炮管的位置来生成,不能乱生成
        switch (getDirection()){
            case 0://Tank朝上
                bullet=new Bullet(getX()+20,getY(),0);
                break;
            case 1://Tank朝右
                bullet=new Bullet(getX()+60,getY()+20,1);
                break;
            case 2://Tank朝下
                bullet=new Bullet(getX()+20,getY()+60,2);
                break;
            case 3://Tank朝左
                bullet=new Bullet(getX(),getY()+20,3);
                break;
        }
        //5.0新增将新生成的Bullet加入vector数组
        bullets.add(bullet);
        //在这里启动bullet线程
        new Thread(bullet).start();
    }
}

其余类不变

6.0版本

新增功能:
1.防止敌人坦克重叠运动
2.记录玩家的成绩,存盘退出【io流】
3.记录当时的敌人坦克坐标,存盘退出【io流】
4、玩游戏时,可以选择是开新游戏还是继续上局游戏

为了防止Tank重叠

可以将其分为以下八种情况
在这里插入图片描述

记录玩家的成绩

记录退出游戏时敌人Tank的坐标和方向

在这里插入图片描述

播放指定音乐

最终代码

AePlayWave类

package TankBattalion.TankGame06;

import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
//6.0新增播放音乐功能
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();
        }

    }
}

Bomb类

package TankBattalion.TankGame06;
//4.0新增实现Tank爆炸效果
public class Bomb {
    //将Bomb的vector数组放在MyPanel类中,因为只在画板上绘制它,它不属于EnemyTank或者MyTank
    int x;
    int y;
    int life=9;
    boolean isLive=true;
    public Bomb(int x, int y) {
        this.x = x;
        this.y = y;
    }
    //减少生命值
    public void lifeDown(){//配合出现炸弹爆炸的效果
        if(life>0){
            life--;
        }
        else {
            isLive=false;
        }
    }
}

Bullet类

package TankBattalion.TankGame06;
//3.0新增的子弹类
//将子弹看成是一个线程
public class Bullet implements Runnable{
    int x;
    int y;
    int directon=0;//子弹的方向,根据不同的方向,移动模式不同
    int speed=5;//设置子弹的速度
    //判断子弹线程是否已经消亡
     boolean isLive=true;
    public Bullet(int x, int y, int directon) {
        this.x = x;
        this.y = y;
        this.directon = directon;
    }
    @Override
    public void run() {
        while(true){
            //子弹需要进行休眠,不然移动太快了,看不见
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //子弹不断地向规定的方向移动
            switch(directon){
                case 0://上
                    y-=speed;
                    break;
                case 1://右
                    x+=speed;
                    break;
                case 2://下
                    y+=speed;
                    break;
                case 3://左
                    x-=speed;
                    break;
            }
            //规定当子弹触碰到画板边界消亡
            //4.0新增当子弹碰到敌人Tank死亡
            if(!(x>=0&&x<=1000&&y>=0&&y<=750&&isLive)){
                System.out.println("子弹越界消亡");
                isLive=false;
                break;//break,代表当前的子弹线程销毁
            }
        }
    }
}

EnemyTank类

package TankBattalion.TankGame06;

import java.util.Vector;

//4.0新增为了让Tank随机地自由移动,需要将Tank当成一个线程
public class EnemyTank extends Tank implements Runnable {
    //4.0新增,在EnemyTank类中加入Vector 数组存放子弹,敌人Tank的寿命
    boolean isLive = true;
    Bullet bullet = null;
    public Vector<Bullet> bullets = new Vector<>();

    public EnemyTank(int x, int y) {
        super(x, y);
    }

    public Vector<EnemyTank> enemyTanks = new Vector<>();

    //6.0新增功能实现EnemyTank无法出现重叠状况
    //为了让敌人Tank和Vector集合中其他敌人Tank进行坐标比较需要获取创建的Vector enemyTank集合,
    // 这里定义一个方法,在MyPanel中使用,创建一个敌人Tank就调用一次该方法
    public void setEnemyTanks(Vector<EnemyTank> enemyTanks) {
        this.enemyTanks = enemyTanks;
    }

    //判断是否碰撞的方法
    public boolean isTouchEnemyTank() {

        //判断当前敌人坦克(this) 方向
        switch (this.getDirection()) {
            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.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++) {
                    //从vector 中取出一个敌人坦克
                    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++) {
                    //从vector 中取出一个敌人坦克
                    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++) {
                    //从vector 中取出一个敌人坦克
                    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;
    }

    //这里继承父类的初始速度没设置,导致Tank一直动不了
    @Override
    public void run() {
        while (true) {
            //5.0新增敌人Tank可以发射多枚子弹
            //注意一定是Tank存活的条件下
            if (isLive && bullets.size() < 5) {
                switch (getDirection()) {
                    case 0://Tank朝上
                        bullet = new Bullet(getX() + 20, getY(), 0);
                        break;
                    case 1://Tank朝右
                        bullet = new Bullet(getX() + 60, getY() + 20, 1);
                        break;
                    case 2://Tank朝下
                        bullet = new Bullet(getX() + 20, getY() + 60, 2);
                        break;
                    case 3://Tank朝左
                        bullet = new Bullet(getX(), getY() + 20, 3);
                        break;
                }
                //记得将子弹加入数组,并且启动子弹线程
                bullets.add(bullet);
                new Thread(bullet).start();
            }
            //自由移动逻辑语句
            //6.0新增在不碰撞敌人Tank的基础上才能继续移动
            switch (getDirection()) {
                case 0:
                    //沿着当前方向继续移动30步
                    //注意需要进行休眠,不然直接瞬移
                    for (int i = 0; i < 30; i++) {
                        if (getY() > 0 && !isTouchEnemyTank()) {
                            MoveUp();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 1:
                    for (int i = 0; i < 30; i++) {
                        if (getX() + 60 < 1000 && !isTouchEnemyTank()) {
                            MoveRight();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 2:
                    for (int i = 0; i < 30; i++) {
                        if (getY() + 60 < 750 && !isTouchEnemyTank()) {
                            MoveDown();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3:
                    for (int i = 0; i < 30; i++) {
                        if (getX() > 0 && !isTouchEnemyTank()) {
                            MoveLeft();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
            //随机转变方法[0,4)
            setDirection((int) (Math.random() * 4));
            //优先就必须考虑什么时候退出问题
            if (!isLive) {
                break;
            }
        }
    }
}

MyPanel类

package TankBattalion.TankGame06;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;

//绘制Tank活动的区域
//02版本让Tank动起来以及绘制敌人的Tank
//03版本实现MyTank可以发射子弹,并需要让画板不断刷新,得把画板看成是一个线程,来实现能看到这个子弹
public class MyPanel extends JPanel implements KeyListener, Runnable {
    //定义我的Tank
    MyTank myTank = null;
    int EnemyTanknums = 3;
    //为了确保线程安全使用Vector数组
    Vector<EnemyTank> enemyTanks = new Vector<>();
    //6.0定义node集合接受信息
    Vector<Node> node=null;
    //4.0新增炸弹数组
    Vector<Bomb> bombs = new Vector<>();
    //爆炸的三张图片
    Image image1 = null;
    Image image2 = null;
    Image image3 = null;
    //6.0定义MyPanel(String Key)方法定义按照上局信息生成对战的方法
    public MyPanel(String Key) {
        //6.0定义node接受上局的Tank信息
        node=Recorder.getNodeAndhitnums();
        //6.0将Mypanel类的enemyTanks传给Recorder类
        Recorder.setEnemyTanks(enemyTanks);
        myTank = new MyTank(800, 100);//初始化自己的Tank
        //初始化Tank移动移动的速度
        myTank.setSpeed(3);
        switch (Key) {
            case "1"://重新开始
                //将敌人Tank放入vector数组
                for (int i = 0; i < EnemyTanknums; i++) {
                    EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0);
                    //6.0新增每创建一个enemyTank对象,就调用setEnemyTanks方法给新创建的Tank对象传递Vector enemyTanks集合
                    enemyTank.setEnemyTanks(enemyTanks);
                    enemyTank.setDirection(2);
                    //4.0新增,在敌人Tank创建时,启动线程
                    new Thread(enemyTank).start();
                    //5.0新增这里Tank设计逻辑太呆了出生时就发射子弹,且与Tank出生方向一致,应该做到随机
//            //4.0新增,每绘制一个EnenmyTank就新增一个Bullet加入数组
//            Bullet bullet = new Bullet(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirection());
//            enemyTank.bullets.add(bullet);
//            //启动bullet线程
//            new Thread(bullet).start();
                    enemyTanks.add(enemyTank);
                }
                break;
            case "2"://继续上把
                //初始化敌人坦克
                for (int i = 0; i < node.size(); i++) {
                    Node n = node.get(i);
                    //创建一个敌人的坦克
                    EnemyTank enemyTank = new EnemyTank(n.getX(), n.getY());
                    //将enemyTanks 设置给 enemyTank !!!
                    enemyTank.setEnemyTanks(enemyTanks);
                    //设置方向
                    enemyTank.setDirection(n.getDirection());
                    //启动敌人坦克线程,让他动起来
                    new Thread(enemyTank).start();
                    //给该enemyTank 加入一颗子弹
                    Bullet shot = new Bullet(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirection());
                    //加入enemyTank的Vector 成员
                    enemyTank.bullets.add(shot);
                    //启动 shot 对象
                    new Thread(shot).start();
                    //加入
                    enemyTanks.add(enemyTank);
                }
                break;
        }
        //初始化图片对象
        image1 = Toolkit.getDefaultToolkit().getImage("E:\\Java\\notebook\\out\\production\\bomb_1.gif");
        image2 = Toolkit.getDefaultToolkit().getImage("E:\\Java\\notebook\\out\\production\\bomb_2.gif");
        image3 = Toolkit.getDefaultToolkit().getImage("E:\\Java\\notebook\\out\\production\\bomb_3.gif");
        //6.0新增播放音乐功能
        String musicfilename="E:\\Java\\notebook\\src\\TankBattalion\\111.wav";
        new AePlayWave(musicfilename).start();
    }
    //6.0编写记录玩家总成绩的方法
    public void record(Graphics g){
        g.setColor(Color.black);
        g.setFont(new Font("宋体",Font.BOLD,30));
        g.drawString("您累积击毁敌方坦克",1020,30);
        //绘制一个敌人Tank图形
        drawTank(1020,60,g,0,1);
        //绘制击败Tank数目
        g.setColor(Color.black);
        g.drawString(Recorder.getHitnums()+"",1080,100);
    }
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //将画板填充,默认为黑色
        g.fillRect(0, 0, 1000, 750);
        record(g);
        if (myTank.isLive) {
            drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 0);
        }

        //3.0新增,画出MyTank射出的子弹
        //按下J键才会初始化myTank的bullet,所以需要先判定它是否为空,不然一直是false
        //为什么if()判定条件调换就错了???
        //5.0新增绘制多枚子弹
        for (int i = 0; i < myTank.bullets.size(); i++) {
            Bullet bullet = myTank.bullets.get(i);
            if (bullet != null && bullet.isLive == true) {
                g.drawOval(bullet.x, bullet.y, 5, 5);
                g.fillOval(bullet.x, bullet.y, 5, 5);
            }
            //别忘了将已经消亡的子弹从bullets数组中取出,不然一只Tank一辈子只能打5发子弹
            else {
                myTank.bullets.remove(bullet);
            }
        }
        //在绘制敌人坦克时,需要遍历敌人坦克对象Vector,绘制所有的子弹,当子弹isLive == false时,就从Vector移除
        for (int i = 0; i < enemyTanks.size(); i++) {
            //从Vector 取出坦克
            EnemyTank enemyTank = enemyTanks.get(i);

            //4.0新增,只有当enemyTank活着时,才绘制它
            if (enemyTank.isLive) {
                drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 1);
                //5.0新增,画出 enemyTank 所有子弹
                for (int j = 0; j < enemyTank.bullets.size(); j++) {
                    //取出子弹
                    Bullet bullet = enemyTank.bullets.get(j);
                    //绘制
                    if (bullet.isLive) { //isLive == true
                        g.draw3DRect(bullet.x, bullet.y, 1, 1, false);
                    } else {
                        //从Vector 移除
                        enemyTank.bullets.remove(bullet);
                    }
                }
            }
        }
        //4.0新增绘制爆炸效果
        for (int i = 0; i < bombs.size(); i++) {
            //取出炸弹
            Bomb bomb = bombs.get(i);
            if (bomb.life > 6) {
                g.drawImage(image1, bomb.x, bomb.y, 60, 60, this);
            } else if (bomb.life > 3) {
                g.drawImage(image2, bomb.x, bomb.y, 60, 60, this);
            } else {
                g.drawImage(image3, bomb.x, bomb.y, 60, 60, this);
            }
            //  bomb的生命值不断减少
            bomb.lifeDown();
            if (bomb.life == 0) {
                bombs.remove(bomb);
            }
        }
    }

    //设置绘制Tank函数,需要获得Tank的初始位置,画笔g,direction坦克的方向,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) {
            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;
        }
    }

    //4.0新增,判断子弹是否击中Tank
    //5.0将原本的参数EnemyTank enemyTank 改为Tank tank,这样就可以写hitMyTank和hitEnemyTank方法了
    public void hitTank(Bullet bullet, Tank tank) {
        //根据敌人tank方向的不同,判断子弹是否在tank的范围内
        switch (tank.getDirection()) {
            case 0:
            case 2://上下方向是一样的
                if (bullet.x > tank.getX() && bullet.x < tank.getX() + 40
                        && bullet.y > tank.getY() && bullet.y < tank.getY() + 60) {
                    tank.isLive = false;
                    bullet.isLive = false;//子弹消亡
                    //6.0新增假如击中的不是我方Tank,hitnums++
                    if(tank instanceof EnemyTank) {
                        Recorder.addAllEnemyTankNum();
                    }
                    //别忘了将enemyTank从vector数组中拿出来,不然子弹命中Tank位置,虽然Tank消失,但是任出现爆炸效果
                    enemyTanks.remove(tank);

                    //4.0新增根据死亡的Tank位置生成炸弹
                    Bomb bomb = new Bomb(tank.getX(), tank.getY());
                    bombs.add(bomb);
                }
                break;
            case 1:
            case 3://向左和向右
                if (bullet.x > tank.getX() && bullet.x < tank.getX() + 60
                        && bullet.y > tank.getY() && bullet.y < tank.getY() + 40) {
                    tank.isLive = false;
                    bullet.isLive = false;
                    //6.0新增假如击中的不是我方Tank,hitnums++
                    if(tank instanceof EnemyTank) {
                        Recorder.addAllEnemyTankNum();
                    }
                    enemyTanks.remove(tank);
                    Bomb bomb = new Bomb(tank.getX(), tank.getY());
                    bombs.add(bomb);
                }
                break;
        }
    }

    //5.0新增因为原方法是取出单独的子弹与enemyTank集合中的所有Tank一一比较,判断有没有命中
    //这样设计,现在可以发射多枚子弹,容易产生bullets集合中的一枚子弹击中了Tank,但没有爆炸的BUG
    //解决方案是将bullets集合中所有子弹取出与enemyTank中所有Tank一一比较
    public void hitEnemyTank() {
//        //单颗子弹
//        if (myTank.bullet != null && myTank.bullet.isLive) {
//            for (int i = 0; i < enemyTanks.size(); i++) {
//                EnemyTank tank = enemyTanks.get(i);
//                //遍历Enemy数组,看子弹是否命中其中一只
//                hitTank(myTank.bullet, tank);
//            }
//
//        }
        //多颗子弹
        for (int i = 0; i < myTank.bullets.size(); i++) {
            Bullet bullet = myTank.bullets.get(i);
            if (bullet != null && bullet.isLive) {
                for (int j = 0; j < enemyTanks.size(); j++) {
                    EnemyTank tank = enemyTanks.get(j);
                    //遍历Enemy数组,看子弹是否命中其中一只
                    hitTank(bullet, tank);
                }
            }
        }
    }

    public void hitMyTank() {
        for (int i = 0; i < enemyTanks.size(); i++) {
            EnemyTank enemyTank = enemyTanks.get(i);
            for (int j = 0; j < enemyTank.bullets.size(); j++) {
                Bullet bullet = enemyTank.bullets.get(j);
                if (myTank.isLive && bullet != null && bullet.isLive) {
                    hitTank(bullet, myTank);
                }
            }
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //根据键盘输入的WASD移动Tank
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W) {
            //Tank朝上
            myTank.setDirection(0);
            //Tank向上移动
            if (myTank.getY() > 0)
                myTank.MoveUp();
        } else if (e.getKeyCode() == KeyEvent.VK_D) {
            //Tank朝右
            myTank.setDirection(1);
            //Tank向右移动
            if (myTank.getX() + 60 < 1000)
                myTank.MoveRight();
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            //Tank朝下
            myTank.setDirection(2);
            //Tank向下移动
            if (myTank.getY() + 60 < 750)
                myTank.MoveDown();
        } else if (e.getKeyCode() == KeyEvent.VK_A) {
            //Tank朝左
            myTank.setDirection(3);
            //Tank向左移动
            if (myTank.getX() > 0)
                myTank.MoveLeft();
        }

        //3.0新增用户按下J就发射子弹
        if (e.getKeyCode() == KeyEvent.VK_J) {
            //按下J键实现发射子弹
            myTank.shotEnemy();
        }
        //repaint方法别忘了
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    //3.0新增每隔 100毫秒,重绘区域, 刷新绘图区域, 子弹呈现移动状态
    //4.0新增在刷新途中不断判断子弹是否命中敌人Tank
    @Override
    public void run() {
        //每隔100ms刷新
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            hitEnemyTank();
            hitMyTank();
            this.repaint();
        }
    }
}

MyTank类

package TankBattalion.TankGame06;

import java.util.Vector;

public class MyTank extends Tank {
    //5.0新增功能让MyTank可以发射多枚子弹,并且限定为5颗
    Vector<Bullet> bullets= new Vector<>();
    //定义一个Bullet对象, 表示一个子弹(线程)
    Bullet bullet=null;
    public MyTank(int x, int y) {
        super(x, y);
    }
    //3.0新增
    //为MyTank类增加射出子弹方法
    public void shotEnemy(){
        //限定5颗子弹,子弹数目过5就不再new
        if(bullets.size()==5){
            return ;
        }
        //首先子弹需要根据MyTank炮管的位置来生成,不能乱生成
        switch (getDirection()){
            case 0://Tank朝上
                bullet=new Bullet(getX()+20,getY(),0);
                break;
            case 1://Tank朝右
                bullet=new Bullet(getX()+60,getY()+20,1);
                break;
            case 2://Tank朝下
                bullet=new Bullet(getX()+20,getY()+60,2);
                break;
            case 3://Tank朝左
                bullet=new Bullet(getX(),getY()+20,3);
                break;
        }
        //5.0新增将新生成的Bullet加入vector数组
        bullets.add(bullet);
        //在这里启动bullet线程
        new Thread(bullet).start();
    }
}

Node类

package TankBattalion.TankGame06;

public class Node {
    int x;
    int y;
    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;
    }
}

Recorder类

package TankBattalion.TankGame06;

import java.io.*;
import java.util.Vector;

public class Recorder {
    //用于保存我方击败Tank数量
    //在关闭界面后将数据保存
    private static int hitnums=0;
    private static BufferedWriter bfw=null;
    private static BufferedReader bfr=null;
    private static String filename="E:\\Java\\notebook\\src\\record.txt";

    public static int getHitnums() {
        return hitnums;
    }
    //6.0设置关闭窗口可以保存数据的方法
    //设计关闭窗口后记录退出时敌人Tank的位置和方向,要求获取Vector enemyTanks
    private static Vector<EnemyTank> enemyTanks=null;
    //node用来存放Tank的位置和方向
    private static Vector<Node> node=new Vector<>();
    public static void setEnemyTanks(Vector<EnemyTank> enemyTanks) {
        Recorder.enemyTanks = enemyTanks;
    }

    //增加一个方法返回node集合和hitnums
    public static Vector<Node> getNodeAndhitnums(){
        try {
            bfr = new BufferedReader(new FileReader(filename));
            hitnums = Integer.parseInt(bfr.readLine());
            //循环读取文件,生成nodes 集合
            String line = "";//255 40 0
            while ((line = bfr.readLine()) != null) {
                String[] xyd = line.split(" ");
                Node n = new Node(Integer.parseInt(xyd[0]), Integer.parseInt(xyd[1]),
                        Integer.parseInt(xyd[2]));
                node.add(n);//放入nodes Vector
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bfr!=null){
                try {
                    bfr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return node;
    }

    public static void keepRecord(){
        try {
            bfw=new BufferedWriter(new FileWriter(filename));
            bfw.write(hitnums+"");
            bfw.newLine();
            //6.0将Tank信息存入文档
            for(int i=0;i<enemyTanks.size();i++){
                EnemyTank enemyTank = enemyTanks.get(i);
                bfw.write(enemyTank.getX()+" "+enemyTank.getY()+" "+enemyTank.getDirection());
                bfw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bfw!=null){
                try {
                    bfw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void setHitnums(int hitnums) {
        Recorder.hitnums = hitnums;
    }
    //当我方坦克击毁一个敌人坦克,就应当 allEnemyTankNum++
    public static void addAllEnemyTankNum() {
        Recorder.hitnums++;
    }
}

Tank类

package TankBattalion.TankGame06;

public class Tank {
    private int x;
    private int y;
    private int direction;//0:向上,1向右,2向下,3向左
    private int speed=3;//控制Tank移动速度
    public boolean isLive=true;
    public Tank(int x, int y) {
        this.x = x;
        this.y = y;
    }
    //根据自己定义的speed控制Tank移动方法
    public void MoveUp() {
        y -= speed;
    }

    public void MoveRight() {
        x += speed;
    }

    public void MoveDown() {
        y += speed;
    }

    public void MoveLeft() {
        x -= speed;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection(int direction) {
        this.direction = direction;
    }

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = 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;
    }
}

TankGame06类

package TankBattalion.TankGame06;

import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Scanner;

//TankGame02目的是为了让Tank实现朝向的改变以及移动
//以及绘制敌人的Tank
public class TankGame06 extends JFrame {
    private MyPanel mp = null;

    public static void main(String[] args) {
        new TankGame06();
    }

    public TankGame06() {
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入1:重新开始,输入2:继续上把");
        String choice =scanner.next();
        mp = new MyPanel(choice);
        //3.0新增,把画板当成线程不断刷新
        Thread thread=new Thread(mp);
        thread.start();
        this.add(mp);
        this.addKeyListener(mp);//让JFrame监听mp
        this.setSize(1350, 750);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
        //6.0在JFrame中增加能响应关闭窗口的方法
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                Recorder.keepRecord();
                System.exit(0);
            }
        });
    }
}


  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值