day03-2-应用线程02

打印 上一主题 下一主题

主题 955|帖子 955|积分 2865

JavaGUI-坦克大战03-2

7.线程的应用02

7.3.坦克大战4.0版

坦克大战4.0版
增加功能:
功能1.让敌人的坦克也能够发射子弹(可以有多个子弹)
功能2.当我方坦克集中敌人坦克时,敌人的坦克就消失,如果能做出爆炸的效果更好
功能3.让敌人的坦克也可以自由随机地上下左右移动
功能4.控制我方坦克和敌人坦克在规定的范围内移动
7.3.1功能1:敌方发射子弹

功能1思路

  • 在敌人坦克类使用Vector保存多个Shot
  • 当每创建一个敌人坦克时,就给该敌人坦克初始化一个Shot对象,同时启动Shot线程
  • 在绘制敌人坦克时,需要遍历敌人坦克对象Vector,绘制所有的子弹,当子弹isLive==false时,就从Vector移除
EnemyTank:
  1. package li.TankGame.version04;
  2. import java.util.Vector;
  3. public class EnemyTank extends Tank {
  4.     //在敌人坦克类使用Vector保存多个Shot
  5.     Vector<Shot> shots = new Vector<>();
  6.     public EnemyTank(int x, int y) {
  7.         super(x, y);
  8.     }
  9. }
