java实现坦克大战------让你理解面向对象编程的强大之处

话不多说,先上实现的部分效果图

2022-06-17 13-45-01

此游戏的实现过程全程是跟韩顺平老师去实现的,当然其中也有我独自实现的功能,比如障碍物或者钢板… 程序里面的每一句代码我基本上写的都有注释,大部分也是我基于对这个代码的理解。不得不说,能实现到这里,多亏了韩老师的视频,同时这个程序写出来之后,会让你更加理解java世界的面向对象编程,不得不说受益匪浅。发此博文的目的就是为了记录我的学习过程。很值得!!!

程序的入口:

package com.wqc.tankgame8;


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

/**
 * @author 高瞻远瞩
 * @version 坦克5.0版本
 * 这个实现了游戏界面的区域
 * 窗口的初始化
 * 坦克大战7版本新增加了 钢板和墙
 */
@SuppressWarnings("all")
public class TankGame08 extends JFrame {
    //定义一个MyPanel
    MyPanel mp = null;
    Win victory;
    static String next;//把用户输入内容的定义为成员变量  方便在别的类进行操控
    Scanner scanner = new Scanner(System.in);
    public static void main(String[] args) {
        TankGame08 tankGame02 = new TankGame08();//初始化窗口
    }

    public TankGame08() {
        System.out.println("请输入你的选择 1,开启新游戏 2,继续上一局游戏 3,双人游戏");
        next = scanner.next();
        mp = new MyPanel(next);//接收一个字符串
//        thread.setDaemon(true);//做成守护线程  随着主线程的结束而结束
        new Thread(mp).start();//启动MyPanel这个线程 进行无限重绘面板
        super.add(mp);//添加MyPanel这个对象  这个对象可以理解成画板 上面有初始化的坦克
        super.setSize(1900, 1300);//设置窗口的大小
        super.addKeyListener(mp);//这里有个向上转型  让JFrame 监听键盘事件
        super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  //当点击窗口的关闭,程序会自动退出
        if ("1".equals(next) || "2".equals(next) || "3".equals(next)) {//只有用户输入1 或者2 才会打印指定的信息
            super.setVisible(true);//可视化  当第一次可视化的时候  面板的paint方法会被调用
            //this访问方法的话  先从本类查找 再从父类查找  这个add方法本类明显就没有  所以继承JFrame的目的是
            //调用父类的add方法
        }

        //在这里增加响应关闭窗口的处理
        super.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                if (mp.hero == null) {//双人游戏不记录到文件中
                    Record.recordHitNum();//监听用户关闭窗口的动作 就调用记录玩家击败坦克数目的方法
                }
            }
        });
    }
}

坦克的基类(作为所有坦克的父类)

package com.wqc.tankgame8;

/**
 * @author 高瞻远瞩
 * @version 坦克5.0版本
 */
@SuppressWarnings("all")
public class Tank{
    private int x;//定义横坐标
    private int y;//纵坐标
    private int direct;//定义坦克的方向 0上 1右 2下 3左  //默认为向上  因为direct初始化默认 = 0
    private int speed = 1;//设置坦克的移动速度  初始速度为1  把他设置成成员属性  方便以后改变它
    private boolean isLive = true;

    public boolean isLive() {
        return isLive;
    }

    public void setLive(boolean live) {
        isLive = live;
    }

    public int getSpeed() {
        return speed;
    }

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

    public int getDirect() {
        return direct;
    }

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

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

    //写一个移动坐标的方法  方便子类可以调用来完成坦克移动
    public void varyRight(){
        x += speed;
    }
    public void varyLeft(){
        x -= speed;
    }
    public void varyDown(){
        y += speed;
    }
    public void varyUp(){
        y -= speed;
    }

}

第二个玩家坦克类(在双人游戏中 上下左右控制移动 除号控制攻击 ),继承了父类坦克

在这里插入图片描述

package com.wqc.tankgame8;

import java.util.Vector;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * 这个类做成第二个玩家
 */
@SuppressWarnings("all")
public class Hero extends Tank {
    Vector<Bullet> heroBullet = new Vector<>();
    Bullet bullet = null;


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

    //写一个第二个玩家发射子弹的方法
    public void shot(){
        switch(this.getDirect()){
            case 0:
                bullet = new Bullet(this.getX() + 20, this.getY(), 0);
                break;
            case 1:
                bullet = new Bullet(this.getX() + 60, this.getY() + 20, 1);
                break;
            case 2:
                bullet = new Bullet(this.getX() + 20, this.getY() + 60, 2);
                break;
            case 3:
                bullet = new Bullet(this.getX(), this.getY() + 20, 3);
                break;
        }
        new Thread(bullet).start();//启动这个子弹线程
        heroBullet.add(bullet);//并把这个子弹加入到集合里面
    }
}

我的坦克类 继承了父类坦克(wasd 控制移动 j是发射攻击)

在这里插入图片描述

package com.wqc.tankgame8;

import java.util.Vector;

/**
 * @author 高瞻远瞩
 * @version 坦克5.0版本
 * 定义自己的坦克
 */
@SuppressWarnings("all")
public class MyTank extends Tank {
    private Bullet bullet = null;//定义子弹的对象
    Vector<Bullet> myBullets = new Vector<>();//定义一个我自己发出的子弹集合

    public Bullet getBullet() {
        return bullet;
    }

    public void setBullet(Bullet bullet) {
        this.bullet = bullet;
    }

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

    //写一个方法  来打出子弹
    public void shot() {
        //bullet = new Bullet(this.getX(), this.getY(), this.getDirect());这个注释是我自己的写法
        // 用户每按一次J就调用一次这个方法 然后就新创建一个子弹对象  并获取到此时坦克的坐标以及方向
        // 使子弹对象每次创建时坐标都与坦克坐标保持一致
        //然后以下这个switch 是根据坦克不同的方向  进而修改子弹该射击的位置

        //实现发射自定义数量的子弹 比如一次最多只能发射5颗  只需在创建对象这里加一个判断即可
        //if(myBullets.size() == 5){ //如果我的坦克的集合已经有5颗子弹了 被发射出但还没消亡
            //即使用户按下了J键 虽然也在不停的调用这个方法 但是都会被这个条件给过滤掉 直到子弹消亡
            //从集合中移除
            //return;
        //}
        switch (this.getDirect()) {//坦克在不同的方向  要保证发射出的子弹会在炮筒上打出来

            case 0: //上
                bullet = new Bullet(this.getX() + 20, this.getY(), 0);
//                bullet.setX(this.getX() + 20);
                break;
            case 1://右
                bullet = new Bullet(this.getX() + 60, this.getY() + 20, 1);
//                bullet.setX(this.getX() + 60);
//                bullet.setY(this.getY() + 20);
                break;
            case 2://下
                bullet = new Bullet(this.getX() + 20, this.getY() + 60, 2);
//                bullet.setX(this.getX() + 20);
//                bullet.setY(this.getY() + 60);
                break;
            case 3://左
                bullet = new Bullet(this.getX(), this.getY() + 20, 3);
//                bullet.setY(this.getY() + 20);
                break;
        }
        myBullets.add(bullet);//每判断一次Switch 就把新创建的子弹对象加入到我的子弹集合里
        new Thread(bullet).start(); //当我的子弹对象被创建后  就启动子弹线程

    }
}

电脑坦克类

在这里插入图片描述

package com.wqc.tankgame8;

import java.util.Vector;

/**
 * @author 高瞻远瞩
 * @version 坦克5.0版本
 * 电脑坦克
 * 让敌人的坦克可以自由移动
 * 思路 把敌人的坦克做成一个线程  -->  写一个敌人自由移动的方法 --> 然后在每次创建敌人坦克的时候
 * 启动这个线程  因为创建敌人坦克的坐标和方向已经确定 所以可以通过这个线程的run方法改变敌人坦克的
 * 坐标来实现坦克的自由移动  进而在画板上画的时候可以实现梦幻联动!!
 * 在什么时候炸弹对象被创建  然后被加到对象集合里  最后  在面板被绘出  所以需要写一个方法来
 * 判断敌人的坦克击中我方坦克的时机
 * 有两个方法可以实现避免相撞   1,provoke() + binOverlap(Vector<PC> pc, Tank tk2) 这是我自己写的方法 基于老师画的图
 * 2,isTouched()  老师的方法  核心都是 把Mypanel 类中的 定义的 坦克集合 映射到 PC类中
 * 通过一个方法 setEnemyVector(Vector<PC> enemyVector)  判断的地方是在PC类中坦克的自由移动的判断条件里
 */
@SuppressWarnings("ALL")
public class PC extends Tank implements Runnable {
    Vector<Bullet> bulletVector = new Vector<>();//创建一个子弹集合  用来存放电脑的子弹对象
    Bullet bullet;//定义子弹的对象 方便添加到子弹集合
    private boolean loop = true;//定义一个结束坦克自由移动线程的变量
    Vector<PC> enemyVector = new Vector<>();//在这里重新定义一个  存放电脑坦克的集合
    // 然后通过setEnemyVector()这个方法
    //让Mypanel 类的电脑坦克集合映射到这个集合里

    public void setEnemyVector(Vector<PC> enemyVector) {
        this.enemyVector = enemyVector;
    }

    public boolean isLoop() {
        return loop;
    }

    public void setLoop(boolean loop) {
        this.loop = loop;
    }



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


    //这是我自己的方法成功避免了电脑坦克的重叠
    //写一个验证坦克之间是否重叠的方法
    public boolean proof() {
        for (int i = 0; i < enemyVector.size(); i++) {//遍历一次pc坦克集合 让一个坦克与所有坦克进行重叠检测
            PC pc = enemyVector.get(i);
            if (binOverlap(enemyVector, pc) == false) {//如果此条件成立  说明返回假  坦克发生了重叠现象
                return false;//发生重叠
            }
        }
        return true;//没有发生重叠
    }


