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

    版本2.5

    功能:添加“血块”
    步骤:
            1)添加blood类
            2)添加必要的方法:eat方法等
            3)让blood对象固定轨迹运动, 并在一定时间后消失

    具体代码实现:

    新增的blood类:

     1 import java.awt.Color;
     2 import java.awt.Graphics;
     3 import java.awt.Rectangle;
     4 
     5 //模拟血块,坦克吃了可以补血
     6 public class Blood {
     7     int x, y, w, h;
     8 
     9     TankClient tc;
    10 
    11     private boolean live = true;
    12 
    13     public void setLive(boolean live) {
    14         this.live = live;
    15     }
    16 
    17     public boolean isLive() {
    18         return live;
    19     }
    20 
    21     int step = 0;
    22 
    23     // 定义血块的位置,是不断变化的
    24     private int position[][] = { { 350, 300 }, { 360, 300 }, { 375, 275 },
    25             { 400, 200 }, { 360, 270 }, { 365, 290 }, { 340, 280 } };
    26 
    27     public Blood() {
    28         x = position[0][0];
    29         y = position[0][1];
    30         w = h = 15;
    31     }
    32 
    33     //血块的draw方法
    34     public void draw(Graphics g) {
    35         if (!live) {
    36             return;
    37         }
    38         Color c = g.getColor();
    39         g.setColor(Color.MAGENTA);
    40         g.fillRect(x, y, w, h);
    41         g.setColor(c);
    42         move();
    43     }
    44 
    45     private void move() {
    46         step++;
    47         if (step == position.length) {
    48             step = 0;
    49         }
    50         x = position[step][0];
    51         y = position[step][1];
    52     }
    53 
    54     public Rectangle getRect() {
    55         return new Rectangle(x, y, w, h);
    56     }
    57 }

    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

    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 
     36     public Missile(int x, int y, boolean good, Tank.Direction dir, TankClient tc) {
     37         this(x, y, dir);
     38         this.good = good;
     39         this.tc = tc;
     40     }
     41 
     42     // 炮弹自己的draw方法
     43     public void draw(Graphics g) {
     44         // 炮弹消亡就不需要再画出来了
     45         if (!live) {
     46             tc.missiles.remove(this);
     47             return;
     48         }
     49         Color c = g.getColor();
     50         g.setColor(Color.BLACK);
     51         // 炮弹形状不要比坦克大,这里设置成10,10;
     52         g.fillOval(x, y, WIDTH, HEIGHT);
     53         g.setColor(c);
     54         move();
     55     }
     56 
     57     public void move() {
     58         switch (dir) {
     59         case L:
     60             x -= XSPEED;
     61             break;
     62         case R:
     63             x += XSPEED;
     64             break;
     65         case U:
     66             y -= YSPEED;
     67             break;
     68         case D:
     69             y += YSPEED;
     70             break;
     71         case LU:
     72             x -= XSPEED;
     73             y -= YSPEED;
     74             break;
     75         case LD:
     76             x -= XSPEED;
     77             y += YSPEED;
     78             break;
     79         case RU:
     80             x += XSPEED;
     81             y -= YSPEED;
     82             break;
     83         case RD:
     84             x += XSPEED;
     85             y += YSPEED;
     86             break;
     87         // 炮弹就没有STOP这个枚举类型的值了
     88         /*
     89          * case STOP: break;
     90          */
     91         }
     92         // 判断炮弹出边界则消亡
     93         // 注意x,y只有正数值,x向右递增,y向下递增
     94         if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
     95                 || y > TankClient.GAME_HEIGHT) {
     96             live = false;
     97         }
     98     }
     99 
    100     public boolean hitTank(Tank t) {
    101         // 炮弹的方框和坦克的方框碰在一起了并且坦克是存活着的,后面的判断我们是一伙的我就不打你了
    102         if (this.live && this.getRect().intersects(t.getRect()) && t.isLive()
    103                 && this.good != t.isGood()) {
    104             if (t.isGood()) {
    105                 t.setLife(t.getLife() - 20);
    106                 if (t.getLife() <= 0) {
    107                     t.setLive(false);
    108                 }
    109             } else {
    110                 t.setLive(false);
    111             }
    112             this.live = false;
    113 
    114             // 炮弹击中坦克,发生爆炸
    115             Explode e = new Explode(x, y, tc);
    116             tc.explodes.add(e);
    117             return true;
    118         }
    119         return false;
    120     }
    121 
    122     // 碰撞检测类Rectangle
    123     // 拿到包围在炮弹周围的小方块
    124     public Rectangle getRect() {
    125         return new Rectangle(x, y, WIDTH, HEIGHT);
    126     }
    127 
    128     // 添加hitTanks方法
    129     public boolean hitTanks(List<Tank> tanks) {
    130         for (int i = 0; i < tanks.size(); i++) {
    131             if (hitTank(tanks.get(i))) {
    132                 return true;
    133             }
    134         }
    135         return false;
    136 
    137     }
    138 
    139     public boolean hitWall(Wall w) {
    140         if (this.live && this.getRect().intersects(w.getRect())) {
    141             this.live = false;
    142             return true;
    143         }
    144         return false;
    145     }
    146 }
    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 
     9     public Wall(int x, int y, int w, int h, TankClient tc) {
    10         super();
    11         this.x = x;
    12         this.y = y;
    13         this.w = w;
    14         this.h = h;
    15         this.tc = tc;
    16     }
    17 
    18     public void draw(Graphics g) {
    19         g.fillRect(x, y, w, h);
    20     }
    21 
    22     // 碰撞检测
    23     public Rectangle getRect() {
    24         return new Rectangle(x, y, w, h);
    25     }
    26 }
    View Code

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

    版本2.6
    功能:修正2.5版本中bug,实现敌人死光了能够重新生成一些坦克加入战斗,我军死掉了F2开始新的游戏

    具体代码实现:主要更改的是tank类、Tankclient类中的代码

    Tank:

    在KeyPressed方法中的switch语句中添加:

    1 case KeyEvent.VK_F2:
    2             if(!this.live){
    3                 this.live=true;
    4                 this.life=100;
    5             }
    6             break;

    完整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_F2:
    274             if(!this.live){
    275                 this.live=true;
    276                 this.life=100;
    277             }
    278             break;
    279         case KeyEvent.VK_LEFT:
    280             bL = true;
    281             break;
    282         case KeyEvent.VK_UP:
    283             bU = true;
    284             break;
    285         case KeyEvent.VK_RIGHT:
    286             bR = true;
    287             break;
    288         case KeyEvent.VK_DOWN:
    289             bD = true;
    290             break;
    291         }
    292         locateDirection();
    293     }
    294 
    295     public void keyReleased(KeyEvent e) {
    296         int key = e.getKeyCode();
    297         // 判断不同的按键,指挥坦克的运动方向
    298         // 哪个键按下了,就把对应方向的布尔类型置为false
    299         switch (key) {
    300         // 为了防止一直按着Ctrl键的时候,炮弹太过于密集
    301         // 因此我们定义在Ctrl键抬起的时候才发炮弹
    302         // 这样炮弹不至于太过密集
    303         case KeyEvent.VK_CONTROL:
    304             fire();
    305             break;
    306         case KeyEvent.VK_LEFT:
    307             bL = false;
    308             break;
    309         case KeyEvent.VK_UP:
    310             bU = false;
    311             break;
    312         case KeyEvent.VK_RIGHT:
    313             bR = false;
    314             break;
    315         case KeyEvent.VK_DOWN:
    316             bD = false;
    317             break;
    318 
    319         // 当按键A被按下时,会发出超级炮弹superFire()
    320         case KeyEvent.VK_A:
    321             superFire();
    322             break;
    323         }
    324         // 重新定位一下
    325         locateDirection();
    326     }
    327 
    328     public Missile fire() {
    329         if (!live) {
    330             return null;
    331         }
    332         // 计算子弹的位置,使得子弹从坦克的中间发出来
    333         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    334         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    335         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    336         Missile m = new Missile(x, y, good, ptDir, tc);
    337         // 将新产生的炮弹放置到List容器中
    338         tc.missiles.add(m);
    339         return m;
    340     }
    341 
    342     public Missile fire(Direction dir) {
    343         if (!live) {
    344             return null;
    345         }
    346         // 计算子弹的位置,使得子弹从坦克的中间发出来
    347         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
    348         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
    349         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
    350         Missile m = new Missile(x, y, good, ptDir, tc);
    351         // 将新产生的炮弹放置到List容器中
    352         tc.missiles.add(m);
    353         return m;
    354     }
    355 
    356     // 拿到包围坦克的那个方块
    357     public Rectangle getRect() {
    358 
    359         return new Rectangle(x, y, WIDTH, HEIGHT);
    360     }
    361 
    362     public boolean collidesWithWall(Wall w) {
    363 
    364         if (this.live && this.getRect().intersects(w.getRect())) {
    365             this.dir = Direction.STOP;
    366             // 当坦克撞到墙上的时候,停一下,再回到上一步的位置
    367             this.stay();
    368             return true;
    369         }
    370         return false;
    371 
    372     }
    373 
    374     // 坦克和坦克之间的碰撞检测
    375     public boolean collidesWithTanks(java.util.List<Tank> tanks) {
    376         for (int i = 0; i < tanks.size(); i++) {
    377             Tank t = tanks.get(i);
    378             if (this != t) {
    379                 if (this.live && t.isLive()
    380                         && this.getRect().intersects(t.getRect())) {
    381                     this.stay();
    382                     t.stay();
    383                 }
    384             }
    385         }
    386         return bD;
    387     }
    388 
    389     // 超级炮弹
    390     private void superFire() {
    391         Direction[] dirs = Direction.values();
    392         for (int i = 0; i < 8; i++) {
    393             // 朝八个方向各打一发
    394             fire(dirs[i]);
    395         }
    396     }
    397 
    398     // 内部类定义坦克的图形化血量显示
    399     private class BloodBar {
    400         public void draw(Graphics g) {
    401             Color c = g.getColor();
    402             g.setColor(Color.RED);
    403             // 空心方块
    404             g.drawRect(x, y - 10, WIDTH, 10);
    405 
    406             // 根据我方坦克的生命值来画代表血量的实体快的大小
    407             int w = WIDTH * life / 100;
    408 
    409             g.fillRect(x, y - 10, w, 10);
    410             g.setColor(c);
    411         }
    412     }
    413 
    414     // 坦克吃掉血块的函数
    415     public boolean eat(Blood b) {
    416         if (this.live && b.isLive() && this.getRect().intersects(b.getRect())) {
    417             this.life = 100;
    418             b.setLive(false);
    419             return true;
    420         }
    421         return false;
    422     }
    423 }
    View Code

    Tankclient:

    在paint方法中添加判断坦克坦克是否全部死掉

    1 //判断敌方坦克是否死光,死光则重新开始游戏
    2         //我方死关了则F2重新开始游戏
    3         if(tanks.size()<=0){
    4             for (int i = 0; i < 5; i++) {
    5                 tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Tank.Direction.D,
    6                         this));
    7             }
    8         }
      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     Blood b = new Blood();
     37 
     38     public void paint(Graphics g) {
     39         // 记录屏幕上的子弹数目
     40         g.drawString("missiles count:" + missiles.size(), 10, 50);
     41         // 添加上方标记栏,记录爆炸次数
     42         g.drawString("explodes count:" + explodes.size(), 10, 70);
     43         // 记录现在屏幕上一共有多少敌方坦克
     44         g.drawString("tanks count:" + tanks.size(), 10, 90);
     45         // 我们坦克的生命值
     46         g.drawString("tanks life:" + myTank.getLife(), 10, 110);
     47 
     48         //判断敌方坦克是否死光,死光则重新开始游戏
     49         //我方死关了则F2重新开始游戏
     50         if(tanks.size()<=0){
     51             for (int i = 0; i < 5; i++) {
     52                 tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Tank.Direction.D,
     53                         this));
     54             }
     55         }
     56         // 遍历结合,发出多发炮弹
     57         for (int i = 0; i < missiles.size(); i++) {
     58             Missile m = missiles.get(i);
     59             // 对于每一发炮弹,都可以将tanks集合中的敌方炮弹干掉
     60             m.hitTanks(tanks);
     61             m.hitTank(myTank);
     62             m.hitWall(w1);
     63             m.hitWall(w2);
     64             m.draw(g);
     65         }
     66 
     67         for (int i = 0; i < explodes.size(); i++) {
     68             Explode e = explodes.get(i);
     69             e.draw(g);
     70         }
     71 
     72         for (int i = 0; i < tanks.size(); i++) {
     73             Tank t = tanks.get(i);
     74             t.collidesWithWall(w1);
     75             t.collidesWithWall(w2);
     76             t.collidesWithTanks(tanks);
     77             t.draw(g);
     78         }
     79         // 不改变前景色
     80         myTank.draw(g);
     81         myTank.eat(b);
     82         w1.draw(g);
     83         w2.draw(g);
     84         b.draw(g);
     85     }
     86 
     87     // 刷新操作
     88     public void update(Graphics g) {
     89         if (offScreenImage == null) {
     90             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
     91         }
     92         Graphics gOffScreen = offScreenImage.getGraphics();
     93         Color c = gOffScreen.getColor();
     94         gOffScreen.setColor(Color.GREEN);
     95         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
     96         gOffScreen.setColor(c);
     97         paint(gOffScreen);
     98         g.drawImage(offScreenImage, 0, 0, null);
     99     }
    100 
    101     public void lauchFrame() {
    102 
    103         // 添加多辆坦克
    104         for (int i = 0; i < 10; i++) {
    105             tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Tank.Direction.D,
    106                     this));
    107         }
    108         // this.setLocation(400, 300);
    109         this.setSize(GAME_WIDTH, GAME_HEIGHT);
    110         this.setTitle("TankWar");
    111         this.addWindowListener(new WindowAdapter() {
    112             public void windowClosing(WindowEvent e) {
    113                 System.exit(0);
    114             }
    115         });
    116         this.setResizable(false);
    117         this.setBackground(Color.GREEN);
    118 
    119         this.addKeyListener(new KeyMonitor());
    120 
    121         setVisible(true);
    122 
    123         new Thread(new PaintThread()).start();
    124     }
    125 
    126     public static void main(String[] args) {
    127         TankClient tc = new TankClient();
    128         tc.lauchFrame();
    129     }
    130 
    131     private class PaintThread implements Runnable {
    132 
    133         public void run() {
    134             while (true) {
    135                 repaint();
    136                 try {
    137                     // 为了爆炸效果,改成1000
    138                     Thread.sleep(50);
    139                 } catch (InterruptedException e) {
    140                     e.printStackTrace();
    141                 }
    142             }
    143         }
    144     }
    145 
    146     // 创建键盘时间监听
    147     private class KeyMonitor extends KeyAdapter {
    148 
    149         // 直接调用myTank自己的方法根据相应的按键信息进行移动
    150         public void keyPressed(KeyEvent e) {
    151             myTank.KeyPressed(e);
    152             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
    153             // 而不是一直按照一个方向走下去
    154         }
    155 
    156         public void keyReleased(KeyEvent e) {
    157             myTank.keyReleased(e);
    158         }
    159 
    160     }
    161 }
    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 
     36     public Missile(int x, int y, boolean good, Tank.Direction dir, TankClient tc) {
     37         this(x, y, dir);
     38         this.good = good;
     39         this.tc = tc;
     40     }
     41 
     42     // 炮弹自己的draw方法
     43     public void draw(Graphics g) {
     44         // 炮弹消亡就不需要再画出来了
     45         if (!live) {
     46             tc.missiles.remove(this);
     47             return;
     48         }
     49         Color c = g.getColor();
     50         g.setColor(Color.BLACK);
     51         // 炮弹形状不要比坦克大,这里设置成10,10;
     52         g.fillOval(x, y, WIDTH, HEIGHT);
     53         g.setColor(c);
     54         move();
     55     }
     56 
     57     public void move() {
     58         switch (dir) {
     59         case L:
     60             x -= XSPEED;
     61             break;
     62         case R:
     63             x += XSPEED;
     64             break;
     65         case U:
     66             y -= YSPEED;
     67             break;
     68         case D:
     69             y += YSPEED;
     70             break;
     71         case LU:
     72             x -= XSPEED;
     73             y -= YSPEED;
     74             break;
     75         case LD:
     76             x -= XSPEED;
     77             y += YSPEED;
     78             break;
     79         case RU:
     80             x += XSPEED;
     81             y -= YSPEED;
     82             break;
     83         case RD:
     84             x += XSPEED;
     85             y += YSPEED;
     86             break;
     87         // 炮弹就没有STOP这个枚举类型的值了
     88         /*
     89          * case STOP: break;
     90          */
     91         }
     92         // 判断炮弹出边界则消亡
     93         // 注意x,y只有正数值,x向右递增,y向下递增
     94         if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
     95                 || y > TankClient.GAME_HEIGHT) {
     96             live = false;
     97         }
     98     }
     99 
    100     public boolean hitTank(Tank t) {
    101         // 炮弹的方框和坦克的方框碰在一起了并且坦克是存活着的,后面的判断我们是一伙的我就不打你了
    102         if (this.live && this.getRect().intersects(t.getRect()) && t.isLive()
    103                 && this.good != t.isGood()) {
    104             if (t.isGood()) {
    105                 t.setLife(t.getLife() - 20);
    106                 if (t.getLife() <= 0) {
    107                     t.setLive(false);
    108                 }
    109             } else {
    110                 t.setLive(false);
    111             }
    112             this.live = false;
    113 
    114             // 炮弹击中坦克,发生爆炸
    115             Explode e = new Explode(x, y, tc);
    116             tc.explodes.add(e);
    117             return true;
    118         }
    119         return false;
    120     }
    121 
    122     // 碰撞检测类Rectangle
    123     // 拿到包围在炮弹周围的小方块
    124     public Rectangle getRect() {
    125         return new Rectangle(x, y, WIDTH, HEIGHT);
    126     }
    127 
    128     // 添加hitTanks方法
    129     public boolean hitTanks(List<Tank> tanks) {
    130         for (int i = 0; i < tanks.size(); i++) {
    131             if (hitTank(tanks.get(i))) {
    132                 return true;
    133             }
    134         }
    135         return false;
    136 
    137     }
    138 
    139     public boolean hitWall(Wall w) {
    140         if (this.live && this.getRect().intersects(w.getRect())) {
    141             this.live = false;
    142             return true;
    143         }
    144         return false;
    145     }
    146 }
    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 
     9     public Wall(int x, int y, int w, int h, TankClient tc) {
    10         super();
    11         this.x = x;
    12         this.y = y;
    13         this.w = w;
    14         this.h = h;
    15         this.tc = tc;
    16     }
    17 
    18     public void draw(Graphics g) {
    19         g.fillRect(x, y, w, h);
    20     }
    21 
    22     // 碰撞检测
    23     public Rectangle getRect() {
    24         return new Rectangle(x, y, w, h);
    25     }
    26 }
    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

    Blood:

     1 import java.awt.Color;
     2 import java.awt.Graphics;
     3 import java.awt.Rectangle;
     4 
     5 //模拟血块,坦克吃了可以补血
     6 public class Blood {
     7     int x, y, w, h;
     8 
     9     TankClient tc;
    10 
    11     private boolean live = true;
    12 
    13     public void setLive(boolean live) {
    14         this.live = live;
    15     }
    16 
    17     public boolean isLive() {
    18         return live;
    19     }
    20 
    21     int step = 0;
    22     private int position[][] = { { 350, 300 }, { 360, 300 }, { 375, 275 },
    23             { 400, 200 }, { 360, 270 }, { 365, 290 }, { 340, 280 } };
    24 
    25     public Blood() {
    26         x = position[0][0];
    27         y = position[0][1];
    28         w = h = 15;
    29     }
    30 
    31     public void draw(Graphics g) {
    32         if (!live) {
    33             return;
    34         }
    35         Color c = g.getColor();
    36         g.setColor(Color.MAGENTA);
    37         g.fillRect(x, y, w, h);
    38         g.setColor(c);
    39         move();
    40     }
    41 
    42     private void move() {
    43         step++;
    44         if (step == position.length) {
    45             step = 0;
    46         }
    47         x = position[step][0];
    48         y = position[step][1];
    49     }
    50 
    51     public Rectangle getRect() {
    52         return new Rectangle(x, y, w, h);
    53     }
    54 }
    View Code

    版本2.7
    功能:修正上一版本不是很合理的地方

            1)更改enum Direction为单独的类

            2)区分好炮弹坏炮弹的颜色

    我们之前的版本在炮弹或者坦克的移动方向的变化中,用到的是我们在tank类中定义的一个枚举类型Direction

    1 enum Direction {
    2         L, R, U, D, LU, LD, RU, RD, STOP
    3     };

    在炮弹类Missile中使用的Direction中的枚举元素的时候总是使用,Tank.Direction.xxx,显得很不方面并且误导性很强,因此,我们就将Direction单独定义出来(像定义一个类一样):

    1 public enum Direction {
    2     L, R, U, D, LU, LD, RU, RD, STOP
    3 }

    在我们需要使用方向的时候就可以这样写了,比如在Missile的移动过程中就直接可以使用Direction.xxx的形式了,一个小的改进;
    在Missile的draw方法中对我方炮弹和敌方炮弹加以区分

    1     //敌我坦克的子弹颜色不同
    2         if(good){
    3             g.setColor(Color.RED);
    4         }else {    
    5             g.setColor(Color.BLACK);
    6         }

    完整代码:

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

    Blood:

     1 import java.awt.Color;
     2 import java.awt.Graphics;
     3 import java.awt.Rectangle;
     4 
     5 //模拟血块,坦克吃了可以补血
     6 public class Blood {
     7     int x, y, w, h;
     8 
     9     TankClient tc;
    10 
    11     private boolean live = true;
    12 
    13     public void setLive(boolean live) {
    14         this.live = live;
    15     }
    16 
    17     public boolean isLive() {
    18         return live;
    19     }
    20 
    21     int step = 0;
    22     private int position[][] = { { 350, 300 }, { 360, 300 }, { 375, 275 },
    23             { 400, 200 }, { 360, 270 }, { 365, 290 }, { 340, 280 } };
    24 
    25     public Blood() {
    26         x = position[0][0];
    27         y = position[0][1];
    28         w = h = 15;
    29     }
    30 
    31     public void draw(Graphics g) {
    32         if (!live) {
    33             return;
    34         }
    35         Color c = g.getColor();
    36         g.setColor(Color.MAGENTA);
    37         g.fillRect(x, y, w, h);
    38         g.setColor(c);
    39         move();
    40     }
    41 
    42     private void move() {
    43         step++;
    44         if (step == position.length) {
    45             step = 0;
    46         }
    47         x = position[step][0];
    48         y = position[step][1];
    49     }
    50 
    51     public Rectangle getRect() {
    52         return new Rectangle(x, y, w, h);
    53     }
    54 }
    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 
     9     public Wall(int x, int y, int w, int h, TankClient tc) {
    10         super();
    11         this.x = x;
    12         this.y = y;
    13         this.w = w;
    14         this.h = h;
    15         this.tc = tc;
    16     }
    17 
    18     public void draw(Graphics g) {
    19         g.fillRect(x, y, w, h);
    20     }
    21 
    22     // 碰撞检测
    23     public Rectangle getRect() {
    24         return new Rectangle(x, y, w, h);
    25     }
    26 }
    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

    Direction:

    1 public enum Direction {
    2     L, R, U, D, LU, LD, RU, RD, STOP
    3 }
    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, 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     Blood b = new Blood();
     37 
     38     public void paint(Graphics g) {
     39         // 记录屏幕上的子弹数目
     40         g.drawString("missiles count:" + missiles.size(), 10, 50);
     41         // 添加上方标记栏,记录爆炸次数
     42         g.drawString("explodes count:" + explodes.size(), 10, 70);
     43         // 记录现在屏幕上一共有多少敌方坦克
     44         g.drawString("tanks count:" + tanks.size(), 10, 90);
     45         // 我们坦克的生命值
     46         g.drawString("tanks life:" + myTank.getLife(), 10, 110);
     47 
     48         //判断敌方坦克是否死光,死光则重新开始游戏
     49         //我方死关了则F2重新开始游戏
     50         if(tanks.size()<=0){
     51             for (int i = 0; i < 5; i++) {
     52                 tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Direction.D,
     53                         this));
     54             }
     55         }
     56         // 遍历结合,发出多发炮弹
     57         for (int i = 0; i < missiles.size(); i++) {
     58             Missile m = missiles.get(i);
     59             // 对于每一发炮弹,都可以将tanks集合中的敌方炮弹干掉
     60             m.hitTanks(tanks);
     61             m.hitTank(myTank);
     62             m.hitWall(w1);
     63             m.hitWall(w2);
     64             m.draw(g);
     65         }
     66 
     67         for (int i = 0; i < explodes.size(); i++) {
     68             Explode e = explodes.get(i);
     69             e.draw(g);
     70         }
     71 
     72         for (int i = 0; i < tanks.size(); i++) {
     73             Tank t = tanks.get(i);
     74             t.collidesWithWall(w1);
     75             t.collidesWithWall(w2);
     76             t.collidesWithTanks(tanks);
     77             t.draw(g);
     78         }
     79         // 不改变前景色
     80         myTank.draw(g);
     81         myTank.eat(b);
     82         w1.draw(g);
     83         w2.draw(g);
     84         b.draw(g);
     85     }
     86 
     87     // 刷新操作
     88     public void update(Graphics g) {
     89         if (offScreenImage == null) {
     90             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
     91         }
     92         Graphics gOffScreen = offScreenImage.getGraphics();
     93         Color c = gOffScreen.getColor();
     94         gOffScreen.setColor(Color.GREEN);
     95         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
     96         gOffScreen.setColor(c);
     97         paint(gOffScreen);
     98         g.drawImage(offScreenImage, 0, 0, null);
     99     }
    100 
    101     public void lauchFrame() {
    102 
    103         // 添加多辆坦克
    104         for (int i = 0; i < 10; i++) {
    105             tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Direction.D,
    106                     this));
    107         }
    108         // this.setLocation(400, 300);
    109         this.setSize(GAME_WIDTH, GAME_HEIGHT);
    110         this.setTitle("TankWar");
    111         this.addWindowListener(new WindowAdapter() {
    112             public void windowClosing(WindowEvent e) {
    113                 System.exit(0);
    114             }
    115         });
    116         this.setResizable(false);
    117         this.setBackground(Color.GREEN);
    118 
    119         this.addKeyListener(new KeyMonitor());
    120 
    121         setVisible(true);
    122 
    123         new Thread(new PaintThread()).start();
    124     }
    125 
    126     public static void main(String[] args) {
    127         TankClient tc = new TankClient();
    128         tc.lauchFrame();
    129     }
    130 
    131     private class PaintThread implements Runnable {
    132 
    133         public void run() {
    134             while (true) {
    135                 repaint();
    136                 try {
    137                     // 为了爆炸效果,改成1000
    138                     Thread.sleep(50);
    139                 } catch (InterruptedException e) {
    140                     e.printStackTrace();
    141                 }
    142             }
    143         }
    144     }
    145 
    146     // 创建键盘时间监听
    147     private class KeyMonitor extends KeyAdapter {
    148 
    149         // 直接调用myTank自己的方法根据相应的按键信息进行移动
    150         public void keyPressed(KeyEvent e) {
    151             myTank.KeyPressed(e);
    152             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
    153             // 而不是一直按照一个方向走下去
    154         }
    155 
    156         public void keyReleased(KeyEvent e) {
    157             myTank.keyReleased(e);
    158         }
    159 
    160     }
    161 }
    View Code

    版本2.8
    功能:加入图片
            1)在classpath中添加资源
            2)反射的初步概念:对于classloader, 每一个.class实际就是一个Class对象,Class是对类信息的表述, 是类的元数据

    图片资源:链接:http://pan.baidu.com/s/1c18LMne 密码:oypn

    加入图片的主要代码步骤:注意images文件夹一定要放在projeect的src目录下面

     1 // 工具包,使用工具包的方法把硬盘上的图片拿到我们的java程序中
     2     private static Toolkit tk = Toolkit.getDefaultToolkit();
     3     // 加载图片,使用到了反射机制
     4         private static Image[] imgs = {
     5             tk.getImage(Explode.class.getClassLoader().getResource("images/0.gif")),
     6             tk.getImage(Explode.class.getClassLoader().getResource("images/1.gif")),
     7             tk.getImage(Explode.class.getClassLoader().getResource("images/2.gif")),
     8             tk.getImage(Explode.class.getClassLoader().getResource("images/3.gif")),
     9             tk.getImage(Explode.class.getClassLoader().getResource("images/4.gif")),
    10             tk.getImage(Explode.class.getClassLoader().getResource("images/5.gif")),
    11             tk.getImage(Explode.class.getClassLoader().getResource("images/6.gif")),
    12             tk.getImage(Explode.class.getClassLoader().getResource("images/7.gif")),
    13             tk.getImage(Explode.class.getClassLoader().getResource("images/8.gif")),
    14             tk.getImage(Explode.class.getClassLoader().getResource("images/9.gif")),
    15         };

    具体代码实现:

    Tank:

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

    Wall:

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

    Explode:

     1 import java.awt.Graphics;
     2 import java.awt.Image;
     3 import java.awt.Toolkit;
     4 
     5 public class Explode {
     6     // 爆炸的位置
     7     int x, y;
     8     // 爆炸是否存在
     9     private boolean live = true;
    10 
    11     // 持有一个Tankclient的引用
    12     private TankClient tc;
    13 
    14     // 工具包,使用工具包的方法把硬盘上的图片拿到我们的java程序中
    15     private static Toolkit tk = Toolkit.getDefaultToolkit();
    16     // 加载图片,使用到了反射机制
    17         private static Image[] imgs = {
    18             tk.getImage(Explode.class.getClassLoader().getResource("images/0.gif")),
    19             tk.getImage(Explode.class.getClassLoader().getResource("images/1.gif")),
    20             tk.getImage(Explode.class.getClassLoader().getResource("images/2.gif")),
    21             tk.getImage(Explode.class.getClassLoader().getResource("images/3.gif")),
    22             tk.getImage(Explode.class.getClassLoader().getResource("images/4.gif")),
    23             tk.getImage(Explode.class.getClassLoader().getResource("images/5.gif")),
    24             tk.getImage(Explode.class.getClassLoader().getResource("images/6.gif")),
    25             tk.getImage(Explode.class.getClassLoader().getResource("images/7.gif")),
    26             tk.getImage(Explode.class.getClassLoader().getResource("images/8.gif")),
    27             tk.getImage(Explode.class.getClassLoader().getResource("images/9.gif")),
    28         };
    29     // 爆炸发生到哪一个阶段了,对应相应大小的直径
    30     int step = 0;
    31 
    32     private static boolean init=false;
    33     
    34     public Explode(int x, int y, TankClient tc) {
    35         this.x = x;
    36         this.y = y;
    37         this.tc = tc;
    38     }
    39 
    40     public void draw(Graphics g) {
    41         if(!init){
    42             for (int i = 0; i < imgs.length; i++) {
    43                 g.drawImage(imgs[i], -100, -100, null);
    44             }
    45             init=true;
    46         }
    47         if (!live) {
    48             // 爆炸发生,将相应直径的爆炸圆从集合explodes中去除
    49             tc.explodes.remove(this);
    50             return;
    51         }
    52 
    53         if (step == imgs.length) {
    54             live = false;
    55             step = 0;
    56             return;
    57         }
    58         // 直接用图片
    59         g.drawImage(imgs[step], x, y, null);
    60         step++;
    61     }
    62 }
    View Code

    Missile:

      1 import java.awt.Color;
      2 import java.awt.Graphics;
      3 import java.awt.Image;
      4 import java.awt.Rectangle;
      5 import java.awt.Toolkit;
      6 import java.util.HashMap;
      7 import java.util.List;
      8 import java.util.Map;
      9 
     10 public class Missile {
     11     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
     12     public static final int XSPEED = 10;
     13     public static final int YSPEED = 10;
     14     // 将子弹的高度和宽度设置为常量
     15     public static final int WIDTH = 10;
     16     public static final int HEIGHT = 10;
     17     // 炮弹自己的三个属性
     18     int x;
     19     int y;
     20     Direction dir;
     21 
     22     // 同一阵营的的坦克发出的子弹不能伤害自己人
     23     private boolean good;
     24     // 定义一个布尔类型的变量来判断炮弹是否已经消亡
     25     private boolean live = true;
     26     // 我们在Missile类中也持有一个TankClient的引用
     27     // 在炮弹出界的时候就可以从装炮弹的missiles集合中去除该炮弹,不再对其重画
     28     private TankClient tc;
     29     // 为加入图片
     30         private static Toolkit tk = Toolkit.getDefaultToolkit();
     31         // 加载图片,使用到了反射机制
     32         private static Image[] missileImages = null;
     33         private static Map<String, Image> imgs = new HashMap<String, Image>();
     34         //用到了静态代码块,类加载的时候会先执行这段代码
     35         static {
     36             missileImages = new Image[] {
     37                     tk.getImage(Tank.class.getClassLoader().getResource(
     38                             "images/missileD.gif")),
     39                     tk.getImage(Tank.class.getClassLoader().getResource(
     40                             "images/missileL.gif")),
     41                     tk.getImage(Tank.class.getClassLoader().getResource(
     42                             "images/missileLD.gif")),
     43                     tk.getImage(Tank.class.getClassLoader().getResource(
     44                             "images/missileLU.gif")),
     45                     tk.getImage(Tank.class.getClassLoader().getResource(
     46                             "images/missileR.gif")),
     47                     tk.getImage(Tank.class.getClassLoader().getResource(
     48                             "images/missileRD.gif")),
     49                     tk.getImage(Tank.class.getClassLoader().getResource(
     50                             "images/missileRU.gif")),
     51                     tk.getImage(Tank.class.getClassLoader().getResource(
     52                             "images/missileU.gif")), };
     53             imgs.put("D", missileImages[0]);
     54             imgs.put("L", missileImages[1]);
     55             imgs.put("LD", missileImages[2]);
     56             imgs.put("LU", missileImages[3]);
     57             imgs.put("R", missileImages[4]);
     58             imgs.put("RD", missileImages[5]);
     59             imgs.put("RU", missileImages[6]);
     60             imgs.put("U", missileImages[7]);
     61         }
     62 
     63     public boolean isLive() {
     64         return live;
     65     }
     66 
     67     public Missile(int x, int y, Direction dir) {
     68         this.x = x;
     69         this.y = y;
     70         this.dir = dir;
     71     }
     72 
     73     public Missile(int x, int y, boolean good, Direction dir, TankClient tc) {
     74         this(x, y, dir);
     75         this.good = good;
     76         this.tc = tc;
     77     }
     78 
     79     // 炮弹自己的draw方法
     80     public void draw(Graphics g) {
     81         // 炮弹消亡就不需要再画出来了
     82         if (!live) {
     83             tc.missiles.remove(this);
     84             return;
     85         }
     86         switch (dir) {
     87         case L:
     88             g.drawImage(imgs.get("L"), x, y, null);
     89             break;
     90         case R:
     91             g.drawImage(imgs.get("R"), x, y, null);
     92 
     93             break;
     94         case U:
     95             g.drawImage(imgs.get("U"), x, y, null);
     96             break;
     97         case D:
     98             g.drawImage(imgs.get("D"), x, y, null);
     99             break;
    100         case LU:
    101             g.drawImage(imgs.get("LU"), x, y, null);
    102         case LD:
    103             g.drawImage(imgs.get("LD"), x, y, null);
    104 
    105             break;
    106         case RU:
    107             g.drawImage(imgs.get("RU"), x, y, null);
    108             break;
    109         case RD:
    110             g.drawImage(imgs.get("RD"), x, y, null);
    111 
    112             break;
    113         /*
    114          * case STOP: break;
    115          */
    116         }
    117         move();
    118     }
    119 
    120 
    121     public void move() {
    122         switch (dir) {
    123         case L:
    124             x -= XSPEED;
    125             break;
    126         case R:
    127             x += XSPEED;
    128             break;
    129         case U:
    130             y -= YSPEED;
    131             break;
    132         case D:
    133             y += YSPEED;
    134             break;
    135         case LU:
    136             x -= XSPEED;
    137             y -= YSPEED;
    138             break;
    139         case LD:
    140             x -= XSPEED;
    141             y += YSPEED;
    142             break;
    143         case RU:
    144             x += XSPEED;
    145             y -= YSPEED;
    146             break;
    147         case RD:
    148             x += XSPEED;
    149             y += YSPEED;
    150             break;
    151         // 炮弹就没有STOP这个枚举类型的值了
    152         /*
    153          * case STOP: break;
    154          */
    155         }
    156         // 判断炮弹出边界则消亡
    157         // 注意x,y只有正数值,x向右递增,y向下递增
    158         if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
    159                 || y > TankClient.GAME_HEIGHT) {
    160             live = false;
    161         }
    162     }
    163 
    164     public boolean hitTank(Tank t) {
    165         // 炮弹的方框和坦克的方框碰在一起了并且坦克是存活着的,后面的判断我们是一伙的我就不打你了
    166         if (this.live && this.getRect().intersects(t.getRect()) && t.isLive()
    167                 && this.good != t.isGood()) {
    168             if (t.isGood()) {
    169                 t.setLife(t.getLife() - 20);
    170                 if (t.getLife() <= 0) {
    171                     t.setLive(false);
    172                 }
    173             } else {
    174                 t.setLive(false);
    175             }
    176             this.live = false;
    177 
    178             // 炮弹击中坦克,发生爆炸
    179             Explode e = new Explode(x, y, tc);
    180             tc.explodes.add(e);
    181             return true;
    182         }
    183         return false;
    184     }
    185 
    186     // 碰撞检测类Rectangle
    187     // 拿到包围在炮弹周围的小方块
    188     public Rectangle getRect() {
    189         return new Rectangle(x, y, WIDTH, HEIGHT);
    190     }
    191 
    192     // 添加hitTanks方法
    193     public boolean hitTanks(List<Tank> tanks) {
    194         for (int i = 0; i < tanks.size(); i++) {
    195             if (hitTank(tanks.get(i))) {
    196                 return true;
    197             }
    198         }
    199         return false;
    200 
    201     }
    202 
    203     public boolean hitWall(Wall w) {
    204         if (this.live && this.getRect().intersects(w.getRect())) {
    205             this.live = false;
    206             return true;
    207         }
    208         return false;
    209     }
    210 }
    View Code

    Blood:

     1 import java.awt.Color;
     2 import java.awt.Graphics;
     3 import java.awt.Rectangle;
     4 
     5 //模拟血块,坦克吃了可以补血
     6 public class Blood {
     7     int x, y, w, h;
     8 
     9     TankClient tc;
    10 
    11     private boolean live = true;
    12 
    13     public void setLive(boolean live) {
    14         this.live = live;
    15     }
    16 
    17     public boolean isLive() {
    18         return live;
    19     }
    20 
    21     int step = 0;
    22     private int position[][] = { { 350, 300 }, { 360, 300 }, { 375, 275 },
    23             { 400, 200 }, { 360, 270 }, { 365, 290 }, { 340, 280 } };
    24 
    25     public Blood() {
    26         x = position[0][0];
    27         y = position[0][1];
    28         w = h = 15;
    29     }
    30 
    31     public void draw(Graphics g) {
    32         if (!live) {
    33             return;
    34         }
    35         Color c = g.getColor();
    36         g.setColor(Color.MAGENTA);
    37         g.fillRect(x, y, w, h);
    38         g.setColor(c);
    39         move();
    40     }
    41 
    42     private void move() {
    43         step++;
    44         if (step == position.length) {
    45             step = 0;
    46         }
    47         x = position[step][0];
    48         y = position[step][1];
    49     }
    50 
    51     public Rectangle getRect() {
    52         return new Rectangle(x, y, w, h);
    53     }
    54 }
    View Code

    Direction:

    1 public enum Direction {
    2     L, R, U, D, LU, LD, RU, RD, STOP
    3 }
    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, Direction.STOP, this);
     17 
     18     Wall w1 = new Wall(100, 200, 20, 150, this);
     19     Wall w2 = new Wall(300, 500, 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     Blood b = new Blood();
     37 
     38     public void paint(Graphics g) {
     39         // 记录屏幕上的子弹数目
     40         g.drawString("missiles count:" + missiles.size(), 10, 50);
     41         // 添加上方标记栏,记录爆炸次数
     42         g.drawString("explodes count:" + explodes.size(), 10, 70);
     43         // 记录现在屏幕上一共有多少敌方坦克
     44         g.drawString("tanks count:" + tanks.size(), 10, 90);
     45         // 我们坦克的生命值
     46         g.drawString("tanks life:" + myTank.getLife(), 10, 110);
     47 
     48         //判断敌方坦克是否死光,死光则重新开始游戏
     49         //我方死关了则F2重新开始游戏
     50         if(tanks.size()<=0){
     51             for (int i = 0; i < 5; i++) {
     52                 tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Direction.D,
     53                         this));
     54             }
     55         }
     56         // 遍历结合,发出多发炮弹
     57         for (int i = 0; i < missiles.size(); i++) {
     58             Missile m = missiles.get(i);
     59             // 对于每一发炮弹,都可以将tanks集合中的敌方炮弹干掉
     60             m.hitTanks(tanks);
     61             m.hitTank(myTank);
     62             m.hitWall(w1);
     63             m.hitWall(w2);
     64             m.draw(g);
     65         }
     66 
     67         for (int i = 0; i < explodes.size(); i++) {
     68             Explode e = explodes.get(i);
     69             e.draw(g);
     70         }
     71 
     72         for (int i = 0; i < tanks.size(); i++) {
     73             Tank t = tanks.get(i);
     74             t.collidesWithWall(w1);
     75             t.collidesWithWall(w2);
     76             t.collidesWithTanks(tanks);
     77             t.draw(g);
     78         }
     79         // 不改变前景色
     80         myTank.draw(g);
     81         myTank.eat(b);
     82         w1.draw(g);
     83         w2.draw(g);
     84         b.draw(g);
     85     }
     86 
     87     // 刷新操作
     88     public void update(Graphics g) {
     89         if (offScreenImage == null) {
     90             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
     91         }
     92         Graphics gOffScreen = offScreenImage.getGraphics();
     93         Color c = gOffScreen.getColor();
     94         gOffScreen.setColor(Color.BLACK);
     95         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
     96         gOffScreen.setColor(c);
     97         paint(gOffScreen);
     98         g.drawImage(offScreenImage, 0, 0, null);
     99     }
    100 
    101     public void lauchFrame() {
    102 
    103         // 添加多辆坦克
    104         for (int i = 0; i < 10; i++) {
    105             tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Direction.D,
    106                     this));
    107         }
    108         // this.setLocation(400, 300);
    109         this.setSize(GAME_WIDTH, GAME_HEIGHT);
    110         this.setTitle("TankWar");
    111         this.addWindowListener(new WindowAdapter() {
    112             public void windowClosing(WindowEvent e) {
    113                 System.exit(0);
    114             }
    115         });
    116         this.setResizable(false);
    117         this.setBackground(Color.GREEN);
    118 
    119         this.addKeyListener(new KeyMonitor());
    120 
    121         setVisible(true);
    122 
    123         new Thread(new PaintThread()).start();
    124     }
    125 
    126     public static void main(String[] args) {
    127         TankClient tc = new TankClient();
    128         tc.lauchFrame();
    129     }
    130 
    131     private class PaintThread implements Runnable {
    132 
    133         public void run() {
    134             while (true) {
    135                 repaint();
    136                 try {
    137                     // 为了爆炸效果,改成1000
    138                     Thread.sleep(50);
    139                 } catch (InterruptedException e) {
    140                     e.printStackTrace();
    141                 }
    142             }
    143         }
    144     }
    145 
    146     // 创建键盘时间监听
    147     private class KeyMonitor extends KeyAdapter {
    148 
    149         // 直接调用myTank自己的方法根据相应的按键信息进行移动
    150         public void keyPressed(KeyEvent e) {
    151             myTank.KeyPressed(e);
    152             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
    153             // 而不是一直按照一个方向走下去
    154         }
    155 
    156         public void keyReleased(KeyEvent e) {
    157             myTank.keyReleased(e);
    158         }
    159 
    160     }
    161 }
    View Code

    版本2.9
    功能:配置文件的使用
            Properties类
            Singleton模式(单例设计模式)

    我们的坦克大战基本已经完成,这个版本只是为了优化一些操作。比如我们在游戏开始生成一定数量的坦克,在敌方坦克被全部消灭之后新生成一些坦克的代码设计中,类似于下面这样

    1 if(tanks.size()<=0){
    2             for (int i = 0; i < 5; i++) {
    3                 tanks.add(new Tank(50 + 40 * (i + 1), 50, false, Direction.D,
    4                         this));
    5             }
    6         }

    如果我们想生成10辆坦克而不是5辆坦克呢?为了应对可能频繁更改的需求,我们在这里使用Properties以及配置文件来解决这一问题(今后的学习中要沿用这种形式的代码设计和组织),并且为了防止频繁将我们的配置文件加载进内存,我们使用一个类PropertyManager以及单例设计模式(singleton)来完成这一优化目的;

     1 public class PropertyManager {
     2     private static Properties props = new Properties();
     3     private PropertyManager(){
     4         
     5     }
     6     static {
     7         try {
     8             props.load(Properties.class.getClass().getClassLoader()
     9                     .getResourceAsStream("config/tank.properties"));
    10         } catch (IOException e1) {
    11             e1.printStackTrace();
    12         }
    13     }
    14 
    15     public static String getProperty(String key) {
    16         return props.getProperty(key);
    17 
    18     }
    19 }

    完整代码:
    Tank:

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

    Wall:

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

    Missile:

      1 import java.awt.Color;
      2 import java.awt.Graphics;
      3 import java.awt.Image;
      4 import java.awt.Rectangle;
      5 import java.awt.Toolkit;
      6 import java.util.HashMap;
      7 import java.util.List;
      8 import java.util.Map;
      9 
     10 public class Missile {
     11     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
     12     public static final int XSPEED = 10;
     13     public static final int YSPEED = 10;
     14     // 将子弹的高度和宽度设置为常量
     15     public static final int WIDTH = 10;
     16     public static final int HEIGHT = 10;
     17     // 炮弹自己的三个属性
     18     int x;
     19     int y;
     20     Direction dir;
     21 
     22     // 同一阵营的的坦克发出的子弹不能伤害自己人
     23     private boolean good;
     24     // 定义一个布尔类型的变量来判断炮弹是否已经消亡
     25     private boolean live = true;
     26     // 我们在Missile类中也持有一个TankClient的引用
     27     // 在炮弹出界的时候就可以从装炮弹的missiles集合中去除该炮弹,不再对其重画
     28     private TankClient tc;
     29     // 为加入图片
     30         private static Toolkit tk = Toolkit.getDefaultToolkit();
     31         // 加载图片,使用到了反射机制
     32         private static Image[] missileImages = null;
     33         private static Map<String, Image> imgs = new HashMap<String, Image>();
     34         //用到了静态代码块,类加载的时候会先执行这段代码
     35         static {
     36             missileImages = new Image[] {
     37                     tk.getImage(Tank.class.getClassLoader().getResource(
     38                             "images/missileD.gif")),
     39                     tk.getImage(Tank.class.getClassLoader().getResource(
     40                             "images/missileL.gif")),
     41                     tk.getImage(Tank.class.getClassLoader().getResource(
     42                             "images/missileLD.gif")),
     43                     tk.getImage(Tank.class.getClassLoader().getResource(
     44                             "images/missileLU.gif")),
     45                     tk.getImage(Tank.class.getClassLoader().getResource(
     46                             "images/missileR.gif")),
     47                     tk.getImage(Tank.class.getClassLoader().getResource(
     48                             "images/missileRD.gif")),
     49                     tk.getImage(Tank.class.getClassLoader().getResource(
     50                             "images/missileRU.gif")),
     51                     tk.getImage(Tank.class.getClassLoader().getResource(
     52                             "images/missileU.gif")), };
     53             imgs.put("D", missileImages[0]);
     54             imgs.put("L", missileImages[1]);
     55             imgs.put("LD", missileImages[2]);
     56             imgs.put("LU", missileImages[3]);
     57             imgs.put("R", missileImages[4]);
     58             imgs.put("RD", missileImages[5]);
     59             imgs.put("RU", missileImages[6]);
     60             imgs.put("U", missileImages[7]);
     61         }
     62 
     63     public boolean isLive() {
     64         return live;
     65     }
     66 
     67     public Missile(int x, int y, Direction dir) {
     68         this.x = x;
     69         this.y = y;
     70         this.dir = dir;
     71     }
     72 
     73     public Missile(int x, int y, boolean good, Direction dir, TankClient tc) {
     74         this(x, y, dir);
     75         this.good = good;
     76         this.tc = tc;
     77     }
     78 
     79     // 炮弹自己的draw方法
     80     public void draw(Graphics g) {
     81         // 炮弹消亡就不需要再画出来了
     82         if (!live) {
     83             tc.missiles.remove(this);
     84             return;
     85         }
     86         switch (dir) {
     87         case L:
     88             g.drawImage(imgs.get("L"), x, y, null);
     89             break;
     90         case R:
     91             g.drawImage(imgs.get("R"), x, y, null);
     92 
     93             break;
     94         case U:
     95             g.drawImage(imgs.get("U"), x, y, null);
     96             break;
     97         case D:
     98             g.drawImage(imgs.get("D"), x, y, null);
     99             break;
    100         case LU:
    101             g.drawImage(imgs.get("LU"), x, y, null);
    102         case LD:
    103             g.drawImage(imgs.get("LD"), x, y, null);
    104 
    105             break;
    106         case RU:
    107             g.drawImage(imgs.get("RU"), x, y, null);
    108             break;
    109         case RD:
    110             g.drawImage(imgs.get("RD"), x, y, null);
    111 
    112             break;
    113         /*
    114          * case STOP: break;
    115          */
    116         }
    117         move();
    118     }
    119 
    120 
    121     public void move() {
    122         switch (dir) {
    123         case L:
    124             x -= XSPEED;
    125             break;
    126         case R:
    127             x += XSPEED;
    128             break;
    129         case U:
    130             y -= YSPEED;
    131             break;
    132         case D:
    133             y += YSPEED;
    134             break;
    135         case LU:
    136             x -= XSPEED;
    137             y -= YSPEED;
    138             break;
    139         case LD:
    140             x -= XSPEED;
    141             y += YSPEED;
    142             break;
    143         case RU:
    144             x += XSPEED;
    145             y -= YSPEED;
    146             break;
    147         case RD:
    148             x += XSPEED;
    149             y += YSPEED;
    150             break;
    151         // 炮弹就没有STOP这个枚举类型的值了
    152         /*
    153          * case STOP: break;
    154          */
    155         }
    156         // 判断炮弹出边界则消亡
    157         // 注意x,y只有正数值,x向右递增,y向下递增
    158         if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
    159                 || y > TankClient.GAME_HEIGHT) {
    160             live = false;
    161         }
    162     }
    163 
    164     public boolean hitTank(Tank t) {
    165         // 炮弹的方框和坦克的方框碰在一起了并且坦克是存活着的,后面的判断我们是一伙的我就不打你了
    166         if (this.live && this.getRect().intersects(t.getRect()) && t.isLive()
    167                 && this.good != t.isGood()) {
    168             if (t.isGood()) {
    169                 t.setLife(t.getLife() - 20);
    170                 if (t.getLife() <= 0) {
    171                     t.setLive(false);
    172                 }
    173             } else {
    174                 t.setLive(false);
    175             }
    176             this.live = false;
    177 
    178             // 炮弹击中坦克,发生爆炸
    179             Explode e = new Explode(x, y, tc);
    180             tc.explodes.add(e);
    181             return true;
    182         }
    183         return false;
    184     }
    185 
    186     // 碰撞检测类Rectangle
    187     // 拿到包围在炮弹周围的小方块
    188     public Rectangle getRect() {
    189         return new Rectangle(x, y, WIDTH, HEIGHT);
    190     }
    191 
    192     // 添加hitTanks方法
    193     public boolean hitTanks(List<Tank> tanks) {
    194         for (int i = 0; i < tanks.size(); i++) {
    195             if (hitTank(tanks.get(i))) {
    196                 return true;
    197             }
    198         }
    199         return false;
    200 
    201     }
    202 
    203     public boolean hitWall(Wall w) {
    204         if (this.live && this.getRect().intersects(w.getRect())) {
    205             this.live = false;
    206             return true;
    207         }
    208         return false;
    209     }
    210 }
    View Code

    Explode:

     1 import java.awt.Graphics;
     2 import java.awt.Image;
     3 import java.awt.Toolkit;
     4 
     5 public class Explode {
     6     // 爆炸的位置
     7     int x, y;
     8     // 爆炸是否存在
     9     private boolean live = true;
    10 
    11     // 持有一个Tankclient的引用
    12     private TankClient tc;
    13 
    14     // 工具包,使用工具包的方法把硬盘上的图片拿到我们的java程序中
    15     private static Toolkit tk = Toolkit.getDefaultToolkit();
    16     // 加载图片,使用到了反射机制
    17         private static Image[] imgs = {
    18             tk.getImage(Explode.class.getClassLoader().getResource("images/0.gif")),
    19             tk.getImage(Explode.class.getClassLoader().getResource("images/1.gif")),
    20             tk.getImage(Explode.class.getClassLoader().getResource("images/2.gif")),
    21             tk.getImage(Explode.class.getClassLoader().getResource("images/3.gif")),
    22             tk.getImage(Explode.class.getClassLoader().getResource("images/4.gif")),
    23             tk.getImage(Explode.class.getClassLoader().getResource("images/5.gif")),
    24             tk.getImage(Explode.class.getClassLoader().getResource("images/6.gif")),
    25             tk.getImage(Explode.class.getClassLoader().getResource("images/7.gif")),
    26             tk.getImage(Explode.class.getClassLoader().getResource("images/8.gif")),
    27             tk.getImage(Explode.class.getClassLoader().getResource("images/9.gif")),
    28         };
    29     // 爆炸发生到哪一个阶段了,对应相应大小的直径
    30     int step = 0;
    31 
    32     private static boolean init=false;
    33     
    34     public Explode(int x, int y, TankClient tc) {
    35         this.x = x;
    36         this.y = y;
    37         this.tc = tc;
    38     }
    39 
    40     public void draw(Graphics g) {
    41         if(!init){
    42             for (int i = 0; i < imgs.length; i++) {
    43                 g.drawImage(imgs[i], -100, -100, null);
    44             }
    45             init=true;
    46         }
    47         if (!live) {
    48             // 爆炸发生,将相应直径的爆炸圆从集合explodes中去除
    49             tc.explodes.remove(this);
    50             return;
    51         }
    52 
    53         if (step == imgs.length) {
    54             live = false;
    55             step = 0;
    56             return;
    57         }
    58         // 直接用图片
    59         g.drawImage(imgs[step], x, y, null);
    60         step++;
    61     }
    62 }
    View Code

    Blood:

     1 import java.awt.Color;
     2 import java.awt.Graphics;
     3 import java.awt.Rectangle;
     4 
     5 //模拟血块,坦克吃了可以补血
     6 public class Blood {
     7     int x, y, w, h;
     8 
     9     TankClient tc;
    10 
    11     private boolean live = true;
    12 
    13     public void setLive(boolean live) {
    14         this.live = live;
    15     }
    16 
    17     public boolean isLive() {
    18         return live;
    19     }
    20 
    21     int step = 0;
    22     private int position[][] = { { 350, 300 }, { 360, 300 }, { 375, 275 },
    23             { 400, 200 }, { 360, 270 }, { 365, 290 }, { 340, 280 } };
    24 
    25     public Blood() {
    26         x = position[0][0];
    27         y = position[0][1];
    28         w = h = 15;
    29     }
    30 
    31     public void draw(Graphics g) {
    32         if (!live) {
    33             return;
    34         }
    35         Color c = g.getColor();
    36         g.setColor(Color.MAGENTA);
    37         g.fillRect(x, y, w, h);
    38         g.setColor(c);
    39         move();
    40     }
    41 
    42     private void move() {
    43         step++;
    44         if (step == position.length) {
    45             step = 0;
    46         }
    47         x = position[step][0];
    48         y = position[step][1];
    49     }
    50 
    51     public Rectangle getRect() {
    52         return new Rectangle(x, y, w, h);
    53     }
    54 }
    View Code

    Direction:

    1 public enum Direction {
    2     L, R, U, D, LU, LD, RU, RD, STOP
    3 }
    View Code

    PropertyManager:

     1 import java.io.IOException;
     2 import java.util.Properties;
     3 
     4 //使用到了单例模式提高效率
     5 //不需要每次都将配置文件load进内存
     6 public class PropertyManager {
     7     private static Properties props = new Properties();
     8     private PropertyManager(){
     9         
    10     }
    11     static {
    12         try {
    13             props.load(Properties.class.getClass().getClassLoader()
    14                     .getResourceAsStream("config/tank.properties"));
    15         } catch (IOException e1) {
    16             e1.printStackTrace();
    17         }
    18     }
    19 
    20     public static String getProperty(String key) {
    21         return props.getProperty(key);
    22 
    23     }
    24 }
    View Code

    TankClient:

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

    未完待续。。。。。。

  • 相关阅读:
    解决Nginx不支持pathinfo的问题
    PHP获取当前服务器信息的基本语句
    权重结构的加权排序算法
    《深入探讨C++对象模型》笔记 二
    链表的一些常用操作
    invalidate作用
    GetMessage()和PeekMessage()区别
    C语言程序编译的内存分配
    assert() 宏用法
    开始写博客
  • 原文地址:https://www.cnblogs.com/ysw-go/p/5532634.html
Copyright © 2011-2022 走看看