坦克大战(版本1.7-版本2.4)

版本1.7

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

具体代码实现:

Tank:

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

Missile:

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

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 }

TankClient:

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

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

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

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

具体代码实现:

Tank:

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

Missile:

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

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 }

TankClient:

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

版本1.9

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

Tank:

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

Missile:

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

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 }

Tankclient:

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

版本2.0

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

具体代码实现:

Tank:

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

Missile:

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

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 }

Wall:

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

TankClient:

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

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

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

TankClient类中的paint方法中:

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

具体代码实现:

Tank:

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

Missile:

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

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 }

Wall:

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

TankClient:

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

版本2.2

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

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

Tank类 :

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

版本2.3

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

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

Tank:

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

Missile:

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

TankClient:

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

版本2.4

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

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

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

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

未完待续。。。。。。

时间: 2024-11-15 18:10:32

坦克大战(版本1.7-版本2.4)的相关文章

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

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

坦克大战系列(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画的,不是很好看.如果想要好看的得