坦克大战(版本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 }

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 }

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 }

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 }

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 }

版本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 }

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 }

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 }

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 }

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 }

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 }

版本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 }

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 }

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 }

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 }

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 }

Direction:

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

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 }

版本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 }

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 }

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 }

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 }

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 }

Direction:

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

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 }

版本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 }

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 }

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 }

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 }

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 }

Direction:

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

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 }

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 }

未完待续。。。。。。

时间: 2024-10-12 12:07:22

坦克大战(版本2.5-版本2.9)的相关文章

Html 5 坦克大战(韩顺平版本)

html 5代码部分如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> </head> <body onkeydown="getCommand();"> <h1>hmtl5-经典的坦克大战</h1> <!--坦克大战的战场--> <canvas id="tankMap"

Java__线程---基础知识全面实战---坦克大战系列为例

今天想将自己去年自己编写的坦克大战的代码与大家分享一下,主要面向学习过java但对java运用并不是很熟悉的同学,该编程代码基本上涉及了java基础知识的各个方面,大家可以通过练习该程序对自己的java进行一下实战. 每个程序版本代码中,都附有相关注释,看完注释大家就可以对本程序设计有个很明显的思路.真的很有趣,想对java重新温习的同学完全不必再对厚厚的java基础书籍进行阅读了,在跟着本次代码练习并分析后,大家一定会对java各方面基础知识 尤其是线程的知识有更深一步的了解!!! 本次坦克大

Java坦克大战(一)

接下来的几篇博客,想记录一下通过学习坦克大战项目来循序渐进的学习Java基础.主要是为了巩固基础知识,当然学习编程重要的还是多敲,问题通常是在敲代码的过程中发现的,积累也是在敲代码中寻求的经验.这个坦克大战项目是利用Java图形界面来做的,比较简陋.但是,在不断的往里面加功能的时候,可以学到很多知识,最重要的还是体会Java的面向对象编程思想.下面介绍几个用的上的Demo,最后是坦克大战的1.0版本. Demo1:回顾事件处理机制 /* * 功能:事件处理机制(ActionListener的应用

Java坦克大战(三)

关于这个坦克大战的项目是在学习Java基础的时候,拿来练习的最近看到这些代码,感觉很亲切,就把他们都复制下来,编辑成博客.回首看去,Java基础的学习确实应该建立在找项目练习上,这样才能将学到的基础知识用到实际当中,不然你知道什么是面向对象编程,什么是线程,什么是死锁,概念都了解了一大堆,等到实际应用的时候,还是力不从心.初学者千万不要灰心,真心是敲着敲着就有感觉了.下面还是循序渐进的介绍这个项目的几个版本,注释我写的很详细,新功能添加后部分代码有改动,如果感兴趣,可以看前几篇博客. 坦克大战(

坦克大战系列(3.0版)

无论头上是怎样的天空,我准备承受任何风暴.--拜伦 本讲内容:坦克3.0版(面向对象的思想) 要求:画出我方坦克会动并且会发射子弹.画出敌人坦克 一.同一个包下建二个文件分别为:MyTankGame.Members(负责其它成员譬如:制造坦克.子弹等) MyTankGame类 /** * 功能:坦克游戏的3.0版本 * 1:画出坦克 * 2:实现我方坦克移动并且會發子彈,并 画出敌人的坦克 */ package a; import javax.swing.*; import java.awt.*

坦克大战系列(8.0版)

人生在勤,不索何获.--张衡 本讲内容:坦克大战8.0版(面向对象的思想) 一.解决:防止敌人坦克重叠运动 1.定义一个Vector容器,装所有敌人的坦克(为了得到所有坦克坐标) 2.定义一个方法getEts()可以得到敌人的坦克(为了得到所有坦克坐标) 3.在我的我的面板的构造方法调用getEts() 4.定义一个判断是否碰到了别的敌人的坦克的方法isTouchOtherEnemy() 5.在设置敌人坦克随机走动那调用isTouchOtherEnemy() 二.解决:我方坦克死亡(即隐身)后,

坦克大战系列11.0版

好学而不勤问非真好学者. 本讲内容:坦克大战11.0版(面向对象的思想) 一.解决:播放声音 1.定义一个播放声音的类AePlayWave 2.在面板构造方法启动声音 二.同一个包下建二个文件分别为:MyTankGame.Members(负责其它成员譬如:制造坦克.子弹等) MyTankGame类 /** * 功能:坦克游戏的8.0版本 1:画出坦克 * 2:实现我方坦克可以上下左右移动 * 3:可以發射子彈,子彈可以連發(最多可以发5颗) * 4:画出敌人的坦克 * 5:当我方坦克击中敌人坦克

超多经典 canvas 实例,动态离子背景、移动炫彩小球、贪吃蛇、坦克大战、是男人就下100层、心形文字等等等

超多经典 canvas 实例 普及:<canvas> 元素用于在网页上绘制图形.这是一个图形容器,您可以控制其每一像素,必须使用脚本来绘制图形. 注意:IE 8 以及更早的版本不支持 <canvas> 元素. 贴士:全部例子都分享在我的 GayHub - https://github.com/bxm0927/canvas-special 尤雨溪个人主页炫彩三角纽带效果,点击还可变换 GitHub源码 . Demo演示 知乎登录注册页动态离子背景效果 GitHub源码 . Demo演

基于java的坦克大战实例

原创性声明 此博文的出处 为 http://blog.csdn.net/zhujunxxxxx/article/details/40460931如果进行转载请注明出处.本文作者原创,邮箱[email protected],如有问题请联系作者 前言 很久没碰java了,今天突然找出了大二时期写的一个坦克大战游戏的源码,然后运行了一下,竟然依然如此的亲切,突然想到了以前大二的时候的点点时光.好了不废话了,先给出一张图. 这个就是整个游戏的界面了,界面全是用的java画的,不是很好看.如果想要好看的得