    //写一个方法  防止敌人坦克重叠的方法 参数肯定有一个敌人的坦克集合 另一个参数就是敌人的坦克
    //一共八种情况  具体的图请参照插入的图片
    public boolean binOverlap(Vector<PC> pc, Tank tk2) {//这个方法返回true为不发生重叠
        for (int i = 0; i < pc.size(); i++) {
            PC tk1 = pc.get(i);//先得到集合中每一个敌方的坦克
            //然后根据传进来的坦克(tk2)的方向进行判断 一共四种
            //然后在每一种方向的判断中(又可以和坦克集合中得到的坦克(tk1)的上下或者左右进行判断)  上下为一种情况 左右为种情况
            //判断重叠的方法是  根据tk2坦克最左边的点和最右边的点是否会接触到tk1坦克的大小范围(tk1的坦克方向上下是一种大小范围
            // 左右又是一种大小范围)
            if (tk1 != tk2) {  //比较的坦克不能是自己
                switch (tk2.getDirect()) {
                    case 0://传进来的tank2的方向为上
                        switch (tk1.getDirect()) {
                            case 0://tk1的方向为上
                            case 2://tk1的方向为下
                                if (tk2.getY() >= tk1.getY() && tk2.getY() <= tk1.getY() + 60 &&
                                        !(tk2.getX() >= tk1.getX() + 40 || tk2.getX() + 40 <= tk1.getX())) {//这种情况下为撞到了
                                    return false;
                                }
                                break;
                            case 1://tk1的方向为右
                            case 3://tk1的方向为左
                                if (tk2.getY() >= tk1.getY() && tk2.getY() <= tk1.getY() + 40 &&
                                        !(tk2.getX() >= tk1.getX() + 60 || tk2.getX() + 40 <= tk1.getX())) {
                                    return false;
                                }
                                break;
                        }
                        break;
                    case 1://传进来的tank2的方向为右
                        switch (tk1.getDirect()) {
                            case 0://tk1的方向为上
                            case 2://tk1的方向为下
                                if (tk2.getX() + 60 >= tk1.getX() && tk2.getX() + 60 <= tk1.getX() + 40 &&
                                        !(tk2.getY() >= tk1.getY() + 60 || tk2.getY() + 40 <= tk1.getY())) {//这种情况下为撞到了
                                    return false;
                                }
                                break;
                            case 1://tk1的方向为右
                            case 3://tk1的方向为左
                                if (tk2.getX() >= tk1.getX() && tk2.getX() <= tk1.getX() + 60 &&
                                        !(tk2.getY() >= tk1.getY() + 40 || tk2.getY() + 40 <= tk1.getY())) {
                                    return false;
                                }
                                break;
                        }
                        break;
                    case 2://传进来的tank2的方向为下
                        switch (tk1.getDirect()) {
                            case 0://tk1的方向为上
                            case 2://tk1的方向为下
                                if (tk2.getY() + 60 >= tk1.getY() && tk2.getY() + 60 <= tk1.getY() + 60 &&
                                        !(tk2.getX() >= tk1.getX() + 40 || tk2.getX() + 40 <= tk1.getX())) {//这种情况下为撞到了
                                    return false;
                                }
                                break;
                            case 1://tk1的方向为右
                            case 3://tk1的方向为左
                                if (tk2.getY() + 60 >= tk1.getY() && tk2.getY() + 60 <= tk1.getY() + 40 &&
                                        !(tk2.getX() >= tk1.getX() + 60 || tk2.getX() + 40 <= tk1.getX())) {
                                    return false;
                                }
                                break;
                        }
                        break;
                    case 3://传进来的tank2的方向为左
                        switch (tk1.getDirect()) {
                            case 0://tk1的方向为上
                            case 2://tk1的方向为下
                                if (tk2.getX() >= tk1.getX() && tk2.getX() <= tk1.getX() + 40 &&
                                        !(tk2.getY() >= tk1.getY() + 60 || tk2.getY() + 40 <= tk1.getY())) {//这种情况下为撞到了
                                    return false;
                                }
                                break;
                            case 1://tk1的方向为右
                            case 3://tk1的方向为左
                                if (tk2.getX() >= tk1.getX() && tk2.getX() <= tk1.getX() + 60 &&
                                        !(tk2.getY() >= tk1.getY() + 40 || tk2.getY() + 40 <= tk1.getY())) {
                                    return false;
                                }
                                break;
                        }
                        break;
                }
            }
        }
        return true;
    }

    //怎么才能避免两个电脑坦克的相撞呢?害  现在好像还处理不了.....
    //现在用老师的方法来避免坦克相撞
    //老师方法的思路  让调用这个线程的坦克和这里循环得到的坦克进行比较
    //最笨的思路 -->  调用这个线程的坦克有  上下左右四种方向  然后和他比较的坦克在每一种每种方向里面又有两种的比较情况
    //所以一共是8种   另外在每一种方向的比较中都有一个循环比较
    public boolean isTouched() {//如果返回false  就认为重叠  返回true 就没有发生重叠
        switch (this.getDirect()) {
            case 0://调用这个线程的坦克方向为上
                //遍历坦克集合进行判断
                for (int i = 0; i < enemyVector.size(); i++) {
                    PC pc1 = enemyVector.get(i);
                    if(pc1 != this){//不和自己的坦克比较
                        // 这两个点都要判断   这两个点都要在敌方坦克的范围
                        //此时坦克的两点的坐标分别是[getX(),getY()],[getX() + 40,getY()]
                        //敌方坦克的范围是[pc1.getX(),pc1.getX() + 40]   [pc1.getY(),pc1.getY() + 60]
                        if(pc1.getDirect() == 0 || pc1.getDirect() == 2){//(1)遍历得到的坦克方向为上或下
                            if(getX() >= pc1.getX() && getX() <= pc1.getX() + 40 &&
                                    getY() >= pc1.getY() && getY() <= pc1.getY() + 60){//第一个点在敌方坦克的范围
                                return false;
                            }
                            if(getX() + 40 >= pc1.getX() && getX() + 40 <= pc1.getX() + 40 &&
                                    getY() >= pc1.getY() && getY() <= pc1.getY() + 60){//第二个点在敌方坦克的范围
                                return false;
                            }
                        }
                        if(pc1.getDirect() == 1 || pc1.getDirect() == 3){//(2)遍历得到的坦克方向为右或左
                            //此时坦克的两点的坐标分别是[getX(),getY()],[getX() + 40,getY()]
                            //敌方坦克的范围是[pc1.getX(),pc1.getX() + 60]   [pc1.getY(),pc1.getY() + 40]
                            if(getX() >= pc1.getX() && getX() <= pc1.getX() + 60 &&
                                    getY() >= pc1.getY() && getY() <= pc1.getY() + 40){//第一个点在敌方坦克的范围
                                return false;
                            }
                            if(getX() + 40 >= pc1.getX() && getX() + 40 <= pc1.getX() + 60 &&
                                    getY() >= pc1.getY() && getY() <= pc1.getY() + 40){//第二个点在敌方坦克的范围
                                return false;
                            }
                        }
                    }
                }
                break;
            case 1://调用这个线程的坦克方向为右
                //遍历坦克集合进行判断
                for (int i = 0; i < enemyVector.size(); i++) {
                    PC pc1 = enemyVector.get(i);
                    if(pc1 != this){//不和自己的坦克比较
                        // 这两个点都要判断   这两个点都要在敌方坦克的范围
                        //此时坦克的两点的坐标分别是[getX() + 60,getY()],[getX() + 60,getY() + 40]
                        //敌方坦克的范围是[pc1.getX(),pc1.getX() + 40]   [pc1.getY(),pc1.getY() + 60]
                        if(pc1.getDirect() == 0 || pc1.getDirect() == 2){//(1)遍历得到的坦克方向为上或下
                            if(getX() + 60 >= pc1.getX() && getX() + 60 <= pc1.getX() + 40 &&
                                    getY() >= pc1.getY() && getY() <= pc1.getY() + 60){//第一个点在敌方坦克的范围
                                return false;
                            }
                            if(getX() + 60 >= pc1.getX() && getX() + 60 <= pc1.getX() + 40 &&
                                    getY() + 40 >= pc1.getY() && getY() + 40 <= pc1.getY() + 60){//第二个点在敌方坦克的范围
                                return false;
                            }
                        }
                        if(pc1.getDirect() == 1 || pc1.getDirect() == 3){//(2)遍历得到的坦克方向为右或左
                            //此时坦克的两点的坐标分别是[getX() + 60,getY()],[getX() + 60,getY() + 40]
                            //敌方坦克的范围是[pc1.getX(),pc1.getX() + 60]   [pc1.getY(),pc1.getY() + 40]
                            if(getX() + 60 >= pc1.getX() && getX() + 60 <= pc1.getX() + 60 &&
                                    getY() >= pc1.getY() && getY() <= pc1.getY() + 40){//第一个点在敌方坦克的范围
                                return false;
                            }
                            if(getX() + 60 >= pc1.getX() && getX() + 60 <= pc1.getX() + 60 &&
                                    getY() + 40 >= pc1.getY() && getY() + 40 <= pc1.getY() + 40){//第二个点在敌方坦克的范围
                                return false;
                            }
                        }
                    }
                }
                break;
            case 2://调用这个线程的坦克方向为下
                //遍历坦克集合进行判断
                for (int i = 0; i < enemyVector.size(); i++) {
                    PC pc1 = enemyVector.get(i);
                    if(pc1 != this){//不和自己的坦克比较
                        // 这两个点都要判断   这两个点都要在敌方坦克的范围
                        //此时坦克的两点的坐标分别是[getX(),getY() + 60],[getX() + 40,getY() + 60]
                        //敌方坦克的范围是[pc1.getX(),pc1.getX() + 40]   [pc1.getY(),pc1.getY() + 60]
                        if(pc1.getDirect() == 0 || pc1.getDirect() == 2){//(1)遍历得到的坦克方向为上或下
                            if(getX() >= pc1.getX() && getX() <= pc1.getX() + 40 &&
                                    getY() + 60 >= pc1.getY() && getY() + 60 <= pc1.getY() + 60){//第一个点在敌方坦克的范围
                                return false;
                            }
                            if(getX() + 40 >= pc1.getX() && getX() + 40 <= pc1.getX() + 40 &&
                                    getY() + 60 >= pc1.getY() && getY() + 60 <= pc1.getY() + 60){//第二个点在敌方坦克的范围
                                return false;
                            }
                        }
                        if(pc1.getDirect() == 1 || pc1.getDirect() == 3){//(2)遍历得到的坦克方向为右或左
                            //此时坦克的两点的坐标分别是[getX(),getY() + 60],[getX() + 40,getY() + 60]
                            //敌方坦克的范围是[pc1.getX(),pc1.getX() + 60]   [pc1.getY(),pc1.getY() + 40]
                            if(getX() >= pc1.getX() && getX() <= pc1.getX() + 60 &&
                                    getY() + 60 >= pc1.getY() && getY() + 60 <= pc1.getY() + 40){//第一个点在敌方坦克的范围
                                return false;
                            }
                            if(getX() + 40 >= pc1.getX() && getX() + 40 <= pc1.getX() + 60 &&
                                    getY() + 60 >= pc1.getY() && getY() + 60 <= pc1.getY() + 40){//第二个点在敌方坦克的范围
                                return false;
                            }
                        }
                    }
                }
                break;
            case 3://调用这个线程的坦克方向为左
                //遍历坦克集合进行判断
                for (int i = 0; i < enemyVector.size(); i++) {
                    PC pc1 = enemyVector.get(i);
                    if(pc1 != this){//不和自己的坦克比较
                        // 这两个点都要判断   这两个点都要在敌方坦克的范围
                        //此时坦克的两点的坐标分别是[getX(),getY()],[getX(),getY() + 40]
                        //敌方坦克的范围是[pc1.getX(),pc1.getX() + 40]   [pc1.getY(),pc1.getY() + 60]
                        if(pc1.getDirect() == 0 || pc1.getDirect() == 2){//(1)遍历得到的坦克方向为上或下
                            if(getX() >= pc1.getX() && getX() <= pc1.getX() + 40 &&
                                    getY() >= pc1.getY() && getY() <= pc1.getY() + 60){//第一个点在敌方坦克的范围
                                return false;
                            }
                            if(getX() >= pc1.getX() && getX() <= pc1.getX() + 40 &&
                                    getY() + 40 >= pc1.getY() && getY() + 40 <= pc1.getY() + 60){//第二个点在敌方坦克的范围
                                return false;
                            }
                        }
                        if(pc1.getDirect() == 1 || pc1.getDirect() == 3){//(2)遍历得到的坦克方向为右或左
                            //此时坦克的两点的坐标分别是[getX(),getY()],[getX(),getY() + 40]
                            //敌方坦克的范围是[pc1.getX(),pc1.getX() + 60]   [pc1.getY(),pc1.getY() + 40]
                            if(getX() >= pc1.getX() && getX() <= pc1.getX() + 60 &&
                                    getY() >= pc1.getY() && getY() <= pc1.getY() + 40){//第一个点在敌方坦克的范围
                                return false;
                            }
                            if(getX() >= pc1.getX() && getX() <= pc1.getX() + 60 &&
                                    getY() + 40 >= pc1.getY() && getY() + 40 <= pc1.getY() + 40){//第二个点在敌方坦克的范围
                                return false;
                            }
                        }
                    }
                }
                break;
        }
        return true;//返回真说明没有发生相撞
    }

