标签:
版本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 }
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 }
版本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 }
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 }
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 }
版本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 }
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 }
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 }
经过这个版本我们的坦克可以根据炮筒的方向发出炮弹了;
版本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 }
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 }
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 }
版本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 }
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 }
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 }
版本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 }
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 }
版本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 }
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 }
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 }
未完待续。。。。。。
标签:
原文地址:http://www.cnblogs.com/ysw-go/p/5509713.html