码迷,mamicode.com
首页 > 其他好文 > 详细

坦克大战(版本1.0-版本1.6)

时间:2016-05-19 19:18:30      阅读:261      评论:0      收藏:0      [点我收藏+]

标签:

版本1.0

功能: 添加子弹类

步骤: 1)添加Missile类;

         2)添加x,y,dir等属性以及常量;

         3)添加构造方法、draw方法等必要方法;

         4)根据不同方向,进行不同的运动;

         5)在TankClient中模拟一颗子弹;

         6)new一颗子弹出来;

         7)画出来

注意: 不一定一次写到位,进行多次试验,将步骤分解开来逐一进行调试;

具体代码实现:

Tank:

技术分享
  1 import java.awt.*;
  2 import java.awt.event.*;
  3 
  4 public class Tank {
  5     // 方便后期更改
  6     public static final int XSPEED = 5;
  7     public static final int YSPEED = 5;
  8     private int x;
  9     private int y;
 10     // 添加记录按键状态的布尔量
 11     private boolean bL = false;
 12     private boolean bR = false;
 13     private boolean bU = false;
 14     private boolean bD = false;
 15 
 16     // 添加代表方向的量(使用枚举)
 17     enum Direction {
 18         L, R, U, D, LU, LD, RU, RD, STOP
 19     };
 20 
 21     private Direction dir = Direction.STOP;
 22 
 23     public Tank(int x, int y) {
 24         this.x = x;
 25         this.y = y;
 26     }
 27 
 28     // Tank对象的draw方法
 29     public void draw(Graphics g) {
 30         Color c = g.getColor();
 31         g.setColor(Color.RED);
 32         g.fillOval(x, y, 30, 30);
 33         g.setColor(c);
 34         move();
 35     }
 36 
 37     public void move() {
 38         switch (dir) {
 39         case L:
 40             x -= XSPEED;
 41             break;
 42         case R:
 43             x += XSPEED;
 44             break;
 45         case U:
 46             y -= YSPEED;
 47             break;
 48         case D:
 49             y += YSPEED;
 50             break;
 51         case LU:
 52             x -= XSPEED;
 53             y -= YSPEED;
 54             break;
 55         case LD:
 56             x -= XSPEED;
 57             y += YSPEED;
 58             break;
 59         case RU:
 60             x += XSPEED;
 61             y -= YSPEED;
 62             break;
 63         case RD:
 64             x += XSPEED;
 65             y += YSPEED;
 66             break;
 67 
 68         case STOP:
 69             break;
 70         }
 71     }
 72 
 73     public void locateDirection() {
 74         if (bL && !bU && !bR && !bD)
 75             dir = Direction.L;
 76         else if (bL && bU && !bR && !bD)
 77             dir = Direction.LU;
 78         else if (!bL && bU && !bR && !bD)
 79             dir = Direction.U;
 80         else if (!bL && bU && bR && !bD)
 81             dir = Direction.RU;
 82         else if (!bL && !bU && bR && !bD)
 83             dir = Direction.R;
 84         else if (!bL && !bU && bR && bD)
 85             dir = Direction.RD;
 86         else if (!bL && !bU && !bR && bD)
 87             dir = Direction.D;
 88         else if (bL && !bU && !bR && bD)
 89             dir = Direction.LD;
 90         else if (!bL && !bU && !bR && !bD)
 91             dir = Direction.STOP;
 92 
 93     }
 94 
 95     // 坦克自己向哪个方向移动,它自己最清楚;
 96     public void KeyPressed(KeyEvent e) {
 97         // 获得所按下的键所对应的虚拟码:
 98         // Returns the integer keyCode associated with the key in this event
 99         int key = e.getKeyCode();
100         // 判断不同的按键,指挥坦克的运动方向
101         switch (key) {
102         case KeyEvent.VK_LEFT:
103             bL = true;
104             break;
105         case KeyEvent.VK_UP:
106             bU = true;
107             break;
108         case KeyEvent.VK_RIGHT:
109             bR = true;
110             break;
111         case KeyEvent.VK_DOWN:
112             bD = true;
113             break;
114         }
115         locateDirection();
116     }
117 
118     public void keyReleased(KeyEvent e) {
119         int key = e.getKeyCode();
120         // 判断不同的按键,指挥坦克的运动方向
121         // 哪个键按下了,就把对应方向的布尔类型置为false
122         switch (key) {
123         case KeyEvent.VK_LEFT:
124             bL = false;
125             break;
126         case KeyEvent.VK_UP:
127             bU = false;
128             break;
129         case KeyEvent.VK_RIGHT:
130             bR = false;
131             break;
132         case KeyEvent.VK_DOWN:
133             bD = false;
134             break;
135         }
136         // 重新定位一下
137         locateDirection();
138     }
139 }
View Code

