【JavaSE】坦克大战,代码含超详细注释(核心类每行都有),看了就懂。包含:面向对象 OOP IO编程 文件编程 算法 GUI 多线程 线程知识


实现功能

  1. 绘制我方与敌方坦克
  2. 键盘控制我方坦克移动与发射
  3. 敌方发射子弹
  4. 子弹打到坦克,坦克会消失并且爆炸
  5. 敌方坦克自由移动且自由发射
  6. 我方坦克发射多颗子弹,并且设置同时存在的子弹数量上限
  7. 防止敌方坦克重叠并且设置移动范围
  8. 记录成绩,并且按照记录的信息继续上一局游戏

本次项目使用了:面向对象 OOP IO编程 文件编程 算法 GUI 多线程 线程知识

一、编写各种类

Tank类

  • 坦克类是我放坦克和敌方坦克的父类,包含了坦克的横坐标,纵坐标,方向与速度,还定义了坦克上下左右移动的方法
package Tank2.Tank;

import javax.swing.*;

public class Tank extends JFrame {
    private int x;//坦克的横坐标
    private int y;//坦克的纵坐标
    private int diret;//坦克的方向
    private int speed = 1;//控制速度
    boolean islive = true;

    public Tank(int x, int y )  {
        this.x = x;
        this.y = y;

    }

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    //上右下左移动方法
    public void moveUp() {
        if (y > 0){
            y -= speed;
        }
    }
    public void moveRight() {
        if (x + 60 < 1000){
            x += speed;
        }
    }
    public void moveDown() {
        if (y + 60 < 750){
            y += speed;
        }
    }
    public void moveLeft() {
        if (x > 0){
            x -= speed;
        }
    }

    public int getDiret() {
        return diret;
    }

    public void setDiret(int diret) {
        this.diret = diret;
    }

    @Override
    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    @Override
    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}

Shot类

  • 子弹类,同样定义了横纵坐标还有方向与速度,但也加入了判断子弹是否还存活的布尔型,为后面子弹打中坦克会消亡做功能铺垫
  • 同时定义了移动的线程,因为子弹要一直动,所以要用到线程
  • 同时还加入了什么时候将子弹生命置为false的判断语句
package Tank2.Tank;

public class Shot implements Runnable{
    int x; //子弹x坐标
    int y; //子弹y坐标
    int direct = 0; //子弹方向
    int speed = 3; //子弹的速度
    boolean isLive = true; //子弹是否还存活

    //构造器
    public Shot(int x, int y, int direct) {
        this.x = x;
        this.y = y;
        this.direct = direct;
    }

    //子弹要一直动,而且不能一下子就到边界,所以用了线程然后还加入了休眠代码
    @Override
    public void run() {//射击
        while (true) {

            //休眠 50毫秒
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //根据方向来改变x,y坐标
            switch (direct) {
                case 0://上
                    y -= speed;
                    break;
                case 1://右
                    x += speed;
                    break;
                case 2://下
                    y += speed;
                    break;
                case 3://左
                    x -= speed;
                    break;
            }
            //当子弹移动到面板的边界时,就应该销毁(把启动的子弹的线程销毁),
            //判断子弹是否存活,当子弹打到坦克时会被置为flase,线程随之退出
            if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750 && isLive) ) {
                isLive = false;
                break;
            }

        }
    }
}

enemyTank类

  • 敌方坦克的类,继承了Tank父类,因为要一直动所以同样加入了线程
  • 本类多加了Shot集合,用来存放敌方坦克发射的子弹,也加入了判断是否死亡的布尔型
  • 这里提供一个方法,可以将MyPanel 的成员 Vector enemyTanks = new Vector<>();
    //设置到 enemyTank 的成员 enemyTanks
  • 提供了判断敌方坦克重叠的方法
  • 提供了坦克发射子弹的方法可以设置子弹上限,还有坦克自己随机移动的方法
package Tank2.Tank;

import java.util.Vector;

public class enemyTank extends Tank implements Runnable{
    //类里创建Shot集合
    Vector<Shot> shots = new Vector<>();
    //判断敌方坦克是否存在
    boolean islive = true;

    //增加成员,enemyTank 可以得到敌人坦克的Vector
    Vector<enemyTank> enemyTanks = new Vector<>();

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