复制代码
MyPanel:在创建敌人坦克的时候,每初始化一个敌人坦克,就创建并初始化一个shot对象,将该shot对象加入到shots集合中,然后启动这个shot对象线程
  1. package li.TankGame.version04;
  2. import javax.swing.*;
  3. import java.awt.*;
  4. import java.awt.event.KeyEvent;
  5. import java.awt.event.KeyListener;
  6. import java.util.Vector;
  7. /**
  8. * @author 李
  9. * @version 4.0
  10. * 坦克大战的绘图区域
  11. */
  12. //为了监听键盘事件,要实现 KeyListener
  13. //为了让Panel不停地重绘子弹,需要将MyPanel实现Runnable,
  14. // 当做一个线程使用(因为需要不停地重绘子弹的图案)
  15. public class MyPanel extends JPanel implements KeyListener, Runnable {
  16.     //定义我的坦克
  17.     Hero hero = null;
  18.     //定义敌方坦克,放入到Vector集合中
  19.     Vector<EnemyTank> enemyTanks = new Vector<>();
  20.     int enemyTankNum = 3;//初始化坦克数量
  21.     public MyPanel() {
  22.         hero = new Hero(100, 100);//初始化自己的坦克
  23.         //hero.setSpeed(5); //改变坦克的速度
  24.         //初始化敌人的坦克
  25.         for (int i = 0; i < enemyTankNum; i++) {
  26.             //创建一个敌人的坦克
  27.             EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0);
  28.             //初始化敌人坦克方向向下
  29.             enemyTank.setDirect(2);
  30.             //给该enemyTank加入一颗子弹
  31.             Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());
  32.             //将该子弹加入到enemyTank的Vector集合中
  33.             enemyTank.shots.add(shot);
  34.             //启动 shot对象
  35.             new Thread(shot).start();
  36.             //将设置好的的敌人坦克放入到集合中
  37.             enemyTanks.add(enemyTank);
  38.         }
  39.     }
  40.     @Override
  41.     public void paint(Graphics g) {
  42.         super.paint(g);
  43.         g.fillRect(0, 0, 700, 550);//填充矩形,默认为黑色
  44.         //画出自己的坦克-封装方法
  45.         drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);
  46.         //画出hero发射的子弹
  47.         //如果子弹对象不为空,并且处于存活状态(即没有超出边界)
  48.         if (hero.shot != null && hero.shot.isLive) {
  49.             System.out.println("hero的子弹被绘制");
  50.             g.draw3DRect(hero.shot.x, hero.shot.y, 2, 2, false);
  51.         }
  52.         //画出敌人的坦克,遍历Vector
  53.         for (int i = 0; i < enemyTanks.size(); i++) {
  54.             //取出敌人的坦克
  55.             EnemyTank enemyTank = enemyTanks.get(i);
  56.             drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
  57.             //画出enemyTank所有子弹
  58.             for (int j = 0; j < enemyTank.shots.size(); j++) {
  59.                 //enemyTank.shots.size()是当前的enemy对象的shots集合的大小
  60.                 //取出子弹
  61.                 Shot shot = enemyTank.shots.get(j);
  62.                 //绘制子弹
  63.                 if (shot.isLive) {//shot.isLive==true
  64.                     g.draw3DRect(shot.x, shot.y, 2, 2, false);
  65.                 }else{
  66.                     //从Vector移除子弹shot对象
  67.                     enemyTank.shots.remove(shot);
  68.                 }
  69.             }
  70.         }
  71.     }
  72.     /**
  73.      * 编写方法,画出坦克
  74.      *
  75.      * @param x      坦克的左上角横坐标
  76.      * @param y      坦克的左上角纵坐标
  77.      * @param g      画笔
  78.      * @param direct 坦克方向(上下左右)
  79.      * @param type   坦克的类型(我方,敌方)
  80.      */
  81.     public void drawTank(int x, int y, Graphics g, int direct, int type) {
  82.         //根据不同类型的坦克设置不同的颜色
  83.         switch (type) {
  84.             case 0://敌方坦克
  85.                 g.setColor(Color.cyan);//设置我方坦克颜色
  86.                 break;
  87.             case 1://我方坦克
  88.                 g.setColor(Color.yellow);//设敌方坦克颜色
  89.                 break;
  90.         }
  91.         //根据坦克坐标方向,来绘制对应方向的坦克
  92.         //direct表示方向(0:向上  1:向右  2:向下  3:向左)
  93.         switch (direct) {
  94.             case 0://表示向上
  95.                 g.fill3DRect(x, y, 10, 60, false);//画出坦克左边的轮子
  96.                 g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边的轮子
  97.                 g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克主体
  98.                 g.fillOval(x + 10, y + 20, 20, 20);//画出坦克舱体
  99.                 g.drawLine(x + 20, y + 30, x + 20, y);//画出炮管
  100.                 break;
  101.             case 1://表示向右
  102.                 //注意:以坦克左上角的(x,y)坐标为参考画图案
  103.                 g.fill3DRect(x, y, 60, 10, false);//画出坦克上边的轮子
  104.                 g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边的轮子
  105.                 g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克方形主体
  106.                 g.fillOval(x + 20, y + 10, 20, 20);//画出坦克舱体
  107.                 g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮管
  108.                 break;
  109.             case 2://表示向下
  110.                 //向下只需要将向上的炮筒的位置改变即可
  111.                 g.fill3DRect(x, y, 10, 60, false);//画出坦克左边的轮子
  112.                 g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边的轮子
  113.                 g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克主体
  114.                 g.fillOval(x + 10, y + 20, 20, 20);//画出坦克舱体
  115.                 g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮管
  116.                 break;
  117.             case 3://表示向左
  118.                 //向左只需要将向右的炮筒的位置改变即可
  119.                 g.fill3DRect(x, y, 60, 10, false);//画出坦克上边的轮子
  120.                 g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边的轮子
  121.                 g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克方形主体
  122.                 g.fillOval(x + 20, y + 10, 20, 20);//画出坦克舱体
  123.                 g.drawLine(x + 30, y + 20, x, y + 20);//画出炮管
  124.                 break;
  125.             default:
  126.                 System.out.println("暂时没有处理");
  127.         }
  128.     }
  129.     @Override
  130.     public void keyTyped(KeyEvent e) {
  131.     }
  132.     //控制方向--处理 WSAD 键按下的情况
  133.     @Override
  134.     public void keyPressed(KeyEvent e) {
  135.         if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键-向上
  136.             //改变坦克的方向
  137.             hero.setDirect(0);
  138.             //修改坦克的坐标
  139.             hero.moveUp();
  140.         } else if (e.getKeyCode() == KeyEvent.VK_D) {//按下D键-向右
  141.             hero.setDirect(1);
  142.             hero.moveRight();
  143.         } else if (e.getKeyCode() == KeyEvent.VK_S) {//按下S键-向下
  144.             hero.setDirect(2);
  145.             hero.moveDown();
  146.         } else if (e.getKeyCode() == KeyEvent.VK_A) {//按下A键-向左
  147.             hero.setDirect(3);
  148.             hero.moveLeft();
  149.         }
  150.         //如果用户按下j键,hero就发射子弹
  151.         if (e.getKeyCode() == KeyEvent.VK_J) {
  152.             hero.shotEnemyTank();
  153.         }
  154.         //让面板重绘
  155.         this.repaint();
  156.     }
  157.     @Override
  158.     public void keyReleased(KeyEvent e) {
  159.     }
  160.     @Override
  161.     public void run() {//每个100毫秒,重绘区域,刷新绘图区域,子弹就移动
  162.         while (true) {
  163.             try {
  164.                 Thread.sleep(100);
  165.             } catch (InterruptedException e) {
  166.                 e.printStackTrace();
  167.             }
  168.             this.repaint();
  169.         }
  170.     }
  171. }