    @Override
    public void run() {
        while (loop) {//循环判断  不停的改变电脑坦克的坐标 进行绘画时达到不停的移动

            //这个判断是让敌方坦克发出多颗子弹
            if (isLive() && bulletVector.size() < 5) {//敌人的坦克时活着的 而且 集合的子弹数量为0
                switch (getDirect()) {//根据取出的电脑坦克的不同方向  来创建以实时的子弹对象  加入到子弹集合里面
                    case 0://电脑坦克的方向为上
                        bullet = new Bullet(getX() + 20, getY(), 0);
                        break;
                    case 1://右
                        bullet = new Bullet(getX() + 60, getY() + 20, 1);
                        break;
                    case 2://下
                        bullet = new Bullet(getX() + 20, getY() + 60, 2);
                        break;
                    case 3://左
                        bullet = new Bullet(getX(), getY() + 20, 3);
                        break;
                }
                bullet.setSpeed(9);
                new Thread(bullet).start();//创建了这个子弹对象 就启动这个线程
                bulletVector.add(bullet);
            }


            switch (this.getDirect()) {//实现坦克的自由移动
                case 0://向上移动
                    for (int i = 0; i < 30; i++) {
                        //这里做成一个循环的目的是让坦克朝着一个方向的移动持续一段时间
                        if (getY() > 0 && isTouched() && !(getX() + 40 > 213 && getX() < 1130 &&
                                getY() > 200 && getY() < 250) && !(getX() + 40 > 222
                                && getX() < 1200 && getY() > 600 && getY() < 655)) {
                            //向上移动  必须要坦克左上角的纵坐标必须要大于0
                            //这个判断限制了电脑坦克的移动范围
                            //又增加了一个判断  坦克之间还不能发生碰撞 如果这两个条件都不成立 走else直接跳出循环
                            //重新获取一个方向   直到两个坦克不相撞而且还在规定的范围内进行移动
                            this.varyUp();
                        } else {
                            break;//否则跳出向上持续移动的循环  让坦克重新获得一个方向 重新移动
                        }
                        try {//每移动一次就休眠50毫秒
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 1://for (int i = 0; i < 30; i++) {
                        if (getX() + 60 < 1500 && isTouched() && !(getX() + 60 > 200 && getX() + 60 < 1130
                                && getY()  + 40 > 195 && getY() < 240) && !(getX() + 60 > 210
                                && getX() + 60 < 1190 && getY() + 40 > 600 && getY() < 640)) {
                            //向右移动 坦克的左上角坐标加上身长60必须要小于界面的宽度1000
                            this.varyRight();
                        } else {
                            break;
                        }
                        try {//每移动一次就休眠50毫秒
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    break;
                case 2://for (int i = 0; i < 30; i++) {
                        if (getY() + 60 < 1000 && isTouched() && !(getX() + 40 > 213 &&getX() < 1130
                                && getY() + 60 > 190 &&getY() + 60 < 250) && !(getX() + 40 > 220
                                && getX() < 1200 && getY() + 60 > 590 && getY() + 60 < 650)) {
                            //向下移动的范围必须是左上角的坐标加上身长必须小于界面的高度
                            this.varyDown();
                        } else {
                            break;
                        }
                        try {//每移动一次就休眠50毫秒
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3://for (int i = 0; i < 30; i++) {
                        if (getX() > 0 && isTouched() && !(getX() > 213 && getX() <= 1141
                                && getY()  + 40 > 195 && getY() < 240) && !(getX() > 220 &&
                                getX() < 1215 && getY() + 40 > 600 && getY() < 630)) {
                            //想左移动的话 坦克左上角的x坐标必须要大于0
                            this.varyLeft();
                        } else {
                            break;
                        }
                        try {//每移动一次就休眠50毫秒
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
            setDirect((int) (Math.random() * 4));//然后随机给一个方向 [0~3]

            if (!isLive()) {//如果不存在生命特征  就结束这个线程
                break;
            }
        }
    }
}

实现音乐播放的类

package com.wqc.tankgame8;

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

/**
 * @author 高瞻远瞩
 * @version 1.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();
        }
    }
}

在游戏界面中充当墙的类

在这里插入图片描述

package com.wqc.tankgame8;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * 这个版本想实现在地图中有墙  怎么实现?
 * 创建他的对象 然后在paint方法中画出  通过一个循环
 * 现在来实现坦克不能穿过黄色的墙  x:213 ~ 1123 y:200 ~ 243 加了判断简单的实现了
 * 现在继续实现一个功能 --> 子弹碰到墙销毁  需要写一个方法  来判断
 */
public class Barrier {
    int x;
    int y;
    private boolean isLive = true;//让它具有生命值 可以被坦克打爆

    public boolean isLive() {
        return isLive;
    }

    public void setLive(boolean live) {
        isLive = live;
    }

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

实现击败坦克或障碍物时出现爆炸效果的类

在这里插入图片描述

package com.wqc.tankgame8;

/**
 * @author 高瞻远瞩
 * @version 坦克5.0版本
 * 定义一个炸弹对象  显示爆炸特效的思路 -->  1,创建一个炸弹集合   并设置生命周期 通过写一个生命周期逐渐减少的方法
 * 来实现炸弹的特效
 * 2,在每一次敌方坦克被我方坦克子弹击中时  就创建一个炸弹对象(这个炸弹对象的坐标和被击中的坦克的坐标保持一致)
 * 并把它加入炸弹集合中
 * 3,在画板的类里  定义三张爆炸的照片(爆炸特效的本质还是这三张图片的不停的被刷新)定义为null
 * 把它设置成成员属性的目的是为了以后可以在MyPanel类直接调用他  然后在构造器里初始化这三张照片
 * 4,在paint方法里  遍历炸弹集合 取出每一个炸弹对象 然后通过一个if-else if 判断生命周期 分别描绘
 * 这三张炸弹图片  配合面板不断被重绘  来实现 炸弹特效 最后判断炸弹的生命周期是否==0 --> 从集合中移除
 */
public class Bomb {
    private int x;
    private int y;
    private int lifeTime = 12;//生命周期
    private boolean isLive = true;//存活的判断

    public Bomb(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;
    }

    public int getLifeTime() {
        return lifeTime;
    }

    public void setLifeTime(int liveTime) {
        this.lifeTime = liveTime;
    }

    public boolean isLive() {
        return isLive;
    }

    public void setLive(boolean live) {
        isLive = live;
    }

    public void liveDown(){
        if(lifeTime >0){
            lifeTime--;
        }else{
            isLive = false;
        }
    }
}

子弹类

在这里插入图片描述

package com.wqc.tankgame8;

/**
 * @author 高瞻远瞩
 * @version 坦克5.0版本
 * 创建一个子弹对象  让它成为一个线程
 */
@SuppressWarnings("all")
public class Bullet implements Runnable {
    private int x;//子弹的实时横坐标
    private int y;//子弹的实时纵坐标
    private int direct;//子弹的方向
    private int speed = 5;//子弹的速度
    private boolean isLive = true;//子弹是否还存在屏幕上的特征  没有打中敌方坦克消失了算false
    //打中敌方坦克消失了也算false

    public boolean isLive() {
        return isLive;
    }

    public void setLive(boolean live) {
        isLive = live;
    }

    public int getX() {
        return x;
    }

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

    public int getY() {
        return y;
    }

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

    public int getDirect() {
        return direct;
    }

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

    public int getSpeed() {
        return speed;
    }

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

    public Bullet(int x, int y, int direct) {//子弹的位置由创建的时候确定  速度根据需要设定
        this.x = x;
        this.y = y;
        this.direct = direct;
    }

    @Override
    public void run() {
        while (true) {//无限循环
            //先进行休眠
            try {
                Thread.sleep(50);//50毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            switch (direct) {//随着坦克方向的改变  子弹的射击方向也在发生变化
                case 0://上
                    y -= speed;
                    break;
                case 1://右
                    x += speed;
                    break;
                case 2://下
                    y += speed;
                    break;

                case 3://左
                    x -= speed;
                    break;
            }
            //在这里记录一下子弹的位置 (测试)
            //System.out.println("x=" + x + " y=" + y);
            //在这里进行判断 如果大于边界的话或者打中敌方坦克  就跳出while循环  结束线程
            if (!(x >= 0 && x <= 1500 && y >= 0 && y <= 1000 && isLive)) {
                //如果子弹运动到边界之外且子弹不存活  结束子弹线程
                isLive = false;
                //System.out.println("子弹线程结束...");
                break;//跳出循环  结束子弹线程
            }
        }
    }
}

画板类 (最重要的一个类 也就是你能看见游戏界面上的所有场景,都是它在起作用.代码有点多 请耐心一点)

package com.wqc.tankgame8;

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


/**
 * @author 高瞻远瞩
 * @version 坦克8.0版本
 * 坦克大战的绘图区域
 * 实现了画板的区域
 * 1 + 2 标注的方法可以实现多发子弹打中坦克的效果 思路是  先遍历电脑的坦克集合 然后遍历我发出的子弹集合
 * 然后调用hitTank(Vector<Bullet> myBullets, PC pc) 把取出的第一个坦克去和子弹集合去比较是否被击中
 * 3 + 4  思路是 先遍历我发出的子弹集合 再遍历电脑坦克集合  在遍历坦克集合去调用hitTank(Bullet bullet, PC pc)
 * 让取出的第一个子弹和电脑坦克集合进行比较电脑坦克是否被击中
 * 等于说  如果子弹打中了坦克 先应把生命值设为false 然后需要考虑的是把双方的线程结束 并且不在面板重绘
 * 并且如果子弹和坦克是个集合的话 ---> 需要把子弹和坦克对象从集合中移除
 * 坦克大战项目基本是完成了   但是还有一些功能需要完善  就是继续游戏的砖还有钢板
 * 先实现障碍物的继续游戏  目前这个功能我实现不了....
 */
@SuppressWarnings("all")
public class MyPanel extends JPanel implements KeyListener, Runnable {
    //继承JPanel的目的是为了调用父类的paint方法进行画笔操作
    //实现KeyListener接口的目的是接收用户键盘的输入然后进行监听处理
    //实现Runnable接口的目的是 把面板做成一个线程  然后不停的重绘
    Vector<PC> enemyVector = new Vector<>();//这个相当于成员属性  用vector集合存放敌人的坦克
    Vector<Barrier> birVector = new Vector<>();//定义一个存放障碍物的集合
    Vector<Plate> peVector = new Vector<>();//定义存放钢板对象的集合
    MyTank tank = null;//先把他设置成null 方便在这个类里  操控他的属性!
    // 进而坦克对象的创建在构造器里  方便!!
    PC pc = null;//先定义电脑坦克  先不实例化 目的是方便在这个类里使用它 因为它是成员属性
    Hero hero = null;//定义第二个玩家的坦克
    int PcSize = 5;//设置敌人坦克的数量为5
    Vector<Bomb> bomb = new Vector<>();//定义一个打掉坦克的炸弹对象集合
    Vector<Bomb> bombBr = new Vector<>();//定义一个打掉障碍物的炸弹对象集合
    Image image1 = null;//定义炸弹的图片1
    Image image2 = null;//定义炸弹的图片2
    Image image3 = null;//定义炸弹的图片
    Image image4 = null;//定义大吉大利今晚吃鸡的图片
    Bullet bullet;//提前定义好子弹的对象  用于创建电脑坦克的子弹
    Win win = null;//定义一个胜利的号角
    Vector<Node> nodes = new Vector<>();//创建一个对象集合
    Barrier bir = null;//定义障碍物的对象
    int birNum = 70;//定义这个障碍物的数量
    Plate plate;//定义钢板的对象
    int peSize = 45;//钢板的数量
    int birSize = (birNum + 1) * 2;//障碍物的总数

    public MyPanel(String key) {//构造器

        //初始化钢板
        for (int i = 0; i < peSize; i++) {
            Plate plate = new Plate(200 + 22 * (i + 1), 600);
            Plate plate1 = new Plate(200 + 22 * (i + 1), 622);
            peVector.add(plate);
            peVector.add(plate1);
        }

        //如果文件不存在的话 如果继续上局游戏的话 读取不到指定的文件  所以会抛出一个异常
        //所以在这个进行一个处理
        File file = new File(Record.getFilePath());//得到这个文件
        if (file.exists()) {//如果这个文件存在   才能调用这个方法
            nodes = Record.readEnemyTank();//调用这个方法  返回一个Nodes集合

        } else {//不存在的话  就把key置1   这种情况应对的是假设文件不存在 用户输入2的话
            //会走这个逻辑  然后就又把key置1   就试下了开启新游戏的效果
            System.out.println("无法继续上一句游戏  只能开启新游戏");
            key = "1";
        }

        Record.setEnemyTanks(enemyVector);//把MyPanel里面的坦克集合对象设置给Record里面的坦克集合对象
        //便于遍历这个坦克集合 得到想要的信息  然后进行写入文件的操作
        Record.setBirVector(birVector);
        switch (key) {
            case "1"://开启一局新游戏  先把玩家击败的坦克数量设置成0
                Record.setHitEnemyTankNum(0);
                //初始化障碍物的对象  并把它加入到集合中
                for (int i = 0; i <= birNum; i++) {
                    Barrier barrier = new Barrier(200 + 13 * (i + 1), 200);
                    Barrier barrier1 = new Barrier(200 + 13 * (i + 1), 223);
                    birVector.add(barrier);
                    birVector.add(barrier1);
                    Record.setBirVector(birVector);
                }
                tank = new MyTank(1150, 700);//初始化坦克在画板的起始位置
                tank.setSpeed(15);//设置我的坦克速度为10
                Record.setHero(tank);//把这个类的坦克对象映射到Record类中  用于在文件中进行存取
                //设置一个循环,把创建的坦克对象添加到tankVector集合中
                for (int i = 0; i < PcSize; i++) {//因为这个循环在构造器里 所以把PcSize做成PC的成员属性不合适
                    // 因为MyPanel构造器的初始化优先于 MyTank和PC对象的创建
                    //vector.add(new PC(100 * (i + 1), 0));
                    PC pc = new PC(60 * (i + 1), 0);//先得到敌人坦克对象
                    PC pc1 = new PC(60 * (i + 1), 400);//先得到敌人坦克对象
                    pc.setDirect(2);//然后把方向设置成向下
                    pc.setSpeed(4);//设置坦克的移动速度
                    new Thread(pc).start();//启动这个不断改变坐标的线程
                    enemyVector.add(pc);//最后把坦克对象添加到vector集合中
                    pc1.setDirect(2);//然后把方向设置成向下
                    pc1.setSpeed(4);//设置坦克的移动速度
                    new Thread(pc1).start();//启动这个不断改变坐标的线程
                    enemyVector.add(pc1);//最后把坦克对象添加到vector集合中


                    //每创建一个电脑坦克 就让它具有射击功能-->创建实时子弹对象  把他装到bulletVector集合里
                    bullet = new Bullet(pc.getX() + 20, pc.getY() + 60, pc.getDirect());
                    //因为电脑坦克初始化方向为向下 所以子弹的坐标为(pc.getX() + 20,pc.getY() + 60)
                    bullet.setSpeed(6);//设置创建的坦克发出的子弹速度为6
                    pc.bulletVector.add(bullet);//把电脑的子弹对象加入到这个集合里面
                    new Thread(bullet).start();//并启动这个实时的子弹线程
                    //实时可以理解为 --> 每创建一个电脑的坦克对象 都会创建一个子弹线程在他的炮管上

                    //pc.setEnemyVector(enemyVector);//把电脑坦克集合映射到PC这个类里  为了防止重叠
                    //******************这是我放的位置 只能放到循环里的原因是在循环里才会创建PC对象
                    //所以如果放到别的位置会报空指针异常

                }
                break;
            case "2"://继续上局游戏
                Record.setBirVector(birVector);
                //设置一个循环,把创建的坦克对象添加到tankVector集合中
                for (int i = 0; i < nodes.size() - birSize; i++) {
                    Node node = nodes.get(i);
                    pc = new PC(node.getX(), node.getY());//先得到敌人坦克对象
                    pc.setDirect(node.getDirect());//然后把方向设置成向下
                    pc.setSpeed(4);//设置坦克的移动速度
                    new Thread(pc).start();//启动这个不断改变坐标的线程
                    enemyVector.add(pc);//最后把坦克对象添加到vector集合中

                    //每创建一个电脑坦克 就让它具有射击功能-->创建实时子弹对象  把他装到bulletVector集合里
                    bullet = new Bullet(pc.getX() + 20, pc.getY() + 60, pc.getDirect());
                    //因为电脑坦克初始化方向为向下 所以子弹的坐标为(pc.getX() + 20,pc.getY() + 60)
                    bullet.setSpeed(6);//设置创建的坦克发出的子弹速度为6
                    pc.bulletVector.add(bullet);//把电脑的子弹对象加入到这个集合里面
                    new Thread(bullet).start();//并启动这个实时的子弹线程
                    //实时可以理解为 --> 每创建一个电脑的坦克对象 都会创建一个子弹线程在他的炮管上
                }

                //初始化障碍物的对象  并把它加入到集合中
                for (int i = nodes.size() - birSize; i < nodes.size() - 1; i++) {
                    Node node = nodes.get(i);
                    Barrier barrier = new Barrier(node.getX(), node.getY());
                    birVector.add(barrier);
                    Record.setBirVector(birVector);
                }

                for (int i = nodes.size() - 1; i < nodes.size(); i++) {
                    Node node = nodes.get(i);
                    tank = new MyTank(node.getX(), node.getY());//初始化坦克在画板的起始位置
                    Record.setHero(tank);
                    tank.setDirect(node.getDirect());
                    tank.setSpeed(15);//设置我的坦克速度为10
                }
                break;
            case "3"://在这个逻辑写双人游戏
                for (int i = 0; i <= birNum; i++) {
                    Barrier barrier = new Barrier(200 + 13 * (i + 1), 200);
                    Barrier barrier1 = new Barrier(200 + 13 * (i + 1), 223);
                    birVector.add(barrier);
                    birVector.add(barrier1);
                }
                tank = new MyTank(1150, 700);//初始化坦克在画板的起始位置
                tank.setSpeed(15);//设置我的坦克速度为10
                hero = new Hero(100, 500);//初始化第二个玩家坦克
                hero.setSpeed(15);
                break;
            default:
                System.out.println("您的输入有误...");
                break;
        }

        //初始化三张爆炸的图片
        image1 = Toolkit.getDefaultToolkit().createImage(Panel.class.getResource("/bomb_1.gif"));
        image2 = Toolkit.getDefaultToolkit().createImage(Panel.class.getResource("/bomb_2.gif"));
        image3 = Toolkit.getDefaultToolkit().createImage(Panel.class.getResource("/bomb_3.gif"));
        image4 = Toolkit.getDefaultToolkit().createImage(Panel.class.getResource("/win.png"));
        win = new Win(450, 548);
        win.start();//这是个浮屏的线程
        new AePlayWave("src\\qfl.wav").start();//启动音乐线程
    }

    @Override
    public void paint(Graphics g) {//这个方法在首次显示窗口的时候会被自动调用
        super.paint(g);//初始化画笔的信息
        g.fillRect(0, 0, 1500, 1000);//填充矩形 默认为黑色 画板
        //这句话可以理解为初始化画板的大小

        //遍历障碍物集合  画出障碍物
        for (int i = 0; i < birVector.size(); i++) {
            Barrier barrier = birVector.get(i);//取出每一个障碍物对象
            if (barrier.isLive()) {//判断生存了才画出
                g.setColor(Color.ORANGE);//设置颜色
                g.fill3DRect(barrier.x, barrier.y, 10, 20, true);
            } else {
                birVector.remove(barrier);
            }
        }

        //遍历钢板集合  画出钢板
        for (int i = 0; i < peVector.size(); i++) {
            Plate plate = peVector.get(i);
            g.setColor(Color.white);
            g.fill3DRect(plate.x, plate.y, 20, 20, true);
        }

        //把坦克的方向设置成变量   设置为成员属性  每次去获取它!!!!
        if (tank != null && tank.isLive()) {//在这里来判断是否画出我的坦克
            drawTank(tank.getX(), tank.getY(), g, tank.getDirect(), 1);//调用方法 初始化我自己的坦克
        }

        if (hero != null && hero.isLive()) {//画出第二个玩家坦克
            drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 0);
        }

        //画出第二个玩家坦克的子弹集合
        if (hero != null) {
            for (int i = 0; i < hero.heroBullet.size(); i++) {
                Bullet bullet = hero.heroBullet.get(i);
                //取出子弹后进行判断
                if (bullet.isLive()) {
                    shotMove(g, 1, 2, bullet);
                } else {//否则就把子弹从坦克集合中移除了
                    hero.heroBullet.remove(bullet);
                }
            }
        }

        //遍历电脑坦克集合  得到每一个电脑坦克  然后调用drawTank方法在界面绘画出敌人的坦克
        for (int i = 0; i < enemyVector.size(); i++) {//感情的自然流露!!!!
            PC enemyTank = enemyVector.get(i);//得到的就是集合第一个坦克的对象引用
            //遍历过程原理解毒   遍历集合的第一个电脑对象 然后enemyTank-->第一个电脑坦克对象-->然后在面板绘出
            //然后遍历第二个电脑对象 然后enemyTank-->第二个电脑坦克对象-->然后在面板绘出
//            pc.setDirect(2);

            enemyTank.setEnemyVector(enemyVector);//把电脑坦克集合映射到PC这个类里
            //************************这是老师放的位置********************


            if (enemyTank.isLive()) {//在这里加判断  只有敌人坦克isLive = true才进行画的动作
                //这个判断  就是为了玩家打到敌方坦克时  让敌方的坦克消失  消失的原理还是让它不在面板上画出
                drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
                //这个enemyTank.getX()已经是定值了  在创建对象的时候就已经初始化过了

                //遍历电脑坦克的子弹集合  使子弹对象在画板中显示  并且加一个判断 判断子弹对象是否存活
                for (int j = 0; j < enemyTank.bulletVector.size(); j++) {
                    Bullet bullet = enemyTank.bulletVector.get(j);//从集合中得到每一个子弹对象
                    if (bullet.isLive()) {//如果电脑发出的子弹isLive() = true 就显示在画板上
                        //如果电脑发出的子弹消失在了画板上  就不进行绘画动作
                        shotMove(g, 6, 2, bullet);
                    } else {
                        enemyTank.bulletVector.remove(bullet);
                        //如果电脑发出的子弹不存在屏幕上的话 即这个子弹线程结束了 就从集合中移除
                    }
                }
            }
        }

        //绘出我自己的子弹集合
        for (int i = 0; i < tank.myBullets.size(); i++) {//遍历我的子弹集合
            Bullet bullet = tank.myBullets.get(i);//取出每一个子弹
            if (bullet != null && bullet.isLive()) {//等价于bullet.isLive() == true 取出的每一个子弹 如果生存就画出 不生存就从集合中移除
                //以上这个不为null的判断极其重要  因为发射这个子弹的前提是用户按下J键
                //用户按下J键的前提是这个程序启动  所以如果这个程序启动了 没有加不为null的判断 就会报一个空指针异常
                //System.out.println("子弹被发出");
                shotMove(g, 3, 2, bullet);//发射出红色矩形子弹
                //repaint();这个方法也可以  做成线程的目的是为了以后进行绘画动作的时候不用再刻意的写repaint方法去刷新屏幕了
                //因为这个重绘的线程目前来说一直在运行  一直在重绘
            } else {//如果我的子弹因为打到敌方坦克或者消失在画板上的话 就把这个子弹从myBullets集合中移除
                tank.myBullets.remove(bullet);
            }
        }

        //绘出炸弹特效  遍历炸弹集合 遍历的目的是为了取出炸弹对象
        for (int i = 0; i < bomb.size(); i++) {
            Bomb bomb1 = this.bomb.get(i);//先取出炸弹对象  然后进行绘画操作  判断生命周期
            //因为面板一致被重绘  可以达到一个动态的效果  通过不停的刷新这三张图片
            if (bomb1.getLifeTime() > 9) {
                g.drawImage(image1, bomb1.getX(), bomb1.getY(), 60, 60, this);
            } else if (bomb1.getLifeTime() > 4) {
                g.drawImage(image2, bomb1.getX(), bomb1.getY(), 60, 60, this);
            } else {
                g.drawImage(image3, bomb1.getX(), bomb1.getY(), 60, 60, this);
            }
            bomb1.liveDown();//生命周期减1
            if (!bomb1.isLive()) {//如果生命周期减到0  即isLive = false  爆炸就没有了生命特征
                //就把这个爆炸的对象从集合中移除
                bomb.remove(bomb1);
            }
        }

        //绘出打掉障碍物的炸弹特效
        for (int i = 0; i < bombBr.size(); i++) {
            Bomb bomb = bombBr.get(i);
            if (bomb.getLifeTime() > 9) {
                g.drawImage(image1, bomb.getX(), bomb.getY(), 30, 30, this);
            } else if (bomb.getLifeTime() > 6) {
                g.drawImage(image2, bomb.getX(), bomb.getY(), 30, 30, this);
            } else {
                g.drawImage(image3, bomb.getX(), bomb.getY(), 30, 30, this);
            }
            bomb.liveDown();
            if (bomb.getLifeTime() == 0) {
                bombBr.remove(bomb);
            }
        }

        if (hero == null) {//走if这个逻辑不适用于双人游戏
            show1(g);//在面板上画出指定的内容
        } else {
            show2(g);//双人游戏的界面
        }
    }

    public void show2(Graphics g) {
        if (!tank.isLive() && hero.isLive()) {
            g.drawImage(image4, win.x, win.y, 506, 55, this);
//            g.setColor(Color.pink);
//            g.setFont(new Font("隶书", Font.BOLD, 100));//隶书 粗体 大小为100
//            g.drawString("蓝绿坦克胜利!!!", win.x, win.y);
        }
        if (!hero.isLive() && tank.isLive()) {
            g.drawImage(image4, win.x, win.y, 506, 55, this);
//            g.setColor(Color.magenta);
//            g.setFont(new Font("隶书", Font.BOLD, 100));
//            g.drawString("黄坦克胜利!!!", win.x, win.y);
        }
        drawTank(1530, 200, g, 0, 0);
        g.setColor(Color.BLACK);
        g.setFont(new Font("隶书", Font.BOLD, 50));
        g.drawString("VS", 1600, 250);
        drawTank(1680, 200, g, 0, 1);
    }

    //编写一个方法  当玩家胜利后  在屏幕上打印  指定的文字  或者  打印指定的文字  比如  记录玩家的击败坦克的数目
    //这个方法适用于单人游戏和继续游戏
    public void show1(Graphics g) {
        if (enemyVector.size() == 0) {//如果敌人的坦克集合为0
            g.setColor(Color.magenta);
            g.setFont(new Font("隶书", Font.BOLD, 100));
            g.drawString("恭喜你胜利了!", win.x, win.y);
        }
        if (!tank.isLive()) {
            g.setColor(Color.magenta);
            g.setFont(new Font("隶书", Font.BOLD, 100));
            g.drawString("你好菜哦!!!", win.x, win.y);
        }
        //记录玩家击败坦克的数目的界面
        g.setColor(Color.black);
        Font font = new Font("隶书", Font.BOLD, 35);//隶书 粗体 大小为30
        g.setFont(font);
        g.drawString("您累计击毁敌方坦克", 1520, 100);
        drawTank(1530, 200, g, 0, 0);
        g.setColor(Color.BLACK);//需要重新设置画笔的颜色
        g.drawString(Record.getHitEnemyTankNum() + "", 1600, 240);
        //这个数目是一个变量 每次玩家打掉一个坦克 就会调用方法去修改打掉坦克的数目  然后用get方法进行获取就可以了
    }

    //编写一个方法  画出子弹颜色不同的子弹 以及类型不同的子弹
    public void shotMove(Graphics g, int color, int type, Bullet bullet) {
        switch (color) {
            case 0:
                g.setColor(Color.BLACK);
                break;
            case 1:
                g.setColor(Color.red);
                break;
            case 2:
                g.setColor(Color.ORANGE);
                break;
            case 3:
                g.setColor(Color.green);
                break;
            case 4:
                g.setColor(Color.magenta);// 洋红色
                break;
            case 5:
                g.setColor(Color.blue);
                break;
            case 6:
                g.setColor(Color.pink);
                break;
            case 7:
                g.setColor(Color.LIGHT_GRAY);//浅灰色
                break;
        }
        switch (type) {//定义子弹的类型  因为这是个对象  坐标在不断的改变 所以很好理解啊!
            case 1:
                g.draw3DRect(bullet.getX(), bullet.getY(), 3, 3, false);
                break;
            case 2:
                g.fillRect(bullet.getX(), bullet.getY(), 3, 3);
                break;
            case 3:
                g.drawLine(bullet.getX(), bullet.getY(), 50, bullet.getY());
                break;
            case 4:
                g.setFont(new Font("隶书", Font.BOLD, 100));//发射出一段字体
                g.drawString("少华是个笨鳖", bullet.getX(), bullet.getY());
                break;
            case 5:
                Image image = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/888.png"));
                g.drawImage(image, bullet.getX() - 35, bullet.getY() - 20, 37, 38, this);
                break;
        }
    }

    /**
     * @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.yellow);
                break;
        }

        //根据坦克的方向,来绘制坦克
        //0 表示 向上  1 表示向右 2表示向下 3表示向左
        switch (direct) {
            case 0://0表示向上
                g.fill3DRect(x, y, 10, 60, false);//先画出坦克的左轮
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//中间部分
                g.fill3DRect(x + 30, y, 10, 60, false);//右轮
                g.fillOval(x + 10, y + 20, 20, 20);//中间那个圆
                g.drawLine(x + 20, y, x + 20, y + 30);//炮管
                break;
            case 1://右
                g.fill3DRect(x, y, 60, 10, false);//先画出坦克的左轮
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//中间部分
                g.fill3DRect(x, y + 30, 60, 10, false);//右轮
                g.fillOval(x + 20, y + 10, 20, 20);//中间那个圆
                g.drawLine(x + 60, y + 20, x + 30, y + 20);//炮管
                break;
            case 2://下
                g.fill3DRect(x, y, 10, 60, false);//先画出坦克的左轮
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//中间部分
                g.fill3DRect(x + 30, y, 10, 60, false);//右轮
                g.fillOval(x + 10, y + 20, 20, 20);//中间那个圆
                g.drawLine(x + 20, y + 60, x + 20, y + 30);//炮管
                break;
            case 3://左
                g.fill3DRect(x, y, 60, 10, false);//先画出坦克的左轮
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//中间部分
                g.fill3DRect(x, y + 30, 60, 10, false);//右轮
                g.fillOval(x + 20, y + 10, 20, 20);//中间那个圆
                g.drawLine(x, y + 20, x + 30, y + 20);//炮管
                break;

            default:
                System.out.println("暂时不做处理");
                break;
        }
    }

    //写一个方法  hero对tank击中判断
    public void heroHitTank() {
        if (tank.isLive()) {//如果tank存活的话  才进行击中判断
            hitTank(hero.heroBullet, tank);//hero击中tank 的判断方法
        }
    }

    //tank击中hero 的判断方法
    public void tankHitHero() {
        if (hero.isLive()) {
            hitTank(tank.myBullets, hero);
        }
    }

    //判断敌方坦克打中我方坦克  先遍历敌方的坦克 再遍历他的坦克子弹集合 最后才调用击中坦克的方法
    public void hitEnemyTank3() {
        for (int k = 0; k < enemyVector.size(); k++) {
            PC pc = enemyVector.get(k);//取出坦克
            for (int i = 0; i < pc.bulletVector.size(); i++) {//再把取出的电脑坦克调用坦克子弹集合进行遍历
                Bullet bullet = pc.bulletVector.get(i);//先得到了电脑坦克的每一个子弹
                if (bullet.isLive() && tank.isLive()) {
                    //这个判断到底有何意义?? 敌方的子弹存活并且我坦克也存活  才能进行判断是是否被击中
                    //这个判断很有意义  假如我的坦克被击中了  生命值为false  则继续会进入以下子弹击中的判断
                    //进而会继续产生爆炸对象
                    hitTank(bullet, tank);//进行判断  子弹是否是否打中敌方坦克
                }
            }
        }
    }


    /************     4       *************/
    public void hitEnemyTank2() {
        for (int i = 0; i < tank.myBullets.size(); i++) {
            Bullet bullet = tank.myBullets.get(i);//先得到了我的每一个子弹
            if (bullet != null && bullet.isLive()) {
                for (int j = 0; j < enemyVector.size(); j++) {
                    PC pc = this.enemyVector.get(j);//然后得到每一个电脑坦克
                    hitTank(bullet, pc);//进行判断  子弹是否是否打中敌方坦克
                }
            }
        }
    }

    /********      3       **********/
    public void hitTank(Bullet bullet, Tank pc) {//这个方法和1构成了方法重载
        switch (pc.getDirect()) {
            case 0://坦克向上  因为上下的坦克所在的范围一样  所以归并为一种情况
            case 2://坦克向下  如果子弹的位置在坦克所在范围之内 就视为打中
                if (bullet.getX() > pc.getX() - 3 && bullet.getX() < pc.getX() + 40 + 3
                        && bullet.getY() > pc.getY() - 1 && bullet.getY() < pc.getY() + 60 + 1) {
                    pc.setLive(false);//被击中 电脑生命值为false
                    bullet.setLive(false);//同时  玩家发射出的子弹也消失了
                    if (pc instanceof PC) {
                        enemyVector.remove(pc);  //同时把这个对象坦克从集合中移除
                        //在这里我没有移除  我是在调用这个方法之后进行判断 然后再移除的
                        //如果被击中  就创建一个炸弹对象  然后把他加入到bomb集合里
                        Record.recordHitEnemyTankNum();
                        //如果传入的坦克类型是敌方坦克的话  才会进入判断 击中的话记录数目+1
                    }
                    Bomb bomb1 = new Bomb(pc.getX(), pc.getY());
                    bomb.add(bomb1);
                }
                break;
            case 1://case 3://if (bullet.getX() > pc.getX() - 3 && bullet.getX() < pc.getX() + 60 + 3
                        && bullet.getY() > pc.getY() - 1 && bullet.getY() < pc.getY() + 40 + 1) {
                    pc.setLive(false);//被击中 电脑生命值为false isLive置为false的目的是在面板上不会描绘了 同时也结束线程
                    bullet.setLive(false);//同时 子弹也消失了
                    // 子弹的生命置为fasle就意味着这个子弹线程结束了  并且也不会进行在面板描绘了  因为在描绘的时候这些子弹会被
                    //if条件过滤掉   子弹的移除判断是在paint方法里进行的
                    if (pc instanceof PC) {
                        enemyVector.remove(pc);//从电脑坦克从集合中移除
                        Record.recordHitEnemyTankNum();//击中的话记录数目+1
                    }
                    //如果被击中  就创建一个炸弹对象  然后把他加入到bomb集合里
                    Bomb bomb1 = new Bomb(pc.getX(), pc.getY());
                    bomb.add(bomb1);
                }
                break;
        }
    }


    /********2********/
    public void hitEnemyTank() {
        for (int j = 0; j < enemyVector.size(); j++) {//遍历电脑坦克集合
            // 判断我的一颗子弹是否打中敌方所有坦克 直到遍历到我的子弹集合的最后一颗子弹
            PC pcTank = enemyVector.get(j);//先得到每个坦克的对象 然后进行判断
            hitTank(tank.myBullets, pcTank);//判断电脑坦克是否被子弹打中
        }
    }

    /******* 1 ********/
    public void hitTank(Vector<Bullet> Bullets, Tank tank1) {//参数的话改成子弹集合 还有电脑坦克
        for (int i = 0; i < Bullets.size(); i++) {//遍历子弹集合
            Bullet bullet = Bullets.get(i);//循环取出每一个子弹  然后进行判断是否击中敌方坦克
            //取出我的子弹后  应该进行判断这时候我的子弹是否存活  存活的话 才进入是否打中敌方坦克的判断

            switch (tank1.getDirect()) {
                case 0://坦克向上  因为上下的坦克所在的范围一样  所以归并为一种情况
                case 2://坦克向下  如果子弹的位置在坦克所在范围之内 就视为打中
                    if (bullet.getX() > tank1.getX() - 3 && bullet.getX() < tank1.getX() + 40 + 3
                            && bullet.getY() > tank1.getY() - 1 && bullet.getY() < tank1.getY() + 60 + 1) {
                        if (tank1 instanceof PC) {
                            enemyVector.remove(tank1);  //同时把这个对象坦克从集合中移除
                            //在这里我没有移除  我是在调用这个方法之后进行判断 然后再移除的
                        }
                        tank1.setLive(false);//被击中 生命值为false
                        bullet.setLive(false);//同时  玩家发射出的子弹也消失了


                        //如果被击中  就创建一个炸弹对象  然后把他加入到bomb集合里
                        Bomb bombs = new Bomb(tank1.getX(), tank1.getY());
                        bomb.add(bombs);

                        //如果击中  面板击败坦克的数量就加1
                        if (!bullet.isLive() && tank1 instanceof PC) {
                            //如果击中的话 以上逻辑就会把子弹设置成不生存 进而出现面板坦克数量增加
                            Record.recordHitEnemyTankNum();
                            return;//如果一颗子弹击中了 直接跳出这个方法 不判断下一颗子弹是否击中
                        }
                    }
                    break;
                case 1://case 3://if (bullet.getX() > tank1.getX() - 3 && bullet.getX() < tank1.getX() + 60 + 3
                            && bullet.getY() > tank1.getY() - 1 && bullet.getY() < tank1.getY() + 40 + 1) {
                        tank1.setLive(false);//被击中 电脑生命值为false
                        bullet.setLive(false);//同时  子弹也消失了
                        if (tank1 instanceof PC) {
                            enemyVector.remove(tank1);
                        }
                        //如果被击中  就创建一个炸弹对象  然后把他加入到bomb集合里
                        Bomb bombs = new Bomb(tank1.getX(), tank1.getY());
                        bomb.add(bombs);

                        //如果击中  面板击败坦克的数量就加1
                        if (!bullet.isLive() && tank1 instanceof PC) {
                            Record.recordHitEnemyTankNum();
                            return;
                        }
                    }
                    break;
            }
        }
    }

    //现在这个方法是证明电脑坦克打到障碍物产生爆炸效果
    public void enemyTankHitBarrier() {
        for (int i = 0; i < pc.enemyVector.size(); i++) {
            PC pc1 = this.pc.enemyVector.get(i);//取出每一个敌方坦克的对象
            for (int j = 0; j < birVector.size(); j++) {
                Barrier barrier = birVector.get(j);//取出每一个障碍物
                if (barrier.isLive()) {//障碍物生存的话才进行判断
                    hitBarrier(pc1.bulletVector, barrier);
                }
                //拿一个敌方坦克的子弹集合 和 障碍物集合进行遍历  外层是遍历所有的敌方坦克对象
            }
        }
    }

    //这个方法用来检验MyTank的子弹和Hero的子弹是否打中了障碍物 如果打中产生爆炸效果
    public void hitBarrierProof() {
        for (int i = 0; i < birVector.size(); i++) {
            Barrier barrier = birVector.get(i);//取出障碍物
            if (barrier != null && barrier.isLive()) {//生存了才进行判断
                hitBarrier(tank.myBullets, barrier);
                if (hero != null) {
                    hitBarrier(hero.heroBullet, barrier);
                }
            }
        }
    }

    //现在来写一个方法来判断子弹击中障碍物 产生爆炸效果  并且让子弹和障碍物都消失
    public void hitBarrier(Vector<Bullet> bt, Barrier br) {
        for (int i = 0; i < bt.size(); i++) {
            Bullet bullet = bt.get(i);
            if (bullet.isLive()) {//取出的子弹生存的话才进行判断
                if (bullet.getX() > br.x && bullet.getX() < br.x + 10 &&
                        bullet.getY() > br.y && bullet.getY() < br.y + 20) {
                    bullet.setLive(false);
                    br.setLive(false);
                    Bomb bombs = new Bomb(br.x, br.y);
                    bombBr.add(bombs);
                    birSize--;
                }
            }
        }
    }

    //写一个方法 hero和tank的子弹碰到钢板  子弹消失
    public void hitPlate(Vector<Bullet> bullet, Plate pe) {
        for (int i = 0; i < bullet.size(); i++) {
            Bullet bullet1 = bullet.get(i);//取出每一个子弹
            if (bullet1.isLive()) {
                if (bullet1.getX() > pe.x && bullet1.getX() < pe.x + 20
                        && bullet1.getY() > pe.y && bullet1.getY() < pe.y + 20) {
                    bullet1.setLive(false);
                }
            }
        }
    }

    //写一个方法  验证hero和tank的子弹碰到钢板消失
    public void proofHitPlate() {
        for (int i = 0; i < peVector.size(); i++) {
            Plate plate = peVector.get(i);
            hitPlate(tank.myBullets, plate);
            if (hero != null) {
                hitPlate(hero.heroBullet, plate);
            }
        }
    }

    //写一个方法  验证pc的子弹击中钢板  子弹消失
    public void pcHitPlate() {
        for (int i = 0; i < enemyVector.size(); i++) {
            PC pc1 = enemyVector.get(i);
            if (pc1 != null) {
                for (int j = 0; j < peVector.size(); j++) {
                    Plate plate = peVector.get(j);
                    hitPlate(pc1.bulletVector, plate);
                }
            }
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //处理 wdsa 键按下的情况
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W) {  //上
            tank.setDirect(0);//修改成员属性direct   改变坦克的方向
            if (tank.getY() > 0 && !(tank.getX() + 40 > 213 && tank.getX() < 1130 &&
                    tank.getY() > 200 && tank.getY() < 250) && !(tank.getX() + 40 > 222
                    && tank.getX() < 1200 && tank.getY() > 600 && tank.getY() < 655)) {
                //限制我方坦克的移动范围
                tank.varyUp();//这是个改变坐标(移动)的方法
            }
        } else if (e.getKeyCode() == KeyEvent.VK_D) {//右
            tank.setDirect(1);
            if (tank.getX() + 60 < 1500 && !(tank.getX() + 60 > 200 && tank.getX() + 60 < 1130 &&
                    tank.getY() + 40 > 195 && tank.getY() < 240) && !(tank.getX() + 60 > 210
                    && tank.getX() + 60 < 1190 && tank.getY() + 40 > 600 && tank.getY() < 640)) {
                tank.varyRight();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_S) {//下
            tank.setDirect(2);
            if (tank.getY() + 60 < 1000 && !(tank.getX() + 40 > 213 && tank.getX() < 1130 &&
                    tank.getY() + 60 > 190 && tank.getY() + 60 < 250) && !(tank.getX() + 40 > 220
                    && tank.getX() < 1200 && tank.getY() + 60 > 590 && tank.getY() + 60 < 650)) {
                tank.varyDown();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_A) {//左
            tank.setDirect(3);
            if (tank.getX() > 0 && !(tank.getX() > 213 && tank.getX() < 1143 &&
                    tank.getY() + 40 > 195 && tank.getY() < 240) && !(tank.getX() > 220 &&
                    tank.getX() < 1215 && tank.getY() + 40 > 600 && tank.getY() < 630)) {
                tank.varyLeft();
            }
        }
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {
//        if (e.getKeyCode() == KeyEvent.VK_W) {  //上
//            tank.setDirect(0);//修改成员属性direct   改变坦克的方向
//            if ((tank.getY() > 0 && tank.getY() + 60 < 200) || tank.getY() > 243) {//限制我方坦克的移动范围
//                tank.varyUp();//这是个改变坐标(移动)的方法
//            }
//        } else if (e.getKeyCode() == KeyEvent.VK_D) {//右
//            tank.setDirect(1);
//            if (tank.getX() + 60 < 1500) {
//                tank.varyRight();
//            }
//        } else if (e.getKeyCode() == KeyEvent.VK_S) {//下
//            tank.setDirect(2);
//            if (tank.getY() + 60 < 1000) {
//                tank.varyDown();
//            }
//        } else if (e.getKeyCode() == KeyEvent.VK_A) {//左
//            tank.setDirect(3);
//            if (tank.getX() > 0) {
//                tank.varyLeft();
//            }
//        }

        if (e.getKeyCode() == KeyEvent.VK_J) {
            //System.out.println("用户按下了J,开始射击...");
            //if (tank.getBullet() == null || !tank.getBullet().isLive()) {//开始的时候我的子弹对象为null
            //虽然碰到了边界 线程会结束  但是这个对象还存在 所以不为null 所以这时候最多只能发射一颗子弹(即使子弹消亡了)
            //如果再加上这个子弹的生命周期为false 用||连接 就可以实现了只发一颗子弹的效果!!
            if (tank.isLive()) {//tank生存才能发射子弹
                tank.shot();//用户每按一次J  就调用一次shot方法 进而创建一个新的子弹对象
            }
            //}
        }
        //重绘面板  repaint方法被调用后paint方法又被调用  达到了重绘画板的效果!!
        this.repaint();  //这里repaint方法已经作为线程在不停的重绘 按理说这里可以不用写
        //但是当我给他去除之后  坦克的移动变得不丝滑了 一卡一卡的  所以这个重绘很重要


        if (e.getKeyCode() == KeyEvent.VK_UP) {
            hero.setDirect(0);
            if (hero.getY() > 0 && !(hero.getX() + 40 > 213 && hero.getX() < 1130 &&
                    hero.getY() > 200 && hero.getY() < 250) && !(hero.getX() + 40 > 222
                    && hero.getX() < 1200 && hero.getY() > 600 && hero.getY() < 655)) {
                hero.varyUp();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            hero.setDirect(1);
            if (hero.getX() + 60 < 1500 && !(hero.getX() + 60 > 200 && hero.getX() + 60 < 1130 &&
                    hero.getY() + 40 > 195 && hero.getY() < 240) && !(hero.getX() + 60 > 210
                    && hero.getX() + 60 < 1190 && hero.getY() + 40 > 600 && hero.getY() < 640)) {
                hero.varyRight();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            hero.setDirect(2);
            if (hero.getY() + 60 < 1000 && !(hero.getX() + 40 > 213 && hero.getX() < 1130 &&
                    hero.getY() + 60 > 190 && hero.getY() + 60 < 250) && !(hero.getX() + 40 > 220
                    && hero.getX() < 1200 && hero.getY() + 60 > 590 && hero.getY() + 60 < 650)) {
                hero.varyDown();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            hero.setDirect(3);
            if (hero.getX() > 0 && !(hero.getX() > 213 && hero.getX() < 1143 &&
                    hero.getY() + 40 > 195 && hero.getY() < 240) && !(hero.getX() > 220 &&
                    hero.getX() < 1218 && hero.getY() + 40 > 600 && hero.getY() < 630)) {
                hero.varyLeft();
            }
        }

        if (e.getKeyCode() == KeyEvent.VK_DIVIDE) {//如果玩家2按下1就触发发射子弹的行为
            // System.out.println("玩家2按下了射击键...");
            if (hero.isLive()) {//hero的坦克生存才能发射子弹
                hero.shot();
            }
        }
        this.repaint();
    }

    //面板的线程  重绘的目的
    @Override
    public void run() {
        while (true) {
            //以下这个循环的目的是实时的检测电脑坦克集合里面的坦克是否被打掉 打掉了就不存在了
            //遍历之前  先判断我的坦克发射出的子弹是否存活 现在我的子弹已经是一个集合了
            // 所以以下这个判断可以不需要了(先留着)
//            if (tank.getBullet() != null && tank.getBullet().isLive()) {//等价于tank.getBullet().isLive()==true
//                //因为还没有按j键 所以这时子弹不存在 所以只用tank.getBullet()作为if条件会报一个空指针异常
//                for (int i = 0; i < pc.tankVector.size(); i++) {
//                    PC pcTank = this.pc.tankVector.get(i);//先得到每个坦克的对象 然后进行判断
//                    hitTank(tank.myBullets, pcTank);//判断电脑坦克是否存活  循环判断
                if (pcTank.isLive() == false) {//如果不存活  就把它从集合中移除了
                    pc.tankVector.remove(pcTank);
                }
//                }
//            }
            //hitEnemyTank();//1 + 2  这个及下面那一个是我方击中敌方的方法
            //这个击中坦克的方法略显不足   击中坦克后 有时候数量能加2 应该就是判断逻辑上出问题了
            //这个内部判断逻辑是所有子弹跟一个坦克比较  外层循环会拿出坦克  所以是不是会出现 两颗子弹同时打中坦克
            //然后数量就加多了  对!!就是这个逻辑   //目前是解决了 这个问题  加了个return  但是目前游戏还有bug
            //就是有时候就剩最后一个坦克  他不会动也不会发射子弹
            hitEnemyTank2();//3 + 4 标注的方法实现的 我方坦克击中敌方坦克
            hitEnemyTank3();//这个是敌方击中我方的方法
            if (tank != null && hero != null) {
                heroHitTank();// hero对tank击中判断
            }
            if (hero != null && tank != null) {
                tankHitHero();// tank对hero击中判断
            }
            hitBarrierProof();//MyTank的子弹打中障碍物的判断
            if (pc != null) {
                enemyTankHitBarrier();//PC的子弹打中障碍物的判断
            }
            proofHitPlate();//验证Tank和Hero的子弹击中钢板 然后消失
            pcHitPlate();//验证PC的子弹击中钢板  子弹消失
            try {
                Thread.sleep(50);//休眠50毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            repaint();//重绘面板
        }
    }
}

Node类 (用于实现继续上一句游戏操作的类)

package com.wqc.tankgame8;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * 这个类用于把读取到的坦克的x,y坐标以及方向  存储到这个类的实例对象中
 */
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;
    }
}

钢板类(子弹打到上面子弹会消失,和障碍物的区别是子弹打到障碍物上,障碍物会和子弹一块消失,但是钢板不会消失)

在这里插入图片描述

package com.wqc.tankgame8;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * 该类要实现钢板的作用 目的是阻碍坦克移动  坦克的子弹打到钢板上  子弹消失
 * 现在来实现创建这个对象  然后进行绘画操作
 */
public class Plate {
    int x;
    int y;

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

记录玩家击败坦克数量的类

在这里插入图片描述

package com.wqc.tankgame8;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * 该类实现了 记录玩家的击败坦克的数目  在屏幕上显示
 * 最后退出的时候  存盘  存到d:\\record.txt
 * 实现功能: 记录玩家退出游戏时敌人坦克的坐标以及方向  存盘退出
 * 我的思路是遍历敌人坦克集合 然后得到x和y坐标存盘退出  怎么在Record这个类中得到敌人的坦克集合 --> 映射
 * 实现功能 :玩游戏时,玩家可以选择开启新游戏或者继续上一局游戏
 * 实现功能 :把我的坦克的坐标存档  每次继续上次游戏时  位置是上一次的位置 需要先获取到我的坦克对象
 */
public class Record {
    //属性和方法都设置成静态的  我的理解是  一方面调用的时侯方便  另一方面是游戏初始化时需要先加载这些记录的信息
    private static int hitEnemyTankNum = 0;//记录玩家击毁坦克数目的变量
    private static String filePath = "src\\record.txt";//记录文件的路径
    private static BufferedWriter bw = null;//用处理字符流BufferedWriter用于写入文件
    private static BufferedReader br = null;//用处理字符流BufferedReader用于读取文件
    private static Vector<PC> enemyTanks = new Vector<>();//这个集合是电脑坦克集合
    private static Vector<Barrier> birVector = new Vector<>();//这个集合是障碍物的集合
    static Vector<Node> Nodes = new Vector<>();//这个集合用来存储Node对象
    static MyTank hero;//定义我的坦克对象  然后设置方法  最后通过映射把别的类定义好的坦克对象映射过来

    public static void setBirVector(Vector<Barrier> birVector) {
        Record.birVector = birVector;
    }

    public static void setHero(MyTank hero) {
        Record.hero = hero;
    }

    public static String getFilePath() {
        return filePath;  //可以让别的类得到这个文件路径
    }

    public static void setEnemyTanks(Vector<PC> enemyTanks) {
        Record.enemyTanks = enemyTanks;
    }

    public static int getHitEnemyTankNum() {
        return hitEnemyTankNum;
    }

    public static void setHitEnemyTankNum(int hitEnemyTankNum) {
        Record.hitEnemyTankNum = hitEnemyTankNum;
    }

    //写一个静态的方法  用于记录玩家打掉的坦克的数目
    public static void recordHitEnemyTankNum() {
        hitEnemyTankNum++;
    }


//    //写一个方法  用于存档我的电脑的位置  方便继续下一场游戏重启读取到我的坦克的数据
//    public static Node readMyTank() {
//        try {
//            br = new BufferedReader(new FileReader(filePath));//读取d:\record.txt这个文件
//            br.readLine();//这是第一行  第一行存放的是击败的坦克数目
//            br.readLine();//又读取了一行  这一行存放的是时间
//            String s = br.readLine();//这一行读取的是我的坦克坐标
//            String[] split = s.split(" ");
//            node = new Node(Integer.parseInt(split[0]),Integer.parseInt(split[1]),Integer.parseInt(split[2]));
//        } catch (IOException e) {
//            e.printStackTrace();
//        } finally {
//            try {
//                br.close();//关闭流对象
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }
//        return node;
//    } // 目前没写出来  我放弃了 ...........  写出来了 还是得有耐心啊

    //写一个方法  读取"d:\\record.txt"文件中数据  把读取到的数据传入创建的Node对象中
    //并加入到Nodes集合中  返回值是Nodes集合  用于存档电脑坦克的位置
    public static Vector<Node> readEnemyTank() {
        String Line;
        try {
            br = new BufferedReader(new FileReader(filePath));
            hitEnemyTankNum = Integer.parseInt(br.readLine());//先读取第一行的数据 获取到玩家打掉坦克的数目
            //在这里重新获取一下hitEnemyTankNum击败坦克的数量 首先这个方法的调用是在MyPanel构造器的初始化里
            //每次重新启动一下游戏  构造器都会初始化  相当于重新获取一下玩家击败的坦克数量
            String s1 = br.readLine();//再读取一行  这次是从第二行开始读取  读取到的是时间
            while ((Line = br.readLine()) != null) {//256 0 3  这是存储的形式  x坐标 y坐标 退出时的方向
                //这里是从第三行开始读取的
                String[] s = Line.split(" "); //用split方法 以一个空格来分  把他分割成一个字符串数组
                Node node1 = new Node(Integer.parseInt(s[0]), Integer.parseInt(s[1]), Integer.parseInt(s[2]));
                //获取到的是一个字符串  所以需要用Integer.parseInt()把他转换成int类型
                Nodes.add(node1);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //先关流
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return Nodes;//返回集合
    }

    //写一个方法  用于存档玩家打掉的坦克数目  在什么时候这个方法应该被调用  在玩家点击关闭窗口的时候  去调用这个方法
    //所以要让JFrame 对应的窗口事件 去监听这个动作
    public static void recordHitNum() {
        Date date = new Date();//记录当前的时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss E");//用于格式化当前的时间
        String format = sdf.format(date);
        try {
            bw = new BufferedWriter(new FileWriter(filePath));//创建一个记录打掉坦克数目的对象
            bw.write(hitEnemyTankNum + "\n");
            bw.write("时间: " + format);
            bw.newLine();//在这里来个换行
            for (int i = 0; i < enemyTanks.size(); i++) {
                PC pc1 = enemyTanks.get(i);
                bw.write(pc1.getX() + " " + pc1.getY() + " " + pc1.getDirect());
                bw.newLine();//记录过后来一个换行
            }
            for (int i = 0; i < birVector.size(); i++) {
                Barrier barrier = birVector.get(i);
                bw.write(barrier.x + " " + barrier.y + " " + 0);
                //因为障碍物没有方向  所以统一默认记到Node结点里的方向为0
                bw.newLine();//换行 在这里所有的障碍物都记到Node结点什么位置?
            }
            bw.write(hero.getX() + " " + hero.getY() + " " + hero.getDirect());//写入我的坦克的坐标到文件中
            bw.newLine();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bw != null) {
                    bw.close();//一定要关闭流  关闭外层流即可
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

用于玩家胜利后和失败后的效果展示的类

在这里插入图片描述

package com.wqc.tankgame8;


/**
 * @author 高瞻远瞩
 * @version 坦克5.0版本
 * 简单实现了胜利后和失败后的文字显示
 */
@SuppressWarnings("all")
public class Win extends Thread {
    int x;
    int y;
    int speed = 4;
    boolean isLive = true;

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

    //写这个方法 让x和y不断的改变 达到浮屏的效果
    public void victory() {
        if (y > 300) {
            y -= speed;
        }
//        if(y == 300) {
//            isLive = false;
//        }
        if (y <= 300) {
            while (true) {
                y += speed;
                if (y == 600) {
                    break;
                }
            }
        }
    }

    @Override
    public void run() {
        while (true) {
            if (y > 245) {
                y -= speed;
            }
//        if(y == 300) {
//            isLive = false;
//        }
            if (y <= 245) {
                while (true) {
                    y += speed; //移动一次休眠一次   可以达到先向上移 再往下移的效果
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (y == 548) {
                        break;
                    }
                }
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
//            if(!isLive){
//                break;
//            }
        }
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值