    //这里提供一个方法,可以将MyPanel 的成员 Vector<enemyTank> enemyTanks = new Vector<>();
    //设置到 enemyTank 的成员 enemyTanks
    public  void setEnemyTanks(Vector<enemyTank> enemyTanks) {
        this.enemyTanks = enemyTanks;
    }
    //编写方法,判断当前的这个敌人坦克,是否和 enemyTanks 中的其他坦克发生的重叠或者碰撞
    public boolean isTouchEnemyTank(){
        //判断当前敌人坦克(this) 方向
        switch (this.getDiret()) {
            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.getDiret() == 0 || enemyTank.getDiret() == 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.getDiret() == 1 || enemyTank.getDiret() == 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.getDiret() == 0 || enemyTank.getDiret() == 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.getDiret() == 1 || enemyTank.getDiret() == 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.getDiret() == 0 || enemyTank.getDiret() == 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.getDiret() == 1 || enemyTank.getDiret() == 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.getDiret() == 0 || enemyTank.getDiret() == 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.getDiret() == 1 || enemyTank.getDiret() == 3) {
                            //2. 当前坦克 左上角的坐标 [this.getX(), this.getY() ]
                            if (this.getX() >= enemyTank.getX()
                                    && this.getX() <= enemyTank.getX() + 60
                                    && this.getY() >= enemyTank.getY()
                                    && this.getY() <= enemyTank.getY() + 40) {
                                return true;
                            }
                            //3. 当前坦克 左下角的坐标 [this.getX(), this.getY() + 40]
                            if (this.getX() >= enemyTank.getX()
                                    && this.getX() <= enemyTank.getX() + 60
                                    && this.getY() + 40 >= enemyTank.getY()
                                    && this.getY() + 40 <= enemyTank.getY() + 40) {
                                return true;
                            }
                        }
                    }
                }
                break;
        }
        return  false;
    }
    @Override
    public void run() {
        while (true){

            //这里我们判断如果shots size() < 15, 创建一颗子弹,放入到
            //shots集合,并启动
            if (islive && shots.size() < 15) {//一部坦克可以同时存在15颗子弹
                Shot s = null;
                //判断坦克的方向,创建对应的子弹
                switch (getDiret()) {
                    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 (getDiret()){
                case 0:////保持一个方向走40步,不然太频繁变动方向了
                    for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveUp();
                        //休眠代码是为了不让坦克一下子就走完40步,就会看不到中间走动的过程所以加上休眠
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    break;
                case 1://for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveRight();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 2://for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveDown();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3://for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveLeft();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
            //然后随机的改变坦克方向 0-3
            setDiret((int) (Math.random() * 4));
            //写并发程序,一定要考虑清楚,该线程什么时候结束
            if (!islive) {
                break; //退出线程.
            }
        }

    }
}

MyHero类

package Tank2.Tank;

import java.util.Vector;

public class MyHero extends Tank {
    Shot shot = null;

    //发射多颗子弹的情况
    //创建一个shots集合来保存多个shot对象
    Vector<Shot> shots02 = new Vector<>();
    public MyHero(int x, int y) {
        super(x, y);
    }

    //射击
    public void shotEnemyTank() {

        //控制我方子弹最多同时存在4个,大于四个就不创建新的shot对象
        if (shots02.size() > 4){
            return;
        }

        //创建 Shot 对象, 根据当前Hero对象的位置和方向来创建Shot
        int a = getDiret();
        switch (a) {//得到Hero对象方向
            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放入集合
        shots02.add(shot);

        //启动我们的Shot线程
        new Thread(shot).start();
    }
}

Bomb类

  • 爆炸类,定义横纵坐标,还有生命周期用于实现渐变效果,还有是否存活
  • 实现生命值减少的方法
package Tank2.Tank;

public class Bomb {
    int x;//爆炸坐标
    int y;//爆炸坐标
    int life = 6;//生命周期
    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;
        }
    }

    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 getLife() {
        return life;
    }

    public void setLife(int life) {
        this.life = life;
    }

    public boolean isIslive() {
        return islive;
    }

    public void setIslive(boolean islive) {
        this.islive = islive;
    }
}

Node类

  • 一个Node 对象,表示一个敌人坦克的信息
package Tank2.Tank;
// 一个Node 对象,表示一个敌人坦克的信息
public class Node {
    private int x;
    private int y;
    private int direct;

    public Node(int x, int y, int direct) {
        this.x = x;
        this.y = y;
        this.direct = direct;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getDirect() {
        return direct;
    }

    public void setDirect(int direct) {
        this.direct = direct;
    }
}

Recorder类

  • 该类用于记录相关信息和文件交互
  • 定义一个Node 的Vector ,用于保存敌人的信息node
  • 建坦克Vector集合,用来接收mypanel中的enemytank集合,用来后面的遍历敌方坦克用
  • 定义变量,记录我方击毁敌人坦克数
  • 当游戏退出时,我们将allEnemyTankNum和保存敌人坦克的坐标和方向保存到 PATH
  • 一个方法,用于读取PATH, 恢复相关信息 该方法,在继续上局的时候调用即可
package Tank2.Tank;

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

//该类用于记录相关信息和文件交互
public class Recorder {
    //定义变量,记录我方击毁敌人坦克数
    private static int allEnemyTankNum = 0;
    //定义输入输出流
    private static FileWriter fw = null;
    private static BufferedWriter bw = null;
    private static BufferedReader br = null;
    //定义静态变量地址
    private static final String PATH = "D:\\JavaProjects\\b站草稿\\坦克大战\\src\\MyRecord.txt";
//创建坦克Vector集合,用来接收mypanel中的enemytank集合,用来后面的遍历敌方坦克用
    private static Vector<enemyTank> enemyTanks = null;
    //定义一个Node 的Vector ,用于保存敌人的信息node
    private static Vector<Node> nodes = new Vector<>();

    public static String getPATH() {
        return PATH;
    }

    //增加一个方法,用于读取PATH, 恢复相关信息
    //该方法,在继续上局的时候调用即可
    public static Vector<Node> getNodesAndEnemyTankRec(){
        try {
            br = new BufferedReader(new FileReader(PATH));
            //从文件中取出击毁敌方坦克数量
            allEnemyTankNum = Integer.parseInt(br.readLine());
            String line = "";//255 40 0
            //将读出的数据存入line字符串
            while ((line = br.readLine())!=null){//循环取出,只要不为空就取出
                String[] xyd = line.split(" ");//以空格作为分隔符形成新的对象
                //0号是x坐标,1号是y坐标,2号是方向,并且要转成int类型
                //将取出的数据当作node对象存入nodes集合里
                Node node = new Node(Integer.parseInt(xyd[0]),
                        Integer.parseInt(xyd[1]),Integer.parseInt(xyd[2]));
                nodes.add(node);
            }

            //固定格式
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (br!=null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //返回nodes对象
        return nodes;
    }


    public void setenemy(Vector<enemyTank> enemyTanks){
        Recorder.enemyTanks = enemyTanks;
    }

    //增加一个方法,当游戏退出时,我们将allEnemyTankNum 保存到 PATH
    //对keepRecord 进行升级, 保存敌人坦克的坐标和方向
    public static void keepRecord(){
        try {
             bw = new BufferedWriter(new FileWriter(PATH));
             //写入文件时要转成String类型不然会乱码
             bw.write(Integer.toString(allEnemyTankNum));
             bw.newLine();
            //遍历enemytanks集合,取出每个坦克,然后吧坐标和方向写入文件
             for (int i = 0; i < enemyTanks.size(); i++) {
                enemyTank en = enemyTanks.get(i);
                //用空格隔开方便取出时用空格作为分隔符形成新代码
                String s = en.getX() + " " + en.getY() + " " + en.getDiret();
                bw.write(s);
                bw.newLine();
            }
             bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        //固定写法
        }finally {
            if (bw != null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static int getAllEnemyTankNum() {
        return allEnemyTankNum;
    }

    public static void setAllEnemyTankNum(int allEnemyTankNum) {
        Recorder.allEnemyTankNum = allEnemyTankNum;
    }

    public static void addenemytankNum(){
        Recorder.allEnemyTankNum++;
    }
}

AePlayWave类

  • 此类用来播放音乐
package Tank2.Tank;

import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;

public class AePlayWave extends Thread {
    private String filename;

    public AePlayWave(String wavfile) { //构造器 , 指定文件
        filename = wavfile;
    }

    public void run() {

        File soundFile = new File(filename);

        AudioInputStream audioInputStream = null;
        try {
            audioInputStream = AudioSystem.getAudioInputStream(soundFile);
        } catch (Exception e1) {
            e1.printStackTrace();
            return;
        }

        AudioFormat format = audioInputStream.getFormat();
        SourceDataLine auline = null;
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

        try {
            auline = (SourceDataLine) AudioSystem.getLine(info);
            auline.open(format);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        auline.start();
        int nBytesRead = 0;
        //这是缓冲
        byte[] abData = new byte[512];

        try {
            while (nBytesRead != -1) {
                nBytesRead = audioInputStream.read(abData, 0, abData.length);
                if (nBytesRead >= 0)
                    auline.write(abData, 0, nBytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return;
        } finally {
            auline.drain();
            auline.close();
        }
    }
}

全项目最重点MyPanel类

  • 坦克大战的绘图区域,所有物件包括坦克,子弹都在这里绘制,消亡啥的都在这里操作
package Tank2.Tank;

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



/**
 *
 * @version 1.0
 * 坦克大战的绘图区域
 */

public class MyPanel extends JPanel implements KeyListener,Runnable {
    //初始hero类
    MyHero hero = null;

    //定义敌方坦克
    //创建Vector集合,线程安全,ArrayList线程不安全所以不用,定义类型为enemTank的集合
    Vector<enemyTank> enemyTanks = new Vector<>();

    //定义一个存放Node 对象的Vector, 用于恢复敌人坦克的坐标和方向
    Vector<Node> nodes = new Vector<>();
    //定义敌方坦克的数量
    int enemyTanksize = 9;

    //定义存放爆炸特效的集合
    Vector<Bomb> bombs = new Vector<>();
    //定义三张图片
    Image image1 = null;
    Image image2 = null;
    Image image3 = null;

    //定义Recorder类
    Recorder recorder = new Recorder();

    //mypanel的构造方法
    public  MyPanel(String key) {
        //先判断记录的文件是否存在
        //如果存在,就正常执行,如果文件不存在,提示,只能开启新游戏,key = "1"
        File file = new File(Recorder.getPATH());
        if (file.exists()) {
            nodes = Recorder.getNodesAndEnemyTankRec();
        } else {
            System.out.println("文件不存在,只能开启新的游戏");
            key = "1";
        }
        //将从文件得到的数据放入nodes集合里
        nodes = recorder.getNodesAndEnemyTankRec();
        //初始化我的坦克的位置
        hero = new MyHero(500, 600);
        hero.setSpeed(4);//设置我防坦克的移动速度,后期改难度可以改速度
        //将mypanel对象的enemytanks设置给Recorder的enemytanks
        recorder.setenemy(enemyTanks);

        //选择是重新开始还是继续游戏
        switch (key){
            case "1":
                //初始化敌人坦克
                for (int i = 0; i < enemyTanksize; i++) {//初始多少部

                    //定义一个enemTank对象 并且定义位置,坦克之间间隔100像素
                    enemyTank en = new enemyTank(70 * (i + 1), 0);
                    en.setDiret(2);//定义方向
                    //启动线程,敌方坦克走动加射击
                    new Thread(en).start();
                    //初始化敌人坦克子弹,根据敌方坦克的方向来定义子弹方向和位置
                    Shot shot = new Shot(en.getX() + 20, en.getY() + 60, en.getDiret());
                    en.shots.add(shot);//将子弹添加到enemTank类里面的shots集合里
                    new Thread(shot).start();//启动线程
                    enemyTanks.add(en);
                    //将enemyTanks 设置给 enemyTank !!!,传入enemytank类里面的enemytanks集合
                    en.setEnemyTanks(enemyTanks);
                }
                break;


            case "2":
                //初始化击败数量
                // recorder.setAllEnemyTankNum(0);
                //初始化敌人坦克
                for (int i = 0; i < nodes.size(); i++) {//初始多少部就就是nodes里面存有多少部
                    //拿出单个node对象
                    Node node = nodes.get(i);
                    //定义一个enemTank对象 并且定义位置,坦克之间间隔100像素
                    enemyTank en = new enemyTank(node.getX(),node.getY());
                    en.setDiret(node.getDirect());//定义方向
                    new Thread(en).start();
                    //初始化敌人坦克子弹,根据敌方坦克的方向来定义子弹方向和位置
                    Shot shot = new Shot(en.getX() + 20, en.getY() + 60, en.getDiret());
                    en.shots.add(shot);//将子弹添加到enemTank类里面的shots集合里
                    new Thread(shot).start();//启动线程
                    enemyTanks.add(en);
                    //将enemyTanks 设置给 enemyTank !!!,将坦克
                    en.setEnemyTanks(enemyTanks);
                }
                break;
        }



        //初始化图片对象,路径问题尤其重要
        image1 = Toolkit.getDefaultToolkit().getImage("out/production/Java画图/bomb_1.gif");
        image2 = Toolkit.getDefaultToolkit().getImage("out/production/Java画图/bomb_2.gif");
        image3 = Toolkit.getDefaultToolkit().getImage("out/production/Java画图/bomb_3.gif");

        //音乐
        new AePlayWave("D:\\JavaProjects\\b站草稿\\坦克大战\\src\\111.wav").start();
    }

    public void showinof(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,1);

        g.setColor(Color.BLACK);
        //在recorder里拿出击败敌方坦克的数量然后显示
        g.drawString(Integer.toString(Recorder.getAllEnemyTankNum()),1080,100);
    }

    //一整个作图的敌方,背景填充和敌我方坦克和子弹的显示
    @Override
    public void paint(Graphics g) {
        //作图部分
        super.paint(g);
        //画一个正方形填满界面
        g.fillRect(0, 0, 1000, 750);
        showinof(g);

        //运行DrawTank画出我方坦克方法
        //假如我方坦克还活着,就按坐标画出坦克
        if (hero.islive){
            drawTank(hero.getX(), hero.getY(), g, hero.getDiret(), 0);
        }

        //画出我方坦克子弹,因为会发射多颗子弹所以用Vector存储子弹
        for (int i = 0; i < hero.shots02.size(); i++) {
            //从集合里面逐一拿出shot对象
            Shot s1 = hero.shots02.get(i);
            //判断子弹是否还存在并且是否为空
            if (s1 != null && s1.isLive) {
                //画出子弹
                g.setColor(Color.GREEN);
                //子弹坐标已经在Myhero中定义过了
                    g.fillRect(s1.x, s1.y, 3, 3);
            }else {
                //移除子弹
                hero.shots02.remove(s1);
            }
        }

        //画出爆炸效果,从bombs集合中拿出bomb对象,每个bomb对象都会有爆炸特效
        for (int i = 0; i < bombs.size(); i++) {
            Bomb bomb = bombs.get(i);
            //按照生命周期来画出不同的图片来形成爆炸的渐变
            if (bomb.life > 3){
                g.drawImage(image1,bomb.x,bomb.y,60,60,this);
                bomb.lifedown();
            }else if(bomb.life > 1) {
                g.drawImage(image2,bomb.x,bomb.y,60,60,this);
                bomb.lifedown();
            }else {
                g.drawImage(image3,bomb.x,bomb.y,60,60,this);
                bomb.lifedown();
            }
            //子弹不存活时把他从集合中去掉
            if (!bomb.islive){
                bombs.remove(bomb);
            }
        }

        //画多少个敌方坦克
        for (int i = 0; i < enemyTanks.size(); i++) {
            //定义一个enemyTank类来接收enemyTanks集合里面的一个个元素
            enemyTank en = enemyTanks.get(i);
            if (en.islive) {//当敌人坦克存活时才画出,假如敌人被子弹打中islive会变成false,就不会画出来了,就等于坦克消失了
                //运行Draw坦克方法
                drawTank(en.getX(), en.getY(), g, en.getDiret(), 1);
                //同时画出子弹
                for (int j = 0; j < en.shots.size(); j++) {
                    //从shots集合提取一个元素传入shot里
                    Shot shot = en.shots.get(j);
                    if (shot.isLive) {
                        //画出子弹
                        g.setColor(Color.red);
                        g.fillRect(shot.x, shot.y, 3, 3);
                    } else {
                        //移除子弹
                        en.shots.remove(shot);
                    }
                }
            }

        }

    }

    /**
     * @param x      坦克的左上角x坐标
     * @param y      坦克的左上角y坐标
     * @param g      画笔
     * @param direct 坦克方向(上下左右)
     * @param type   坦克类型
     */
    public void drawTank(int x, int y, Graphics g, int direct, int type) {
        //根据不同类型坦克,设置不同颜色
        //定义坦克类型,区分颜色
        switch (type) {
            //我方坦克
            case 0:
                g.setColor(Color.CYAN);
                break;
            //敌方坦克
            case 1:
                g.setColor(Color.red);
                break;
        }

        //根据坦克方向,来绘制坦克
        switch (direct) {
            case 0://上
                g.fill3DRect(x, y, 10, 60, false);//左轮子
                g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边轮子
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克盖子
                g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子
                g.drawLine(x + 20, y + 30, x + 20, y);//画出炮筒
                break;
            case 1: //表示向右
                g.fill3DRect(x, y, 60, 10, false);//画出坦克上边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边轮子
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克盖子
                g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子
                g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮筒
                break;
            case 2: //表示向下
                g.fill3DRect(x, y, 10, 60, false);//画出坦克左边轮子
                g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边轮子
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克盖子
                g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子
                g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮筒
                break;
            case 3: //表示向左
                g.fill3DRect(x, y, 60, 10, false);//画出坦克上边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边轮子
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克盖子
                g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子
                g.drawLine(x + 30, y + 20, x, y + 20);//画出炮筒
                break;
            default:
                System.out.println("暂时没有处理");
        }
    }


    @Override
    public void keyTyped(KeyEvent e) {
    }
    //判断是否打中敌方坦克
    //发射多个子弹,先遍历整个子弹集合在一个个比较有没有打中
    public void hitenemytank(){
        //循环取出我防坦克中的子弹集合
        for (int j = 0; j < hero.shots02.size(); j++) {
            Shot s2 = hero.shots02.get(j);
            //一开始没按下j时子弹不存在,会出现空指针异常,所以要加判断shot不为空
            if (s2!=null && s2.isLive){
                //遍历每个敌方坦克,查看是否击中
                for (int i = 0; i < enemyTanks.size(); i++) {
                    enemyTank enemyTank1 = enemyTanks.get(i);
                    //运行判断是否打中坦克方法
                    hitTank(s2,enemyTank1);
                }
            }
        }
    }
    //敌人是否击中我们
    public void hithero(){
        //循环遍历敌方坦克,在遍历一个敌方坦克发射的所有子弹,两层循环
        for (int i = 0; i < enemyTanks.size(); i++) {
            enemyTank e = enemyTanks.get(i);
            for (int j = 0; j < e.shots.size(); j++) {
                Shot sh = e.shots.get(j);
                //判断 shot 是否击中我的坦克
                if (hero.islive && sh.isLive) {
                    hitTank(sh, hero);
                }
            }
        }
    }

    //什么时候判断 子弹是否击中坦克 ? run方法,要一直判断是否击中
    public void hitTank(Shot s, Tank enemyTank1) {
        //得到单个坦克的方向,分上下,左右两种情况编写程序
        int m = enemyTank1.getDiret();
        switch (m) {
            case 0:
            case 2:
                //判断坐标是否有重合
                if (s.x > enemyTank1.getX() && s.x < enemyTank1.getX() + 40
                        && s.y > enemyTank1.getY() && s.y < enemyTank1.getY() + 60) {
                    //击中后子弹和坦克一起消失,生命都为false,然后把坦克从集合中移走
                    s.isLive = false;
                    enemyTank1.islive = false;
                    enemyTanks.remove(enemyTank1);
                    //加判断,如果这个坦克是敌方坦克,击毁敌方坦克的总数就加一
                    if (enemyTank1 instanceof enemyTank){
                        Recorder.addenemytankNum();
                    }
                    //创建bomb对象,并添加到bombs集合
                    Bomb bomb = new Bomb(enemyTank1.getX(),enemyTank1.getY());
                    bombs.add(bomb);
                }
                break;
            case 1: //坦克向右
            case 3: //坦克向左
                if (s.x > enemyTank1.getX() && s.x < enemyTank1.getX() + 60
                        && s.y > enemyTank1.getY() && s.y < enemyTank1.getY() + 40) {
                    //击中后子弹和坦克一起消失
                    s.isLive = false;
                    enemyTank1.islive = false;
                    enemyTanks.remove(enemyTank1);
                    if (enemyTank1 instanceof enemyTank){
                        Recorder.addenemytankNum();
                    }
                    Bomb bomb = new Bomb(enemyTank1.getX(),enemyTank1.getY());
                    bombs.add(bomb);
                }
        }
    }

        //按下键盘会怎么样
        @Override
        public void keyPressed (KeyEvent e){
            if (e.getKeyCode() == KeyEvent.VK_W) {//上
                hero.setDiret(0);
                hero.moveUp();
            } else if (e.getKeyCode() == KeyEvent.VK_S) {//下
                hero.setDiret(2);
                hero.moveDown();
            } else if (e.getKeyCode() == KeyEvent.VK_A) {//左
                hero.setDiret(3);
                hero.moveLeft();
            } else if (e.getKeyCode() == KeyEvent.VK_D) {//右
                hero.setDiret(1);
                hero.moveRight();
            }
            if (e.getKeyCode() == KeyEvent.VK_J) {
                //在Myhero类里定义了shotEnemyTank()方法,里面已经包含了方向速度,直接调用不会显得冗余
                if (hero.islive){
                    hero.shotEnemyTank();
                }
            }
            //让面板重绘
            this.repaint();
        }
        @Override
        public void keyReleased (KeyEvent e){

        }
        //线程,mypanel可以一直判断是否击中坦克
        @Override
        public void run () {
            while (true) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                hithero();
                hitenemytank();
                this.repaint();
            }
        }
    }

TankGame主方法

package Tank2.Tank;

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

public class TankGame02 extends JFrame {
Scanner scanner = new Scanner(System.in);
    //定义MyPanel
    MyPanel mp = null;
    public static void main(String[] args) {
        TankGame02 hspTankGame02 = new TankGame02();
    }
    public TankGame02() {
        System.out.println("请输入选择 1: 新游戏 2: 继续上局");
        String key = scanner.next();
        mp = new MyPanel(key);
        Thread thread = new Thread(mp);
        //调用Mypanel线程,让他一直绘图
        thread.start();
        this.add(mp);//把面板(就是游戏的绘图区域)
        this.setSize(1300, 750);
        this.addKeyListener(mp);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
        //监听窗口程序,点退出的时候会运行Recorder的存盘方法
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                Recorder.keepRecord();
                System.exit(0);
            }
        });
    }
}


二、功能板块分析

1.绘制我方与敌方坦克

坦克每个方向都不一样,所有要画四种坦克的样子,后边改变方向的时候调用
分敌方和我方坦克,两个类型,两种颜色

/**
     * @param x      坦克的左上角x坐标
     * @param y      坦克的左上角y坐标
     * @param g      画笔
     * @param direct 坦克方向(上下左右)
     * @param type   坦克类型
     */
public void drawTank(int x, int y, Graphics g, int direct, int type) {
        //根据不同类型坦克,设置不同颜色
        //定义坦克类型,区分颜色
        switch (type) {
            //我方坦克
            case 0:
                g.setColor(Color.CYAN);
                break;
            //敌方坦克
            case 1:
                g.setColor(Color.red);
                break;
        }

        //根据坦克方向,来绘制坦克
        switch (direct) {
            case 0://上
                g.fill3DRect(x, y, 10, 60, false);//左轮子
                g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边轮子
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克盖子
                g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子
                g.drawLine(x + 20, y + 30, x + 20, y);//画出炮筒
                break;
            case 1: //表示向右
                g.fill3DRect(x, y, 60, 10, false);//画出坦克上边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边轮子
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克盖子
                g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子
                g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮筒
                break;
            case 2: //表示向下
                g.fill3DRect(x, y, 10, 60, false);//画出坦克左边轮子
                g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边轮子
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克盖子
                g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子
                g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮筒
                break;
            case 3: //表示向左
                g.fill3DRect(x, y, 60, 10, false);//画出坦克上边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边轮子
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克盖子
                g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子
                g.drawLine(x + 30, y + 20, x, y + 20);//画出炮筒
                break;
            default:
                System.out.println("暂时没有处理");
        }
    }


2.键盘控制我方坦克移动与发射

在Myhero类里面已经定义了移动和射击的方法,这里直接调用就行

public void keyPressed (KeyEvent e){
            if (e.getKeyCode() == KeyEvent.VK_W) {//上
                hero.setDiret(0);
                hero.moveUp();
            } else if (e.getKeyCode() == KeyEvent.VK_S) {//下
                hero.setDiret(2);
                hero.moveDown();
            } else if (e.getKeyCode() == KeyEvent.VK_A) {//左
                hero.setDiret(3);
                hero.moveLeft();
            } else if (e.getKeyCode() == KeyEvent.VK_D) {//右
                hero.setDiret(1);
                hero.moveRight();
            }
            if (e.getKeyCode() == KeyEvent.VK_J) {
                //在Myhero类里定义了shotEnemyTank()方法,里面已经包含了方向速度,直接调用不会显得冗余
                if (hero.islive){
                    hero.shotEnemyTank();
                }
            }
            //让面板重绘
            this.repaint();
        }

3.敌方发射子弹

在enemytank类中定义发射的方法

//在enemytank类中定义发射的方法
  public void run() {
        while (true){

            //这里我们判断如果shots size() < 15, 创建一颗子弹,放入到
            //shots集合,并启动
            if (islive && shots.size() < 15) {//一部坦克可以同时存在15颗子弹
                Shot s = null;
                //判断坦克的方向,创建对应的子弹
                switch (getDiret()) {
                    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();
            }

然后再myplano定义敌方坦克的时候画出子弹

//画多少个敌方坦克
        for (int i = 0; i < enemyTanks.size(); i++) {
            //定义一个enemyTank类来接收enemyTanks集合里面的一个个元素
            enemyTank en = enemyTanks.get(i);
            if (en.islive) {//当敌人坦克存活时才画出,假如敌人被子弹打中islive会变成false,就不会画出来了,就等于坦克消失了
                //运行Draw坦克方法
                drawTank(en.getX(), en.getY(), g, en.getDiret(), 1);
                //同时画出子弹
                for (int j = 0; j < en.shots.size(); j++) {
                    //从shots集合提取一个元素传入shot里
                    Shot shot = en.shots.get(j);
                    if (shot.isLive) {
                        //画出子弹
                        g.setColor(Color.red);
                        g.fillRect(shot.x, shot.y, 3, 3);
                    } else {
                        //移除子弹
                        en.shots.remove(shot);
                    }
                }
            }
        }

4. 子弹打到坦克,坦克会消失并且爆炸

//什么时候判断 子弹是否击中坦克 ? run方法,要一直判断是否击中
    public void hitTank(Shot s, Tank enemyTank1) {
        //得到单个坦克的方向,分上下,左右两种情况编写程序
        int m = enemyTank1.getDiret();
        switch (m) {
            case 0:
            case 2:
                //判断坐标是否有重合
                if (s.x > enemyTank1.getX() && s.x < enemyTank1.getX() + 40
                        && s.y > enemyTank1.getY() && s.y < enemyTank1.getY() + 60) {
                    //击中后子弹和坦克一起消失,生命都为false,然后把坦克从集合中移走
                    s.isLive = false;
                    enemyTank1.islive = false;
                    enemyTanks.remove(enemyTank1);
                    //加判断,如果这个坦克是敌方坦克,击毁敌方坦克的总数就加一
                    if (enemyTank1 instanceof enemyTank){
                        Recorder.addenemytankNum();
                    }
                    //创建bomb对象,并添加到bombs集合
                    Bomb bomb = new Bomb(enemyTank1.getX(),enemyTank1.getY());
                    bombs.add(bomb);
                }
                break;
            case 1: //坦克向右
            case 3: //坦克向左
                if (s.x > enemyTank1.getX() && s.x < enemyTank1.getX() + 60
                        && s.y > enemyTank1.getY() && s.y < enemyTank1.getY() + 40) {
                    //击中后子弹和坦克一起消失
                    s.isLive = false;
                    enemyTank1.islive = false;
                    enemyTanks.remove(enemyTank1);
                    if (enemyTank1 instanceof enemyTank){
                        Recorder.addenemytankNum();
                    }
                    Bomb bomb = new Bomb(enemyTank1.getX(),enemyTank1.getY());
                    bombs.add(bomb);
                }
        }
    }

同时要启动线程

//线程,mypanel可以一直判断是否击中坦克
        @Override
        public void run () {
            while (true) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                hithero();
                hitenemytank();
                this.repaint();
            }
        }

敌方打到我方

 //敌人是否击中我们
    public void hithero(){
        //循环遍历敌方坦克,在遍历一个敌方坦克发射的所有子弹,两层循环
        for (int i = 0; i < enemyTanks.size(); i++) {
            enemyTank e = enemyTanks.get(i);
            for (int j = 0; j < e.shots.size(); j++) {
                Shot sh = e.shots.get(j);
                //判断 shot 是否击中我的坦克
                if (hero.islive && sh.isLive) {
                    hitTank(sh, hero);
                }
            }
        }
    }

我方打到敌方

//判断是否打中敌方坦克
    //发射多个子弹,先遍历整个子弹集合在一个个比较有没有打中
    public void hitenemytank(){
        //循环取出我防坦克中的子弹集合
        for (int j = 0; j < hero.shots02.size(); j++) {
            Shot s2 = hero.shots02.get(j);
            //一开始没按下j时子弹不存在,会出现空指针异常,所以要加判断shot不为空
            if (s2!=null && s2.isLive){
                //遍历每个敌方坦克,查看是否击中
                for (int i = 0; i < enemyTanks.size(); i++) {
                    enemyTank enemyTank1 = enemyTanks.get(i);
                    //运行判断是否打中坦克方法
                    hitTank(s2,enemyTank1);
                }
            }
        }

5. 敌方坦克自由移动

用random随机数来随机改变坦克方向

 public void run() {
        while (true){
            //根据坦克的方向来继续移动
            //移动时加入判断是否重叠的方法
            switch (getDiret()){
                case 0:////保持一个方向走40步,不然太频繁变动方向了
                    for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveUp();
                        //休眠代码是为了不让坦克一下子就走完40步,就会看不到中间走动的过程所以加上休眠
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    break;
                case 1://for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveRight();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 2://for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveDown();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3://for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveLeft();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
            //然后随机的改变坦克方向 0-3
            setDiret((int) (Math.random() * 4));
            //写并发程序,一定要考虑清楚,该线程什么时候结束
            if (!islive) {
                break; //退出线程.
            }
        }
     }   

6. 我方坦克发射多颗子弹,并且设置同时存在的子弹数量上限

Shot shot = null;

    //发射多颗子弹的情况
    //创建一个shots集合来保存多个shot对象
    Vector<Shot> shots02 = new Vector<>();
    public MyHero(int x, int y) {
        super(x, y);
    }

    //射击
    public void shotEnemyTank() {

        //控制我方子弹最多同时存在4个,大于四个就不创建新的shot对象
        if (shots02.size() > 4){
            return;
        }

        //创建 Shot 对象, 根据当前Hero对象的位置和方向来创建Shot
        int a = getDiret();
        switch (a) {//得到Hero对象方向
            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放入集合
        shots02.add(shot);

        //启动我们的Shot线程
        new Thread(shot).start();
    }

7. 防止敌方坦克重叠并且设置移动范围

总共有四大种八小种情况,将重叠情况列出来,然后得到是否重叠的方法,然后将方法加入到坦克随机移动的方法里面去

 //编写方法,判断当前的这个敌人坦克,是否和 enemyTanks 中的其他坦克发生的重叠或者碰撞
    public boolean isTouchEnemyTank(){
        //判断当前敌人坦克(this) 方向
        switch (this.getDiret()) {
            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.getDiret() == 0 || enemyTank.getDiret() == 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.getDiret() == 1 || enemyTank.getDiret() == 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.getDiret() == 0 || enemyTank.getDiret() == 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.getDiret() == 1 || enemyTank.getDiret() == 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.getDiret() == 0 || enemyTank.getDiret() == 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.getDiret() == 1 || enemyTank.getDiret() == 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.getDiret() == 0 || enemyTank.getDiret() == 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.getDiret() == 1 || enemyTank.getDiret() == 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;
    }

移动时加入判断是否重叠的方法

//根据坦克的方向来继续移动
            //移动时加入判断是否重叠的方法
            switch (getDiret()){
                case 0:////保持一个方向走40步,不然太频繁变动方向了
                    for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveUp();
                        //休眠代码是为了不让坦克一下子就走完40步,就会看不到中间走动的过程所以加上休眠
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    break;
                case 1://for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveRight();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 2://for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveDown();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3://for (int i = 0; i < 40; i++) {
                        if (!isTouchEnemyTank())
                        moveLeft();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
            //然后随机的改变坦克方向 0-3
            setDiret((int) (Math.random() * 4));
            //写并发程序,一定要考虑清楚,该线程什么时候结束
            if (!islive) {
                break; //退出线程.
            }
        }

    }

8. 记录成绩,并且按照记录的信息继续上一局游戏

创建输入输出流,记录击毁敌方数量和坦克位置,选择继续游戏时再把文件信息拿出来然后按信息创建敌方坦克。

//定义变量,记录我方击毁敌人坦克数
    private static int allEnemyTankNum = 0;
    //定义输入输出流
    private static FileWriter fw = null;
    private static BufferedWriter bw = null;
    private static BufferedReader br = null;
    //定义静态变量地址
    private static final String PATH = "D:\\JavaProjects\\b站草稿\\坦克大战\\src\\MyRecord.txt";
//创建坦克Vector集合,用来接收mypanel中的enemytank集合,用来后面的遍历敌方坦克用
    private static Vector<enemyTank> enemyTanks = null;
    //定义一个Node 的Vector ,用于保存敌人的信息node
    private static Vector<Node> nodes = new Vector<>();

    public static String getPATH() {
        return PATH;
    }

    //增加一个方法,用于读取PATH, 恢复相关信息
    //该方法,在继续上局的时候调用即可
    public static Vector<Node> getNodesAndEnemyTankRec(){
        try {
            br = new BufferedReader(new FileReader(PATH));
            //从文件中取出击毁敌方坦克数量
            allEnemyTankNum = Integer.parseInt(br.readLine());
            String line = "";//255 40 0
            //将读出的数据存入line字符串
            while ((line = br.readLine())!=null){//循环取出,只要不为空就取出
                String[] xyd = line.split(" ");//以空格作为分隔符形成新的对象
                //0号是x坐标,1号是y坐标,2号是方向,并且要转成int类型
                //将取出的数据当作node对象存入nodes集合里
                Node node = new Node(Integer.parseInt(xyd[0]),
                        Integer.parseInt(xyd[1]),Integer.parseInt(xyd[2]));
                nodes.add(node);
            }

            //固定格式
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (br!=null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //返回nodes对象
        return nodes;
    }


    public void setenemy(Vector<enemyTank> enemyTanks){
        Recorder.enemyTanks = enemyTanks;
    }

    //增加一个方法,当游戏退出时,我们将allEnemyTankNum 保存到 PATH
    //对keepRecord 进行升级, 保存敌人坦克的坐标和方向
    public static void keepRecord(){
        try {
             bw = new BufferedWriter(new FileWriter(PATH));
             //写入文件时要转成String类型不然会乱码
             bw.write(Integer.toString(allEnemyTankNum));
             bw.newLine();
            //遍历enemytanks集合,取出每个坦克,然后吧坐标和方向写入文件
             for (int i = 0; i < enemyTanks.size(); i++) {
                enemyTank en = enemyTanks.get(i);
                //用空格隔开方便取出时用空格作为分隔符形成新代码
                String s = en.getX() + " " + en.getY() + " " + en.getDiret();
                bw.write(s);
                bw.newLine();
            }
             bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        //固定写法
        }finally {
            if (bw != null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

监听窗口程序,点退出的时候会运行Recorder的存盘方法

//监听窗口程序,点退出的时候会运行Recorder的存盘方法
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                Recorder.keepRecord();
                System.exit(0);
            }
        });

给玩家进行选择,新游戏还是继续游戏,若没有上一次 游戏的记录,就强行新游戏

//先判断记录的文件是否存在
        //如果存在,就正常执行,如果文件不存在,提示,只能开启新游戏,key = "1"
        File file = new File(Recorder.getPATH());
        if (file.exists()) {
            nodes = Recorder.getNodesAndEnemyTankRec();
        } else {
            System.out.println("文件不存在,只能开启新的游戏");
            key = "1";
        }
        //将从文件得到的数据放入nodes集合里
        nodes = recorder.getNodesAndEnemyTankRec();
        //初始化我的坦克的位置
        hero = new MyHero(500, 600);
        hero.setSpeed(4);//设置我防坦克的移动速度,后期改难度可以改速度
        //将mypanel对象的enemytanks设置给Recorder的enemytanks
        recorder.setenemy(enemyTanks);

        //选择是重新开始还是继续游戏
        switch (key){
            case "1":
                //初始化敌人坦克
                for (int i = 0; i < enemyTanksize; i++) {//初始多少部

                    //定义一个enemTank对象 并且定义位置,坦克之间间隔100像素
                    enemyTank en = new enemyTank(70 * (i + 1), 0);
                    en.setDiret(2);//定义方向
                    //启动线程,敌方坦克走动加射击
                    new Thread(en).start();
                    //初始化敌人坦克子弹,根据敌方坦克的方向来定义子弹方向和位置
                    Shot shot = new Shot(en.getX() + 20, en.getY() + 60, en.getDiret());
                    en.shots.add(shot);//将子弹添加到enemTank类里面的shots集合里
                    new Thread(shot).start();//启动线程
                    enemyTanks.add(en);
                    //将enemyTanks 设置给 enemyTank !!!,传入enemytank类里面的enemytanks集合
                    en.setEnemyTanks(enemyTanks);
                }
                break;


            case "2":
                //初始化击败数量
                // recorder.setAllEnemyTankNum(0);
                //初始化敌人坦克
                for (int i = 0; i < nodes.size(); i++) {//初始多少部就就是nodes里面存有多少部
                    //拿出单个node对象
                    Node node = nodes.get(i);
                    //定义一个enemTank对象 并且定义位置,坦克之间间隔100像素
                    enemyTank en = new enemyTank(node.getX(),node.getY());
                    en.setDiret(node.getDirect());//定义方向
                    new Thread(en).start();
                    //初始化敌人坦克子弹,根据敌方坦克的方向来定义子弹方向和位置
                    Shot shot = new Shot(en.getX() + 20, en.getY() + 60, en.getDiret());
                    en.shots.add(shot);//将子弹添加到enemTank类里面的shots集合里
                    new Thread(shot).start();//启动线程
                    enemyTanks.add(en);
                    //将enemyTanks 设置给 enemyTank !!!,将坦克
                    en.setEnemyTanks(enemyTanks);
                }
                break;
        }

三、总结

  1. 面向对象的思想尤其重要,要学会封装方法,方法该放入哪个类,而且要学会把存入外部文件当作一个对象去处理比如这里的node类,如果当成字符串处理就非常繁杂。

  2. 要注意文件的路径千万不要搞错

  3. 要考虑什么时候写并发程序,什么时候要用多线程,写并发程序,一定要考虑清楚,该线程什么时候结束,同时再必要的地方加入休眠代码,不然程序执行太快看不见效果。

  4. 要考虑线程安全问题,所有这里用Vector集合不用ArrayList

  5. 本项目经常会在类中定义集合,然后在实现类里面,把创建的集合传入类中的集合,这样就可以在类中写很多方法,也同样也是面向对象的思想。

比如:

  • 提供一个方法,可以将MyPanel 的成员 Vector enemyTanks = new Vector<>();设置到 enemyTank 的成员 enemyTanks
  • Myhero类中的Vector shots02 = new Vector<>();
  • Recorder类中的创建坦克Vector集合,用来接收mypanel中的enemytank集合,用来后面的遍历敌方坦克用
    private static Vector enemyTanks = null;
    //定义一个Node 的Vector ,用于保存敌人的信息node
    private static Vector nodes = new Vector<>();

四、游戏后期拓展

  1. 加入障碍,敌方坦克炮弹打不穿,我方坦克可以
  2. 加入河流,子弹可以穿过,但坦克不行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值