复制代码
7.3.2功能2:敌方坦克消失

功能2:当我方坦克集中敌人坦克时,敌人的坦克就消失,如果能做出爆炸的效果更好
功能2思路:

  • 关键点在于判断子弹怎么样算击中敌人:即坦克的击中范围的坐标
  • 在run()方法中不断地去判断是否击中敌人
  • 然后在调用drawTank方法前也进行相应的判断,如果敌人未被击中,即enemy.isLive = true,就调用drawTank方法
  • 关于子弹线程的退出问题:之前只是判断了子弹超出面板时会结束线程。现增加新的线程停止条件:当子弹击中敌方坦克时,该子弹的线程也应结束
更改的类:Shot、EnemyTank、MyPanel:
Shot:更改了子弹线程结束的条件
  1. package li.TankGame.version04;
  2. /**
  3. * @author 李
  4. * @version 4.0
  5. * 射击子弹
  6. */
  7. public class Shot implements Runnable {
  8.     int x; // 记录子弹 x 坐标
  9.     int y; // 记录子弹 y 坐标
  10.     int direct = 0; // 子弹方向
  11.     int speed = 2; // 子弹速度
  12.     boolean isLive = true;//记录子弹(线程)存活状态
  13.     //构造器
  14.     public Shot(int x, int y, int direct) {
  15.         this.x = x;
  16.         this.y = y;
  17.         this.direct = direct;
  18.     }
  19.     @Override
  20.     public void run() {//射击
  21.         while (true) {
  22.             //休眠50 毫秒
  23.             try {
  24.                 Thread.sleep(50);
  25.             } catch (InterruptedException e) {
  26.                 e.printStackTrace();
  27.             }
  28.             //根据方向来改变(x,y)坐标
  29.             switch (direct) {
  30.                 case 0://上
  31.                     y -= speed;
  32.                     break;
  33.                 case 1://右
  34.                     x += speed;
  35.                     break;
  36.                 case 2://下
  37.                     y += speed;
  38.                     break;
  39.                 case 3://左
  40.                     x -= speed;
  41.                     break;
  42.             }
  43.             //测试。输出坐标
  44.             System.out.println("x=" + x + "y=" + y);
  45.             //如果子弹移动到面板边界时,就应该销毁(把启动的子弹的线程销毁)
  46.             //当子弹碰到敌人坦克时,也应该结束线程
  47.             if (!(x >= 0 && x <= 700 && y <= 550 && y >= 0 && isLive)) {//如果不符合条件
  48.                 System.out.println("子弹线程退出...");
  49.                 isLive = false;
  50.                 break;//线程退出
  51.             }
  52.         }
  53.     }
  54. }
复制代码
7.3.3功能2plus:坦克爆炸

功能2完善:当子弹击中坦克时,做出爆炸效果
思路:

  • 新建一个炸弹Bomb类
  • 在MyPanel里定义一个Vector集合,用于存放Bomb对象
  • 在hitTank方法中判断,当子弹击中坦克时就加入一个Bomb对象到bombs集合里(这里将hitTank方法改为非静态方法)
  • 想要显示出炸弹的效果图,就要在paint方法里面画出,如果bombs集合中有炸弹对象,就画出来
  • 将爆炸的素材图片放到out目录的根目录下面
Bomb:
  1. package li.TankGame.version04;
  2. import java.util.Vector;
  3. public class EnemyTank extends Tank {
  4.     //在敌人坦克类使用Vector保存多个Shot
  5.     Vector<Shot> shots = new Vector<>();
  6.     boolean isLive = true;
  7.     public EnemyTank(int x, int y) {
  8.         super(x, y);
  9.     }
  10. }