Missile:新添加的子弹类

 1 import java.awt.Color;
 2 import java.awt.Graphics;
 3 
 4 public class Missile {
 5     //炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
 6     public static final int XSPEED=10;
 7     public static final int YSPEED=10;
 8     //炮弹自己的三个属性
 9     int x;
10     int y;
11     Tank.Direction dir;
12     public Missile(int x, int y, Tank.Direction dir) {
13         this.x = x;
14         this.y = y;
15         this.dir = dir;
16     }
17     //炮弹自己的draw方法
18     public void draw(Graphics g){
19         Color c=g.getColor();
20         g.setColor(Color.BLACK);
21         //炮弹形状不要比坦克大,这里设置成10,10;
22         g.fillOval(x, y, 10, 10);
23         g.setColor(c);
24         move();
25     }
26     public  void move() {
27         switch (dir) {
28         case L:
29             x -= XSPEED;
30             break;
31         case R:
32             x += XSPEED;
33             break;
34         case U:
35             y -= YSPEED;
36             break;
37         case D:
38             y += YSPEED;
39             break;
40         case LU:
41             x -= XSPEED;
42             y -= YSPEED;
43             break;
44         case LD:
45             x -= XSPEED;
46             y += YSPEED;
47             break;
48         case RU:
49             x += XSPEED;
50             y -= YSPEED;
51             break;
52         case RD:
53             x += XSPEED;
54             y += YSPEED;
55             break;
56         //炮弹就没有STOP这个枚举类型的值了
57     /*    case STOP:
58             break;*/
59         }
60     }
61 }

TankClient:在TankClient中模拟一颗子弹

技术分享
 1 import java.awt.*;
 2 import java.awt.event.*;
 3 
 4 public class TankClient extends Frame {
 5     // 设置成常量,方便以后的改动
 6     public static final int GAME_WIDTH = 800;
 7     public static final int GAME_HEIGHT = 600;
 8     Tank myTank = new Tank(50, 50);
 9 
10     // 在TankClient中定义炮弹
11     Missile m = new Missile(50, 50, Tank.Direction.R);
12     // 定义虚拟图片,方便后期的一次性显示
13     Image offScreenImage = null;
14 
15     public void paint(Graphics g) {
16         // 把炮弹画出来
17         m.draw(g);
18         // 不改变前景色
19         myTank.draw(g);
20     }
21 
22     // 刷新操作
23     public void update(Graphics g) {
24         if (offScreenImage == null) {
25             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
26         }
27         Graphics gOffScreen = offScreenImage.getGraphics();
28         Color c = gOffScreen.getColor();
29         gOffScreen.setColor(Color.GREEN);
30         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
31         gOffScreen.setColor(c);
32         paint(gOffScreen);
33         g.drawImage(offScreenImage, 0, 0, null);
34     }
35 
36     public void lauchFrame() {
37         this.setLocation(400, 300);
38         this.setSize(GAME_WIDTH, GAME_HEIGHT);
39         this.setTitle("TankWar");
40         this.addWindowListener(new WindowAdapter() {
41             public void windowClosing(WindowEvent e) {
42                 System.exit(0);
43             }
44         });
45         this.setResizable(false);
46         this.setBackground(Color.GREEN);
47 
48         this.addKeyListener(new KeyMonitor());
49 
50         setVisible(true);
51 
52         new Thread(new PaintThread()).start();
53     }
54 
55     public static void main(String[] args) {
56         TankClient tc = new TankClient();
57         tc.lauchFrame();
58     }
59 
60     private class PaintThread implements Runnable {
61 
62         public void run() {
63             while (true) {
64                 repaint();
65                 try {
66                     Thread.sleep(50);
67                 } catch (InterruptedException e) {
68                     e.printStackTrace();
69                 }
70             }
71         }
72     }
73 
74     // 创建键盘时间监听
75     private class KeyMonitor extends KeyAdapter {
76 
77         // 直接调用myTank自己的方法根据相应的按键信息进行移动
78         public void keyPressed(KeyEvent e) {
79             myTank.KeyPressed(e);
80             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
81             // 而不是一直按照一个方向走下去
82         }
83         public void keyReleased(KeyEvent e){
84             myTank.keyReleased(e);
85         }
86 
87     }
88 }
View Code

版本1.1

功能:根据主战坦克的方向和位置,打出子弹,让子弹从坦克的中心打出来;

步骤:

       1)增加对Ctrl键的按键处理;

       2)根据“坦克打出一发子弹”这句话,来确定Tank中的方法fire,其返回值为Missle;

       3)根据Tank方向和位置设定子弹的方向和位置并new出来,然后返回(fire方法的实现)

注意: 掌握面向对象的思维方式来确定类应该具有的方法

下面这个图中左上角是坦克的的位置(x,y),圆心中心是坦克的中心,w是坦克的宽度,h是坦克的高度,我们计算炮弹的位置可以通过下面的公式计算:

注意x轴向右递增,y轴向下递增;

    int x=this.x+Tank.WIDTH/2-Missile.WIDTH/2;
    int y=this.y+Tank.HEIGHT/2-Missile.HEIGHT/2;

 

技术分享

