文章和代码曾经归档至【Github仓库:https://github.com/timerring/java-tutorial 】或者公众号【AIShareLab】回复 java 也可获取。

线程-利用到坦克大战

坦克大战0.3

剖析如何实现当用户按下J键,咱们的坦克就发射一颗子弹,思路:

  1. 当发射一颗子弹后,就相当于启动一个线程
  2. Hero有子弹的对象,当按下J时,咱们就启动一个发射行为(线程),让子弹不停的挪动,造成一个射击的成果。
  3. 咱们MyPanel须要不停的重绘子弹,能力呈现该成果.
  4. 当子弹挪动到面板的边界时,就应该销毁(把启动的子弹的线程销毁)

坦克大战0.4

减少性能

  1. 让敌人的坦克也可能发射子弹(能够有多颗子弹)

    1. 在敌人坦克类,应用Vector保留多个Shot
    2. 当每创立一个敌人坦克对象,给该敌人坦克对象初始化一个Shot对象,同时启动Shot
    3. 在绘制敌人坦克时,须要遍历敌人坦克对象Vector,绘会制所有的子弹,当子弹isLive == false时,就从Vector移除
  2. 当我方坦克击中敌人坦克时,敌人的坦克就隐没,如果能做出爆炸成果更好.
  3. 让敌人的坦克也能够自在随机的上下左右挪动

    1. 因为要求敌人的坦克,能够自在挪动,因而须要将敌人坦克当做线程应用
    2. 咱们须要Enemy Tank implements Runnable
    3. 在run办法写上咱们相应的业务代码.
    4. 在创立敌人坦克对象时,启动线程
  4. 管制我方的坦克和敌人的坦克在规定的范畴挪动剖析->解决

减少性能

  1. 我方坦克在发射的子弹沦亡后,能力发射新的子弹.=>扩大(发多颗子弹怎么办,管制在咱们的面板上,最多只有5颗)-》在课后欠缺

    1.在按下J键,咱们判断以后hero对象的子弹,是否曾经销毁

    2.如果没有销毁,就不去触发shotEnemyTank

    3.如果曾经销毁,才去触发shotEnemyTank

    4.如果要发射多颗子弹,就应用Vector保留

    5.在绘制我方子弹时,须要遍历该Vector汇合

  2. 让敌人坦克发射的子弹沦亡后,能够再发射子弹
  3. 当敌人的坦克击中我方坦克时,我方坦克隐没,并呈现爆炸成果
    思路:编写办法,判断敌人的坦克是否击中我的坦克
  4. 课后练习:让敌人坦克能够最多发射3颗(在面板E),咱们的坦克能够发射3颗.并且可能呈现失常的爆炸成果即可.