复制代码
MyPanel:
  1. package li.TankGame.version04;
  2. import javax.swing.*;
  3. import java.awt.*;
  4. import java.awt.event.KeyEvent;
  5. import java.awt.event.KeyListener;
  6. import java.util.Vector;
  7. /**
  8. * @author 李
  9. * @version 4.0
  10. * 坦克大战的绘图区域
  11. */
  12. //为了监听键盘事件,要实现 KeyListener
  13. //为了让Panel不停地重绘子弹,需要将MyPanel实现Runnable,
  14. // 当做一个线程使用(因为需要不停地重绘子弹的图案)
  15. public class MyPanel extends JPanel implements KeyListener, Runnable {
  16.     //定义我的坦克
  17.     Hero hero = null;
  18.     //定义敌方坦克,放入到Vector集合中
  19.     Vector<EnemyTank> enemyTanks = new Vector<>();
  20.     int enemyTankNum = 3;//初始化坦克数量
  21.     public MyPanel() {
  22.         hero = new Hero(100, 100);//初始化自己的坦克
  23.         //hero.setSpeed(5); //改变坦克的速度
  24.         //初始化敌人的坦克
  25.         for (int i = 0; i < enemyTankNum; i++) {
  26.             //创建一个敌人的坦克
  27.             EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0);
  28.             //初始化敌人坦克方向向下
  29.             enemyTank.setDirect(2);
  30.             //给该enemyTank加入一颗子弹
  31.             Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());
  32.             //将该子弹加入到enemyTank的Vector集合中
  33.             enemyTank.shots.add(shot);
  34.             //启动 shot对象
  35.             new Thread(shot).start();
  36.             //将设置好的的敌人坦克放入到集合中
  37.             enemyTanks.add(enemyTank);
  38.         }
  39.     }
  40.     @Override
  41.     public void paint(Graphics g) {
  42.         super.paint(g);
  43.         g.fillRect(0, 0, 700, 550);//填充矩形,默认为黑色
  44.         //画出自己的坦克-封装方法
  45.         drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);
  46.         //画出hero发射的子弹
  47.         //如果子弹对象不为空,并且处于存活状态(即没有超出边界)
  48.         if (hero.shot != null && hero.shot.isLive) {
  49.             System.out.println("hero的子弹被绘制");
  50.             g.draw3DRect(hero.shot.x, hero.shot.y, 2, 2, false);
  51.         }
  52.         //画出敌人的坦克,遍历Vector
  53.         for (int i = 0; i < enemyTanks.size(); i++) {
  54.             //取出坦克
  55.             EnemyTank enemyTank = enemyTanks.get(i);
  56.             if (enemyTank.isLive) {//当敌人坦克是活的,才将其画出
  57.                 drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
  58.                 //画出enemyTank所有子弹
  59.                 for (int j = 0; j < enemyTank.shots.size(); j++) {
  60.                     //enemyTank.shots.size()是当前的enemy对象的shots集合的大小
  61.                     //取出子弹
  62.                     Shot shot = enemyTank.shots.get(j);
  63.                     //绘制子弹
  64.                     if (shot.isLive) {//shot.isLive==true
  65.                         g.draw3DRect(shot.x, shot.y, 2, 2, false);
  66.                     } else {
  67.                         //从Vector移除子弹shot对象
  68.                         enemyTank.shots.remove(shot);
  69.                     }
  70.                 }
  71.             }
  72.         }
  73.     }
  74.     /**
  75.      * 编写方法,画出坦克
  76.      *
  77.      * @param x      坦克的左上角横坐标
  78.      * @param y      坦克的左上角纵坐标
  79.      * @param g      画笔
  80.      * @param direct 坦克方向(上下左右)
  81.      * @param type   坦克的类型(我方,敌方)
  82.      */
  83.     public void drawTank(int x, int y, Graphics g, int direct, int type) {
  84.         //根据不同类型的坦克设置不同的颜色
  85.         switch (type) {
  86.             case 0://敌方坦克
  87.                 g.setColor(Color.cyan);//设置我方坦克颜色
  88.                 break;
  89.             case 1://我方坦克
  90.                 g.setColor(Color.yellow);//设敌方坦克颜色
  91.                 break;
  92.         }
  93.         //根据坦克坐标方向,来绘制对应方向的坦克
  94.         //direct表示方向(0:向上  1:向右  2:向下  3:向左)
  95.         switch (direct) {
  96.             case 0://表示向上
  97.                 g.fill3DRect(x, y, 10, 60, false);//画出坦克左边的轮子
  98.                 g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边的轮子
  99.                 g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克主体
  100.                 g.fillOval(x + 10, y + 20, 20, 20);//画出坦克舱体
  101.                 g.drawLine(x + 20, y + 30, x + 20, y);//画出炮管
  102.                 break;
  103.             case 1://表示向右
  104.                 //注意:以坦克左上角的(x,y)坐标为参考画图案
  105.                 g.fill3DRect(x, y, 60, 10, false);//画出坦克上边的轮子
  106.                 g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边的轮子
  107.                 g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克方形主体
  108.                 g.fillOval(x + 20, y + 10, 20, 20);//画出坦克舱体
  109.                 g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮管
  110.                 break;
  111.             case 2://表示向下
  112.                 //向下只需要将向上的炮筒的位置改变即可
  113.                 g.fill3DRect(x, y, 10, 60, false);//画出坦克左边的轮子
  114.                 g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边的轮子
  115.                 g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克主体
  116.                 g.fillOval(x + 10, y + 20, 20, 20);//画出坦克舱体
  117.                 g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮管
  118.                 break;
  119.             case 3://表示向左
  120.                 //向左只需要将向右的炮筒的位置改变即可
  121.                 g.fill3DRect(x, y, 60, 10, false);//画出坦克上边的轮子
  122.                 g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边的轮子
  123.                 g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克方形主体
  124.                 g.fillOval(x + 20, y + 10, 20, 20);//画出坦克舱体
  125.                 g.drawLine(x + 30, y + 20, x, y + 20);//画出炮管
  126.                 break;
  127.             default:
  128.                 System.out.println("暂时没有处理");
  129.         }
  130.     }
  131.     /*编写方法,判断我方的子弹是否击中敌人*/
  132.     public static void hitTank(Shot s, EnemyTank enemyTank) {
  133.         //判断s击中坦克
  134.         switch (enemyTank.getDirect()) {
  135.             case 0://敌人坦克向上(向上和向下的击中范围一样)
  136.             case 2://敌人坦克向下
  137.                 if (s.x > enemyTank.getX() && s.y < enemyTank.getX() + 40
  138.                         && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60) {
  139.                     s.isLive = false;
  140.                     enemyTank.isLive = false;
  141.                     //当子弹击中敌人后,将enemyTank从Vector中删除
  142.                     enemyTanks.remove(enemyTank);
  143.                     
  144.                 }
  145.             case 1://右
  146.             case 3://左
  147.                 if (s.x > enemyTank.getX() && s.y < enemyTank.getX() + 60
  148.                         && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40) {
  149.                     s.isLive = false;
  150.                     enemyTank.isLive = false;
  151.                     //当子弹击中敌人后,将enemyTank从Vector中删除
  152.                     enemyTanks.remove(enemyTank);
  153.                     
  154.                 }
  155.                 break;
  156.         }
  157.       
  158.     }
  159.     @Override
  160.     public void keyTyped(KeyEvent e) {
  161.     }
  162.     //控制方向--处理 WSAD 键按下的情况
  163.     @Override
  164.     public void keyPressed(KeyEvent e) {
  165.         if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键-向上
  166.             //改变坦克的方向
  167.             hero.setDirect(0);
  168.             //修改坦克的坐标
  169.             hero.moveUp();
  170.         } else if (e.getKeyCode() == KeyEvent.VK_D) {//按下D键-向右
  171.             hero.setDirect(1);
  172.             hero.moveRight();
  173.         } else if (e.getKeyCode() == KeyEvent.VK_S) {//按下S键-向下
  174.             hero.setDirect(2);
  175.             hero.moveDown();
  176.         } else if (e.getKeyCode() == KeyEvent.VK_A) {//按下A键-向左
  177.             hero.setDirect(3);
  178.             hero.moveLeft();
  179.         }
  180.         //如果用户按下j键,hero就发射子弹
  181.         if (e.getKeyCode() == KeyEvent.VK_J) {
  182.             hero.shotEnemyTank();
  183.         }
  184.         //让面板重绘
  185.         this.repaint();
  186.     }
  187.     @Override
  188.     public void keyReleased(KeyEvent e) {
  189.     }
  190.     @Override
  191.     public void run() {//每个100毫秒,重绘区域,刷新绘图区域,子弹就移动
  192.         while (true) {
  193.             try {
  194.                 Thread.sleep(100);
  195.             } catch (InterruptedException e) {
  196.                 e.printStackTrace();
  197.             }
  198.             //判断是否击中敌人坦克
  199.             if (hero.shot != null && hero.shot.isLive) {//如果当前我方子弹不为空且子弹还存活
  200.                 //就遍历敌人所有的坦克
  201.                 for (int i = 0; i < enemyTanks.size(); i++) {
  202.                     EnemyTank enemyTank = enemyTanks.get(i);
  203.                     hitTank(hero.shot, enemyTank);            
  204.                 }
  205.             }
  206.             this.repaint();
  207.         }
  208.     }
  209. }
复制代码

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

耶耶耶耶耶

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表