具体代码实现:

Tank:

技术分享
  1 import java.awt.*;
  2 import java.awt.event.*;
  3 
  4 public class Tank {
  5     // 方便后期更改
  6     public static final int XSPEED = 5;
  7     public static final int YSPEED = 5;
  8     // 将坦克的高度和宽度设置为常量
  9     public static final int WIDTH = 30;
 10     public static final int HEIGHT = 30;
 11     TankClient tc = null;
 12     private int x;
 13     private int y;
 14     // 添加记录按键状态的布尔量
 15     private boolean bL = false;
 16     private boolean bR = false;
 17     private boolean bU = false;
 18     private boolean bD = false;
 19 
 20     // 添加代表方向的量(使用枚举)
 21     enum Direction {
 22         L, R, U, D, LU, LD, RU, RD, STOP
 23     };
 24 
 25     private Direction dir = Direction.STOP;
 26 
 27     public Tank(int x, int y) {
 28         this.x = x;
 29         this.y = y;
 30     }
 31 
 32     public Tank(int x, int y, TankClient tc) {
 33         // 调用那个有两个参数的构造方法
 34         this(x, y);
 35         // 在这个位置初始化tc
 36         this.tc = tc;
 37     }
 38 
 39     // Tank对象的draw方法
 40     public void draw(Graphics g) {
 41         Color c = g.getColor();
 42         g.setColor(Color.RED);
 43         g.fillOval(x, y, WIDTH, HEIGHT);
 44         g.setColor(c);
 45         move();
 46     }
 47 
 48     public void move() {
 49         switch (dir) {
 50         case L:
 51             x -= XSPEED;
 52             break;
 53         case R:
 54             x += XSPEED;
 55             break;
 56         case U:
 57             y -= YSPEED;
 58             break;
 59         case D:
 60             y += YSPEED;
 61             break;
 62         case LU:
 63             x -= XSPEED;
 64             y -= YSPEED;
 65             break;
 66         case LD:
 67             x -= XSPEED;
 68             y += YSPEED;
 69             break;
 70         case RU:
 71             x += XSPEED;
 72             y -= YSPEED;
 73             break;
 74         case RD:
 75             x += XSPEED;
 76             y += YSPEED;
 77             break;
 78 
 79         case STOP:
 80             break;
 81         }
 82     }
 83 
 84     public void locateDirection() {
 85         if (bL && !bU && !bR && !bD)
 86             dir = Direction.L;
 87         else if (bL && bU && !bR && !bD)
 88             dir = Direction.LU;
 89         else if (!bL && bU && !bR && !bD)
 90             dir = Direction.U;
 91         else if (!bL && bU && bR && !bD)
 92             dir = Direction.RU;
 93         else if (!bL && !bU && bR && !bD)
 94             dir = Direction.R;
 95         else if (!bL && !bU && bR && bD)
 96             dir = Direction.RD;
 97         else if (!bL && !bU && !bR && bD)
 98             dir = Direction.D;
 99         else if (bL && !bU && !bR && bD)
100             dir = Direction.LD;
101         else if (!bL && !bU && !bR && !bD)
102             dir = Direction.STOP;
103 
104     }
105 
106     // 坦克自己向哪个方向移动,它自己最清楚;
107     public void KeyPressed(KeyEvent e) {
108         // 获得所按下的键所对应的虚拟码:
109         // Returns the integer keyCode associated with the key in this event
110         int key = e.getKeyCode();
111         // 判断不同的按键,指挥坦克的运动方向
112         switch (key) {
113         // Ctrl键控制打出炮弹
114         case KeyEvent.VK_CONTROL:
115             // fire函数返回的是一个Missile类型的对象
116             tc.m = fire();
117             break;
118         case KeyEvent.VK_LEFT:
119             bL = true;
120             break;
121         case KeyEvent.VK_UP:
122             bU = true;
123             break;
124         case KeyEvent.VK_RIGHT:
125             bR = true;
126             break;
127         case KeyEvent.VK_DOWN:
128             bD = true;
129             break;
130         }
131         locateDirection();
132     }
133 
134     public void keyReleased(KeyEvent e) {
135         int key = e.getKeyCode();
136         // 判断不同的按键,指挥坦克的运动方向
137         // 哪个键按下了,就把对应方向的布尔类型置为false
138         switch (key) {
139         case KeyEvent.VK_LEFT:
140             bL = false;
141             break;
142         case KeyEvent.VK_UP:
143             bU = false;
144             break;
145         case KeyEvent.VK_RIGHT:
146             bR = false;
147             break;
148         case KeyEvent.VK_DOWN:
149             bD = false;
150             break;
151         }
152         // 重新定位一下
153         locateDirection();
154     }
155 
156     public Missile fire() {
157         // 计算子弹的位置,使得子弹从坦克的中间发出来
158         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
159         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
160         // 将坦克的位置和方向传递给炮弹
161         Missile m = new Missile(x, y, dir);
162         return m;
163     }
164 }
View Code

