zoukankan      html  css  js  c++  java
  • 坦克大战(版本1.7-版本2.4)

    版本1.7

    功能:加入爆炸
    步骤:
            1)添加爆炸类:Explode
               用不同直径的圆模拟爆炸: int[] diameter = {4, 7, 12, 18, 26, 32, 49, 30, 14, 6};
               加入live: private boolean live = true;
               加入位置属性: int x, y;
               加入draw方法
            2)爆炸应该存在于集合类中:
               TankClient加入集合explodes: List<Explode> explodes=new ArrayList<Explode>();
               将集合中的爆炸逐一画出(如果死去就去除):在Missile类的draw方法中,当爆炸live==false的时候,tc.missiles.remove(this);
            3)击毙一辆坦克后应产生爆炸:
               在Missile类的hitTank中,产生爆炸,Explode e=new Explode(x, y, tc); tc.explodes.add(e);

    具体代码实现:

    Tank:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.Random;
      4 
      5 public class Tank {
      6     // 方便后期更改
      7     public static final int XSPEED = 5;
      8     public static final int YSPEED = 5;
      9     // 将坦克的高度和宽度设置为常量
     10     public static final int WIDTH = 30;
     11     public static final int HEIGHT = 30;
     12     TankClient tc;
     13     // 区别是我方坦克还是地方坦克,方便据此进行不同的设置
     14     private boolean good;
     15     
     16     //判断坦克生死的变量
     17     private boolean live=true;
     18 
     19     
     20     public boolean isLive() {
     21         return live;
     22     }
     23 
     24     public void setLive(boolean live) {
     25         this.live = live;
     26     }
     27 
     28     private int x;
     29     private int y;
     30     // 添加记录按键状态的布尔量
     31     private boolean bL = false;
     32     private boolean bR = false;
     33     private boolean bU = false;
     34     private boolean bD = false;
     35 
     36     // 添加代表方向的量(使用枚举)
     37     enum Direction {
     38         L, R, U, D, LU, LD, RU, RD, STOP
     39     };
     40 
     41     private Direction dir = Direction.STOP;
     42 
     43     // 定义炮筒的方向,我们想办法将炮筒的方法调整成和坦克移动方向一致;
     44     // 我们这里会用一条直线来表示炮筒:模拟炮筒
     45     // 我们要根据炮筒的方向画直线表示炮筒
     46     Direction ptDir = Direction.D;
     47 
     48     //更改构造函数
     49     public Tank(int x, int y,boolean good) {
     50         this.x = x;
     51         this.y = y;
     52         this.good=good;
     53     }
     54 
     55     //这个位置的构造函数也相应进行了更改
     56     public Tank(int x, int y,boolean good,TankClient tc) {
     57         // 调用那个有两个参数的构造方法
     58         this(x, y,good);
     59         // 在这个位置初始化tc
     60         this.tc = tc;
     61     }
     62 
     63     // Tank对象的draw方法
     64     public void draw(Graphics g) {
     65         if(!live){
     66             return;
     67         }
     68         Color c = g.getColor();
     69         if(good){    
     70         g.setColor(Color.RED);
     71         }
     72         else {
     73             g.setColor(Color.PINK);
     74         }
     75         g.fillOval(x, y, WIDTH, HEIGHT);
     76         g.setColor(c);
     77         // 根据炮筒的方向画直线来表示我们坦克的炮筒
     78         switch (ptDir) {
     79         case L:
     80             // 画直线:四个参数分别代表:坦克中心点的坐标 直线的另一头的的坐标
     81             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
     82                     + Tank.HEIGHT / 2);
     83             break;
     84         case R:
     85             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
     86                     y + Tank.HEIGHT / 2);
     87 
     88             break;
     89         case U:
     90             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
     91                     / 2, y);
     92 
     93             break;
     94         case D:
     95             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
     96                     / 2, y + Tank.HEIGHT);
     97 
     98             break;
     99         case LU:
    100             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y);
    101             break;
    102         case LD:
    103             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    104                     + Tank.HEIGHT);
    105 
    106             break;
    107         case RU:
    108             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    109                     y);
    110 
    111             break;
    112         case RD:
    113             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    114                     y + Tank.HEIGHT);
    115 
    116             break;
    117         /*
    118          * case STOP: break;
    119          */
    120         }
    121         move();
    122     }
    123 
    124     public void move() {
    125         switch (dir) {
    126         case L:
    127             x -= XSPEED;
    128             break;
    129         case R:
    130             x += XSPEED;
    131             break;
    132         case U:
    133             y -= YSPEED;
    134             break;
    135         case D:
    136             y += YSPEED;
    137             break;
    138         case LU:
    139             x -= XSPEED;
    140             y -= YSPEED;
    141             break;
    142         case LD:
    143             x -= XSPEED;
    144             y += YSPEED;
    145             break;
    146         case RU:
    147             x += XSPEED;
    148             y -= YSPEED;
    149             break;
    150         case RD:
    151             x += XSPEED;
    152             y += YSPEED;
    153             break;
    154 
    155         case STOP:
    156             break;
    157         }
    158         // 如果坦克不是停着的,则将炮筒调整至和坦克移动的方向相同
    159         if (this.dir != Direction.STOP) {
    160             this.ptDir = this.dir;
    161         }
    162         if (x < 0) {
    163             x = 0;
    164         }
    165         // 因为我们的游戏界面有那个missileCount标签,所以在y轴方向不能用y是否<0进行判断
    166         // 否则的话我们的坦克可以从上面出去
    167         if (y < 50) {
    168             y = 50;
    169         }
    170         if (x + Tank.WIDTH > TankClient.GAME_WIDTH) {
    171             x = TankClient.GAME_WIDTH - Tank.WIDTH;
    172         }
    173         if (y + Tank.HEIGHT > TankClient.GAME_HEIGHT) {
    174             y = TankClient.GAME_HEIGHT - Tank.HEIGHT;
    175         }
    176     }
    177 
    178     public void locateDirection() {
    179         if (bL && !bU && !bR && !bD)
    180             dir = Direction.L;
    181         else if (bL && bU && !bR && !bD)
    182             dir = Direction.LU;
    183         else if (!bL && bU && !bR && !bD)
    184             dir = Direction.U;
    185         else if (!bL && bU && bR && !bD)
    186             dir = Direction.RU;
    187         else if (!bL && !bU && bR && !bD)
    188             dir = Direction.R;
    189         else if (!bL && !bU && bR && bD)
    190             dir = Direction.RD;
    191         else if (!bL && !bU && !bR && bD)
    192             dir = Direction.D;
    193         else if (bL && !bU && !bR && bD)
    194             dir = Direction.LD;
    195         else if (!bL && !bU && !bR && !bD)
    196             dir = Direction.STOP;
    197 
    198     }
    199 
    200     // 坦克自己向哪个方向移动,它自己最清楚;
    201     public void KeyPressed(KeyEvent e) {
    202         // 获得所按下的键所对应的虚拟码:
    203         // Returns the integer keyCode associated with the key in this event
    204         int key = e.getKeyCode();
    205         // 判断不同的按键,指挥坦克的运动方向
    206         switch (key) {
    207         case KeyEvent.VK_LEFT:
    208             bL = true;
    209             break;
    210         case KeyEvent.VK_UP:
    211             bU = true;
    212             break;
    213         case KeyEvent.VK_RIGHT:
    214             bR = true;
    215             break;
    216         case KeyEvent.VK_DOWN:
    217             bD = true;
    218             break;
    219         }
    220         locateDirection();
    221     }
    222 
    223     public void keyReleased(KeyEvent e) {
    224         int key = e.getKeyCode();
    225         // 判断不同的按键,指挥坦克的运动方向
    226         // 哪个键按下了,就把对应方向的布尔类型置为false
    227         switch (key) {
    228         // 为了防止一直按着Ctrl键的时候,炮弹太过于密集
    229         // 因此我们定义在Ctrl键抬起的时候才发炮弹
    230         // 这样炮弹不至于太过密集
    231         case KeyEvent.VK_CONTROL:
    232             fire();
    233             break;
    234         case KeyEvent.VK_LEFT:
    235             bL = false;
    236             break;
    237         case KeyEvent.VK_UP:
    238             bU = false;
    239             break;
    240         case KeyEvent.VK_RIGHT:
    241             bR = false;
    242             break;
    243         case KeyEvent.VK_DOWN:
    244             bD = false;
    245             break;
    246         }
    247         // 重新定位一下
    248         locateDirection();
    249     }
    250 
    251     public Missile fire() {
    252         // 计算子弹的位置,使得子弹从坦克的中间发出来
    253         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    254         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    255         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    256         Missile m = new Missile(x, y, ptDir, tc);
    257         // 将新产生的炮弹放置到List容器中
    258         tc.missiles.add(m);
    259         return m;
    260     }
    261 
    262     //拿到包围坦克的那个方块
    263     public Rectangle getRect() {
    264         
    265         return new Rectangle(x,y,WIDTH,HEIGHT);
    266     }
    267 }
    View Code

    Missile:

      1 import java.awt.Color;
      2 import java.awt.Graphics;
      3 import java.awt.Rectangle;
      4 
      5 public class Missile {
      6     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
      7     public static final int XSPEED = 10;
      8     public static final int YSPEED = 10;
      9     // 将子弹的高度和宽度设置为常量
     10     public static final int WIDTH = 10;
     11     public static final int HEIGHT = 10;
     12     // 炮弹自己的三个属性
     13     int x;
     14     int y;
     15     Tank.Direction dir;
     16 
     17     // 定义一个布尔类型的变量来判断炮弹是否已经消亡
     18     private boolean live = true;
     19     //我们在Missile类中也持有一个TankClient的引用
     20     //在炮弹出界的时候就可以从装炮弹的missiles集合中去除该炮弹,不再对其重画
     21     private TankClient tc;
     22 
     23     public boolean isLive() {
     24         return live;
     25     }
     26 
     27     public Missile(int x, int y, Tank.Direction dir) {
     28         this.x = x;
     29         this.y = y;
     30         this.dir = dir;
     31     }
     32     public Missile(int x,int y,Tank.Direction dir,TankClient tc){
     33         this(x, y, dir);
     34         this.tc=tc;
     35     }
     36 
     37     // 炮弹自己的draw方法
     38     public void draw(Graphics g) {
     39         //炮弹消亡就不需要再画出来了
     40         if(!live){
     41             tc.missiles.remove(this);
     42             return;
     43         }
     44         Color c = g.getColor();
     45         g.setColor(Color.BLACK);
     46         // 炮弹形状不要比坦克大,这里设置成10,10;
     47         g.fillOval(x, y, WIDTH, HEIGHT);
     48         g.setColor(c);
     49         move();
     50     }
     51 
     52     public void move() {
     53         switch (dir) {
     54         case L:
     55             x -= XSPEED;
     56             break;
     57         case R:
     58             x += XSPEED;
     59             break;
     60         case U:
     61             y -= YSPEED;
     62             break;
     63         case D:
     64             y += YSPEED;
     65             break;
     66         case LU:
     67             x -= XSPEED;
     68             y -= YSPEED;
     69             break;
     70         case LD:
     71             x -= XSPEED;
     72             y += YSPEED;
     73             break;
     74         case RU:
     75             x += XSPEED;
     76             y -= YSPEED;
     77             break;
     78         case RD:
     79             x += XSPEED;
     80             y += YSPEED;
     81             break;
     82         // 炮弹就没有STOP这个枚举类型的值了
     83         /*
     84          * case STOP: break;
     85          */
     86         }
     87         // 判断炮弹出边界则消亡
     88         // 注意x,y只有正数值,x向右递增,y向下递增
     89         if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
     90                 || y > TankClient.GAME_HEIGHT) {
     91             live = false;
     92         }
     93     }
     94     public boolean hitTank(Tank t){
     95         //炮弹的方框和坦克的方框碰在一起了并且坦克是存活着的
     96         if(this.getRect().intersects(t.getRect())&&t.isLive()){
     97             t.setLive(false);
     98             this.live=false;
     99             
    100             //炮弹击中坦克,发生爆炸
    101             Explode e=new Explode(x, y, tc);
    102             tc.explodes.add(e);
    103             return true;
    104         }
    105         return false;
    106     }
    107     //碰撞检测类Rectangle
    108     //拿到包围在炮弹周围的小方块
    109     public Rectangle getRect(){
    110         return new Rectangle(x,y,WIDTH,HEIGHT);
    111     }
    112 }
    View Code

    Explode:

     1 import java.awt.*;
     2 
     3 public class Explode {
     4     //爆炸的位置
     5     int x, y;
     6     //爆炸是否存在
     7     private boolean live = true;
     8     
     9     //持有一个Tankclient的引用
    10     private TankClient tc ;
    11     
    12     //定义不同直径大小的爆炸
    13     int[] diameter = {4, 7, 12, 18, 26, 32, 49, 30, 14, 6};
    14     //爆炸发生到哪一个阶段了,对应相应大小的直径
    15     int step = 0;
    16     
    17     public Explode(int x, int y, TankClient tc) {
    18         this.x = x;
    19         this.y = y;
    20         this.tc = tc;
    21     }
    22     
    23     public void draw(Graphics g) {
    24         if(!live) {
    25             //爆炸发生,将相应直径的爆炸圆从集合explodes中去除
    26             tc.explodes.remove(this);
    27             return;
    28         }
    29         
    30         if(step == diameter.length) {
    31             live = false;
    32             step = 0;
    33             return;
    34         }
    35         
    36         Color c = g.getColor();
    37         g.setColor(Color.ORANGE);
    38         
    39         //把不同的圆画出来
    40         g.fillOval(x, y, diameter[step], diameter[step]);
    41         g.setColor(c);
    42         
    43         step ++;
    44     }
    45 }
    View Code

    TankClient:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 
      6 public class TankClient extends Frame {
      7     // 设置成常量,方便以后的改动
      8     public static final int GAME_WIDTH = 800;
      9     public static final int GAME_HEIGHT = 600;
     10 
     11     // 将当前的TankClient对象传递给myTank;
     12     // 目的是方便我们在Tank这个类中访问m(炮弹对象)这个成员变量
     13     // 其实就是在Tank类中持有TankClient类对象的一个引用
     14     
     15     //我们这里new我们自己的坦克,用下面的方式
     16     Tank myTank = new Tank(50, 50, true,this);
     17     
     18     //新建敌方坦克
     19     Tank enemyTank=new Tank(100,100,false,this);
     20     //定义爆炸
     21     Explode e=new Explode(70, 70, this);
     22     //定义一个集合,多个爆炸点
     23     List<Explode> explodes=new ArrayList<Explode>();
     24     
     25     //使用容器装炮弹
     26     List<Missile> missiles=new ArrayList<Missile>();
     27 
     28     // 定义虚拟图片,方便后期的一次性显示
     29     Image offScreenImage = null;
     30 
     31     
     32     public void paint(Graphics g) {
     33         //记录屏幕上的子弹数目
     34         g.drawString("missiles count:" + missiles.size(), 10, 50);
     35         //添加上方标记栏,记录爆炸次数
     36         g.drawString("explodes count:" + explodes.size(), 10, 70);
     37 
     38         
     39         //遍历结合,发出多发炮弹
     40         for(int i=0;i<missiles.size();i++){
     41             Missile m=missiles.get(i);
     42             //子弹打敌方坦克
     43             m.hitTank(enemyTank);
     44             
     45             m.draw(g);
     46         }
     47         
     48         for(int i=0;i<explodes.size();i++){
     49             Explode e=explodes.get(i);
     50             e.draw(g);
     51         }
     52         // 不改变前景色
     53         myTank.draw(g);
     54         //敌方坦克调用draw方法
     55         enemyTank.draw(g);
     56     }
     57 
     58     // 刷新操作
     59     public void update(Graphics g) {
     60         if (offScreenImage == null) {
     61             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
     62         }
     63         Graphics gOffScreen = offScreenImage.getGraphics();
     64         Color c = gOffScreen.getColor();
     65         gOffScreen.setColor(Color.GREEN);
     66         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
     67         gOffScreen.setColor(c);
     68         paint(gOffScreen);
     69         g.drawImage(offScreenImage, 0, 0, null);
     70     }
     71 
     72     public void lauchFrame() {
     73 //        this.setLocation(400, 300);
     74         this.setSize(GAME_WIDTH, GAME_HEIGHT);
     75         this.setTitle("TankWar");
     76         this.addWindowListener(new WindowAdapter() {
     77             public void windowClosing(WindowEvent e) {
     78                 System.exit(0);
     79             }
     80         });
     81         this.setResizable(false);
     82         this.setBackground(Color.GREEN);
     83 
     84         this.addKeyListener(new KeyMonitor());
     85 
     86         setVisible(true);
     87 
     88         new Thread(new PaintThread()).start();
     89     }
     90 
     91     public static void main(String[] args) {
     92         TankClient tc = new TankClient();
     93         tc.lauchFrame();
     94     }
     95 
     96     private class PaintThread implements Runnable {
     97 
     98         public void run() {
     99             while (true) {
    100                 repaint();
    101                 try {
    102                     //为了爆炸效果,改成1000
    103                     Thread.sleep(50);
    104                 } catch (InterruptedException e) {
    105                     e.printStackTrace();
    106                 }
    107             }
    108         }
    109     }
    110 
    111     // 创建键盘时间监听
    112     private class KeyMonitor extends KeyAdapter {
    113 
    114         // 直接调用myTank自己的方法根据相应的按键信息进行移动
    115         public void keyPressed(KeyEvent e) {
    116             myTank.KeyPressed(e);
    117             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
    118             // 而不是一直按照一个方向走下去
    119         }
    120 
    121         public void keyReleased(KeyEvent e) {
    122             myTank.keyReleased(e);
    123         }
    124 
    125     }
    126 }
    View Code

    版本1.8
    功能:添加多辆坦克
    步骤:
            1)用容器来装敌人的Tank:在Tankclient类中List<Tank> tanks=new ArrayList<Tank>();
            2)向容器中装入多辆敌人Tank
            3)画出来
            4)运行,不能打掉
               添加hitTanks方法,打一系列Tank:

    1 public boolean hitTanks(List<Tank> tanks){
    2         for(int i=0;i<tanks.size();i++){
    3             if(hitTank(tanks.get(i))){
    4                 return true;
    5             }
    6         }
    7         return false;
    8         
    9     }

              TankClient里面每发子弹都可以打tanks:

    具体代码实现:

    Tank:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.Random;
      4 
      5 public class Tank {
      6     // 方便后期更改
      7     public static final int XSPEED = 5;
      8     public static final int YSPEED = 5;
      9     // 将坦克的高度和宽度设置为常量
     10     public static final int WIDTH = 30;
     11     public static final int HEIGHT = 30;
     12     TankClient tc;
     13     // 区别是我方坦克还是地方坦克,方便据此进行不同的设置
     14     private boolean good;
     15     
     16     //判断坦克生死的变量
     17     private boolean live=true;
     18 
     19     
     20     public boolean isLive() {
     21         return live;
     22     }
     23 
     24     public void setLive(boolean live) {
     25         this.live = live;
     26     }
     27 
     28     private int x;
     29     private int y;
     30     // 添加记录按键状态的布尔量
     31     private boolean bL = false;
     32     private boolean bR = false;
     33     private boolean bU = false;
     34     private boolean bD = false;
     35 
     36     // 添加代表方向的量(使用枚举)
     37     enum Direction {
     38         L, R, U, D, LU, LD, RU, RD, STOP
     39     };
     40 
     41     private Direction dir = Direction.STOP;
     42 
     43     // 定义炮筒的方向,我们想办法将炮筒的方法调整成和坦克移动方向一致;
     44     // 我们这里会用一条直线来表示炮筒:模拟炮筒
     45     // 我们要根据炮筒的方向画直线表示炮筒
     46     Direction ptDir = Direction.D;
     47 
     48     //更改构造函数
     49     public Tank(int x, int y,boolean good) {
     50         this.x = x;
     51         this.y = y;
     52         this.good=good;
     53     }
     54 
     55     //这个位置的构造函数也相应进行了更改
     56     public Tank(int x, int y,boolean good,TankClient tc) {
     57         // 调用那个有两个参数的构造方法
     58         this(x, y,good);
     59         // 在这个位置初始化tc
     60         this.tc = tc;
     61     }
     62 
     63     // Tank对象的draw方法
     64     public void draw(Graphics g) {
     65         if(!live){
     66             //如果死亡的是敌方坦克,在tanks集合中去除该坦克
     67             if(!good){
     68                 tc.tanks.remove(this);
     69             }
     70             //如果是我方坦克,直接返回
     71             return;
     72         }
     73         Color c = g.getColor();
     74         if(good){    
     75         g.setColor(Color.RED);
     76         }
     77         else {
     78             g.setColor(Color.PINK);
     79         }
     80         g.fillOval(x, y, WIDTH, HEIGHT);
     81         g.setColor(c);
     82         // 根据炮筒的方向画直线来表示我们坦克的炮筒
     83         switch (ptDir) {
     84         case L:
     85             // 画直线:四个参数分别代表:坦克中心点的坐标 直线的另一头的的坐标
     86             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
     87                     + Tank.HEIGHT / 2);
     88             break;
     89         case R:
     90             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
     91                     y + Tank.HEIGHT / 2);
     92 
     93             break;
     94         case U:
     95             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
     96                     / 2, y);
     97 
     98             break;
     99         case D:
    100             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    101                     / 2, y + Tank.HEIGHT);
    102 
    103             break;
    104         case LU:
    105             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y);
    106             break;
    107         case LD:
    108             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    109                     + Tank.HEIGHT);
    110 
    111             break;
    112         case RU:
    113             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    114                     y);
    115 
    116             break;
    117         case RD:
    118             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    119                     y + Tank.HEIGHT);
    120 
    121             break;
    122         /*
    123          * case STOP: break;
    124          */
    125         }
    126         move();
    127     }
    128 
    129     public void move() {
    130         switch (dir) {
    131         case L:
    132             x -= XSPEED;
    133             break;
    134         case R:
    135             x += XSPEED;
    136             break;
    137         case U:
    138             y -= YSPEED;
    139             break;
    140         case D:
    141             y += YSPEED;
    142             break;
    143         case LU:
    144             x -= XSPEED;
    145             y -= YSPEED;
    146             break;
    147         case LD:
    148             x -= XSPEED;
    149             y += YSPEED;
    150             break;
    151         case RU:
    152             x += XSPEED;
    153             y -= YSPEED;
    154             break;
    155         case RD:
    156             x += XSPEED;
    157             y += YSPEED;
    158             break;
    159 
    160         case STOP:
    161             break;
    162         }
    163         // 如果坦克不是停着的,则将炮筒调整至和坦克移动的方向相同
    164         if (this.dir != Direction.STOP) {
    165             this.ptDir = this.dir;
    166         }
    167         if (x < 0) {
    168             x = 0;
    169         }
    170         // 因为我们的游戏界面有那个missileCount标签,所以在y轴方向不能用y是否<0进行判断
    171         // 否则的话我们的坦克可以从上面出去
    172         if (y < 50) {
    173             y = 50;
    174         }
    175         if (x + Tank.WIDTH > TankClient.GAME_WIDTH) {
    176             x = TankClient.GAME_WIDTH - Tank.WIDTH;
    177         }
    178         if (y + Tank.HEIGHT > TankClient.GAME_HEIGHT) {
    179             y = TankClient.GAME_HEIGHT - Tank.HEIGHT;
    180         }
    181     }
    182 
    183     public void locateDirection() {
    184         if (bL && !bU && !bR && !bD)
    185             dir = Direction.L;
    186         else if (bL && bU && !bR && !bD)
    187             dir = Direction.LU;
    188         else if (!bL && bU && !bR && !bD)
    189             dir = Direction.U;
    190         else if (!bL && bU && bR && !bD)
    191             dir = Direction.RU;
    192         else if (!bL && !bU && bR && !bD)
    193             dir = Direction.R;
    194         else if (!bL && !bU && bR && bD)
    195             dir = Direction.RD;
    196         else if (!bL && !bU && !bR && bD)
    197             dir = Direction.D;
    198         else if (bL && !bU && !bR && bD)
    199             dir = Direction.LD;
    200         else if (!bL && !bU && !bR && !bD)
    201             dir = Direction.STOP;
    202 
    203     }
    204 
    205     // 坦克自己向哪个方向移动,它自己最清楚;
    206     public void KeyPressed(KeyEvent e) {
    207         // 获得所按下的键所对应的虚拟码:
    208         // Returns the integer keyCode associated with the key in this event
    209         int key = e.getKeyCode();
    210         // 判断不同的按键,指挥坦克的运动方向
    211         switch (key) {
    212         case KeyEvent.VK_LEFT:
    213             bL = true;
    214             break;
    215         case KeyEvent.VK_UP:
    216             bU = true;
    217             break;
    218         case KeyEvent.VK_RIGHT:
    219             bR = true;
    220             break;
    221         case KeyEvent.VK_DOWN:
    222             bD = true;
    223             break;
    224         }
    225         locateDirection();
    226     }
    227 
    228     public void keyReleased(KeyEvent e) {
    229         int key = e.getKeyCode();
    230         // 判断不同的按键,指挥坦克的运动方向
    231         // 哪个键按下了,就把对应方向的布尔类型置为false
    232         switch (key) {
    233         // 为了防止一直按着Ctrl键的时候,炮弹太过于密集
    234         // 因此我们定义在Ctrl键抬起的时候才发炮弹
    235         // 这样炮弹不至于太过密集
    236         case KeyEvent.VK_CONTROL:
    237             fire();
    238             break;
    239         case KeyEvent.VK_LEFT:
    240             bL = false;
    241             break;
    242         case KeyEvent.VK_UP:
    243             bU = false;
    244             break;
    245         case KeyEvent.VK_RIGHT:
    246             bR = false;
    247             break;
    248         case KeyEvent.VK_DOWN:
    249             bD = false;
    250             break;
    251         }
    252         // 重新定位一下
    253         locateDirection();
    254     }
    255 
    256     public Missile fire() {
    257         // 计算子弹的位置,使得子弹从坦克的中间发出来
    258         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    259         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    260         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    261         Missile m = new Missile(x, y, ptDir, tc);
    262         // 将新产生的炮弹放置到List容器中
    263         tc.missiles.add(m);
    264         return m;
    265     }
    266 
    267     //拿到包围坦克的那个方块
    268     public Rectangle getRect() {
    269         
    270         return new Rectangle(x,y,WIDTH,HEIGHT);
    271     }
    272 }
    View Code

    Missile:

      1 import java.awt.Color;
      2 import java.awt.Graphics;
      3 import java.awt.Rectangle;
      4 import java.util.List;
      5 
      6 public class Missile {
      7     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
      8     public static final int XSPEED = 10;
      9     public static final int YSPEED = 10;
     10     // 将子弹的高度和宽度设置为常量
     11     public static final int WIDTH = 10;
     12     public static final int HEIGHT = 10;
     13     // 炮弹自己的三个属性
     14     int x;
     15     int y;
     16     Tank.Direction dir;
     17 
     18     // 定义一个布尔类型的变量来判断炮弹是否已经消亡
     19     private boolean live = true;
     20     //我们在Missile类中也持有一个TankClient的引用
     21     //在炮弹出界的时候就可以从装炮弹的missiles集合中去除该炮弹,不再对其重画
     22     private TankClient tc;
     23 
     24     public boolean isLive() {
     25         return live;
     26     }
     27 
     28     public Missile(int x, int y, Tank.Direction dir) {
     29         this.x = x;
     30         this.y = y;
     31         this.dir = dir;
     32     }
     33     public Missile(int x,int y,Tank.Direction dir,TankClient tc){
     34         this(x, y, dir);
     35         this.tc=tc;
     36     }
     37 
     38     // 炮弹自己的draw方法
     39     public void draw(Graphics g) {
     40         //炮弹消亡就不需要再画出来了
     41         if(!live){
     42             tc.missiles.remove(this);
     43             return;
     44         }
     45         Color c = g.getColor();
     46         g.setColor(Color.BLACK);
     47         // 炮弹形状不要比坦克大,这里设置成10,10;
     48         g.fillOval(x, y, WIDTH, HEIGHT);
     49         g.setColor(c);
     50         move();
     51     }
     52 
     53     public void move() {
     54         switch (dir) {
     55         case L:
     56             x -= XSPEED;
     57             break;
     58         case R:
     59             x += XSPEED;
     60             break;
     61         case U:
     62             y -= YSPEED;
     63             break;
     64         case D:
     65             y += YSPEED;
     66             break;
     67         case LU:
     68             x -= XSPEED;
     69             y -= YSPEED;
     70             break;
     71         case LD:
     72             x -= XSPEED;
     73             y += YSPEED;
     74             break;
     75         case RU:
     76             x += XSPEED;
     77             y -= YSPEED;
     78             break;
     79         case RD:
     80             x += XSPEED;
     81             y += YSPEED;
     82             break;
     83         // 炮弹就没有STOP这个枚举类型的值了
     84         /*
     85          * case STOP: break;
     86          */
     87         }
     88         // 判断炮弹出边界则消亡
     89         // 注意x,y只有正数值,x向右递增,y向下递增
     90         if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
     91                 || y > TankClient.GAME_HEIGHT) {
     92             live = false;
     93         }
     94     }
     95     public boolean hitTank(Tank t){
     96         //炮弹的方框和坦克的方框碰在一起了并且坦克是存活着的
     97         if(this.getRect().intersects(t.getRect())&&t.isLive()){
     98             t.setLive(false);
     99             this.live=false;
    100             
    101             //炮弹击中坦克,发生爆炸
    102             Explode e=new Explode(x, y, tc);
    103             tc.explodes.add(e);
    104             return true;
    105         }
    106         return false;
    107     }
    108     //碰撞检测类Rectangle
    109     //拿到包围在炮弹周围的小方块
    110     public Rectangle getRect(){
    111         return new Rectangle(x,y,WIDTH,HEIGHT);
    112     }
    113     
    114     //添加hitTanks方法
    115     public boolean hitTanks(List<Tank> tanks){
    116         for(int i=0;i<tanks.size();i++){
    117             if(hitTank(tanks.get(i))){
    118                 return true;
    119             }
    120         }
    121         return false;
    122         
    123     }
    124 }
    View Code

    Explode:

     1 import java.awt.*;
     2 
     3 public class Explode {
     4     //爆炸的位置
     5     int x, y;
     6     //爆炸是否存在
     7     private boolean live = true;
     8     
     9     //持有一个Tankclient的引用
    10     private TankClient tc ;
    11     
    12     //定义不同直径大小的爆炸
    13     int[] diameter = {4, 7, 12, 18, 26, 32, 49, 30, 14, 6};
    14     //爆炸发生到哪一个阶段了,对应相应大小的直径
    15     int step = 0;
    16     
    17     public Explode(int x, int y, TankClient tc) {
    18         this.x = x;
    19         this.y = y;
    20         this.tc = tc;
    21     }
    22     
    23     public void draw(Graphics g) {
    24         if(!live) {
    25             //爆炸发生,将相应直径的爆炸圆从集合explodes中去除
    26             tc.explodes.remove(this);
    27             return;
    28         }
    29         
    30         if(step == diameter.length) {
    31             live = false;
    32             step = 0;
    33             return;
    34         }
    35         
    36         Color c = g.getColor();
    37         g.setColor(Color.ORANGE);
    38         
    39         //把不同的圆画出来
    40         g.fillOval(x, y, diameter[step], diameter[step]);
    41         g.setColor(c);
    42         
    43         step ++;
    44     }
    45 }
    View Code

    TankClient:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 
      6 public class TankClient extends Frame {
      7     // 设置成常量,方便以后的改动
      8     public static final int GAME_WIDTH = 800;
      9     public static final int GAME_HEIGHT = 600;
     10 
     11     // 将当前的TankClient对象传递给myTank;
     12     // 目的是方便我们在Tank这个类中访问m(炮弹对象)这个成员变量
     13     // 其实就是在Tank类中持有TankClient类对象的一个引用
     14     
     15     //我们这里new我们自己的坦克,用下面的方式
     16     Tank myTank = new Tank(50, 50, true,this);
     17     
     18     /*//新建敌方坦克,(不再需要这个了)
     19     Tank enemyTank=new Tank(100,100,false,this);*/
     20     //定义爆炸
     21     Explode e=new Explode(70, 70, this);
     22     //定义一个集合,多个爆炸点
     23     List<Explode> explodes=new ArrayList<Explode>();
     24     
     25     //使用容器装炮弹
     26     List<Missile> missiles=new ArrayList<Missile>();
     27 
     28     //用容器来装敌人的Tank
     29     List<Tank> tanks=new ArrayList<Tank>();
     30     // 定义虚拟图片,方便后期的一次性显示
     31     Image offScreenImage = null;
     32 
     33     
     34     public void paint(Graphics g) {
     35         //记录屏幕上的子弹数目
     36         g.drawString("missiles count:" + missiles.size(), 10, 50);
     37         //添加上方标记栏,记录爆炸次数
     38         g.drawString("explodes count:" + explodes.size(), 10, 70);
     39         //记录现在屏幕上一共有多少敌方坦克
     40         g.drawString("tanks count:" + tanks.size(), 10, 90);
     41 
     42         
     43         //遍历结合,发出多发炮弹
     44         for(int i=0;i<missiles.size();i++){
     45             Missile m=missiles.get(i);
     46             //对于每一发炮弹,都可以将tanks集合中的敌方炮弹干掉
     47             m.hitTanks(tanks);
     48             m.draw(g);
     49         }
     50         
     51         for(int i=0;i<explodes.size();i++){
     52             Explode e=explodes.get(i);
     53             e.draw(g);
     54         }
     55         
     56         for(int i=0;i<tanks.size();i++){
     57             Tank t=tanks.get(i);
     58             t.draw(g);
     59         }
     60         // 不改变前景色
     61         myTank.draw(g);
     62     }
     63 
     64     // 刷新操作
     65     public void update(Graphics g) {
     66         if (offScreenImage == null) {
     67             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
     68         }
     69         Graphics gOffScreen = offScreenImage.getGraphics();
     70         Color c = gOffScreen.getColor();
     71         gOffScreen.setColor(Color.GREEN);
     72         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
     73         gOffScreen.setColor(c);
     74         paint(gOffScreen);
     75         g.drawImage(offScreenImage, 0, 0, null);
     76     }
     77 
     78     public void lauchFrame() {
     79         
     80         //添加多辆坦克
     81         for(int i=0;i<10;i++){
     82             tanks.add(new Tank(50+40*(i+1),50,false));
     83         }
     84 //        this.setLocation(400, 300);
     85         this.setSize(GAME_WIDTH, GAME_HEIGHT);
     86         this.setTitle("TankWar");
     87         this.addWindowListener(new WindowAdapter() {
     88             public void windowClosing(WindowEvent e) {
     89                 System.exit(0);
     90             }
     91         });
     92         this.setResizable(false);
     93         this.setBackground(Color.GREEN);
     94 
     95         this.addKeyListener(new KeyMonitor());
     96 
     97         setVisible(true);
     98 
     99         new Thread(new PaintThread()).start();
    100     }
    101 
    102     public static void main(String[] args) {
    103         TankClient tc = new TankClient();
    104         tc.lauchFrame();
    105     }
    106 
    107     private class PaintThread implements Runnable {
    108 
    109         public void run() {
    110             while (true) {
    111                 repaint();
    112                 try {
    113                     //为了爆炸效果,改成1000
    114                     Thread.sleep(50);
    115                 } catch (InterruptedException e) {
    116                     e.printStackTrace();
    117                 }
    118             }
    119         }
    120     }
    121 
    122     // 创建键盘时间监听
    123     private class KeyMonitor extends KeyAdapter {
    124 
    125         // 直接调用myTank自己的方法根据相应的按键信息进行移动
    126         public void keyPressed(KeyEvent e) {
    127             myTank.KeyPressed(e);
    128             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
    129             // 而不是一直按照一个方向走下去
    130         }
    131 
    132         public void keyReleased(KeyEvent e) {
    133             myTank.keyReleased(e);
    134         }
    135 
    136     }
    137 }
    View Code

    版本1.9

    功能:让敌军坦克更加智能
    步骤:
            1)让敌军坦克动起来
               构造函数中可以指定方向
               new敌军坦克的时候指定敌军坦克的方向
            2)让敌军坦克向随机方向移动
              (Tank)静态的,添加随机数产生器 java.util.Random
              move完成后,如果是敌军坦克的,随机产生一个数,来设定坦克下一个方向
              Direction.values();
            3)让敌军坦克向随机方向移动随机的步骤
               添加变量,记录随机步骤
               当==0时,改变方向,否则,只是随机步骤递减
            4)让敌军坦克发射炮弹
               本军炮弹不打本军
               炮弹添加好坏bGood,根据好坏画不同颜色
               修改炮弹的构造方法
               修改Tank的fire方法
               修改hitTank方法,好不能打好,坏不能打坏
            5)敌军炮火不能太猛烈

    Tank:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.Random;
      4 
      5 public class Tank {
      6     // 方便后期更改
      7     public static final int XSPEED = 5;
      8     public static final int YSPEED = 5;
      9     // 将坦克的高度和宽度设置为常量
     10     public static final int WIDTH = 30;
     11     public static final int HEIGHT = 30;
     12     TankClient tc;
     13     // 区别是我方坦克还是地方坦克,方便据此进行不同的设置
     14     private boolean good;
     15 
     16     public boolean isGood() {
     17         return good;
     18     }
     19 
     20     public void setGood(boolean good) {
     21         this.good = good;
     22     }
     23 
     24     // 判断坦克生死的变量
     25     private boolean live = true;
     26 
     27     public boolean isLive() {
     28         return live;
     29     }
     30 
     31     public void setLive(boolean live) {
     32         this.live = live;
     33     }
     34 
     35     private int x;
     36     private int y;
     37 
     38     // 随机数产生器,方便敌方坦克可以任意移动
     39     private static Random r = new Random();
     40     // 添加记录按键状态的布尔量
     41     private boolean bL = false;
     42     private boolean bR = false;
     43     private boolean bU = false;
     44     private boolean bD = false;
     45 
     46     // 添加代表方向的量(使用枚举)
     47     enum Direction {
     48         L, R, U, D, LU, LD, RU, RD, STOP
     49     };
     50 
     51     private Direction dir = Direction.STOP;
     52 
     53     // 定义炮筒的方向,我们想办法将炮筒的方法调整成和坦克移动方向一致;
     54     // 我们这里会用一条直线来表示炮筒:模拟炮筒
     55     // 我们要根据炮筒的方向画直线表示炮筒
     56     Direction ptDir = Direction.D;
     57 
     58     // 为了让敌方坦克在一定方向运动移动时间再自动变换方向
     59     private int step = r.nextInt(12) + 3;
     60 
     61     // 更改构造函数
     62     public Tank(int x, int y, boolean good) {
     63         this.x = x;
     64         this.y = y;
     65         this.good = good;
     66     }
     67 
     68     // 这个位置的构造函数也相应进行了更改
     69     public Tank(int x, int y, boolean good, Direction dir, TankClient tc) {
     70         // 调用那个有两个参数的构造方法
     71         this(x, y, good);
     72         this.dir = dir;
     73         // 在这个位置初始化tc
     74         this.tc = tc;
     75     }
     76 
     77     // Tank对象的draw方法
     78     public void draw(Graphics g) {
     79         if (!live) {
     80             // 如果死亡的是敌方坦克,在tanks集合中去除该坦克
     81             if (!good) {
     82                 tc.tanks.remove(this);
     83             }
     84             // 如果是我方坦克,直接返回
     85             return;
     86         }
     87         Color c = g.getColor();
     88         if (good) {
     89             g.setColor(Color.RED);
     90         } else {
     91             g.setColor(Color.PINK);
     92         }
     93         g.fillOval(x, y, WIDTH, HEIGHT);
     94         g.setColor(c);
     95         // 根据炮筒的方向画直线来表示我们坦克的炮筒
     96         switch (ptDir) {
     97         case L:
     98             // 画直线:四个参数分别代表:坦克中心点的坐标 直线的另一头的的坐标
     99             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    100                     + Tank.HEIGHT / 2);
    101             break;
    102         case R:
    103             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    104                     y + Tank.HEIGHT / 2);
    105 
    106             break;
    107         case U:
    108             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    109                     / 2, y);
    110 
    111             break;
    112         case D:
    113             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    114                     / 2, y + Tank.HEIGHT);
    115 
    116             break;
    117         case LU:
    118             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y);
    119             break;
    120         case LD:
    121             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    122                     + Tank.HEIGHT);
    123 
    124             break;
    125         case RU:
    126             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    127                     y);
    128 
    129             break;
    130         case RD:
    131             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    132                     y + Tank.HEIGHT);
    133 
    134             break;
    135         /*
    136          * case STOP: break;
    137          */
    138         }
    139         move();
    140     }
    141 
    142     public void move() {
    143         switch (dir) {
    144         case L:
    145             x -= XSPEED;
    146             break;
    147         case R:
    148             x += XSPEED;
    149             break;
    150         case U:
    151             y -= YSPEED;
    152             break;
    153         case D:
    154             y += YSPEED;
    155             break;
    156         case LU:
    157             x -= XSPEED;
    158             y -= YSPEED;
    159             break;
    160         case LD:
    161             x -= XSPEED;
    162             y += YSPEED;
    163             break;
    164         case RU:
    165             x += XSPEED;
    166             y -= YSPEED;
    167             break;
    168         case RD:
    169             x += XSPEED;
    170             y += YSPEED;
    171             break;
    172 
    173         case STOP:
    174             break;
    175         }
    176         // 如果坦克不是停着的,则将炮筒调整至和坦克移动的方向相同
    177         if (this.dir != Direction.STOP) {
    178             this.ptDir = this.dir;
    179         }
    180         if (x < 0) {
    181             x = 0;
    182         }
    183         // 因为我们的游戏界面有那个missileCount标签,所以在y轴方向不能用y是否<0进行判断
    184         // 否则的话我们的坦克可以从上面出去
    185         if (y < 50) {
    186             y = 50;
    187         }
    188         if (x + Tank.WIDTH > TankClient.GAME_WIDTH) {
    189             x = TankClient.GAME_WIDTH - Tank.WIDTH;
    190         }
    191         if (y + Tank.HEIGHT > TankClient.GAME_HEIGHT) {
    192             y = TankClient.GAME_HEIGHT - Tank.HEIGHT;
    193         }
    194         // 在move方法中判断如果是敌方坦克
    195         if (!good) {
    196             Direction[] dirs = Direction.values();
    197             // 定义敌方坦克的移动
    198             if (step == 0) {
    199                 step = r.nextInt(12) + 3;
    200                 int rn = r.nextInt(dirs.length);
    201                 dir = dirs[rn];
    202             }
    203             // 使得敌方坦克每隔一定时间就变化方向,values:方向枚举转化为数组
    204 
    205             step--;
    206             //用随机数,使得敌方坦克可以发炮弹,但是不要太猛烈
    207             if(r.nextInt(40)>38){    
    208                 this.fire();
    209             }
    210         }
    211     }
    212 
    213     public void locateDirection() {
    214         if (bL && !bU && !bR && !bD)
    215             dir = Direction.L;
    216         else if (bL && bU && !bR && !bD)
    217             dir = Direction.LU;
    218         else if (!bL && bU && !bR && !bD)
    219             dir = Direction.U;
    220         else if (!bL && bU && bR && !bD)
    221             dir = Direction.RU;
    222         else if (!bL && !bU && bR && !bD)
    223             dir = Direction.R;
    224         else if (!bL && !bU && bR && bD)
    225             dir = Direction.RD;
    226         else if (!bL && !bU && !bR && bD)
    227             dir = Direction.D;
    228         else if (bL && !bU && !bR && bD)
    229             dir = Direction.LD;
    230         else if (!bL && !bU && !bR && !bD)
    231             dir = Direction.STOP;
    232 
    233     }
    234 
    235     // 坦克自己向哪个方向移动,它自己最清楚;
    236     public void KeyPressed(KeyEvent e) {
    237         // 获得所按下的键所对应的虚拟码:
    238         // Returns the integer keyCode associated with the key in this event
    239         int key = e.getKeyCode();
    240         // 判断不同的按键,指挥坦克的运动方向
    241         switch (key) {
    242         case KeyEvent.VK_LEFT:
    243             bL = true;
    244             break;
    245         case KeyEvent.VK_UP:
    246             bU = true;
    247             break;
    248         case KeyEvent.VK_RIGHT:
    249             bR = true;
    250             break;
    251         case KeyEvent.VK_DOWN:
    252             bD = true;
    253             break;
    254         }
    255         locateDirection();
    256     }
    257 
    258     public void keyReleased(KeyEvent e) {
    259         int key = e.getKeyCode();
    260         // 判断不同的按键,指挥坦克的运动方向
    261         // 哪个键按下了,就把对应方向的布尔类型置为false
    262         switch (key) {
    263         // 为了防止一直按着Ctrl键的时候,炮弹太过于密集
    264         // 因此我们定义在Ctrl键抬起的时候才发炮弹
    265         // 这样炮弹不至于太过密集
    266         case KeyEvent.VK_CONTROL:
    267             fire();
    268             break;
    269         case KeyEvent.VK_LEFT:
    270             bL = false;
    271             break;
    272         case KeyEvent.VK_UP:
    273             bU = false;
    274             break;
    275         case KeyEvent.VK_RIGHT:
    276             bR = false;
    277             break;
    278         case KeyEvent.VK_DOWN:
    279             bD = false;
    280             break;
    281         }
    282         // 重新定位一下
    283         locateDirection();
    284     }
    285 
    286     public Missile fire() {
    287         if(!live){
    288             return null;
    289         }
    290         // 计算子弹的位置,使得子弹从坦克的中间发出来
    291         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    292         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    293         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    294         Missile m = new Missile(x, y, good,ptDir, tc);
    295         // 将新产生的炮弹放置到List容器中
    296         tc.missiles.add(m);
    297         return m;
    298     }
    299 
    300     // 拿到包围坦克的那个方块
    301     public Rectangle getRect() {
    302 
    303         return new Rectangle(x, y, WIDTH, HEIGHT);
    304     }
    305 }
    View Code

    Missile:

      1 import java.awt.Color;
      2 import java.awt.Graphics;
      3 import java.awt.Rectangle;
      4 import java.util.List;
      5 
      6 public class Missile {
      7     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
      8     public static final int XSPEED = 10;
      9     public static final int YSPEED = 10;
     10     // 将子弹的高度和宽度设置为常量
     11     public static final int WIDTH = 10;
     12     public static final int HEIGHT = 10;
     13     // 炮弹自己的三个属性
     14     int x;
     15     int y;
     16     Tank.Direction dir;
     17 
     18     //同一阵营的的坦克发出的子弹不能伤害自己人
     19     private boolean good;
     20     // 定义一个布尔类型的变量来判断炮弹是否已经消亡
     21     private boolean live = true;
     22     //我们在Missile类中也持有一个TankClient的引用
     23     //在炮弹出界的时候就可以从装炮弹的missiles集合中去除该炮弹,不再对其重画
     24     private TankClient tc;
     25 
     26     public boolean isLive() {
     27         return live;
     28     }
     29 
     30     public Missile(int x, int y, Tank.Direction dir) {
     31         this.x = x;
     32         this.y = y;
     33         this.dir = dir;
     34     }
     35     public Missile(int x,int y,boolean good,Tank.Direction dir,TankClient tc){
     36         this(x, y, dir);
     37         this.good=good;
     38         this.tc=tc;
     39     }
     40 
     41     // 炮弹自己的draw方法
     42     public void draw(Graphics g) {
     43         //炮弹消亡就不需要再画出来了
     44         if(!live){
     45             tc.missiles.remove(this);
     46             return;
     47         }
     48         Color c = g.getColor();
     49         g.setColor(Color.BLACK);
     50         // 炮弹形状不要比坦克大,这里设置成10,10;
     51         g.fillOval(x, y, WIDTH, HEIGHT);
     52         g.setColor(c);
     53         move();
     54     }
     55 
     56     public void move() {
     57         switch (dir) {
     58         case L:
     59             x -= XSPEED;
     60             break;
     61         case R:
     62             x += XSPEED;
     63             break;
     64         case U:
     65             y -= YSPEED;
     66             break;
     67         case D:
     68             y += YSPEED;
     69             break;
     70         case LU:
     71             x -= XSPEED;
     72             y -= YSPEED;
     73             break;
     74         case LD:
     75             x -= XSPEED;
     76             y += YSPEED;
     77             break;
     78         case RU:
     79             x += XSPEED;
     80             y -= YSPEED;
     81             break;
     82         case RD:
     83             x += XSPEED;
     84             y += YSPEED;
     85             break;
     86         // 炮弹就没有STOP这个枚举类型的值了
     87         /*
     88          * case STOP: break;
     89          */
     90         }
     91         // 判断炮弹出边界则消亡
     92         // 注意x,y只有正数值,x向右递增,y向下递增
     93         if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
     94                 || y > TankClient.GAME_HEIGHT) {
     95             live = false;
     96         }
     97     }
     98     public boolean hitTank(Tank t){
     99         //炮弹的方框和坦克的方框碰在一起了并且坦克是存活着的,后面的判断我们是一伙的我就不打你了
    100         if(this.live&&this.getRect().intersects(t.getRect())&&t.isLive()&&this.good!=t.isGood()){
    101             t.setLive(false);
    102             this.live=false;
    103             
    104             //炮弹击中坦克,发生爆炸
    105             Explode e=new Explode(x, y, tc);
    106             tc.explodes.add(e);
    107             return true;
    108         }
    109         return false;
    110     }
    111     //碰撞检测类Rectangle
    112     //拿到包围在炮弹周围的小方块
    113     public Rectangle getRect(){
    114         return new Rectangle(x,y,WIDTH,HEIGHT);
    115     }
    116     
    117     //添加hitTanks方法
    118     public boolean hitTanks(List<Tank> tanks){
    119         for(int i=0;i<tanks.size();i++){
    120             if(hitTank(tanks.get(i))){
    121                 return true;
    122             }
    123         }
    124         return false;
    125         
    126     }
    127 }
    View Code

    Explode(这个类没有变化):

     1 import java.awt.*;
     2 
     3 public class Explode {
     4     //爆炸的位置
     5     int x, y;
     6     //爆炸是否存在
     7     private boolean live = true;
     8     
     9     //持有一个Tankclient的引用
    10     private TankClient tc ;
    11     
    12     //定义不同直径大小的爆炸
    13     int[] diameter = {4, 7, 12, 18, 26, 32, 49, 30, 14, 6};
    14     //爆炸发生到哪一个阶段了,对应相应大小的直径
    15     int step = 0;
    16     
    17     public Explode(int x, int y, TankClient tc) {
    18         this.x = x;
    19         this.y = y;
    20         this.tc = tc;
    21     }
    22     
    23     public void draw(Graphics g) {
    24         if(!live) {
    25             //爆炸发生,将相应直径的爆炸圆从集合explodes中去除
    26             tc.explodes.remove(this);
    27             return;
    28         }
    29         
    30         if(step == diameter.length) {
    31             live = false;
    32             step = 0;
    33             return;
    34         }
    35         
    36         Color c = g.getColor();
    37         g.setColor(Color.ORANGE);
    38         
    39         //把不同的圆画出来
    40         g.fillOval(x, y, diameter[step], diameter[step]);
    41         g.setColor(c);
    42         
    43         step ++;
    44     }
    45 }
    View Code

    Tankclient:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 
      6 public class TankClient extends Frame {
      7     // 设置成常量,方便以后的改动
      8     public static final int GAME_WIDTH = 800;
      9     public static final int GAME_HEIGHT = 600;
     10 
     11     // 将当前的TankClient对象传递给myTank;
     12     // 目的是方便我们在Tank这个类中访问m(炮弹对象)这个成员变量
     13     // 其实就是在Tank类中持有TankClient类对象的一个引用
     14 
     15     // 我们这里new我们自己的坦克
     16     Tank myTank = new Tank(50, 50, true, Tank.Direction.STOP, this);
     17 
     18     /*
     19      * //新建敌方坦克,(不再需要这个了) Tank enemyTank=new Tank(100,100,false,this);
     20      */
     21     // 定义爆炸
     22     Explode e = new Explode(70, 70, this);
     23     // 定义一个集合,多个爆炸点
     24     List<Explode> explodes = new ArrayList<Explode>();
     25 
     26     // 使用容器装炮弹
     27     List<Missile> missiles = new ArrayList<Missile>();
     28 
     29     // 用容器来装敌人的Tank
     30     List<Tank> tanks = new ArrayList<Tank>();
     31     // 定义虚拟图片,方便后期的一次性显示
     32     Image offScreenImage = null;
     33 
     34     public void paint(Graphics g) {
     35         // 记录屏幕上的子弹数目
     36         g.drawString("missiles count:" + missiles.size(), 10, 50);
     37         // 添加上方标记栏,记录爆炸次数
     38         g.drawString("explodes count:" + explodes.size(), 10, 70);
     39         // 记录现在屏幕上一共有多少敌方坦克
     40         g.drawString("tanks count:" + tanks.size(), 10, 90);
     41 
     42         // 遍历结合,发出多发炮弹
     43         for (int i = 0; i < missiles.size(); i++) {
     44             Missile m = missiles.get(i);
     45             // 对于每一发炮弹,都可以将tanks集合中的敌方炮弹干掉
     46             m.hitTanks(tanks);
     47             m.hitTank(myTank);
     48             m.draw(g);
     49         }
     50 
     51         for (int i = 0; i < explodes.size(); i++) {
     52             Explode e = explodes.get(i);
     53             e.draw(g);
     54         }
     55 
     56         for (int i = 0; i < tanks.size(); i++) {
     57             Tank t = tanks.get(i);
     58             t.draw(g);
     59         }
     60         // 不改变前景色
     61         myTank.draw(g);
     62     }
     63 
     64     // 刷新操作
     65     public void update(Graphics g) {
     66         if (offScreenImage == null) {
     67             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
     68         }
     69         Graphics gOffScreen = offScreenImage.getGraphics();
     70         Color c = gOffScreen.getColor();
     71         gOffScreen.setColor(Color.GREEN);
     72         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
     73         gOffScreen.setColor(c);
     74         paint(gOffScreen);
     75         g.drawImage(offScreenImage, 0, 0, null);
     76     }
     77 
     78     public void lauchFrame() {
     79 
     80         // 添加多辆坦克
     81         for (int i = 0; i < 10; i++) {
     82             tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Tank.Direction.D,
     83                     this));
     84         }
     85         // this.setLocation(400, 300);
     86         this.setSize(GAME_WIDTH, GAME_HEIGHT);
     87         this.setTitle("TankWar");
     88         this.addWindowListener(new WindowAdapter() {
     89             public void windowClosing(WindowEvent e) {
     90                 System.exit(0);
     91             }
     92         });
     93         this.setResizable(false);
     94         this.setBackground(Color.GREEN);
     95 
     96         this.addKeyListener(new KeyMonitor());
     97 
     98         setVisible(true);
     99 
    100         new Thread(new PaintThread()).start();
    101     }
    102 
    103     public static void main(String[] args) {
    104         TankClient tc = new TankClient();
    105         tc.lauchFrame();
    106     }
    107 
    108     private class PaintThread implements Runnable {
    109 
    110         public void run() {
    111             while (true) {
    112                 repaint();
    113                 try {
    114                     // 为了爆炸效果,改成1000
    115                     Thread.sleep(50);
    116                 } catch (InterruptedException e) {
    117                     e.printStackTrace();
    118                 }
    119             }
    120         }
    121     }
    122 
    123     // 创建键盘时间监听
    124     private class KeyMonitor extends KeyAdapter {
    125 
    126         // 直接调用myTank自己的方法根据相应的按键信息进行移动
    127         public void keyPressed(KeyEvent e) {
    128             myTank.KeyPressed(e);
    129             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
    130             // 而不是一直按照一个方向走下去
    131         }
    132 
    133         public void keyReleased(KeyEvent e) {
    134             myTank.keyReleased(e);
    135         }
    136 
    137     }
    138 }
    View Code

    版本2.0

    功能:添加两堵墙
    步骤:1)建Wall类、建立Wall对象、画出来
            2)让每一颗子弹打击每一堵墙
                 hitWall()方法
                 注意:子弹速度不能太快,否则很容易穿过墙
            3)让坦克不能穿过墙
               记录上一次的位置oldX, oldY
               修改构造函数
               每次move之前纪录上一次位置
               添加stay方法
               记录移动前的位置
               当撞到时回到移动前的位置
               当碰到墙的时候stay

    具体代码实现:

    Tank:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.Random;
      4 
      5 public class Tank {
      6     // 方便后期更改
      7     public static final int XSPEED = 5;
      8     public static final int YSPEED = 5;
      9     // 将坦克的高度和宽度设置为常量
     10     public static final int WIDTH = 30;
     11     public static final int HEIGHT = 30;
     12     TankClient tc;
     13     // 区别是我方坦克还是地方坦克,方便据此进行不同的设置
     14     private boolean good;
     15 
     16     public boolean isGood() {
     17         return good;
     18     }
     19 
     20     public void setGood(boolean good) {
     21         this.good = good;
     22     }
     23 
     24     // 判断坦克生死的变量
     25     private boolean live = true;
     26 
     27     public boolean isLive() {
     28         return live;
     29     }
     30 
     31     public void setLive(boolean live) {
     32         this.live = live;
     33     }
     34 
     35     private int x;
     36     private int y;
     37     
     38     //记录坦克上一步的位置,防止坦克一碰到wall,就会依附在上面
     39     private int oldx;
     40     private int oldy;
     41 
     42     // 随机数产生器,方便敌方坦克可以任意移动
     43     private static Random r = new Random();
     44     // 添加记录按键状态的布尔量
     45     private boolean bL = false;
     46     private boolean bR = false;
     47     private boolean bU = false;
     48     private boolean bD = false;
     49 
     50     // 添加代表方向的量(使用枚举)
     51     enum Direction {
     52         L, R, U, D, LU, LD, RU, RD, STOP
     53     };
     54 
     55     private Direction dir = Direction.STOP;
     56 
     57     // 定义炮筒的方向,我们想办法将炮筒的方法调整成和坦克移动方向一致;
     58     // 我们这里会用一条直线来表示炮筒:模拟炮筒
     59     // 我们要根据炮筒的方向画直线表示炮筒
     60     Direction ptDir = Direction.D;
     61 
     62     // 为了让敌方坦克在一定方向运动移动时间再自动变换方向
     63     private int step = r.nextInt(12) + 3;
     64 
     65     // 更改构造函数
     66     public Tank(int x, int y, boolean good) {
     67         this.x = x;
     68         this.y = y;
     69         this.oldx=x;
     70         this.oldy=y;
     71         this.good = good;
     72     }
     73 
     74     // 这个位置的构造函数也相应进行了更改
     75     public Tank(int x, int y, boolean good, Direction dir, TankClient tc) {
     76         // 调用那个有两个参数的构造方法
     77         this(x, y, good);
     78         this.dir = dir;
     79         // 在这个位置初始化tc
     80         this.tc = tc;
     81     }
     82 
     83     // Tank对象的draw方法
     84     public void draw(Graphics g) {
     85         if (!live) {
     86             // 如果死亡的是敌方坦克,在tanks集合中去除该坦克
     87             if (!good) {
     88                 tc.tanks.remove(this);
     89             }
     90             // 如果是我方坦克,直接返回
     91             return;
     92         }
     93         Color c = g.getColor();
     94         if (good) {
     95             g.setColor(Color.RED);
     96         } else {
     97             g.setColor(Color.PINK);
     98         }
     99         g.fillOval(x, y, WIDTH, HEIGHT);
    100         g.setColor(c);
    101         // 根据炮筒的方向画直线来表示我们坦克的炮筒
    102         switch (ptDir) {
    103         case L:
    104             // 画直线:四个参数分别代表:坦克中心点的坐标 直线的另一头的的坐标
    105             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    106                     + Tank.HEIGHT / 2);
    107             break;
    108         case R:
    109             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    110                     y + Tank.HEIGHT / 2);
    111 
    112             break;
    113         case U:
    114             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    115                     / 2, y);
    116 
    117             break;
    118         case D:
    119             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    120                     / 2, y + Tank.HEIGHT);
    121 
    122             break;
    123         case LU:
    124             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y);
    125             break;
    126         case LD:
    127             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    128                     + Tank.HEIGHT);
    129 
    130             break;
    131         case RU:
    132             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    133                     y);
    134 
    135             break;
    136         case RD:
    137             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    138                     y + Tank.HEIGHT);
    139 
    140             break;
    141         /*
    142          * case STOP: break;
    143          */
    144         }
    145         move();
    146     }
    147 
    148     public void move() {
    149         //记录坦克上一步的位置
    150         this.oldx=x;
    151         this.oldy=y;
    152         switch (dir) {
    153         case L:
    154             x -= XSPEED;
    155             break;
    156         case R:
    157             x += XSPEED;
    158             break;
    159         case U:
    160             y -= YSPEED;
    161             break;
    162         case D:
    163             y += YSPEED;
    164             break;
    165         case LU:
    166             x -= XSPEED;
    167             y -= YSPEED;
    168             break;
    169         case LD:
    170             x -= XSPEED;
    171             y += YSPEED;
    172             break;
    173         case RU:
    174             x += XSPEED;
    175             y -= YSPEED;
    176             break;
    177         case RD:
    178             x += XSPEED;
    179             y += YSPEED;
    180             break;
    181 
    182         case STOP:
    183             break;
    184         }
    185         // 如果坦克不是停着的,则将炮筒调整至和坦克移动的方向相同
    186         if (this.dir != Direction.STOP) {
    187             this.ptDir = this.dir;
    188         }
    189         if (x < 0) {
    190             x = 0;
    191         }
    192         // 因为我们的游戏界面有那个missileCount标签,所以在y轴方向不能用y是否<0进行判断
    193         // 否则的话我们的坦克可以从上面出去
    194         if (y < 50) {
    195             y = 50;
    196         }
    197         if (x + Tank.WIDTH > TankClient.GAME_WIDTH) {
    198             x = TankClient.GAME_WIDTH - Tank.WIDTH;
    199         }
    200         if (y + Tank.HEIGHT > TankClient.GAME_HEIGHT) {
    201             y = TankClient.GAME_HEIGHT - Tank.HEIGHT;
    202         }
    203         // 在move方法中判断如果是敌方坦克
    204         if (!good) {
    205             Direction[] dirs = Direction.values();
    206             // 定义敌方坦克的移动
    207             if (step == 0) {
    208                 step = r.nextInt(12) + 3;
    209                 int rn = r.nextInt(dirs.length);
    210                 dir = dirs[rn];
    211             }
    212             // 使得敌方坦克每隔一定时间就变化方向,values:方向枚举转化为数组
    213 
    214             step--;
    215             //用随机数,使得敌方坦克可以发炮弹,但是不要太猛烈
    216             if(r.nextInt(40)>38){    
    217                 this.fire();
    218             }
    219         }
    220     }
    221 
    222     public void locateDirection() {
    223         if (bL && !bU && !bR && !bD)
    224             dir = Direction.L;
    225         else if (bL && bU && !bR && !bD)
    226             dir = Direction.LU;
    227         else if (!bL && bU && !bR && !bD)
    228             dir = Direction.U;
    229         else if (!bL && bU && bR && !bD)
    230             dir = Direction.RU;
    231         else if (!bL && !bU && bR && !bD)
    232             dir = Direction.R;
    233         else if (!bL && !bU && bR && bD)
    234             dir = Direction.RD;
    235         else if (!bL && !bU && !bR && bD)
    236             dir = Direction.D;
    237         else if (bL && !bU && !bR && bD)
    238             dir = Direction.LD;
    239         else if (!bL && !bU && !bR && !bD)
    240             dir = Direction.STOP;
    241 
    242     }
    243 
    244     private void stay(){
    245         x=oldx;
    246         y=oldy;
    247     }
    248     // 坦克自己向哪个方向移动,它自己最清楚;
    249     public void KeyPressed(KeyEvent e) {
    250         // 获得所按下的键所对应的虚拟码:
    251         // Returns the integer keyCode associated with the key in this event
    252         int key = e.getKeyCode();
    253         // 判断不同的按键,指挥坦克的运动方向
    254         switch (key) {
    255         case KeyEvent.VK_LEFT:
    256             bL = true;
    257             break;
    258         case KeyEvent.VK_UP:
    259             bU = true;
    260             break;
    261         case KeyEvent.VK_RIGHT:
    262             bR = true;
    263             break;
    264         case KeyEvent.VK_DOWN:
    265             bD = true;
    266             break;
    267         }
    268         locateDirection();
    269     }
    270 
    271     public void keyReleased(KeyEvent e) {
    272         int key = e.getKeyCode();
    273         // 判断不同的按键,指挥坦克的运动方向
    274         // 哪个键按下了,就把对应方向的布尔类型置为false
    275         switch (key) {
    276         // 为了防止一直按着Ctrl键的时候,炮弹太过于密集
    277         // 因此我们定义在Ctrl键抬起的时候才发炮弹
    278         // 这样炮弹不至于太过密集
    279         case KeyEvent.VK_CONTROL:
    280             fire();
    281             break;
    282         case KeyEvent.VK_LEFT:
    283             bL = false;
    284             break;
    285         case KeyEvent.VK_UP:
    286             bU = false;
    287             break;
    288         case KeyEvent.VK_RIGHT:
    289             bR = false;
    290             break;
    291         case KeyEvent.VK_DOWN:
    292             bD = false;
    293             break;
    294         }
    295         // 重新定位一下
    296         locateDirection();
    297     }
    298 
    299     public Missile fire() {
    300         if(!live){
    301             return null;
    302         }
    303         // 计算子弹的位置,使得子弹从坦克的中间发出来
    304         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    305         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    306         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    307         Missile m = new Missile(x, y, good,ptDir, tc);
    308         // 将新产生的炮弹放置到List容器中
    309         tc.missiles.add(m);
    310         return m;
    311     }
    312 
    313     // 拿到包围坦克的那个方块
    314     public Rectangle getRect() {
    315 
    316         return new Rectangle(x, y, WIDTH, HEIGHT);
    317     }
    318     
    319     public boolean collidesWithWall(Wall w){
    320         
    321         if(this.live&&this.getRect().intersects(w.getRect())){
    322             this.dir=Direction.STOP;
    323             //当坦克撞到墙上的时候,停一下,再回到上一步的位置
    324             this.stay();
    325             return true;
    326         }
    327         return false;
    328         
    329     }
    330 }
    View Code

    Missile:

      1 import java.awt.Color;
      2 import java.awt.Graphics;
      3 import java.awt.Rectangle;
      4 import java.util.List;
      5 
      6 public class Missile {
      7     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
      8     public static final int XSPEED = 10;
      9     public static final int YSPEED = 10;
     10     // 将子弹的高度和宽度设置为常量
     11     public static final int WIDTH = 10;
     12     public static final int HEIGHT = 10;
     13     // 炮弹自己的三个属性
     14     int x;
     15     int y;
     16     Tank.Direction dir;
     17 
     18     //同一阵营的的坦克发出的子弹不能伤害自己人
     19     private boolean good;
     20     // 定义一个布尔类型的变量来判断炮弹是否已经消亡
     21     private boolean live = true;
     22     //我们在Missile类中也持有一个TankClient的引用
     23     //在炮弹出界的时候就可以从装炮弹的missiles集合中去除该炮弹,不再对其重画
     24     private TankClient tc;
     25 
     26     public boolean isLive() {
     27         return live;
     28     }
     29 
     30     public Missile(int x, int y, Tank.Direction dir) {
     31         this.x = x;
     32         this.y = y;
     33         this.dir = dir;
     34     }
     35     public Missile(int x,int y,boolean good,Tank.Direction dir,TankClient tc){
     36         this(x, y, dir);
     37         this.good=good;
     38         this.tc=tc;
     39     }
     40 
     41     // 炮弹自己的draw方法
     42     public void draw(Graphics g) {
     43         //炮弹消亡就不需要再画出来了
     44         if(!live){
     45             tc.missiles.remove(this);
     46             return;
     47         }
     48         Color c = g.getColor();
     49         g.setColor(Color.BLACK);
     50         // 炮弹形状不要比坦克大,这里设置成10,10;
     51         g.fillOval(x, y, WIDTH, HEIGHT);
     52         g.setColor(c);
     53         move();
     54     }
     55 
     56     public void move() {
     57         switch (dir) {
     58         case L:
     59             x -= XSPEED;
     60             break;
     61         case R:
     62             x += XSPEED;
     63             break;
     64         case U:
     65             y -= YSPEED;
     66             break;
     67         case D:
     68             y += YSPEED;
     69             break;
     70         case LU:
     71             x -= XSPEED;
     72             y -= YSPEED;
     73             break;
     74         case LD:
     75             x -= XSPEED;
     76             y += YSPEED;
     77             break;
     78         case RU:
     79             x += XSPEED;
     80             y -= YSPEED;
     81             break;
     82         case RD:
     83             x += XSPEED;
     84             y += YSPEED;
     85             break;
     86         // 炮弹就没有STOP这个枚举类型的值了
     87         /*
     88          * case STOP: break;
     89          */
     90         }
     91         // 判断炮弹出边界则消亡
     92         // 注意x,y只有正数值,x向右递增,y向下递增
     93         if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
     94                 || y > TankClient.GAME_HEIGHT) {
     95             live = false;
     96         }
     97     }
     98     public boolean hitTank(Tank t){
     99         //炮弹的方框和坦克的方框碰在一起了并且坦克是存活着的,后面的判断我们是一伙的我就不打你了
    100         if(this.live&&this.getRect().intersects(t.getRect())&&t.isLive()&&this.good!=t.isGood()){
    101             t.setLive(false);
    102             this.live=false;
    103             
    104             //炮弹击中坦克,发生爆炸
    105             Explode e=new Explode(x, y, tc);
    106             tc.explodes.add(e);
    107             return true;
    108         }
    109         return false;
    110     }
    111     //碰撞检测类Rectangle
    112     //拿到包围在炮弹周围的小方块
    113     public Rectangle getRect(){
    114         return new Rectangle(x,y,WIDTH,HEIGHT);
    115     }
    116     
    117     //添加hitTanks方法
    118     public boolean hitTanks(List<Tank> tanks){
    119         for(int i=0;i<tanks.size();i++){
    120             if(hitTank(tanks.get(i))){
    121                 return true;
    122             }
    123         }
    124         return false;
    125         
    126     }
    127     public boolean hitWall(Wall w){
    128         if(this.live&&this.getRect().intersects(w.getRect())){
    129             this.live=false;
    130             return true;
    131         }
    132         return false;
    133     }
    134 }
    View Code

    Explode:

     1 import java.awt.*;
     2 
     3 public class Explode {
     4     //爆炸的位置
     5     int x, y;
     6     //爆炸是否存在
     7     private boolean live = true;
     8     
     9     //持有一个Tankclient的引用
    10     private TankClient tc ;
    11     
    12     //定义不同直径大小的爆炸
    13     int[] diameter = {4, 7, 12, 18, 26, 32, 49, 30, 14, 6};
    14     //爆炸发生到哪一个阶段了,对应相应大小的直径
    15     int step = 0;
    16     
    17     public Explode(int x, int y, TankClient tc) {
    18         this.x = x;
    19         this.y = y;
    20         this.tc = tc;
    21     }
    22     
    23     public void draw(Graphics g) {
    24         if(!live) {
    25             //爆炸发生,将相应直径的爆炸圆从集合explodes中去除
    26             tc.explodes.remove(this);
    27             return;
    28         }
    29         
    30         if(step == diameter.length) {
    31             live = false;
    32             step = 0;
    33             return;
    34         }
    35         
    36         Color c = g.getColor();
    37         g.setColor(Color.ORANGE);
    38         
    39         //把不同的圆画出来
    40         g.fillOval(x, y, diameter[step], diameter[step]);
    41         g.setColor(c);
    42         
    43         step ++;
    44     }
    45 }
    View Code

    Wall:

     1 import java.awt.Graphics;
     2 import java.awt.Rectangle;
     3 
     4 //
     5 public class Wall {
     6     int x,y,w,h;
     7     TankClient tc;
     8     public Wall(int x, int y, int w, int h, TankClient tc) {
     9         super();
    10         this.x = x;
    11         this.y = y;
    12         this.w = w;
    13         this.h = h;
    14         this.tc = tc;
    15     }
    16     public void draw(Graphics g){
    17         g.fillRect(x, y, w, h);
    18     }
    19     //碰撞检测
    20     public Rectangle getRect(){
    21         return new Rectangle(x,y,w,h);
    22     }
    23 }

    TankClient:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 
      6 public class TankClient extends Frame {
      7     // 设置成常量,方便以后的改动
      8     public static final int GAME_WIDTH = 800;
      9     public static final int GAME_HEIGHT = 600;
     10 
     11     // 将当前的TankClient对象传递给myTank;
     12     // 目的是方便我们在Tank这个类中访问m(炮弹对象)这个成员变量
     13     // 其实就是在Tank类中持有TankClient类对象的一个引用
     14 
     15     // 我们这里new我们自己的坦克
     16     Tank myTank = new Tank(50, 50, true, Tank.Direction.STOP, this);
     17 
     18     Wall w1=new Wall(100, 200,20, 150,this);
     19     Wall w2=new Wall(300, 100,300,20,this);
     20     /*
     21      * //新建敌方坦克,(不再需要这个了) Tank enemyTank=new Tank(100,100,false,this);
     22      */
     23     // 定义爆炸
     24     Explode e = new Explode(70, 70, this);
     25     // 定义一个集合,多个爆炸点
     26     List<Explode> explodes = new ArrayList<Explode>();
     27 
     28     // 使用容器装炮弹
     29     List<Missile> missiles = new ArrayList<Missile>();
     30 
     31     // 用容器来装敌人的Tank
     32     List<Tank> tanks = new ArrayList<Tank>();
     33     // 定义虚拟图片,方便后期的一次性显示
     34     Image offScreenImage = null;
     35 
     36     public void paint(Graphics g) {
     37         // 记录屏幕上的子弹数目
     38         g.drawString("missiles count:" + missiles.size(), 10, 50);
     39         // 添加上方标记栏,记录爆炸次数
     40         g.drawString("explodes count:" + explodes.size(), 10, 70);
     41         // 记录现在屏幕上一共有多少敌方坦克
     42         g.drawString("tanks count:" + tanks.size(), 10, 90);
     43 
     44         // 遍历结合,发出多发炮弹
     45         for (int i = 0; i < missiles.size(); i++) {
     46             Missile m = missiles.get(i);
     47             // 对于每一发炮弹,都可以将tanks集合中的敌方炮弹干掉
     48             m.hitTanks(tanks);
     49             m.hitTank(myTank);
     50             m.hitWall(w1);
     51             m.hitWall(w2);
     52             m.draw(g);
     53         }
     54 
     55         for (int i = 0; i < explodes.size(); i++) {
     56             Explode e = explodes.get(i);
     57             e.draw(g);
     58         }
     59 
     60         for (int i = 0; i < tanks.size(); i++) {
     61             Tank t = tanks.get(i);
     62             t.collidesWithWall(w1);
     63             t.collidesWithWall(w2);
     64             t.draw(g);
     65         }
     66         // 不改变前景色
     67         myTank.draw(g);
     68         w1.draw(g);
     69         w2.draw(g);
     70     }
     71 
     72     // 刷新操作
     73     public void update(Graphics g) {
     74         if (offScreenImage == null) {
     75             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
     76         }
     77         Graphics gOffScreen = offScreenImage.getGraphics();
     78         Color c = gOffScreen.getColor();
     79         gOffScreen.setColor(Color.GREEN);
     80         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
     81         gOffScreen.setColor(c);
     82         paint(gOffScreen);
     83         g.drawImage(offScreenImage, 0, 0, null);
     84     }
     85 
     86     public void lauchFrame() {
     87 
     88         // 添加多辆坦克
     89         for (int i = 0; i < 10; i++) {
     90             tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Tank.Direction.D,
     91                     this));
     92         }
     93         // this.setLocation(400, 300);
     94         this.setSize(GAME_WIDTH, GAME_HEIGHT);
     95         this.setTitle("TankWar");
     96         this.addWindowListener(new WindowAdapter() {
     97             public void windowClosing(WindowEvent e) {
     98                 System.exit(0);
     99             }
    100         });
    101         this.setResizable(false);
    102         this.setBackground(Color.GREEN);
    103 
    104         this.addKeyListener(new KeyMonitor());
    105 
    106         setVisible(true);
    107 
    108         new Thread(new PaintThread()).start();
    109     }
    110 
    111     public static void main(String[] args) {
    112         TankClient tc = new TankClient();
    113         tc.lauchFrame();
    114     }
    115 
    116     private class PaintThread implements Runnable {
    117 
    118         public void run() {
    119             while (true) {
    120                 repaint();
    121                 try {
    122                     // 为了爆炸效果,改成1000
    123                     Thread.sleep(50);
    124                 } catch (InterruptedException e) {
    125                     e.printStackTrace();
    126                 }
    127             }
    128         }
    129     }
    130 
    131     // 创建键盘时间监听
    132     private class KeyMonitor extends KeyAdapter {
    133 
    134         // 直接调用myTank自己的方法根据相应的按键信息进行移动
    135         public void keyPressed(KeyEvent e) {
    136             myTank.KeyPressed(e);
    137             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
    138             // 而不是一直按照一个方向走下去
    139         }
    140 
    141         public void keyReleased(KeyEvent e) {
    142             myTank.keyReleased(e);
    143         }
    144 
    145     }
    146 }
    View Code

    版本2.1
    功能:坦克不能互相穿越
    步骤:
            当坦克撞到Tank时stay:添加碰撞检测,就添加了一个方法

     1 public boolean collidesWithTanks(java.util.List<Tank> tanks) {
     2         for (int i = 0; i < tanks.size(); i++) {
     3             Tank t = tanks.get(i);
     4             if (this != t) {
     5                 if (this.live && t.isLive()
     6                         && this.getRect().intersects(t.getRect())) {
     7                     this.stay();
     8                     t.stay();
     9                 }
    10             }
    11         }
    12         return bD;
    13 
    14     }

    TankClient类中的paint方法中:

    1 for (int i = 0; i < tanks.size(); i++) {
    2             Tank t = tanks.get(i);
    3             t.collidesWithWall(w1);
    4             t.collidesWithWall(w2);
    5             t.collidesWithTanks(tanks);
    6             t.draw(g);
    7         }

    具体代码实现:

    Tank:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.Random;
      4 
      5 public class Tank {
      6     // 方便后期更改
      7     public static final int XSPEED = 5;
      8     public static final int YSPEED = 5;
      9     // 将坦克的高度和宽度设置为常量
     10     public static final int WIDTH = 30;
     11     public static final int HEIGHT = 30;
     12     TankClient tc;
     13     // 区别是我方坦克还是地方坦克,方便据此进行不同的设置
     14     private boolean good;
     15 
     16     public boolean isGood() {
     17         return good;
     18     }
     19 
     20     public void setGood(boolean good) {
     21         this.good = good;
     22     }
     23 
     24     // 判断坦克生死的变量
     25     private boolean live = true;
     26 
     27     public boolean isLive() {
     28         return live;
     29     }
     30 
     31     public void setLive(boolean live) {
     32         this.live = live;
     33     }
     34 
     35     private int x;
     36     private int y;
     37 
     38     // 记录坦克上一步的位置,防止坦克一碰到wall,就会依附在上面
     39     private int oldx;
     40     private int oldy;
     41 
     42     // 随机数产生器,方便敌方坦克可以任意移动
     43     private static Random r = new Random();
     44     // 添加记录按键状态的布尔量
     45     private boolean bL = false;
     46     private boolean bR = false;
     47     private boolean bU = false;
     48     private boolean bD = false;
     49 
     50     // 添加代表方向的量(使用枚举)
     51     enum Direction {
     52         L, R, U, D, LU, LD, RU, RD, STOP
     53     };
     54 
     55     private Direction dir = Direction.STOP;
     56 
     57     // 定义炮筒的方向,我们想办法将炮筒的方法调整成和坦克移动方向一致;
     58     // 我们这里会用一条直线来表示炮筒:模拟炮筒
     59     // 我们要根据炮筒的方向画直线表示炮筒
     60     Direction ptDir = Direction.D;
     61 
     62     // 为了让敌方坦克在一定方向运动移动时间再自动变换方向
     63     private int step = r.nextInt(12) + 3;
     64 
     65     // 更改构造函数
     66     public Tank(int x, int y, boolean good) {
     67         this.x = x;
     68         this.y = y;
     69         this.oldx = x;
     70         this.oldy = y;
     71         this.good = good;
     72     }
     73 
     74     // 这个位置的构造函数也相应进行了更改
     75     public Tank(int x, int y, boolean good, Direction dir, TankClient tc) {
     76         // 调用那个有两个参数的构造方法
     77         this(x, y, good);
     78         this.dir = dir;
     79         // 在这个位置初始化tc
     80         this.tc = tc;
     81     }
     82 
     83     // Tank对象的draw方法
     84     public void draw(Graphics g) {
     85         if (!live) {
     86             // 如果死亡的是敌方坦克,在tanks集合中去除该坦克
     87             if (!good) {
     88                 tc.tanks.remove(this);
     89             }
     90             // 如果是我方坦克,直接返回
     91             return;
     92         }
     93         Color c = g.getColor();
     94         if (good) {
     95             g.setColor(Color.RED);
     96         } else {
     97             g.setColor(Color.PINK);
     98         }
     99         g.fillOval(x, y, WIDTH, HEIGHT);
    100         g.setColor(c);
    101         // 根据炮筒的方向画直线来表示我们坦克的炮筒
    102         switch (ptDir) {
    103         case L:
    104             // 画直线:四个参数分别代表:坦克中心点的坐标 直线的另一头的的坐标
    105             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    106                     + Tank.HEIGHT / 2);
    107             break;
    108         case R:
    109             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    110                     y + Tank.HEIGHT / 2);
    111 
    112             break;
    113         case U:
    114             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    115                     / 2, y);
    116 
    117             break;
    118         case D:
    119             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    120                     / 2, y + Tank.HEIGHT);
    121 
    122             break;
    123         case LU:
    124             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y);
    125             break;
    126         case LD:
    127             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    128                     + Tank.HEIGHT);
    129 
    130             break;
    131         case RU:
    132             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    133                     y);
    134 
    135             break;
    136         case RD:
    137             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    138                     y + Tank.HEIGHT);
    139 
    140             break;
    141         /*
    142          * case STOP: break;
    143          */
    144         }
    145         move();
    146     }
    147 
    148     public void move() {
    149         // 记录坦克上一步的位置
    150         this.oldx = x;
    151         this.oldy = y;
    152         switch (dir) {
    153         case L:
    154             x -= XSPEED;
    155             break;
    156         case R:
    157             x += XSPEED;
    158             break;
    159         case U:
    160             y -= YSPEED;
    161             break;
    162         case D:
    163             y += YSPEED;
    164             break;
    165         case LU:
    166             x -= XSPEED;
    167             y -= YSPEED;
    168             break;
    169         case LD:
    170             x -= XSPEED;
    171             y += YSPEED;
    172             break;
    173         case RU:
    174             x += XSPEED;
    175             y -= YSPEED;
    176             break;
    177         case RD:
    178             x += XSPEED;
    179             y += YSPEED;
    180             break;
    181 
    182         case STOP:
    183             break;
    184         }
    185         // 如果坦克不是停着的,则将炮筒调整至和坦克移动的方向相同
    186         if (this.dir != Direction.STOP) {
    187             this.ptDir = this.dir;
    188         }
    189         if (x < 0) {
    190             x = 0;
    191         }
    192         // 因为我们的游戏界面有那个missileCount标签,所以在y轴方向不能用y是否<0进行判断
    193         // 否则的话我们的坦克可以从上面出去
    194         if (y < 50) {
    195             y = 50;
    196         }
    197         if (x + Tank.WIDTH > TankClient.GAME_WIDTH) {
    198             x = TankClient.GAME_WIDTH - Tank.WIDTH;
    199         }
    200         if (y + Tank.HEIGHT > TankClient.GAME_HEIGHT) {
    201             y = TankClient.GAME_HEIGHT - Tank.HEIGHT;
    202         }
    203         // 在move方法中判断如果是敌方坦克
    204         if (!good) {
    205             Direction[] dirs = Direction.values();
    206             // 定义敌方坦克的移动
    207             if (step == 0) {
    208                 step = r.nextInt(12) + 3;
    209                 int rn = r.nextInt(dirs.length);
    210                 dir = dirs[rn];
    211             }
    212             // 使得敌方坦克每隔一定时间就变化方向,values:方向枚举转化为数组
    213 
    214             step--;
    215             // 用随机数,使得敌方坦克可以发炮弹,但是不要太猛烈
    216             if (r.nextInt(40) > 38) {
    217                 this.fire();
    218             }
    219         }
    220     }
    221 
    222     public void locateDirection() {
    223         if (bL && !bU && !bR && !bD)
    224             dir = Direction.L;
    225         else if (bL && bU && !bR && !bD)
    226             dir = Direction.LU;
    227         else if (!bL && bU && !bR && !bD)
    228             dir = Direction.U;
    229         else if (!bL && bU && bR && !bD)
    230             dir = Direction.RU;
    231         else if (!bL && !bU && bR && !bD)
    232             dir = Direction.R;
    233         else if (!bL && !bU && bR && bD)
    234             dir = Direction.RD;
    235         else if (!bL && !bU && !bR && bD)
    236             dir = Direction.D;
    237         else if (bL && !bU && !bR && bD)
    238             dir = Direction.LD;
    239         else if (!bL && !bU && !bR && !bD)
    240             dir = Direction.STOP;
    241 
    242     }
    243 
    244     private void stay() {
    245         x = oldx;
    246         y = oldy;
    247     }
    248 
    249     // 坦克自己向哪个方向移动,它自己最清楚;
    250     public void KeyPressed(KeyEvent e) {
    251         // 获得所按下的键所对应的虚拟码:
    252         // Returns the integer keyCode associated with the key in this event
    253         int key = e.getKeyCode();
    254         // 判断不同的按键,指挥坦克的运动方向
    255         switch (key) {
    256         case KeyEvent.VK_LEFT:
    257             bL = true;
    258             break;
    259         case KeyEvent.VK_UP:
    260             bU = true;
    261             break;
    262         case KeyEvent.VK_RIGHT:
    263             bR = true;
    264             break;
    265         case KeyEvent.VK_DOWN:
    266             bD = true;
    267             break;
    268         }
    269         locateDirection();
    270     }
    271 
    272     public void keyReleased(KeyEvent e) {
    273         int key = e.getKeyCode();
    274         // 判断不同的按键,指挥坦克的运动方向
    275         // 哪个键按下了,就把对应方向的布尔类型置为false
    276         switch (key) {
    277         // 为了防止一直按着Ctrl键的时候,炮弹太过于密集
    278         // 因此我们定义在Ctrl键抬起的时候才发炮弹
    279         // 这样炮弹不至于太过密集
    280         case KeyEvent.VK_CONTROL:
    281             fire();
    282             break;
    283         case KeyEvent.VK_LEFT:
    284             bL = false;
    285             break;
    286         case KeyEvent.VK_UP:
    287             bU = false;
    288             break;
    289         case KeyEvent.VK_RIGHT:
    290             bR = false;
    291             break;
    292         case KeyEvent.VK_DOWN:
    293             bD = false;
    294             break;
    295         }
    296         // 重新定位一下
    297         locateDirection();
    298     }
    299 
    300     public Missile fire() {
    301         if (!live) {
    302             return null;
    303         }
    304         // 计算子弹的位置,使得子弹从坦克的中间发出来
    305         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    306         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    307         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    308         Missile m = new Missile(x, y, good, ptDir, tc);
    309         // 将新产生的炮弹放置到List容器中
    310         tc.missiles.add(m);
    311         return m;
    312     }
    313 
    314     // 拿到包围坦克的那个方块
    315     public Rectangle getRect() {
    316 
    317         return new Rectangle(x, y, WIDTH, HEIGHT);
    318     }
    319 
    320     public boolean collidesWithWall(Wall w) {
    321 
    322         if (this.live && this.getRect().intersects(w.getRect())) {
    323             this.dir = Direction.STOP;
    324             // 当坦克撞到墙上的时候,停一下,再回到上一步的位置
    325             this.stay();
    326             return true;
    327         }
    328         return false;
    329 
    330     }
    331 
    332     // 坦克和坦克之间的碰撞检测
    333     public boolean collidesWithTanks(java.util.List<Tank> tanks) {
    334         for (int i = 0; i < tanks.size(); i++) {
    335             Tank t = tanks.get(i);
    336             if (this != t) {
    337                 if (this.live && t.isLive()
    338                         && this.getRect().intersects(t.getRect())) {
    339                     this.stay();
    340                     t.stay();
    341                 }
    342             }
    343         }
    344         return bD;
    345 
    346     }
    347 }
    View Code

    Missile:

      1 import java.awt.Color;
      2 import java.awt.Graphics;
      3 import java.awt.Rectangle;
      4 import java.util.List;
      5 
      6 public class Missile {
      7     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
      8     public static final int XSPEED = 10;
      9     public static final int YSPEED = 10;
     10     // 将子弹的高度和宽度设置为常量
     11     public static final int WIDTH = 10;
     12     public static final int HEIGHT = 10;
     13     // 炮弹自己的三个属性
     14     int x;
     15     int y;
     16     Tank.Direction dir;
     17 
     18     //同一阵营的的坦克发出的子弹不能伤害自己人
     19     private boolean good;
     20     // 定义一个布尔类型的变量来判断炮弹是否已经消亡
     21     private boolean live = true;
     22     //我们在Missile类中也持有一个TankClient的引用
     23     //在炮弹出界的时候就可以从装炮弹的missiles集合中去除该炮弹,不再对其重画
     24     private TankClient tc;
     25 
     26     public boolean isLive() {
     27         return live;
     28     }
     29 
     30     public Missile(int x, int y, Tank.Direction dir) {
     31         this.x = x;
     32         this.y = y;
     33         this.dir = dir;
     34     }
     35     public Missile(int x,int y,boolean good,Tank.Direction dir,TankClient tc){
     36         this(x, y, dir);
     37         this.good=good;
     38         this.tc=tc;
     39     }
     40 
     41     // 炮弹自己的draw方法
     42     public void draw(Graphics g) {
     43         //炮弹消亡就不需要再画出来了
     44         if(!live){
     45             tc.missiles.remove(this);
     46             return;
     47         }
     48         Color c = g.getColor();
     49         g.setColor(Color.BLACK);
     50         // 炮弹形状不要比坦克大,这里设置成10,10;
     51         g.fillOval(x, y, WIDTH, HEIGHT);
     52         g.setColor(c);
     53         move();
     54     }
     55 
     56     public void move() {
     57         switch (dir) {
     58         case L:
     59             x -= XSPEED;
     60             break;
     61         case R:
     62             x += XSPEED;
     63             break;
     64         case U:
     65             y -= YSPEED;
     66             break;
     67         case D:
     68             y += YSPEED;
     69             break;
     70         case LU:
     71             x -= XSPEED;
     72             y -= YSPEED;
     73             break;
     74         case LD:
     75             x -= XSPEED;
     76             y += YSPEED;
     77             break;
     78         case RU:
     79             x += XSPEED;
     80             y -= YSPEED;
     81             break;
     82         case RD:
     83             x += XSPEED;
     84             y += YSPEED;
     85             break;
     86         // 炮弹就没有STOP这个枚举类型的值了
     87         /*
     88          * case STOP: break;
     89          */
     90         }
     91         // 判断炮弹出边界则消亡
     92         // 注意x,y只有正数值,x向右递增,y向下递增
     93         if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
     94                 || y > TankClient.GAME_HEIGHT) {
     95             live = false;
     96         }
     97     }
     98     public boolean hitTank(Tank t){
     99         //炮弹的方框和坦克的方框碰在一起了并且坦克是存活着的,后面的判断我们是一伙的我就不打你了
    100         if(this.live&&this.getRect().intersects(t.getRect())&&t.isLive()&&this.good!=t.isGood()){
    101             t.setLive(false);
    102             this.live=false;
    103             
    104             //炮弹击中坦克,发生爆炸
    105             Explode e=new Explode(x, y, tc);
    106             tc.explodes.add(e);
    107             return true;
    108         }
    109         return false;
    110     }
    111     //碰撞检测类Rectangle
    112     //拿到包围在炮弹周围的小方块
    113     public Rectangle getRect(){
    114         return new Rectangle(x,y,WIDTH,HEIGHT);
    115     }
    116     
    117     //添加hitTanks方法
    118     public boolean hitTanks(List<Tank> tanks){
    119         for(int i=0;i<tanks.size();i++){
    120             if(hitTank(tanks.get(i))){
    121                 return true;
    122             }
    123         }
    124         return false;
    125         
    126     }
    127     public boolean hitWall(Wall w){
    128         if(this.live&&this.getRect().intersects(w.getRect())){
    129             this.live=false;
    130             return true;
    131         }
    132         return false;
    133     }
    134 }
    View Code

    Explode:

     1 import java.awt.*;
     2 
     3 public class Explode {
     4     //爆炸的位置
     5     int x, y;
     6     //爆炸是否存在
     7     private boolean live = true;
     8     
     9     //持有一个Tankclient的引用
    10     private TankClient tc ;
    11     
    12     //定义不同直径大小的爆炸
    13     int[] diameter = {4, 7, 12, 18, 26, 32, 49, 30, 14, 6};
    14     //爆炸发生到哪一个阶段了,对应相应大小的直径
    15     int step = 0;
    16     
    17     public Explode(int x, int y, TankClient tc) {
    18         this.x = x;
    19         this.y = y;
    20         this.tc = tc;
    21     }
    22     
    23     public void draw(Graphics g) {
    24         if(!live) {
    25             //爆炸发生,将相应直径的爆炸圆从集合explodes中去除
    26             tc.explodes.remove(this);
    27             return;
    28         }
    29         
    30         if(step == diameter.length) {
    31             live = false;
    32             step = 0;
    33             return;
    34         }
    35         
    36         Color c = g.getColor();
    37         g.setColor(Color.ORANGE);
    38         
    39         //把不同的圆画出来
    40         g.fillOval(x, y, diameter[step], diameter[step]);
    41         g.setColor(c);
    42         
    43         step ++;
    44     }
    45 }
    View Code

    Wall:

     1 import java.awt.Graphics;
     2 import java.awt.Rectangle;
     3 
     4 //
     5 public class Wall {
     6     int x,y,w,h;
     7     TankClient tc;
     8     public Wall(int x, int y, int w, int h, TankClient tc) {
     9         super();
    10         this.x = x;
    11         this.y = y;
    12         this.w = w;
    13         this.h = h;
    14         this.tc = tc;
    15     }
    16     public void draw(Graphics g){
    17         g.fillRect(x, y, w, h);
    18     }
    19     //碰撞检测
    20     public Rectangle getRect(){
    21         return new Rectangle(x,y,w,h);
    22     }
    23 }
    View Code

    TankClient:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 
      6 public class TankClient extends Frame {
      7     // 设置成常量,方便以后的改动
      8     public static final int GAME_WIDTH = 800;
      9     public static final int GAME_HEIGHT = 600;
     10 
     11     // 将当前的TankClient对象传递给myTank;
     12     // 目的是方便我们在Tank这个类中访问m(炮弹对象)这个成员变量
     13     // 其实就是在Tank类中持有TankClient类对象的一个引用
     14 
     15     // 我们这里new我们自己的坦克
     16     Tank myTank = new Tank(50, 50, true, Tank.Direction.STOP, this);
     17 
     18     Wall w1=new Wall(100, 200,20, 150,this);
     19     Wall w2=new Wall(300, 100,300,20,this);
     20     /*
     21      * //新建敌方坦克,(不再需要这个了) Tank enemyTank=new Tank(100,100,false,this);
     22      */
     23     // 定义爆炸
     24     Explode e = new Explode(70, 70, this);
     25     // 定义一个集合,多个爆炸点
     26     List<Explode> explodes = new ArrayList<Explode>();
     27 
     28     // 使用容器装炮弹
     29     List<Missile> missiles = new ArrayList<Missile>();
     30 
     31     // 用容器来装敌人的Tank
     32     List<Tank> tanks = new ArrayList<Tank>();
     33     // 定义虚拟图片,方便后期的一次性显示
     34     Image offScreenImage = null;
     35 
     36     public void paint(Graphics g) {
     37         // 记录屏幕上的子弹数目
     38         g.drawString("missiles count:" + missiles.size(), 10, 50);
     39         // 添加上方标记栏,记录爆炸次数
     40         g.drawString("explodes count:" + explodes.size(), 10, 70);
     41         // 记录现在屏幕上一共有多少敌方坦克
     42         g.drawString("tanks count:" + tanks.size(), 10, 90);
     43 
     44         // 遍历结合,发出多发炮弹
     45         for (int i = 0; i < missiles.size(); i++) {
     46             Missile m = missiles.get(i);
     47             // 对于每一发炮弹,都可以将tanks集合中的敌方炮弹干掉
     48             m.hitTanks(tanks);
     49             m.hitTank(myTank);
     50             m.hitWall(w1);
     51             m.hitWall(w2);
     52             m.draw(g);
     53         }
     54 
     55         for (int i = 0; i < explodes.size(); i++) {
     56             Explode e = explodes.get(i);
     57             e.draw(g);
     58         }
     59 
     60         for (int i = 0; i < tanks.size(); i++) {
     61             Tank t = tanks.get(i);
     62             t.collidesWithWall(w1);
     63             t.collidesWithWall(w2);
     64             t.collidesWithTanks(tanks);
     65             t.draw(g);
     66         }
     67         // 不改变前景色
     68         myTank.draw(g);
     69         w1.draw(g);
     70         w2.draw(g);
     71     }
     72 
     73     // 刷新操作
     74     public void update(Graphics g) {
     75         if (offScreenImage == null) {
     76             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
     77         }
     78         Graphics gOffScreen = offScreenImage.getGraphics();
     79         Color c = gOffScreen.getColor();
     80         gOffScreen.setColor(Color.GREEN);
     81         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
     82         gOffScreen.setColor(c);
     83         paint(gOffScreen);
     84         g.drawImage(offScreenImage, 0, 0, null);
     85     }
     86 
     87     public void lauchFrame() {
     88 
     89         // 添加多辆坦克
     90         for (int i = 0; i < 10; i++) {
     91             tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Tank.Direction.D,
     92                     this));
     93         }
     94         // this.setLocation(400, 300);
     95         this.setSize(GAME_WIDTH, GAME_HEIGHT);
     96         this.setTitle("TankWar");
     97         this.addWindowListener(new WindowAdapter() {
     98             public void windowClosing(WindowEvent e) {
     99                 System.exit(0);
    100             }
    101         });
    102         this.setResizable(false);
    103         this.setBackground(Color.GREEN);
    104 
    105         this.addKeyListener(new KeyMonitor());
    106 
    107         setVisible(true);
    108 
    109         new Thread(new PaintThread()).start();
    110     }
    111 
    112     public static void main(String[] args) {
    113         TankClient tc = new TankClient();
    114         tc.lauchFrame();
    115     }
    116 
    117     private class PaintThread implements Runnable {
    118 
    119         public void run() {
    120             while (true) {
    121                 repaint();
    122                 try {
    123                     // 为了爆炸效果,改成1000
    124                     Thread.sleep(50);
    125                 } catch (InterruptedException e) {
    126                     e.printStackTrace();
    127                 }
    128             }
    129         }
    130     }
    131 
    132     // 创建键盘时间监听
    133     private class KeyMonitor extends KeyAdapter {
    134 
    135         // 直接调用myTank自己的方法根据相应的按键信息进行移动
    136         public void keyPressed(KeyEvent e) {
    137             myTank.KeyPressed(e);
    138             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
    139             // 而不是一直按照一个方向走下去
    140         }
    141 
    142         public void keyReleased(KeyEvent e) {
    143             myTank.keyReleased(e);
    144         }
    145 
    146     }
    147 }
    View Code

    版本2.2

    功能:超级炮弹
    步骤:
            处理按键A

    具体代码实现:只有Tank类发生了变化

    Tank类 :

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.Random;
      4 
      5 public class Tank {
      6     // 方便后期更改
      7     public static final int XSPEED = 5;
      8     public static final int YSPEED = 5;
      9     // 将坦克的高度和宽度设置为常量
     10     public static final int WIDTH = 30;
     11     public static final int HEIGHT = 30;
     12     TankClient tc;
     13     // 区别是我方坦克还是地方坦克,方便据此进行不同的设置
     14     private boolean good;
     15 
     16     public boolean isGood() {
     17         return good;
     18     }
     19 
     20     public void setGood(boolean good) {
     21         this.good = good;
     22     }
     23 
     24     // 判断坦克生死的变量
     25     private boolean live = true;
     26 
     27     public boolean isLive() {
     28         return live;
     29     }
     30 
     31     public void setLive(boolean live) {
     32         this.live = live;
     33     }
     34 
     35     private int x;
     36     private int y;
     37 
     38     // 记录坦克上一步的位置,防止坦克一碰到wall,就会依附在上面
     39     private int oldx;
     40     private int oldy;
     41 
     42     // 随机数产生器,方便敌方坦克可以任意移动
     43     private static Random r = new Random();
     44     // 添加记录按键状态的布尔量
     45     private boolean bL = false;
     46     private boolean bR = false;
     47     private boolean bU = false;
     48     private boolean bD = false;
     49 
     50     // 添加代表方向的量(使用枚举)
     51     enum Direction {
     52         L, R, U, D, LU, LD, RU, RD, STOP
     53     };
     54 
     55     private Direction dir = Direction.STOP;
     56 
     57     // 定义炮筒的方向,我们想办法将炮筒的方法调整成和坦克移动方向一致;
     58     // 我们这里会用一条直线来表示炮筒:模拟炮筒
     59     // 我们要根据炮筒的方向画直线表示炮筒
     60     Direction ptDir = Direction.D;
     61 
     62     // 为了让敌方坦克在一定方向运动移动时间再自动变换方向
     63     private int step = r.nextInt(12) + 3;
     64 
     65     // 更改构造函数
     66     public Tank(int x, int y, boolean good) {
     67         this.x = x;
     68         this.y = y;
     69         this.oldx = x;
     70         this.oldy = y;
     71         this.good = good;
     72     }
     73 
     74     // 这个位置的构造函数也相应进行了更改
     75     public Tank(int x, int y, boolean good, Direction dir, TankClient tc) {
     76         // 调用那个有两个参数的构造方法
     77         this(x, y, good);
     78         this.dir = dir;
     79         // 在这个位置初始化tc
     80         this.tc = tc;
     81     }
     82 
     83     // Tank对象的draw方法
     84     public void draw(Graphics g) {
     85         if (!live) {
     86             // 如果死亡的是敌方坦克,在tanks集合中去除该坦克
     87             if (!good) {
     88                 tc.tanks.remove(this);
     89             }
     90             // 如果是我方坦克,直接返回
     91             return;
     92         }
     93         Color c = g.getColor();
     94         if (good) {
     95             g.setColor(Color.RED);
     96         } else {
     97             g.setColor(Color.PINK);
     98         }
     99         g.fillOval(x, y, WIDTH, HEIGHT);
    100         g.setColor(c);
    101         // 根据炮筒的方向画直线来表示我们坦克的炮筒
    102         switch (ptDir) {
    103         case L:
    104             // 画直线:四个参数分别代表:坦克中心点的坐标 直线的另一头的的坐标
    105             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    106                     + Tank.HEIGHT / 2);
    107             break;
    108         case R:
    109             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    110                     y + Tank.HEIGHT / 2);
    111 
    112             break;
    113         case U:
    114             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    115                     / 2, y);
    116 
    117             break;
    118         case D:
    119             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    120                     / 2, y + Tank.HEIGHT);
    121 
    122             break;
    123         case LU:
    124             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y);
    125             break;
    126         case LD:
    127             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    128                     + Tank.HEIGHT);
    129 
    130             break;
    131         case RU:
    132             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    133                     y);
    134 
    135             break;
    136         case RD:
    137             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    138                     y + Tank.HEIGHT);
    139 
    140             break;
    141         /*
    142          * case STOP: break;
    143          */
    144         }
    145         move();
    146     }
    147 
    148     public void move() {
    149         // 记录坦克上一步的位置
    150         this.oldx = x;
    151         this.oldy = y;
    152         switch (dir) {
    153         case L:
    154             x -= XSPEED;
    155             break;
    156         case R:
    157             x += XSPEED;
    158             break;
    159         case U:
    160             y -= YSPEED;
    161             break;
    162         case D:
    163             y += YSPEED;
    164             break;
    165         case LU:
    166             x -= XSPEED;
    167             y -= YSPEED;
    168             break;
    169         case LD:
    170             x -= XSPEED;
    171             y += YSPEED;
    172             break;
    173         case RU:
    174             x += XSPEED;
    175             y -= YSPEED;
    176             break;
    177         case RD:
    178             x += XSPEED;
    179             y += YSPEED;
    180             break;
    181 
    182         case STOP:
    183             break;
    184         }
    185         // 如果坦克不是停着的,则将炮筒调整至和坦克移动的方向相同
    186         if (this.dir != Direction.STOP) {
    187             this.ptDir = this.dir;
    188         }
    189         if (x < 0) {
    190             x = 0;
    191         }
    192         // 因为我们的游戏界面有那个missileCount标签,所以在y轴方向不能用y是否<0进行判断
    193         // 否则的话我们的坦克可以从上面出去
    194         if (y < 50) {
    195             y = 50;
    196         }
    197         if (x + Tank.WIDTH > TankClient.GAME_WIDTH) {
    198             x = TankClient.GAME_WIDTH - Tank.WIDTH;
    199         }
    200         if (y + Tank.HEIGHT > TankClient.GAME_HEIGHT) {
    201             y = TankClient.GAME_HEIGHT - Tank.HEIGHT;
    202         }
    203         // 在move方法中判断如果是敌方坦克
    204         if (!good) {
    205             Direction[] dirs = Direction.values();
    206             // 定义敌方坦克的移动
    207             if (step == 0) {
    208                 step = r.nextInt(12) + 3;
    209                 int rn = r.nextInt(dirs.length);
    210                 dir = dirs[rn];
    211             }
    212             // 使得敌方坦克每隔一定时间就变化方向,values:方向枚举转化为数组
    213 
    214             step--;
    215             // 用随机数,使得敌方坦克可以发炮弹,但是不要太猛烈
    216             if (r.nextInt(40) > 38) {
    217                 this.fire();
    218             }
    219         }
    220     }
    221 
    222     public void locateDirection() {
    223         if (bL && !bU && !bR && !bD)
    224             dir = Direction.L;
    225         else if (bL && bU && !bR && !bD)
    226             dir = Direction.LU;
    227         else if (!bL && bU && !bR && !bD)
    228             dir = Direction.U;
    229         else if (!bL && bU && bR && !bD)
    230             dir = Direction.RU;
    231         else if (!bL && !bU && bR && !bD)
    232             dir = Direction.R;
    233         else if (!bL && !bU && bR && bD)
    234             dir = Direction.RD;
    235         else if (!bL && !bU && !bR && bD)
    236             dir = Direction.D;
    237         else if (bL && !bU && !bR && bD)
    238             dir = Direction.LD;
    239         else if (!bL && !bU && !bR && !bD)
    240             dir = Direction.STOP;
    241 
    242     }
    243 
    244     private void stay() {
    245         x = oldx;
    246         y = oldy;
    247     }
    248 
    249     // 坦克自己向哪个方向移动,它自己最清楚;
    250     public void KeyPressed(KeyEvent e) {
    251         // 获得所按下的键所对应的虚拟码:
    252         // Returns the integer keyCode associated with the key in this event
    253         int key = e.getKeyCode();
    254         // 判断不同的按键,指挥坦克的运动方向
    255         switch (key) {
    256         case KeyEvent.VK_LEFT:
    257             bL = true;
    258             break;
    259         case KeyEvent.VK_UP:
    260             bU = true;
    261             break;
    262         case KeyEvent.VK_RIGHT:
    263             bR = true;
    264             break;
    265         case KeyEvent.VK_DOWN:
    266             bD = true;
    267             break;
    268         }
    269         locateDirection();
    270     }
    271 
    272     public void keyReleased(KeyEvent e) {
    273         int key = e.getKeyCode();
    274         // 判断不同的按键,指挥坦克的运动方向
    275         // 哪个键按下了,就把对应方向的布尔类型置为false
    276         switch (key) {
    277         // 为了防止一直按着Ctrl键的时候,炮弹太过于密集
    278         // 因此我们定义在Ctrl键抬起的时候才发炮弹
    279         // 这样炮弹不至于太过密集
    280         case KeyEvent.VK_CONTROL:
    281             fire();
    282             break;
    283         case KeyEvent.VK_LEFT:
    284             bL = false;
    285             break;
    286         case KeyEvent.VK_UP:
    287             bU = false;
    288             break;
    289         case KeyEvent.VK_RIGHT:
    290             bR = false;
    291             break;
    292         case KeyEvent.VK_DOWN:
    293             bD = false;
    294             break;
    295             
    296         //当按键A被按下时,会发出超级炮弹superFire()
    297         case KeyEvent.VK_A:
    298             superFire();
    299             break;
    300         }
    301         // 重新定位一下
    302         locateDirection();
    303     }
    304 
    305     public Missile fire() {
    306         if (!live) {
    307             return null;
    308         }
    309         // 计算子弹的位置,使得子弹从坦克的中间发出来
    310         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    311         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    312         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    313         Missile m = new Missile(x, y, good, ptDir, tc);
    314         // 将新产生的炮弹放置到List容器中
    315         tc.missiles.add(m);
    316         return m;
    317     }
    318     
    319     public Missile fire(Direction dir){
    320         if (!live) {
    321             return null;
    322         }
    323         // 计算子弹的位置,使得子弹从坦克的中间发出来
    324         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    325         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    326         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    327         Missile m = new Missile(x, y, good, ptDir, tc);
    328         // 将新产生的炮弹放置到List容器中
    329         tc.missiles.add(m);
    330         return m;
    331     }
    332 
    333     // 拿到包围坦克的那个方块
    334     public Rectangle getRect() {
    335 
    336         return new Rectangle(x, y, WIDTH, HEIGHT);
    337     }
    338 
    339     public boolean collidesWithWall(Wall w) {
    340 
    341         if (this.live && this.getRect().intersects(w.getRect())) {
    342             this.dir = Direction.STOP;
    343             // 当坦克撞到墙上的时候,停一下,再回到上一步的位置
    344             this.stay();
    345             return true;
    346         }
    347         return false;
    348 
    349     }
    350 
    351     // 坦克和坦克之间的碰撞检测
    352     public boolean collidesWithTanks(java.util.List<Tank> tanks) {
    353         for (int i = 0; i < tanks.size(); i++) {
    354             Tank t = tanks.get(i);
    355             if (this != t) {
    356                 if (this.live && t.isLive()
    357                         && this.getRect().intersects(t.getRect())) {
    358                     this.stay();
    359                     t.stay();
    360                 }
    361             }
    362         }
    363         return bD;
    364     }
    365     
    366     //超级炮弹
    367     private void superFire(){
    368         Direction[] dirs=Direction.values();
    369         for(int i=0;i<8;i++){
    370             //朝八个方向各打一发
    371             fire(dirs[i]);
    372         }
    373     }
    374 }
    View Code

    版本2.3

    功能:主战坦克的生命值(我们的坦克不能一炮就被打死吧,敌方坦克我们可以一炮打死),代码的变化相较于上一个版本Wall类和Explode类没有变化
    步骤:  

            1)加入life变量:在Tank类中private int life=100;并添加get和set方法
            2)在窗口显示生命值:TankClient的paint方法中:g.drawString("tanks life:" + myTank.getLife(), 10, 110);

    Tank:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.Random;
      4 
      5 public class Tank {
      6     // 方便后期更改
      7     public static final int XSPEED = 5;
      8     public static final int YSPEED = 5;
      9     // 将坦克的高度和宽度设置为常量
     10     public static final int WIDTH = 30;
     11     public static final int HEIGHT = 30;
     12     TankClient tc;
     13     // 区别是我方坦克还是地方坦克,方便据此进行不同的设置
     14     private boolean good;
     15     
     16     //坦克的生命值
     17     private int life=100;
     18 
     19     public int getLife() {
     20         return life;
     21     }
     22 
     23     public void setLife(int life) {
     24         this.life = life;
     25     }
     26 
     27     public boolean isGood() {
     28         return good;
     29     }
     30 
     31     public void setGood(boolean good) {
     32         this.good = good;
     33     }
     34 
     35     // 判断坦克生死的变量
     36     private boolean live = true;
     37 
     38     public boolean isLive() {
     39         return live;
     40     }
     41 
     42     public void setLive(boolean live) {
     43         this.live = live;
     44     }
     45 
     46     private int x;
     47     private int y;
     48 
     49     // 记录坦克上一步的位置,防止坦克一碰到wall,就会依附在上面
     50     private int oldx;
     51     private int oldy;
     52 
     53     // 随机数产生器,方便敌方坦克可以任意移动
     54     private static Random r = new Random();
     55     // 添加记录按键状态的布尔量
     56     private boolean bL = false;
     57     private boolean bR = false;
     58     private boolean bU = false;
     59     private boolean bD = false;
     60 
     61     // 添加代表方向的量(使用枚举)
     62     enum Direction {
     63         L, R, U, D, LU, LD, RU, RD, STOP
     64     };
     65 
     66     private Direction dir = Direction.STOP;
     67 
     68     // 定义炮筒的方向,我们想办法将炮筒的方法调整成和坦克移动方向一致;
     69     // 我们这里会用一条直线来表示炮筒:模拟炮筒
     70     // 我们要根据炮筒的方向画直线表示炮筒
     71     Direction ptDir = Direction.D;
     72 
     73     // 为了让敌方坦克在一定方向运动移动时间再自动变换方向
     74     private int step = r.nextInt(12) + 3;
     75 
     76     // 更改构造函数
     77     public Tank(int x, int y, boolean good) {
     78         this.x = x;
     79         this.y = y;
     80         this.oldx = x;
     81         this.oldy = y;
     82         this.good = good;
     83     }
     84 
     85     // 这个位置的构造函数也相应进行了更改
     86     public Tank(int x, int y, boolean good, Direction dir, TankClient tc) {
     87         // 调用那个有两个参数的构造方法
     88         this(x, y, good);
     89         this.dir = dir;
     90         // 在这个位置初始化tc
     91         this.tc = tc;
     92     }
     93 
     94     // Tank对象的draw方法
     95     public void draw(Graphics g) {
     96         if (!live) {
     97             // 如果死亡的是敌方坦克,在tanks集合中去除该坦克
     98             if (!good) {
     99                 tc.tanks.remove(this);
    100             }
    101             // 如果是我方坦克,直接返回
    102             return;
    103         }
    104         Color c = g.getColor();
    105         if (good) {
    106             g.setColor(Color.RED);
    107         } else {
    108             g.setColor(Color.PINK);
    109         }
    110         g.fillOval(x, y, WIDTH, HEIGHT);
    111         g.setColor(c);
    112         // 根据炮筒的方向画直线来表示我们坦克的炮筒
    113         switch (ptDir) {
    114         case L:
    115             // 画直线:四个参数分别代表:坦克中心点的坐标 直线的另一头的的坐标
    116             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    117                     + Tank.HEIGHT / 2);
    118             break;
    119         case R:
    120             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    121                     y + Tank.HEIGHT / 2);
    122 
    123             break;
    124         case U:
    125             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    126                     / 2, y);
    127 
    128             break;
    129         case D:
    130             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    131                     / 2, y + Tank.HEIGHT);
    132 
    133             break;
    134         case LU:
    135             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y);
    136             break;
    137         case LD:
    138             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    139                     + Tank.HEIGHT);
    140 
    141             break;
    142         case RU:
    143             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    144                     y);
    145 
    146             break;
    147         case RD:
    148             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    149                     y + Tank.HEIGHT);
    150 
    151             break;
    152         /*
    153          * case STOP: break;
    154          */
    155         }
    156         move();
    157     }
    158 
    159     public void move() {
    160         // 记录坦克上一步的位置
    161         this.oldx = x;
    162         this.oldy = y;
    163         switch (dir) {
    164         case L:
    165             x -= XSPEED;
    166             break;
    167         case R:
    168             x += XSPEED;
    169             break;
    170         case U:
    171             y -= YSPEED;
    172             break;
    173         case D:
    174             y += YSPEED;
    175             break;
    176         case LU:
    177             x -= XSPEED;
    178             y -= YSPEED;
    179             break;
    180         case LD:
    181             x -= XSPEED;
    182             y += YSPEED;
    183             break;
    184         case RU:
    185             x += XSPEED;
    186             y -= YSPEED;
    187             break;
    188         case RD:
    189             x += XSPEED;
    190             y += YSPEED;
    191             break;
    192 
    193         case STOP:
    194             break;
    195         }
    196         // 如果坦克不是停着的,则将炮筒调整至和坦克移动的方向相同
    197         if (this.dir != Direction.STOP) {
    198             this.ptDir = this.dir;
    199         }
    200         if (x < 0) {
    201             x = 0;
    202         }
    203         // 因为我们的游戏界面有那个missileCount标签,所以在y轴方向不能用y是否<0进行判断
    204         // 否则的话我们的坦克可以从上面出去
    205         if (y < 50) {
    206             y = 50;
    207         }
    208         if (x + Tank.WIDTH > TankClient.GAME_WIDTH) {
    209             x = TankClient.GAME_WIDTH - Tank.WIDTH;
    210         }
    211         if (y + Tank.HEIGHT > TankClient.GAME_HEIGHT) {
    212             y = TankClient.GAME_HEIGHT - Tank.HEIGHT;
    213         }
    214         // 在move方法中判断如果是敌方坦克
    215         if (!good) {
    216             Direction[] dirs = Direction.values();
    217             // 定义敌方坦克的移动
    218             if (step == 0) {
    219                 step = r.nextInt(12) + 3;
    220                 int rn = r.nextInt(dirs.length);
    221                 dir = dirs[rn];
    222             }
    223             // 使得敌方坦克每隔一定时间就变化方向,values:方向枚举转化为数组
    224 
    225             step--;
    226             // 用随机数,使得敌方坦克可以发炮弹,但是不要太猛烈
    227             if (r.nextInt(40) > 38) {
    228                 this.fire();
    229             }
    230         }
    231     }
    232 
    233     public void locateDirection() {
    234         if (bL && !bU && !bR && !bD)
    235             dir = Direction.L;
    236         else if (bL && bU && !bR && !bD)
    237             dir = Direction.LU;
    238         else if (!bL && bU && !bR && !bD)
    239             dir = Direction.U;
    240         else if (!bL && bU && bR && !bD)
    241             dir = Direction.RU;
    242         else if (!bL && !bU && bR && !bD)
    243             dir = Direction.R;
    244         else if (!bL && !bU && bR && bD)
    245             dir = Direction.RD;
    246         else if (!bL && !bU && !bR && bD)
    247             dir = Direction.D;
    248         else if (bL && !bU && !bR && bD)
    249             dir = Direction.LD;
    250         else if (!bL && !bU && !bR && !bD)
    251             dir = Direction.STOP;
    252 
    253     }
    254 
    255     private void stay() {
    256         x = oldx;
    257         y = oldy;
    258     }
    259 
    260     // 坦克自己向哪个方向移动,它自己最清楚;
    261     public void KeyPressed(KeyEvent e) {
    262         // 获得所按下的键所对应的虚拟码:
    263         // Returns the integer keyCode associated with the key in this event
    264         int key = e.getKeyCode();
    265         // 判断不同的按键,指挥坦克的运动方向
    266         switch (key) {
    267         case KeyEvent.VK_LEFT:
    268             bL = true;
    269             break;
    270         case KeyEvent.VK_UP:
    271             bU = true;
    272             break;
    273         case KeyEvent.VK_RIGHT:
    274             bR = true;
    275             break;
    276         case KeyEvent.VK_DOWN:
    277             bD = true;
    278             break;
    279         }
    280         locateDirection();
    281     }
    282 
    283     public void keyReleased(KeyEvent e) {
    284         int key = e.getKeyCode();
    285         // 判断不同的按键,指挥坦克的运动方向
    286         // 哪个键按下了,就把对应方向的布尔类型置为false
    287         switch (key) {
    288         // 为了防止一直按着Ctrl键的时候,炮弹太过于密集
    289         // 因此我们定义在Ctrl键抬起的时候才发炮弹
    290         // 这样炮弹不至于太过密集
    291         case KeyEvent.VK_CONTROL:
    292             fire();
    293             break;
    294         case KeyEvent.VK_LEFT:
    295             bL = false;
    296             break;
    297         case KeyEvent.VK_UP:
    298             bU = false;
    299             break;
    300         case KeyEvent.VK_RIGHT:
    301             bR = false;
    302             break;
    303         case KeyEvent.VK_DOWN:
    304             bD = false;
    305             break;
    306             
    307         //当按键A被按下时,会发出超级炮弹superFire()
    308         case KeyEvent.VK_A:
    309             superFire();
    310             break;
    311         }
    312         // 重新定位一下
    313         locateDirection();
    314     }
    315 
    316     public Missile fire() {
    317         if (!live) {
    318             return null;
    319         }
    320         // 计算子弹的位置,使得子弹从坦克的中间发出来
    321         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    322         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    323         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    324         Missile m = new Missile(x, y, good, ptDir, tc);
    325         // 将新产生的炮弹放置到List容器中
    326         tc.missiles.add(m);
    327         return m;
    328     }
    329     
    330     public Missile fire(Direction dir){
    331         if (!live) {
    332             return null;
    333         }
    334         // 计算子弹的位置,使得子弹从坦克的中间发出来
    335         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    336         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    337         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    338         Missile m = new Missile(x, y, good, ptDir, tc);
    339         // 将新产生的炮弹放置到List容器中
    340         tc.missiles.add(m);
    341         return m;
    342     }
    343 
    344     // 拿到包围坦克的那个方块
    345     public Rectangle getRect() {
    346 
    347         return new Rectangle(x, y, WIDTH, HEIGHT);
    348     }
    349 
    350     public boolean collidesWithWall(Wall w) {
    351 
    352         if (this.live && this.getRect().intersects(w.getRect())) {
    353             this.dir = Direction.STOP;
    354             // 当坦克撞到墙上的时候,停一下,再回到上一步的位置
    355             this.stay();
    356             return true;
    357         }
    358         return false;
    359 
    360     }
    361 
    362     // 坦克和坦克之间的碰撞检测
    363     public boolean collidesWithTanks(java.util.List<Tank> tanks) {
    364         for (int i = 0; i < tanks.size(); i++) {
    365             Tank t = tanks.get(i);
    366             if (this != t) {
    367                 if (this.live && t.isLive()
    368                         && this.getRect().intersects(t.getRect())) {
    369                     this.stay();
    370                     t.stay();
    371                 }
    372             }
    373         }
    374         return bD;
    375     }
    376     
    377     //超级炮弹
    378     private void superFire(){
    379         Direction[] dirs=Direction.values();
    380         for(int i=0;i<8;i++){
    381             //朝八个方向各打一发
    382             fire(dirs[i]);
    383         }
    384     }
    385 }
    View Code

    Missile:

      1 import java.awt.Color;
      2 import java.awt.Graphics;
      3 import java.awt.Rectangle;
      4 import java.util.List;
      5 
      6 public class Missile {
      7     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
      8     public static final int XSPEED = 10;
      9     public static final int YSPEED = 10;
     10     // 将子弹的高度和宽度设置为常量
     11     public static final int WIDTH = 10;
     12     public static final int HEIGHT = 10;
     13     // 炮弹自己的三个属性
     14     int x;
     15     int y;
     16     Tank.Direction dir;
     17 
     18     //同一阵营的的坦克发出的子弹不能伤害自己人
     19     private boolean good;
     20     // 定义一个布尔类型的变量来判断炮弹是否已经消亡
     21     private boolean live = true;
     22     //我们在Missile类中也持有一个TankClient的引用
     23     //在炮弹出界的时候就可以从装炮弹的missiles集合中去除该炮弹,不再对其重画
     24     private TankClient tc;
     25 
     26     public boolean isLive() {
     27         return live;
     28     }
     29 
     30     public Missile(int x, int y, Tank.Direction dir) {
     31         this.x = x;
     32         this.y = y;
     33         this.dir = dir;
     34     }
     35     public Missile(int x,int y,boolean good,Tank.Direction dir,TankClient tc){
     36         this(x, y, dir);
     37         this.good=good;
     38         this.tc=tc;
     39     }
     40 
     41     // 炮弹自己的draw方法
     42     public void draw(Graphics g) {
     43         //炮弹消亡就不需要再画出来了
     44         if(!live){
     45             tc.missiles.remove(this);
     46             return;
     47         }
     48         Color c = g.getColor();
     49         g.setColor(Color.BLACK);
     50         // 炮弹形状不要比坦克大,这里设置成10,10;
     51         g.fillOval(x, y, WIDTH, HEIGHT);
     52         g.setColor(c);
     53         move();
     54     }
     55 
     56     public void move() {
     57         switch (dir) {
     58         case L:
     59             x -= XSPEED;
     60             break;
     61         case R:
     62             x += XSPEED;
     63             break;
     64         case U:
     65             y -= YSPEED;
     66             break;
     67         case D:
     68             y += YSPEED;
     69             break;
     70         case LU:
     71             x -= XSPEED;
     72             y -= YSPEED;
     73             break;
     74         case LD:
     75             x -= XSPEED;
     76             y += YSPEED;
     77             break;
     78         case RU:
     79             x += XSPEED;
     80             y -= YSPEED;
     81             break;
     82         case RD:
     83             x += XSPEED;
     84             y += YSPEED;
     85             break;
     86         // 炮弹就没有STOP这个枚举类型的值了
     87         /*
     88          * case STOP: break;
     89          */
     90         }
     91         // 判断炮弹出边界则消亡
     92         // 注意x,y只有正数值,x向右递增,y向下递增
     93         if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
     94                 || y > TankClient.GAME_HEIGHT) {
     95             live = false;
     96         }
     97     }
     98     public boolean hitTank(Tank t){
     99         //炮弹的方框和坦克的方框碰在一起了并且坦克是存活着的,后面的判断我们是一伙的我就不打你了
    100         if(this.live&&this.getRect().intersects(t.getRect())&&t.isLive()&&this.good!=t.isGood()){
    101             if(t.isGood()){
    102                 t.setLife(t.getLife()-20);
    103                 if(t.getLife()<=0){
    104                     t.setLive(false);
    105                 }
    106             }else {
    107                 t.setLive(false);
    108             }
    109             this.live=false;
    110             
    111             //炮弹击中坦克,发生爆炸
    112             Explode e=new Explode(x, y, tc);
    113             tc.explodes.add(e);
    114             return true;
    115         }
    116         return false;
    117     }
    118     //碰撞检测类Rectangle
    119     //拿到包围在炮弹周围的小方块
    120     public Rectangle getRect(){
    121         return new Rectangle(x,y,WIDTH,HEIGHT);
    122     }
    123     
    124     //添加hitTanks方法
    125     public boolean hitTanks(List<Tank> tanks){
    126         for(int i=0;i<tanks.size();i++){
    127             if(hitTank(tanks.get(i))){
    128                 return true;
    129             }
    130         }
    131         return false;
    132         
    133     }
    134     public boolean hitWall(Wall w){
    135         if(this.live&&this.getRect().intersects(w.getRect())){
    136             this.live=false;
    137             return true;
    138         }
    139         return false;
    140     }
    141 }
    View Code

    TankClient:

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 
      6 public class TankClient extends Frame {
      7     // 设置成常量,方便以后的改动
      8     public static final int GAME_WIDTH = 800;
      9     public static final int GAME_HEIGHT = 600;
     10 
     11     // 将当前的TankClient对象传递给myTank;
     12     // 目的是方便我们在Tank这个类中访问m(炮弹对象)这个成员变量
     13     // 其实就是在Tank类中持有TankClient类对象的一个引用
     14 
     15     // 我们这里new我们自己的坦克
     16     Tank myTank = new Tank(50, 50, true, Tank.Direction.STOP, this);
     17 
     18     Wall w1=new Wall(100, 200,20, 150,this);
     19     Wall w2=new Wall(300, 100,300,20,this);
     20     /*
     21      * //新建敌方坦克,(不再需要这个了) Tank enemyTank=new Tank(100,100,false,this);
     22      */
     23     // 定义爆炸
     24     Explode e = new Explode(70, 70, this);
     25     // 定义一个集合,多个爆炸点
     26     List<Explode> explodes = new ArrayList<Explode>();
     27 
     28     // 使用容器装炮弹
     29     List<Missile> missiles = new ArrayList<Missile>();
     30 
     31     // 用容器来装敌人的Tank
     32     List<Tank> tanks = new ArrayList<Tank>();
     33     // 定义虚拟图片,方便后期的一次性显示
     34     Image offScreenImage = null;
     35 
     36     public void paint(Graphics g) {
     37         // 记录屏幕上的子弹数目
     38         g.drawString("missiles count:" + missiles.size(), 10, 50);
     39         // 添加上方标记栏,记录爆炸次数
     40         g.drawString("explodes count:" + explodes.size(), 10, 70);
     41         // 记录现在屏幕上一共有多少敌方坦克
     42         g.drawString("tanks count:" + tanks.size(), 10, 90);
     43         //我们坦克的生命值
     44         g.drawString("tanks life:" + myTank.getLife(), 10, 110);
     45 
     46         // 遍历结合,发出多发炮弹
     47         for (int i = 0; i < missiles.size(); i++) {
     48             Missile m = missiles.get(i);
     49             // 对于每一发炮弹,都可以将tanks集合中的敌方炮弹干掉
     50             m.hitTanks(tanks);
     51             m.hitTank(myTank);
     52             m.hitWall(w1);
     53             m.hitWall(w2);
     54             m.draw(g);
     55         }
     56 
     57         for (int i = 0; i < explodes.size(); i++) {
     58             Explode e = explodes.get(i);
     59             e.draw(g);
     60         }
     61 
     62         for (int i = 0; i < tanks.size(); i++) {
     63             Tank t = tanks.get(i);
     64             t.collidesWithWall(w1);
     65             t.collidesWithWall(w2);
     66             t.collidesWithTanks(tanks);
     67             t.draw(g);
     68         }
     69         // 不改变前景色
     70         myTank.draw(g);
     71         w1.draw(g);
     72         w2.draw(g);
     73     }
     74 
     75     // 刷新操作
     76     public void update(Graphics g) {
     77         if (offScreenImage == null) {
     78             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
     79         }
     80         Graphics gOffScreen = offScreenImage.getGraphics();
     81         Color c = gOffScreen.getColor();
     82         gOffScreen.setColor(Color.GREEN);
     83         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
     84         gOffScreen.setColor(c);
     85         paint(gOffScreen);
     86         g.drawImage(offScreenImage, 0, 0, null);
     87     }
     88 
     89     public void lauchFrame() {
     90 
     91         // 添加多辆坦克
     92         for (int i = 0; i < 10; i++) {
     93             tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Tank.Direction.D,
     94                     this));
     95         }
     96         // this.setLocation(400, 300);
     97         this.setSize(GAME_WIDTH, GAME_HEIGHT);
     98         this.setTitle("TankWar");
     99         this.addWindowListener(new WindowAdapter() {
    100             public void windowClosing(WindowEvent e) {
    101                 System.exit(0);
    102             }
    103         });
    104         this.setResizable(false);
    105         this.setBackground(Color.GREEN);
    106 
    107         this.addKeyListener(new KeyMonitor());
    108 
    109         setVisible(true);
    110 
    111         new Thread(new PaintThread()).start();
    112     }
    113 
    114     public static void main(String[] args) {
    115         TankClient tc = new TankClient();
    116         tc.lauchFrame();
    117     }
    118 
    119     private class PaintThread implements Runnable {
    120 
    121         public void run() {
    122             while (true) {
    123                 repaint();
    124                 try {
    125                     // 为了爆炸效果,改成1000
    126                     Thread.sleep(50);
    127                 } catch (InterruptedException e) {
    128                     e.printStackTrace();
    129                 }
    130             }
    131         }
    132     }
    133 
    134     // 创建键盘时间监听
    135     private class KeyMonitor extends KeyAdapter {
    136 
    137         // 直接调用myTank自己的方法根据相应的按键信息进行移动
    138         public void keyPressed(KeyEvent e) {
    139             myTank.KeyPressed(e);
    140             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
    141             // 而不是一直按照一个方向走下去
    142         }
    143 
    144         public void keyReleased(KeyEvent e) {
    145             myTank.keyReleased(e);
    146         }
    147 
    148     }
    149 }
    View Code

    版本2.4

    功能:图形化表示主战坦克的生命值
    步骤:
            根据不同的life值进行的不同的显示:在Tank类中我们添加一个内部类BloodBar(血条),编写一个函数

     1 private class BloodBar{
     2         public void draw(Graphics g){
     3             Color c=g.getColor();
     4             g.setColor(Color.RED);
     5             //空心方块
     6             g.drawRect(x, y-10, WIDTH, 10);
     7             
     8             //根据我方坦克的生命值来画代表血量的实体快的大小
     9             int w=WIDTH*life/100;
    10             
    11             g.fillRect(x, y-10, w,10);
    12             g.setColor(c);
    13         }
    14     }

    具体代码实现:只有Tank类中的代码发生了变化

      1 import java.awt.*;
      2 import java.awt.event.*;
      3 import java.util.Random;
      4 
      5 public class Tank {
      6     // 方便后期更改
      7     public static final int XSPEED = 5;
      8     public static final int YSPEED = 5;
      9     // 将坦克的高度和宽度设置为常量
     10     public static final int WIDTH = 30;
     11     public static final int HEIGHT = 30;
     12     TankClient tc;
     13     // 区别是我方坦克还是地方坦克,方便据此进行不同的设置
     14     private boolean good;
     15     
     16     private BloodBar bb=new BloodBar();
     17     //坦克的生命值
     18     private int life=100;
     19 
     20     public int getLife() {
     21         return life;
     22     }
     23 
     24     public void setLife(int life) {
     25         this.life = life;
     26     }
     27 
     28     public boolean isGood() {
     29         return good;
     30     }
     31 
     32     public void setGood(boolean good) {
     33         this.good = good;
     34     }
     35 
     36     // 判断坦克生死的变量
     37     private boolean live = true;
     38 
     39     public boolean isLive() {
     40         return live;
     41     }
     42 
     43     public void setLive(boolean live) {
     44         this.live = live;
     45     }
     46 
     47     private int x;
     48     private int y;
     49 
     50     // 记录坦克上一步的位置,防止坦克一碰到wall,就会依附在上面
     51     private int oldx;
     52     private int oldy;
     53 
     54     // 随机数产生器,方便敌方坦克可以任意移动
     55     private static Random r = new Random();
     56     // 添加记录按键状态的布尔量
     57     private boolean bL = false;
     58     private boolean bR = false;
     59     private boolean bU = false;
     60     private boolean bD = false;
     61 
     62     // 添加代表方向的量(使用枚举)
     63     enum Direction {
     64         L, R, U, D, LU, LD, RU, RD, STOP
     65     };
     66 
     67     private Direction dir = Direction.STOP;
     68 
     69     // 定义炮筒的方向,我们想办法将炮筒的方法调整成和坦克移动方向一致;
     70     // 我们这里会用一条直线来表示炮筒:模拟炮筒
     71     // 我们要根据炮筒的方向画直线表示炮筒
     72     Direction ptDir = Direction.D;
     73 
     74     // 为了让敌方坦克在一定方向运动移动时间再自动变换方向
     75     private int step = r.nextInt(12) + 3;
     76 
     77     // 更改构造函数
     78     public Tank(int x, int y, boolean good) {
     79         this.x = x;
     80         this.y = y;
     81         this.oldx = x;
     82         this.oldy = y;
     83         this.good = good;
     84     }
     85 
     86     // 这个位置的构造函数也相应进行了更改
     87     public Tank(int x, int y, boolean good, Direction dir, TankClient tc) {
     88         // 调用那个有两个参数的构造方法
     89         this(x, y, good);
     90         this.dir = dir;
     91         // 在这个位置初始化tc
     92         this.tc = tc;
     93     }
     94 
     95     // Tank对象的draw方法
     96     public void draw(Graphics g) {
     97         if (!live) {
     98             // 如果死亡的是敌方坦克,在tanks集合中去除该坦克
     99             if (!good) {
    100                 tc.tanks.remove(this);
    101             }
    102             // 如果是我方坦克,直接返回
    103             return;
    104         }
    105         Color c = g.getColor();
    106         if (good) {
    107             g.setColor(Color.YELLOW);
    108         } else {
    109             g.setColor(Color.PINK);
    110         }
    111         g.fillOval(x, y, WIDTH, HEIGHT);
    112         g.setColor(c);
    113         //判断一下,我方坦克才有血条显示
    114         if(good){
    115             
    116             bb.draw(g);
    117         }
    118         // 根据炮筒的方向画直线来表示我们坦克的炮筒
    119         switch (ptDir) {
    120         case L:
    121             // 画直线:四个参数分别代表:坦克中心点的坐标 直线的另一头的的坐标
    122             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    123                     + Tank.HEIGHT / 2);
    124             break;
    125         case R:
    126             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    127                     y + Tank.HEIGHT / 2);
    128 
    129             break;
    130         case U:
    131             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    132                     / 2, y);
    133 
    134             break;
    135         case D:
    136             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
    137                     / 2, y + Tank.HEIGHT);
    138 
    139             break;
    140         case LU:
    141             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y);
    142             break;
    143         case LD:
    144             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
    145                     + Tank.HEIGHT);
    146 
    147             break;
    148         case RU:
    149             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    150                     y);
    151 
    152             break;
    153         case RD:
    154             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
    155                     y + Tank.HEIGHT);
    156 
    157             break;
    158         /*
    159          * case STOP: break;
    160          */
    161         }
    162         move();
    163     }
    164 
    165     public void move() {
    166         // 记录坦克上一步的位置
    167         this.oldx = x;
    168         this.oldy = y;
    169         switch (dir) {
    170         case L:
    171             x -= XSPEED;
    172             break;
    173         case R:
    174             x += XSPEED;
    175             break;
    176         case U:
    177             y -= YSPEED;
    178             break;
    179         case D:
    180             y += YSPEED;
    181             break;
    182         case LU:
    183             x -= XSPEED;
    184             y -= YSPEED;
    185             break;
    186         case LD:
    187             x -= XSPEED;
    188             y += YSPEED;
    189             break;
    190         case RU:
    191             x += XSPEED;
    192             y -= YSPEED;
    193             break;
    194         case RD:
    195             x += XSPEED;
    196             y += YSPEED;
    197             break;
    198 
    199         case STOP:
    200             break;
    201         }
    202         // 如果坦克不是停着的,则将炮筒调整至和坦克移动的方向相同
    203         if (this.dir != Direction.STOP) {
    204             this.ptDir = this.dir;
    205         }
    206         if (x < 0) {
    207             x = 0;
    208         }
    209         // 因为我们的游戏界面有那个missileCount标签,所以在y轴方向不能用y是否<0进行判断
    210         // 否则的话我们的坦克可以从上面出去
    211         if (y < 50) {
    212             y = 50;
    213         }
    214         if (x + Tank.WIDTH > TankClient.GAME_WIDTH) {
    215             x = TankClient.GAME_WIDTH - Tank.WIDTH;
    216         }
    217         if (y + Tank.HEIGHT > TankClient.GAME_HEIGHT) {
    218             y = TankClient.GAME_HEIGHT - Tank.HEIGHT;
    219         }
    220         // 在move方法中判断如果是敌方坦克
    221         if (!good) {
    222             Direction[] dirs = Direction.values();
    223             // 定义敌方坦克的移动
    224             if (step == 0) {
    225                 step = r.nextInt(12) + 3;
    226                 int rn = r.nextInt(dirs.length);
    227                 dir = dirs[rn];
    228             }
    229             // 使得敌方坦克每隔一定时间就变化方向,values:方向枚举转化为数组
    230 
    231             step--;
    232             // 用随机数,使得敌方坦克可以发炮弹,但是不要太猛烈
    233             if (r.nextInt(40) > 38) {
    234                 this.fire();
    235             }
    236         }
    237     }
    238 
    239     public void locateDirection() {
    240         if (bL && !bU && !bR && !bD)
    241             dir = Direction.L;
    242         else if (bL && bU && !bR && !bD)
    243             dir = Direction.LU;
    244         else if (!bL && bU && !bR && !bD)
    245             dir = Direction.U;
    246         else if (!bL && bU && bR && !bD)
    247             dir = Direction.RU;
    248         else if (!bL && !bU && bR && !bD)
    249             dir = Direction.R;
    250         else if (!bL && !bU && bR && bD)
    251             dir = Direction.RD;
    252         else if (!bL && !bU && !bR && bD)
    253             dir = Direction.D;
    254         else if (bL && !bU && !bR && bD)
    255             dir = Direction.LD;
    256         else if (!bL && !bU && !bR && !bD)
    257             dir = Direction.STOP;
    258 
    259     }
    260 
    261     private void stay() {
    262         x = oldx;
    263         y = oldy;
    264     }
    265 
    266     // 坦克自己向哪个方向移动,它自己最清楚;
    267     public void KeyPressed(KeyEvent e) {
    268         // 获得所按下的键所对应的虚拟码:
    269         // Returns the integer keyCode associated with the key in this event
    270         int key = e.getKeyCode();
    271         // 判断不同的按键,指挥坦克的运动方向
    272         switch (key) {
    273         case KeyEvent.VK_LEFT:
    274             bL = true;
    275             break;
    276         case KeyEvent.VK_UP:
    277             bU = true;
    278             break;
    279         case KeyEvent.VK_RIGHT:
    280             bR = true;
    281             break;
    282         case KeyEvent.VK_DOWN:
    283             bD = true;
    284             break;
    285         }
    286         locateDirection();
    287     }
    288 
    289     public void keyReleased(KeyEvent e) {
    290         int key = e.getKeyCode();
    291         // 判断不同的按键,指挥坦克的运动方向
    292         // 哪个键按下了,就把对应方向的布尔类型置为false
    293         switch (key) {
    294         // 为了防止一直按着Ctrl键的时候,炮弹太过于密集
    295         // 因此我们定义在Ctrl键抬起的时候才发炮弹
    296         // 这样炮弹不至于太过密集
    297         case KeyEvent.VK_CONTROL:
    298             fire();
    299             break;
    300         case KeyEvent.VK_LEFT:
    301             bL = false;
    302             break;
    303         case KeyEvent.VK_UP:
    304             bU = false;
    305             break;
    306         case KeyEvent.VK_RIGHT:
    307             bR = false;
    308             break;
    309         case KeyEvent.VK_DOWN:
    310             bD = false;
    311             break;
    312             
    313         //当按键A被按下时,会发出超级炮弹superFire()
    314         case KeyEvent.VK_A:
    315             superFire();
    316             break;
    317         }
    318         // 重新定位一下
    319         locateDirection();
    320     }
    321 
    322     public Missile fire() {
    323         if (!live) {
    324             return null;
    325         }
    326         // 计算子弹的位置,使得子弹从坦克的中间发出来
    327         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    328         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    329         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    330         Missile m = new Missile(x, y, good, ptDir, tc);
    331         // 将新产生的炮弹放置到List容器中
    332         tc.missiles.add(m);
    333         return m;
    334     }
    335     
    336     public Missile fire(Direction dir){
    337         if (!live) {
    338             return null;
    339         }
    340         // 计算子弹的位置,使得子弹从坦克的中间发出来
    341         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    342         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    343         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    344         Missile m = new Missile(x, y, good, ptDir, tc);
    345         // 将新产生的炮弹放置到List容器中
    346         tc.missiles.add(m);
    347         return m;
    348     }
    349 
    350     // 拿到包围坦克的那个方块
    351     public Rectangle getRect() {
    352 
    353         return new Rectangle(x, y, WIDTH, HEIGHT);
    354     }
    355 
    356     public boolean collidesWithWall(Wall w) {
    357 
    358         if (this.live && this.getRect().intersects(w.getRect())) {
    359             this.dir = Direction.STOP;
    360             // 当坦克撞到墙上的时候,停一下,再回到上一步的位置
    361             this.stay();
    362             return true;
    363         }
    364         return false;
    365 
    366     }
    367 
    368     // 坦克和坦克之间的碰撞检测
    369     public boolean collidesWithTanks(java.util.List<Tank> tanks) {
    370         for (int i = 0; i < tanks.size(); i++) {
    371             Tank t = tanks.get(i);
    372             if (this != t) {
    373                 if (this.live && t.isLive()
    374                         && this.getRect().intersects(t.getRect())) {
    375                     this.stay();
    376                     t.stay();
    377                 }
    378             }
    379         }
    380         return bD;
    381     }
    382     
    383     //超级炮弹
    384     private void superFire(){
    385         Direction[] dirs=Direction.values();
    386         for(int i=0;i<8;i++){
    387             //朝八个方向各打一发
    388             fire(dirs[i]);
    389         }
    390     }
    391     //内部类定义坦克的图形化血量显示
    392     private class BloodBar{
    393         public void draw(Graphics g){
    394             Color c=g.getColor();
    395             g.setColor(Color.RED);
    396             //空心方块
    397             g.drawRect(x, y-10, WIDTH, 10);
    398             
    399             //根据我方坦克的生命值来画代表血量的实体快的大小
    400             int w=WIDTH*life/100;
    401             
    402             g.fillRect(x, y-10, w,10);
    403             g.setColor(c);
    404         }
    405     }
    406 }
    View Code

    未完待续。。。。。。

  • 相关阅读:
    Python字符串学习相关问题
    Python函数学习遇到的问题
    How to configure ODBC DSN to access local DB2 for Windows
    如何在Eclipse CDT中编译含有多个main函数的项目
    MakeFile 文件的作用
    云技术:负载均衡SLB
    云技术:弹性计算ECS
    《老罗的Android之旅》导读PPT
    服务器集群负载均衡技术
    杭电ACM 1004题
  • 原文地址:https://www.cnblogs.com/ysw-go/p/5526783.html
Copyright © 2011-2022 走看看