package com.hspedu.tankgame4;/** * 炸弹 */public class Bomb {    int x, y; //炸弹的坐标    int life = 9; //炸弹的生命周期    boolean isLive = true; //是否还存活    public Bomb(int x, int y) {        this.x = x;        this.y = y;    }    //缩小生命值    public void lifeDown() { //配合呈现图片的爆炸成果        if(life > 0) {            life--;        } else {            isLive = false;        }    }}
package com.hspedu.tankgame4;import java.util.Vector;/** * 敌人的坦克 */@SuppressWarnings({"all"})public class EnemyTank extends Tank implements Runnable {    //在敌人坦克类,应用Vector 保留多个Shot    Vector<Shot> shots = new Vector<>();    boolean isLive = true;    public EnemyTank(int x, int y) {        super(x, y);    }    @Override    public void run() {        while (true) {            //这里咱们判断如果shots size() =0, 创立一颗子弹,放入到            //shots汇合,并启动            if (isLive && shots.size() < 1) {                Shot s = null;                //判断坦克的方向,创立对应的子弹                switch (getDirect()) {                    case 0:                        s = new Shot(getX() + 20, getY(), 0);                        break;                    case 1:                        s = new Shot(getX() + 60, getY() + 20, 1);                        break;                    case 2: //向下                        s = new Shot(getX() + 20, getY() + 60, 2);                        break;                    case 3://向左                        s = new Shot(getX(), getY() + 20, 3);                        break;                }                shots.add(s);                //启动                new Thread(s).start();            }            //依据坦克的方向来持续动            switch (getDirect()) {                case 0:  //向上                    //让坦克放弃一个方向,走30步                    for (int i = 0; i < 30; i++) {                        if (getY() > 0) {                            moveUp();                        }                        //休眠50毫秒                        try {                            Thread.sleep(50);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                    break;                case 1:  //向右                    for (int i = 0; i < 30; i++) {                        if (getX() + 60 < 1000) {                            moveRight();                        }                        //休眠50毫秒                        try {                            Thread.sleep(50);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                    break;                case 2:  //向下                    for (int i = 0; i < 30; i++) {                        if (getY() + 60 < 750) {                            moveDown();                        }                        //休眠50毫秒                        try {                            Thread.sleep(50);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                    break;                case 3:  //向左                    for (int i = 0; i < 30; i++) {                        if (getX() > 0) {                            moveLeft();                        }                        //休眠50毫秒                        try {                            Thread.sleep(50);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                    break;            }            // 而后随机的扭转坦克方向 0-3            setDirect((int) (Math.random() * 4));            // 写并发程序,肯定要思考分明,该线程什么时候完结            if (!isLive) {                break; //退出线程.            }        }    }}
package com.hspedu.tankgame4;import java.util.Vector;/** * 本人的坦克 */public class Hero extends Tank {    //定义一个Shot对象, 示意一个射击(线程)    Shot shot = null;    //能够发射多颗子弹    //Vector<Shot> shots = new Vector<>();    public Hero(int x, int y) {        super(x, y);    }    //射击    public void shotEnemyTank() {        //发多颗子弹怎么办, 管制在咱们的面板上,最多只有5颗//        if(shots.size() == 5) {//            return;//        }        //创立 Shot 对象, 依据以后Hero对象的地位和方向来创立Shot        switch (getDirect()) {//失去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放入到shots        //shots.add(shot);        //启动咱们的Shot线程        new Thread(shot).start();    }}
package com.hspedu.tankgame4;import javax.swing.*;public class HspTankGame04 extends JFrame {    //定义MyPanel    MyPanel mp = null;    public static void main(String[] args) {        HspTankGame04 hspTankGame01 = new HspTankGame04();    }    public HspTankGame04() {        mp = new MyPanel();        //将mp 放入到Thread ,并启动        Thread thread = new Thread(mp);        thread.start();        this.add(mp);//把面板(就是游戏的绘图区域)        this.setSize(1200, 950);        this.addKeyListener(mp);//让JFrame 监听mp的键盘事件        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        this.setVisible(true);    }}
package com.hspedu.tankgame4;import javax.swing.*;import java.awt.*;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.util.Vector;/** * 坦克大战的绘图区域 *///为了监听 键盘事件, 实现KeyListener//为了让Panel 不停的重绘子弹,须要将 MyPanel 实现Runnable ,当做一个线程应用public class MyPanel extends JPanel implements KeyListener, Runnable {    //定义我的坦克    Hero hero = null;    //定义敌人坦克,放入到Vector    Vector<EnemyTank> enemyTanks = new Vector<>();    //定义一个Vector ,用于寄存炸弹    //阐明,当子弹击中坦克时,退出一个Bomb对象到bombs    Vector<Bomb> bombs = new Vector<>();    int enemyTankSize = 3;    //定义三张炸弹图片,用于显示爆炸成果    Image image1 = null;    Image image2 = null;    Image image3 = null;    public MyPanel() {        hero = new Hero(500, 100);//初始化本人坦克        //初始化敌人坦克        for (int i = 0; i < enemyTankSize; i++) {            //创立一个敌人的坦克            EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);            //设置方向            enemyTank.setDirect(2);            //启动敌人坦克线程,让他动起来            new Thread(enemyTank).start();            //给该enemyTank 退出一颗子弹            Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());            //退出enemyTank的Vector 成员            enemyTank.shots.add(shot);            //启动 shot 对象            new Thread(shot).start();            //退出            enemyTanks.add(enemyTank);        }        //初始化图片对象        image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif"));        image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));        image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));    }    @Override    public void paint(Graphics g) {        super.paint(g);        g.fillRect(0, 0, 1000, 750);//填充矩形,默认彩色        if(hero != null && hero.isLive) {            //画出本人坦克-封装办法            drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);        }        //画出hero射击的子弹        if (hero.shot != null && hero.shot.isLive == true) {            g.draw3DRect(hero.shot.x, hero.shot.y, 1, 1, false);        }        //将hero的子弹汇合 shots ,遍历取出绘制//        for(int i = 0; i < hero.shots.size(); i++) {//            Shot shot = hero.shots.get(i);//            if (shot != null && shot.isLive) {//                g.draw3DRect(shot.x, shot.y, 1, 1, false);////            } else {//如果该shot对象曾经有效 ,就从shots汇合中拿掉//                hero.shots.remove(shot);//            }//        }        //如果bombs 汇合中有对象,就画出        for (int i = 0; i < bombs.size(); i++) {            //取出炸弹            Bomb bomb = bombs.get(i);            //依据以后这个bomb对象的life值去画出对应的图片            if (bomb.life > 6) {                g.drawImage(image1, bomb.x, bomb.y, 60, 60, this);            } else if (bomb.life > 3) {                g.drawImage(image2, bomb.x, bomb.y, 60, 60, this);            } else {                g.drawImage(image3, bomb.x, bomb.y, 60, 60, this);            }            //让这个炸弹的生命值缩小            bomb.lifeDown();            //如果bomb life 为0, 就从bombs 的汇合中删除            if (bomb.life == 0) {                bombs.remove(bomb);            }        }        //画出敌人的坦克, 遍历Vector        for (int i = 0; i < enemyTanks.size(); i++) {            //从Vector 取出坦克            EnemyTank enemyTank = enemyTanks.get(i);            //判断以后坦克是否还存活            if (enemyTank.isLive) {//当敌人坦克是存活的,才画出该坦克                drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);                //画出 enemyTank 所有子弹                for (int j = 0; j < enemyTank.shots.size(); j++) {                    //取出子弹                    Shot shot = enemyTank.shots.get(j);                    //绘制                    if (shot.isLive) { //isLive == true                        g.draw3DRect(shot.x, shot.y, 1, 1, false);                    } else {                        //从Vector 移除                        enemyTank.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.yellow);                break;        }        //依据坦克方向,来绘制对应形态坦克        //direct 示意方向(0: 向上 1 向右 2 向下 3 向左 )        //        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("临时没有解决");        }    }    //如果咱们的坦克能够发射多个子弹    //在判断我方子弹是否击中敌人坦克时,就须要把咱们的子弹汇合中    //所有的子弹,都取出和敌人的所有坦克,进行判断    //老韩给的局部代码..    public void hitEnemyTank() {//        //遍历咱们的子弹//        for(int j = 0;j < hero.shots.size();j++) {//            Shot shot = hero.shots.get(j);//            //判断是否击中了敌人坦克//            if (shot != null && hero.shot.isLive) {//当我的子弹还存活////                //遍历敌人所有的坦克//                for (int i = 0; i < enemyTanks.size(); i++) {//                    EnemyTank enemyTank = enemyTanks.get(i);//                    hitTank(hero.shot, enemyTank);//                }////            }//        }        //单颗子弹。        if (hero.shot != null && hero.shot.isLive) {//当我的子弹还存活            //遍历敌人所有的坦克            for (int i = 0; i < enemyTanks.size(); i++) {                EnemyTank enemyTank = enemyTanks.get(i);                hitTank(hero.shot, enemyTank);            }        }    }    //编写办法,判断敌人坦克是否击中我的坦克    public void hitHero() {        //遍历所有的敌人坦克        for (int i = 0; i < enemyTanks.size(); i++) {            //取出敌人坦克            EnemyTank enemyTank = enemyTanks.get(i);            //遍历enemyTank 对象的所有子弹            for (int j = 0; j < enemyTank.shots.size(); j++) {                //取出子弹                Shot shot = enemyTank.shots.get(j);                //判断 shot 是否击中我的坦克                if (hero.isLive && shot.isLive) {                    hitTank(shot, hero);                }            }        }    }    //编写办法,判断我方的子弹是否击中敌人坦克.    //什么时候判断 我方的子弹是否击中敌人坦克 ? run办法    //前面咱们将 enemyTank 改成 tank名称    public void hitTank(Shot s, Tank enemyTank) {        //判断s 击中坦克        switch (enemyTank.getDirect()) {            case 0: //坦克向上            case 2: //坦克向下                if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 40                        && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60) {                    s.isLive = false;                    enemyTank.isLive = false;                    //当我的子弹击中敌人坦克后,将enemyTank 从Vector 拿掉                    enemyTanks.remove(enemyTank);                    //创立Bomb对象,退出到bombs汇合                    Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());                    bombs.add(bomb);                }                break;            case 1: //坦克向右            case 3: //坦克向左                if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 60                        && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40) {                    s.isLive = false;                    enemyTank.isLive = false;                    //创立Bomb对象,退出到bombs汇合                    Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());                    bombs.add(bomb);                }                break;        }    }    @Override    public void keyTyped(KeyEvent e) {    }    //解决wdsa 键按下的状况    @Override    public void keyPressed(KeyEvent e) {        System.out.println(e.getKeyCode());        if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键            //扭转坦克的方向            hero.setDirect(0);//            //批改坦克的坐标 y -= 1            if (hero.getY() > 0) {                hero.moveUp();            }        } else if (e.getKeyCode() == KeyEvent.VK_D) {//D键, 向右            hero.setDirect(1);            if (hero.getX() + 60 < 1000) {                hero.moveRight();            }        } else if (e.getKeyCode() == KeyEvent.VK_S) {//S键            hero.setDirect(2);            if (hero.getY() + 60 < 750) {                hero.moveDown();            }        } else if (e.getKeyCode() == KeyEvent.VK_A) {//A键            hero.setDirect(3);            if (hero.getX() > 0) {                hero.moveLeft();            }        }        //如果用户按下的是J,就发射        if (e.getKeyCode() == KeyEvent.VK_J) {            //判断hero的子弹是否销毁,发射一颗子弹            if (hero.shot == null || !hero.shot.isLive) {                hero.shotEnemyTank();            }            //发射多颗子弹            //hero.shotEnemyTank();        }        //让面板重绘        this.repaint();    }    @Override    public void keyReleased(KeyEvent e) {    }    @Override    public void run() { //每隔 100毫秒,重绘区域, 刷新绘图区域, 子弹就挪动        while (true) {            try {                Thread.sleep(100);            } catch (InterruptedException e) {                e.printStackTrace();            }            //判断是咱们子弹否击中了敌人坦克            hitEnemyTank();            //判断敌人坦克是否击中咱们            hitHero();            this.repaint();        }    }}
package com.hspedu.tankgame4;/** * 射击子弹 */public class Shot implements Runnable {    int x; //子弹x坐标    int y; //子弹y坐标    int direct = 0; //子弹方向    int speed = 2; //子弹的速度    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;            }            //老师测试,这里咱们输入x,y的坐标            System.out.println("子弹 x=" + x + " y=" + y);            //当子弹挪动到面板的边界时,就应该销毁(把启动的子弹的线程销毁)            //当子弹碰到敌人坦克时,也应该完结线程            if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750 && isLive)) {                System.out.println("子弹线程退出");                isLive = false;                break;            }        }    }}
package com.hspedu.tankgame4;public class Tank {    private int x;//坦克的横坐标    private int y;//坦克的纵坐标    private int direct = 0;//坦克方向 0 上1 右 2下 3左    private int speed = 1;    boolean isLive = true;    public int getSpeed() {        return speed;    }    public void setSpeed(int speed) {        this.speed = speed;    }    //上右下左挪动办法    public void moveUp() {        y -= speed;    }    public void moveRight() {        x += speed;    }    public void moveDown() {        y += speed;    }    public void moveLeft() {        x -= 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;    }}