Missile:

技术分享
 1 import java.awt.Color;
 2 import java.awt.Graphics;
 3 
 4 public class Missile {
 5     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
 6     public static final int XSPEED = 10;
 7     public static final int YSPEED = 10;
 8     // 将子弹的高度和宽度设置为常量
 9     public static final int WIDTH = 10;
10     public static final int HEIGHT = 10;
11     // 炮弹自己的三个属性
12     int x;
13     int y;
14     Tank.Direction dir;
15 
16     public Missile(int x, int y, Tank.Direction dir) {
17         this.x = x;
18         this.y = y;
19         this.dir = dir;
20     }
21 
22     // 炮弹自己的draw方法
23     public void draw(Graphics g) {
24         Color c = g.getColor();
25         g.setColor(Color.BLACK);
26         // 炮弹形状不要比坦克大,这里设置成10,10;
27         g.fillOval(x, y, WIDTH, HEIGHT);
28         g.setColor(c);
29         move();
30     }
31 
32     public void move() {
33         switch (dir) {
34         case L:
35             x -= XSPEED;
36             break;
37         case R:
38             x += XSPEED;
39             break;
40         case U:
41             y -= YSPEED;
42             break;
43         case D:
44             y += YSPEED;
45             break;
46         case LU:
47             x -= XSPEED;
48             y -= YSPEED;
49             break;
50         case LD:
51             x -= XSPEED;
52             y += YSPEED;
53             break;
54         case RU:
55             x += XSPEED;
56             y -= YSPEED;
57             break;
58         case RD:
59             x += XSPEED;
60             y += YSPEED;
61             break;
62         // 炮弹就没有STOP这个枚举类型的值了
63         /*
64          * case STOP: break;
65          */
66         }
67     }
68 }
View Code

TankClient:

技术分享
 1 import java.awt.*;
 2 import java.awt.event.*;
 3 
 4 public class TankClient extends Frame {
 5     // 设置成常量,方便以后的改动
 6     public static final int GAME_WIDTH = 800;
 7     public static final int GAME_HEIGHT = 600;
 8 
 9     //将当前的TankClient对象传递给myTank;
10     //目的是方便我们在Tank这个类中访问m(炮弹对象)这个成员变量
11     //其实就是在Tank类中持有TankClient类对象的一个引用
12     Tank myTank = new Tank(50, 50,this);
13 
14     //炮弹不是固定的了,这里先不给出初始化
15     Missile m =null;
16     // 定义虚拟图片,方便后期的一次性显示
17     Image offScreenImage = null;
18 
19     public void paint(Graphics g) {
20         //m!=null的时候才将炮弹画出来,否则会报空指针异常
21         if(m!=null){
22             // 把炮弹画出来
23             m.draw(g);        
24         }
25         // 不改变前景色
26         myTank.draw(g);
27     }
28 
29     // 刷新操作
30     public void update(Graphics g) {
31         if (offScreenImage == null) {
32             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
33         }
34         Graphics gOffScreen = offScreenImage.getGraphics();
35         Color c = gOffScreen.getColor();
36         gOffScreen.setColor(Color.GREEN);
37         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
38         gOffScreen.setColor(c);
39         paint(gOffScreen);
40         g.drawImage(offScreenImage, 0, 0, null);
41     }
42 
43     public void lauchFrame() {
44         this.setLocation(400, 300);
45         this.setSize(GAME_WIDTH, GAME_HEIGHT);
46         this.setTitle("TankWar");
47         this.addWindowListener(new WindowAdapter() {
48             public void windowClosing(WindowEvent e) {
49                 System.exit(0);
50             }
51         });
52         this.setResizable(false);
53         this.setBackground(Color.GREEN);
54 
55         this.addKeyListener(new KeyMonitor());
56 
57         setVisible(true);
58 
59         new Thread(new PaintThread()).start();
60     }
61 
62     public static void main(String[] args) {
63         TankClient tc = new TankClient();
64         tc.lauchFrame();
65     }
66 
67     private class PaintThread implements Runnable {
68 
69         public void run() {
70             while (true) {
71                 repaint();
72                 try {
73                     Thread.sleep(50);
74                 } catch (InterruptedException e) {
75                     e.printStackTrace();
76                 }
77             }
78         }
79     }
80 
81     // 创建键盘时间监听
82     private class KeyMonitor extends KeyAdapter {
83 
84         // 直接调用myTank自己的方法根据相应的按键信息进行移动
85         public void keyPressed(KeyEvent e) {
86             myTank.KeyPressed(e);
87             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
88             // 而不是一直按照一个方向走下去
89         }
90         public void keyReleased(KeyEvent e){
91             myTank.keyReleased(e);
92         }
93 
94     }
95 }
View Code

版本1.2

功能:为了解决坦克停下也能打出炮弹的问题—画出炮筒,因为你会发现我们的版本1.1中坦克不动,按下Ctrl键是没有炮弹的,我们要实现坦克停着也要能发炮弹;

步骤:

        1)Tank类增加新的属性ptDir

        2)每次move后根据Tank新的方向确定炮筒的方向

        3)将炮筒用直线的形式表现出来

技术分享

相较于炮弹的坐标(即图中左上角的x,y)值,计算炮筒(即我们用一条直线代表炮筒)的另一头(也就是炮筒的出弹口)的坐标,在代码中有体现;

Tank:

技术分享
  1 import java.awt.*;
  2 import java.awt.event.*;
  3 
  4 public class Tank {
  5     // 方便后期更改
  6     public static final int XSPEED = 5;
  7     public static final int YSPEED = 5;
  8     // 将坦克的高度和宽度设置为常量
  9     public static final int WIDTH = 30;
 10     public static final int HEIGHT = 30;
 11     TankClient tc = null;
 12     private int x;
 13     private int y;
 14     // 添加记录按键状态的布尔量
 15     private boolean bL = false;
 16     private boolean bR = false;
 17     private boolean bU = false;
 18     private boolean bD = false;
 19 
 20     // 添加代表方向的量(使用枚举)
 21     enum Direction {
 22         L, R, U, D, LU, LD, RU, RD, STOP
 23     };
 24 
 25     private Direction dir = Direction.STOP;
 26 
 27     // 定义炮筒的方向,我们想办法将炮筒的方法调整成和坦克移动方向一致;
 28     // 我们这里会用一条直线来表示炮筒:模拟炮筒
 29     // 我们要根据炮筒的方向画直线表示炮筒
 30     Direction ptDir = Direction.D;
 31 
 32     public Tank(int x, int y) {
 33         this.x = x;
 34         this.y = y;
 35     }
 36 
 37     public Tank(int x, int y, TankClient tc) {
 38         // 调用那个有两个参数的构造方法
 39         this(x, y);
 40         // 在这个位置初始化tc
 41         this.tc = tc;
 42     }
 43 
 44     // Tank对象的draw方法
 45     public void draw(Graphics g) {
 46         Color c = g.getColor();
 47         g.setColor(Color.RED);
 48         g.fillOval(x, y, WIDTH, HEIGHT);
 49         g.setColor(c);
 50         // 根据炮筒的方向画直线来表示我们坦克的炮筒
 51         switch (ptDir) {
 52         case L:
 53             // 画直线:四个参数分别代表:坦克中心点的坐标 直线的另一头的的坐标
 54             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
 55                     + Tank.HEIGHT / 2);
 56             break;
 57         case R:
 58             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
 59                     y + Tank.HEIGHT / 2);
 60 
 61             break;
 62         case U:
 63             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
 64                     / 2, y);
 65 
 66             break;
 67         case D:
 68             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH
 69                     / 2, y + Tank.HEIGHT);
 70 
 71             break;
 72         case LU:
 73             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y);
 74             break;
 75         case LD:
 76             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x, y
 77                     + Tank.HEIGHT);
 78 
 79             break;
 80         case RU:
 81             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
 82                     y);
 83 
 84             break;
 85         case RD:
 86             g.drawLine(x + Tank.WIDTH / 2, y + Tank.HEIGHT / 2, x + Tank.WIDTH,
 87                     y + Tank.HEIGHT);
 88 
 89             break;
 90         /*
 91          * case STOP: break;
 92          */
 93         }
 94         move();
 95     }
 96 
 97     public void move() {
 98         switch (dir) {
 99         case L:
100             x -= XSPEED;
101             break;
102         case R:
103             x += XSPEED;
104             break;
105         case U:
106             y -= YSPEED;
107             break;
108         case D:
109             y += YSPEED;
110             break;
111         case LU:
112             x -= XSPEED;
113             y -= YSPEED;
114             break;
115         case LD:
116             x -= XSPEED;
117             y += YSPEED;
118             break;
119         case RU:
120             x += XSPEED;
121             y -= YSPEED;
122             break;
123         case RD:
124             x += XSPEED;
125             y += YSPEED;
126             break;
127 
128         case STOP:
129             break;
130         }
131         // 如果坦克不是停着的,则将炮筒调整至和坦克移动的方向相同
132         if (this.dir != Direction.STOP) {
133             this.ptDir = this.dir;
134         }
135     }
136 
137     public void locateDirection() {
138         if (bL && !bU && !bR && !bD)
139             dir = Direction.L;
140         else if (bL && bU && !bR && !bD)
141             dir = Direction.LU;
142         else if (!bL && bU && !bR && !bD)
143             dir = Direction.U;
144         else if (!bL && bU && bR && !bD)
145             dir = Direction.RU;
146         else if (!bL && !bU && bR && !bD)
147             dir = Direction.R;
148         else if (!bL && !bU && bR && bD)
149             dir = Direction.RD;
150         else if (!bL && !bU && !bR && bD)
151             dir = Direction.D;
152         else if (bL && !bU && !bR && bD)
153             dir = Direction.LD;
154         else if (!bL && !bU && !bR && !bD)
155             dir = Direction.STOP;
156 
157     }
158 
159     // 坦克自己向哪个方向移动,它自己最清楚;
160     public void KeyPressed(KeyEvent e) {
161         // 获得所按下的键所对应的虚拟码:
162         // Returns the integer keyCode associated with the key in this event
163         int key = e.getKeyCode();
164         // 判断不同的按键,指挥坦克的运动方向
165         switch (key) {
166         // Ctrl键控制打出炮弹
167         case KeyEvent.VK_CONTROL:
168             // fire函数返回的是一个Missile类型的对象
169             tc.m = fire();
170             break;
171         case KeyEvent.VK_LEFT:
172             bL = true;
173             break;
174         case KeyEvent.VK_UP:
175             bU = true;
176             break;
177         case KeyEvent.VK_RIGHT:
178             bR = true;
179             break;
180         case KeyEvent.VK_DOWN:
181             bD = true;
182             break;
183         }
184         locateDirection();
185     }
186 
187     public void keyReleased(KeyEvent e) {
188         int key = e.getKeyCode();
189         // 判断不同的按键,指挥坦克的运动方向
190         // 哪个键按下了,就把对应方向的布尔类型置为false
191         switch (key) {
192         case KeyEvent.VK_LEFT:
193             bL = false;
194             break;
195         case KeyEvent.VK_UP:
196             bU = false;
197             break;
198         case KeyEvent.VK_RIGHT:
199             bR = false;
200             break;
201         case KeyEvent.VK_DOWN:
202             bD = false;
203             break;
204         }
205         // 重新定位一下
206         locateDirection();
207     }
208 
209     public Missile fire() {
210         // 计算子弹的位置,使得子弹从坦克的中间发出来
211         int x = this.x + Tank.WIDTH / 2 - Missile.WIDTH / 2;
212         int y = this.y + Tank.HEIGHT / 2 - Missile.HEIGHT / 2;
213         // 这个时候我们就根据炮筒的方向来发炮弹了,之前是根据坦克的方向来发炮弹
214         Missile m = new Missile(x, y, dir);
215         return m;
216     }
217 }
View Code

Missile:

技术分享
 1 import java.awt.Color;
 2 import java.awt.Graphics;
 3 
 4 public class Missile {
 5     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
 6     public static final int XSPEED = 10;
 7     public static final int YSPEED = 10;
 8     // 将子弹的高度和宽度设置为常量
 9     public static final int WIDTH = 10;
10     public static final int HEIGHT = 10;
11     // 炮弹自己的三个属性
12     int x;
13     int y;
14     Tank.Direction dir;
15 
16     public Missile(int x, int y, Tank.Direction dir) {
17         this.x = x;
18         this.y = y;
19         this.dir = dir;
20     }
21 
22     // 炮弹自己的draw方法
23     public void draw(Graphics g) {
24         Color c = g.getColor();
25         g.setColor(Color.BLACK);
26         // 炮弹形状不要比坦克大,这里设置成10,10;
27         g.fillOval(x, y, WIDTH, HEIGHT);
28         g.setColor(c);
29         move();
30     }
31 
32     public void move() {
33         switch (dir) {
34         case L:
35             x -= XSPEED;
36             break;
37         case R:
38             x += XSPEED;
39             break;
40         case U:
41             y -= YSPEED;
42             break;
43         case D:
44             y += YSPEED;
45             break;
46         case LU:
47             x -= XSPEED;
48             y -= YSPEED;
49             break;
50         case LD:
51             x -= XSPEED;
52             y += YSPEED;
53             break;
54         case RU:
55             x += XSPEED;
56             y -= YSPEED;
57             break;
58         case RD:
59             x += XSPEED;
60             y += YSPEED;
61             break;
62         // 炮弹就没有STOP这个枚举类型的值了
63         /*
64          * case STOP: break;
65          */
66         }
67     }
68 }
View Code

TankClient:

技术分享
 1 import java.awt.*;
 2 import java.awt.event.*;
 3 
 4 public class TankClient extends Frame {
 5     // 设置成常量,方便以后的改动
 6     public static final int GAME_WIDTH = 800;
 7     public static final int GAME_HEIGHT = 600;
 8 
 9     // 将当前的TankClient对象传递给myTank;
10     // 目的是方便我们在Tank这个类中访问m(炮弹对象)这个成员变量
11     // 其实就是在Tank类中持有TankClient类对象的一个引用
12     Tank myTank = new Tank(50, 50, this);
13 
14     // 炮弹不是固定的了,这里先不给出初始化
15     Missile m = null;
16     // 定义虚拟图片,方便后期的一次性显示
17     Image offScreenImage = null;
18 
19     public void paint(Graphics g) {
20         // m!=null的时候才将炮弹画出来,否则会报空指针异常
21         if (m != null) {
22             // 把炮弹画出来
23             m.draw(g);
24         }
25         // 不改变前景色
26         myTank.draw(g);
27     }
28 
29     // 刷新操作
30     public void update(Graphics g) {
31         if (offScreenImage == null) {
32             offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
33         }
34         Graphics gOffScreen = offScreenImage.getGraphics();
35         Color c = gOffScreen.getColor();
36         gOffScreen.setColor(Color.GREEN);
37         gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
38         gOffScreen.setColor(c);
39         paint(gOffScreen);
40         g.drawImage(offScreenImage, 0, 0, null);
41     }
42 
43     public void lauchFrame() {
44         this.setLocation(400, 300);
45         this.setSize(GAME_WIDTH, GAME_HEIGHT);
46         this.setTitle("TankWar");
47         this.addWindowListener(new WindowAdapter() {
48             public void windowClosing(WindowEvent e) {
49                 System.exit(0);
50             }
51         });
52         this.setResizable(false);
53         this.setBackground(Color.GREEN);
54 
55         this.addKeyListener(new KeyMonitor());
56 
57         setVisible(true);
58 
59         new Thread(new PaintThread()).start();
60     }
61 
62     public static void main(String[] args) {
63         TankClient tc = new TankClient();
64         tc.lauchFrame();
65     }
66 
67     private class PaintThread implements Runnable {
68 
69         public void run() {
70             while (true) {
71                 repaint();
72                 try {
73                     Thread.sleep(50);
74                 } catch (InterruptedException e) {
75                     e.printStackTrace();
76                 }
77             }
78         }
79     }
80 
81     // 创建键盘时间监听
82     private class KeyMonitor extends KeyAdapter {
83 
84         // 直接调用myTank自己的方法根据相应的按键信息进行移动
85         public void keyPressed(KeyEvent e) {
86             myTank.KeyPressed(e);
87             // 添加了处理键抬起的事件,可以控制坦克起步以后的状态
88             // 而不是一直按照一个方向走下去
89         }
90 
91         public void keyReleased(KeyEvent e) {
92             myTank.keyReleased(e);
93         }
94 
95     }
96 }
View Code

经过这个版本我们的坦克可以根据炮筒的方向发出炮弹了;

版本1.3

功能:打出多发炮弹

步骤:

      1)使用容器装炮弹

      2)每当抬起Ctrl键就往容器中加入新的炮弹

      3)逐一画出每一发炮弹

注意: 泛型的使用

Tank:

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

Missle:

技术分享
 1 import java.awt.Color;
 2 import java.awt.Graphics;
 3 
 4 public class Missile {
 5     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
 6     public static final int XSPEED = 10;
 7     public static final int YSPEED = 10;
 8     // 将子弹的高度和宽度设置为常量
 9     public static final int WIDTH = 10;
10     public static final int HEIGHT = 10;
11     // 炮弹自己的三个属性
12     int x;
13     int y;
14     Tank.Direction dir;
15 
16     public Missile(int x, int y, Tank.Direction dir) {
17         this.x = x;
18         this.y = y;
19         this.dir = dir;
20     }
21 
22     // 炮弹自己的draw方法
23     public void draw(Graphics g) {
24         Color c = g.getColor();
25         g.setColor(Color.BLACK);
26         // 炮弹形状不要比坦克大,这里设置成10,10;
27         g.fillOval(x, y, WIDTH, HEIGHT);
28         g.setColor(c);
29         move();
30     }
31 
32     public void move() {
33         switch (dir) {
34         case L:
35             x -= XSPEED;
36             break;
37         case R:
38             x += XSPEED;
39             break;
40         case U:
41             y -= YSPEED;
42             break;
43         case D:
44             y += YSPEED;
45             break;
46         case LU:
47             x -= XSPEED;
48             y -= YSPEED;
49             break;
50         case LD:
51             x -= XSPEED;
52             y += YSPEED;
53             break;
54         case RU:
55             x += XSPEED;
56             y -= YSPEED;
57             break;
58         case RD:
59             x += XSPEED;
60             y += YSPEED;
61             break;
62         // 炮弹就没有STOP这个枚举类型的值了
63         /*
64          * case STOP: break;
65          */
66         }
67     }
68 }
View Code

TankClient:

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

版本1.4.1

功能:解决炮弹不消亡的问题,解决坦克出界的问题

步骤:1)加入控制炮弹生死的量bLive(Missle):private boolean live = true;

        2)当炮弹已经死去就不需要对其重画:我们在Missile类中也持有一个TankClient的引用,在炮弹出界的时候就可以从装炮弹的missiles集合中去除该炮弹,不再对其重画

        3)当炮弹飞出边界就死亡,判断方式:

1 if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
2                 || y > TankClient.GAME_HEIGHT) {
3             live = false;
4             
5         }

        4)当炮弹死亡就从容器中去除:

1     if (x < 0 || y < 0 || x > TankClient.GAME_WIDTH
2                 || y > TankClient.GAME_HEIGHT) {
3             live = false;
4             //炮弹消亡,从集合中删除该元素
5             tc.missiles.remove(this);
6         }

在Tank类中,当我们在fire函数中生成炮弹的时候使用Missile m = new Missile(x, y, ptDir,tc);这个构造方法,将tc也作为参数传入,运行程序你会发现当所有炮弹触碰边界消亡的时候,missleCount的值变成了0;

注意: 将思维转化为代码

具体代码实现:

Tank:

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

Missile:

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

TankCLient:

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

版本1.4.2

功能:解决坦克可以出开出边界的问题,代码中相较于上一个版本只有Tank类的move方法中做了修改,其他两个类没有进行更改;

 1 if(x<0){
 2             x=0;
 3         }
 4         //因为我们的游戏界面有那个missileCount标签,所以在y轴方向不能用y是否<0进行判断
 5         //否则的话我们的坦克可以从上面出去
 6         if(y<50){
 7             y=50;
 8         }
 9         if(x+Tank.WIDTH>TankClient.GAME_WIDTH){
10             x=TankClient.GAME_WIDTH-Tank.WIDTH;
11         }
12         if(y+Tank.HEIGHT>TankClient.GAME_HEIGHT){
13             y=TankClient.GAME_HEIGHT-Tank.HEIGHT;
14         }

具体代码实现:

Tank类:

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

版本1.5

功能:画一辆敌人的坦克

步骤:

        1)加入区别敌我的量good:private boolean good;

        2)根据敌我的不同设置不同的颜色;

        3)更新Tank的构造函数,加入good:可能会有其他方法调用了我们的构造方法,我们更新Tank的构造函数会导致原先的调用失败,为方便更改我们需要知道都有那些地方调用了我们的Tank构造方法:

    可以使用下面的方式:选中Tank类名->右键->选择Open Call Hierarchy->在控制台位置就可以看到在那些位置我们需要在更新完Tank构造方法后进行更改;

        4)TankClient中new出敌人的坦克并画出:

1 //我们这里new我们自己的坦克,用下面的方式
2 Tank myTank = new Tank(50, 50, true,this);
3 //新建敌方坦克
4 Tank enemyTank=new Tank(100,100,false,this);

具体代码实现:(只有Tank类和TankClient类做了更改)

Tank:

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

TankClient:

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

版本1.6

功能: 将敌人坦克击毙

分析: 一颗子弹击中敌人坦克

步骤:

        1)Missle中加入hitTank(Tank)方法,返回布尔类型

        2)碰撞检测的辅助类Rectangle

        3为Tank和Missle都加入getRect方法

        4)当击中敌人坦克时,坦克被打死,子弹也死去

        5)增加控制Tank生死的量live

        6)如果死去就不画了

注意: 不要照抄代码,沿着思路往里加入代码

具体代码实现:

Tank:

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

Missile:

技术分享
  1 import java.awt.Color;
  2 import java.awt.Graphics;
  3 import java.awt.Rectangle;
  4 
  5 public class Missile {
  6     // 炮弹的移动速度,不要比坦克的移动速度慢,不然你看到的是满屏的坦克追着炮弹跑
  7     public static final int XSPEED = 10;
  8     public static final int YSPEED = 10;
  9     // 将子弹的高度和宽度设置为常量
 10     public static final int WIDTH = 10;
 11     public static final int HEIGHT = 10;
 12     // 炮弹自己的三个属性
 13     int x;
 14     int y;
 15     Tank.Direction dir;
 16 
 17     // 定义一个布尔类型的变量来判断炮弹是否已经消亡
 18     private boolean live = true;
 19     //我们在Missile类中也持有一个TankClient的引用
 20     //在炮弹出界的时候就可以从装炮弹的missiles集合中去除该炮弹,不再对其重画
 21     private TankClient tc;
 22 
 23     public boolean isLive() {
 24         return live;
 25     }
 26 
 27     public Missile(int x, int y, Tank.Direction dir) {
 28         this.x = x;
 29         this.y = y;
 30         this.dir = dir;
 31     }
 32     public Missile(int x,int y,Tank.Direction dir,TankClient tc){
 33         this(x, y, dir);
 34         this.tc=tc;
 35     }
 36 
 37     // 炮弹自己的draw方法
 38     public void draw(Graphics g) {
 39         //炮弹消亡就不需要再画出来了
 40         if(!isLive()){
 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             tc.missiles.remove(this);
 94         }
 95     }
 96     public boolean hitTank(Tank t){
 97         //炮弹的方框和坦克的方框碰在一起了并且坦克是存活着的
 98         if(this.getRect().intersects(t.getRect())&&t.isLive()){
 99             t.setLive(false);
100             this.live=false;
101             return true;
102         }
103         return false;
104     }
105     //碰撞检测类Rectangle
106     //拿到包围在炮弹周围的小方块
107     public Rectangle getRect(){
108         return new Rectangle(x,y,WIDTH,HEIGHT);
109     }
110 }
View Code

TankClient:

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

未完待续。。。。。。

 

坦克大战(版本1.0-版本1.6)

标签:

原文地址:http://www.cnblogs.com/ysw-go/p/5509713.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!