> 文章列表 > java坦克大战(2.0)

java坦克大战(2.0)

java坦克大战(2.0)

坦克大战(2.0)

在前面的坦克大战1.0已经完成了移动 绘制我方/敌方坦克。
接下来会运用到前面学习的多线程的基础,来实现发射子弹的效果

发射子弹

想要坦克发射子弹,可以用以下思路

按J发射子弹
将子弹变成一个线程,让子弹的x,y值不停变换,画板重绘,当子弹到达边界时就将线程销毁

步骤如下:

子弹类

  1. 创建子弹类shoot,定义对应的x,y,发射的方向,移动的速度,实现Runnable接口
  2. 在run方法中根据坦克的朝向,改变子弹的x或者y,且当子弹达到边界时就销毁

子弹类(线程)定义好了自然要启动,而子弹是从坦克发送出来的,所以启动线程就在坦克类中定义

坦克类

  1. 定义一个子弹类的属性,但先不赋值,定义一个发射子弹的方法fire(),用于确定子弹发射的初始位置,根据坦克的位置创建不同的子弹类的实例
  2. 创建完子弹后,启动子弹线程

监听器

  1. 在按下键盘做出操作的方法,新建一个当按下J的时候就调用fire方法

画板类绘制子弹
1.因为子弹类是只有在按J之后才创建的,所以不能直接在paint绘制,而是需要加if判断子弹类不是空,且子弹线程是正在运行的,才绘制
2.又因为paint现在是只有在移动的时候才会重新绘制,但是子弹要求是一直在动的,所以需要将MyPanel画板类变成一个线程,run方法中一直不断重绘,才能让子弹动起来,启动放在画框类

//shoot类
public class shoot implements Runnable{int x;//子弹的x坐标int y;//子弹的y坐标int direct;//子弹的方向int speed = 2;//子弹的速度boolean lief = true;//子弹线程的状态public shoot(int x, int y, int direct) {this.x = x;this.y = y;this.direct = direct;}@Overridepublic void run() {while (lief){//因为要让子弹移动的别太快,所以休眠一会try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}//根据方向,开始移动子弹switch (direct){case 0://当坦克朝上时y-=speed;break;case 1://当坦克朝右时x+=speed;break;case 2://当坦克朝下时y+=speed;break;case 3://当坦克朝左边时x-=speed;break;}//当子弹到达边界时if (!(x<500&&x>0 && y<600&& y>0)){lief = false;break;}}}
}
//Tank类
public class Tank {int x;int y;int direct;public Tank(int x, int y, int direct) {this.x = x;this.y = y;this.direct = direct;}shoot shoot;//定义一个子弹属性public void fire(){switch (direct){//根据此时坦克的朝向创建对应的子弹发送初始位置case 0://当坦克朝上时System.out.println("上");shoot = new shoot(x+20,y-6,direct);break;case 1://当坦克朝右时shoot = new shoot(x+60,y+24,direct);break;case 2://当坦克朝下时System.out.println("下");shoot = new shoot(x+21,y+60,direct);break;case 3://当坦克朝左边时System.out.println("左");shoot = new shoot(x-8,y+23,direct);break;}//创建完子弹,启动子弹new Thread(shoot).start();}
}
//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{int speek = 1;Tank tank;Vector<EnemyTanks> enemyTanks = new Vector<>();public MyPanel(){tank = new Tank(20,460,0);for (int i = 0; i < 3; i++) {EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);enemyTanks.add(enemyTank);}}@Overridepublic void paint(Graphics g) {super.paint(g);System.out.println("调用paint");g.fillRect(0,0,500,600);//画我方坦克drawTank(tank.x,tank.y,g,tank.direct,0);for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = enemyTanks.get(i);//画敌方坦克drawTank(e.x,e.y,g,e.direct,1);}//画子弹if (tank.shoot!= null && tank.shoot.lief!=false){g.setColor(Color.PINK);g.drawRect(tank.shoot.x,tank.shoot.y,3,3);}}/@param x 坦克的起始横坐标@param y 坦克的起始纵坐标@param g 画笔@param direct 根据方向绘制不同朝向的坦克@param type  根据0或者1 绘制不同颜色的坦克*/public void drawTank(int x,int y,Graphics g,int direct,int type){switch (type){case 0:g.setColor(Color.PINK);//自己的坦克就粉色break;case 1:g.setColor(Color.RED);//敌人的坦克就红色break;}switch (direct){//0上,1右,2下,3左case 0://0表示绘制方向朝上的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+22,y-5,x+22,y+25);break;case 1://1表示绘制方向朝右的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x+58,y+25);break;case 2://2表示绘制方向朝下的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+12,25,25);g.drawLine(x+22,y+55,x+22,y+25);break;case 3://3表示绘制方向朝左的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x-8,y+25);break;default://其他情况暂不考虑break;}}@Override //字符输出时,该方法触发public void keyTyped(KeyEvent e) {}@Override//有按键按下时,该方法触发public void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W){//前进moveUp();tank.direct = 0;}else if(e.getKeyCode() == KeyEvent.VK_S){moveDown();tank.direct = 2;}else if(e.getKeyCode() == KeyEvent.VK_A){moveLeft();tank.direct = 3;}else if(e.getKeyCode() == KeyEvent.VK_D){moveRight();tank.direct = 1;}else if(e.getKeyCode() == 32){System.out.println("氮气加速~");speek +=5;}else{System.out.println(e.getKeyCode());}if (e.getKeyCode() == KeyEvent.VK_J){tank.fire();}repaint();}@Override//有按键松开时,该方法触发public void keyReleased(KeyEvent e) {}public void moveUp(){tank.y-=speek;}public void moveLeft(){tank.x-=speek;}public void moveRight(){tank.x+=speek;}public void moveDown(){tank.y+=speek;}@Overridepublic void run() {while (true){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}this.repaint();}}
}
//Window类
public class Window extends JFrame {MyPanel m = null;public Window(){m  = new MyPanel();this.add(m);new Thread(m).start();this.setSize(500,600);this.addKeyListener(m);//让JFram监听m的事件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);}public static void main(String[] args) {new Window();}
}

让敌方坦克发射子弹

思路在EnemyTanks类使用Vector类创建一个集合,用于存放子弹
然后在MyPanel类初始化敌方坦克的敌方,每创建一个敌方坦克就初始化一个子弹对象,同时启动这个子弹
接着到绘制敌方坦克的敌方,每绘制一个敌人坦克,就绘制一个敌方坦克的子弹

//MyPanel
public class MyPanel extends Panel implements KeyListener,Runnable{int speek = 1;Tank tank;Vector<EnemyTanks> enemyTanks = new Vector<>();public MyPanel(){//初始化我方坦克tank = new Tank(20,460,0);//初始化敌方坦克for (int i = 0; i < 3; i++) {EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);//初始化敌方坦克子弹shoot shoot = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);//将子弹放入敌方坦克的shoots集合进行管理enemyTank.shoots.add(shoot);//启动敌方坦克的子弹new Thread(shoot).start();enemyTanks.add(enemyTank);}}@Overridepublic void paint(Graphics g) {super.paint(g);System.out.println("调用paint");g.fillRect(0,0,500,600);//画我方坦克drawTank(tank.x,tank.y,g,tank.direct,0);//画敌方坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = enemyTanks.get(i);drawTank(e.x,e.y,g,e.direct,1);//画出敌方坦克子弹for (int j = 0; j < e.shoots.size(); j++) {shoot s = e.shoots.get(j);if (s.lief){g.setColor(Color.RED);g.drawRect(s.x+20,s.y+60,3,3);}else {e.shoots.remove(s);}}}//画子弹if (tank.shoot!= null && tank.shoot.lief!=false){g.setColor(Color.PINK);g.drawRect(tank.shoot.x,tank.shoot.y,3,3);}}/@param x 坦克的起始横坐标@param y 坦克的起始纵坐标@param g 画笔@param direct 根据方向绘制不同朝向的坦克@param type  根据0或者1 绘制不同颜色的坦克*/public void drawTank(int x,int y,Graphics g,int direct,int type){switch (type){case 0:g.setColor(Color.PINK);//自己的坦克就粉色break;case 1:g.setColor(Color.RED);//敌人的坦克就红色break;}switch (direct){//0上,1右,2下,3左case 0://0表示绘制方向朝上的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+22,y-5,x+22,y+25);break;case 1://1表示绘制方向朝右的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x+58,y+25);break;case 2://2表示绘制方向朝下的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+12,25,25);g.drawLine(x+22,y+55,x+22,y+25);break;case 3://3表示绘制方向朝左的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x-8,y+25);break;default://其他情况暂不考虑break;}}@Override //字符输出时,该方法触发public void keyTyped(KeyEvent e) {}@Override//有按键按下时,该方法触发public void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W){//前进moveUp();tank.direct = 0;}else if(e.getKeyCode() == KeyEvent.VK_S){moveDown();tank.direct = 2;}else if(e.getKeyCode() == KeyEvent.VK_A){moveLeft();tank.direct = 3;}else if(e.getKeyCode() == KeyEvent.VK_D){moveRight();tank.direct = 1;}else if(e.getKeyCode() == 32){System.out.println("氮气加速~");speek +=5;}else{System.out.println(e.getKeyCode());}if (e.getKeyCode() == KeyEvent.VK_J){tank.fire();}repaint();}@Override//有按键松开时,该方法触发public void keyReleased(KeyEvent e) {}public void moveUp(){tank.y-=speek;}public void moveLeft(){tank.x-=speek;}public void moveRight(){tank.x+=speek;}public void moveDown(){tank.y+=speek;}@Overridepublic void run() {while (true){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}this.repaint();}}
}

当子弹击中敌方坦克,敌人就消失

判断子弹是否击中坦克,肯定是看子弹有没有进入到坦克的x和y的范围内。
根据这个思路可以想到,坦克几乎是有两种形态,上下 和 左右,这两类坦克范围的x和y是有不同的,所以根据这个特性,写出两个x和y的范围,加以判断即可。是否击中可以判断了,让被被击中坦克消失直接将该坦克从集合中删除即可。

根据上面的思路,可以将这个思路封装到一个方法中,再加上一个条件,先在敌人坦克类加一个boolean属性Live。如果子弹击中敌人坦克,就把敌人坦克的live改成false,同时也把打出的子弹改成false。

那么由于我们不知道什么时候子弹会击中敌人的坦克,所以需要在MyPanel类的run方法中调用此方法,且我们不知道到底会击中哪个坦克,所以需要便利敌人坦克的集合。

根据上面的判断,当我方子弹击中敌人坦克时,敌人的坦克的live会变成false。如果这时在MyPanel类绘制敌人坦克的时候,加一个判断,只有当敌人坦克的live是true的时候才进行绘制。那么一旦子弹击中敌人坦克,在下一次的重绘中就不会绘制被击中的坦克,以此就可以实现敌人坦克消失的效果。


//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{int speek = 1;Tank tank;Vector<EnemyTanks> enemyTanks = new Vector<>();public MyPanel(){//初始化我方坦克tank = new Tank(20,460,0);//初始化敌方坦克for (int i = 0; i < 3; i++) {EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);//初始化敌方坦克子弹shoot shoot = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);//将子弹放入敌方坦克的shoots集合进行管理enemyTank.shoots.add(shoot);//启动敌方坦克的子弹new Thread(shoot).start();enemyTanks.add(enemyTank);}}@Overridepublic void paint(Graphics g) {super.paint(g);System.out.println("调用paint");g.fillRect(0,0,500,600);//画我方坦克drawTank(tank.x,tank.y,g,tank.direct,0);//画敌方坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = enemyTanks.get(i);if (e.live) {//如果敌方坦克的live不是false才进行绘制drawTank(e.x, e.y, g, e.direct, 1);//画出敌方坦克子弹for (int j = 0; j < e.shoots.size(); j++) {shoot s = e.shoots.get(j);if (s.lief) {g.setColor(Color.RED);g.drawRect(s.x + 20, s.y + 60, 3, 3);} else {e.shoots.remove(s);}}}}//画子弹if (tank.shoot!= null && tank.shoot.lief!=false){g.setColor(Color.PINK);g.drawRect(tank.shoot.x,tank.shoot.y,3,3);}}/@param x 坦克的起始横坐标@param y 坦克的起始纵坐标@param g 画笔@param direct 根据方向绘制不同朝向的坦克@param type  根据0或者1 绘制不同颜色的坦克*/public void drawTank(int x,int y,Graphics g,int direct,int type){switch (type){case 0:g.setColor(Color.PINK);//自己的坦克就粉色break;case 1:g.setColor(Color.RED);//敌人的坦克就红色break;}switch (direct){//0上,1右,2下,3左case 0://0表示绘制方向朝上的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+22,y-5,x+22,y+25);break;case 1://1表示绘制方向朝右的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x+58,y+25);break;case 2://2表示绘制方向朝下的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+12,25,25);g.drawLine(x+22,y+55,x+22,y+25);break;case 3://3表示绘制方向朝左的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x-8,y+25);break;default://其他情况暂不考虑break;}}@Override //字符输出时,该方法触发public void keyTyped(KeyEvent e) {}@Override//有按键按下时,该方法触发public void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W){//前进moveUp();tank.direct = 0;}else if(e.getKeyCode() == KeyEvent.VK_S){moveDown();tank.direct = 2;}else if(e.getKeyCode() == KeyEvent.VK_A){moveLeft();tank.direct = 3;}else if(e.getKeyCode() == KeyEvent.VK_D){moveRight();tank.direct = 1;}else if(e.getKeyCode() == 32){System.out.println("氮气加速~");speek +=5;}else{System.out.println(e.getKeyCode());}if (e.getKeyCode() == KeyEvent.VK_J){tank.fire();}repaint();}@Override//有按键松开时,该方法触发public void keyReleased(KeyEvent e) {}public void moveUp(){tank.y-=speek;}public void moveLeft(){tank.x-=speek;}public void moveRight(){tank.x+=speek;}public void moveDown(){tank.y+=speek;}//此方法判断我方坦克子弹是否击中敌人坦克public static void himEnemyTank(shoot s , EnemyTanks e ){//上下和左右的x,y是相同的,所以写两个判断即可switch (e.direct){case 0:case 2:if (s.x>e.x&&s.x< e.x+40&& s.y>e.y&&s.y<e.y+60){//如果此条件成立就代表,地方在朝上或者朝下的方向被击中了e.live = false;s.lief = false;}break;case 1:case 3:if (s.x>e.x&&s.x< e.x+60&& s.y>e.y&&s.y<e.y+40){//如果此条件成立就代表,地方在朝左或者朝右的方向被击中了e.live = false;s.lief = false;}}}@Overridepublic void run() {while (true){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (tank.shoot!=null && tank.shoot.lief != false){//如果我方坦克的子弹创建了,且线程未结束就对每个敌人坦克进行判断for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = this.enemyTanks.get(i);himEnemyTank(tank.shoot,e);}}this.repaint();}}
}

除此之外还可以在MyPanel类的run方法中再加一个判断,如果敌人坦克的live不是true的时候,就将此坦克从集合中删除,以防消失后还在吃子弹,因为上面只是不绘制被击中的坦克,实际上他还是存在的。所以再加一个判断,从集合中删除掉,就不会吃子弹了。以此实现真正的死亡

for (int i = 0; i < enemyTanks.size(); i++) {//如果敌人坦克被击中,那么就把此坦克从集合中删除if (!(enemyTanks.get(i).live)){enemyTanks.remove(i);}}

坦克死亡爆炸效果

首先可以将爆炸效果写成一个类 boom
再分析
爆炸效果其实就是几张图片快速切换重绘,要实现这个效果需分析几点。
爆炸效果肯定是坦克的当前位置,所以也得有x和y。
再因为爆炸效果是几张图片不断重绘,所以可以添加一个int值作为生命周期,不同的生命周期绘制不同的图片
再写一个生命周期不断–的方法,当生命周期为0时爆炸效果结束。

boom类定义好了后,在MyPanel类定义一个爆炸的集合,用于保存爆炸效果。
再定义三张图片,分别是爆炸开始到结束的效果。
然后在判断是否击中坦克的方法中,每当判断击中坦克时,就new 一个boom类,将被击中的坦克x和y传入。
然后在paint方法中,判断如果booms集合不为null就代表有坦克被击中了,需要执行爆炸效果。
接着在boom的生命周期的不同节点,绘制不同的图片,以此爆炸效果就完成了。

//boom类
public class boom {int x;int y;int duration = 9;//爆炸效果生命周期boolean live = true;public boom(int x, int y) {this.x = x;this.y = y;}public void DownDur(){if (duration>0){duration--;}else{live = false;}}
}
//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{int speek = 1;Tank tank;Vector<EnemyTanks> enemyTanks = new Vector<>();//用于保存敌人坦克Vector<boom>booms = new Vector<>();//用于保存要爆炸的地方Image image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b1.png"));//用于保存爆炸效果的图片Image image2 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b2.png"));Image image3 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b3.png"));public MyPanel(){//初始化我方坦克tank = new Tank(20,460,0);//初始化敌方坦克for (int i = 0; i < 3; i++) {EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);//初始化敌方坦克子弹shoot shoot = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);//将子弹放入敌方坦克的shoots集合进行管理enemyTank.shoots.add(shoot);//启动敌方坦克的子弹new Thread(shoot).start();enemyTanks.add(enemyTank);}}@Overridepublic void paint(Graphics g) {super.paint(g);System.out.println("调用paint");g.fillRect(0,0,500,600);//画我方坦克drawTank(tank.x,tank.y,g,tank.direct,0);//画敌方坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = enemyTanks.get(i);if (e.live) {//如果敌方坦克的live不是false才进行绘制drawTank(e.x, e.y, g, e.direct, 1);//画出敌方坦克子弹for (int j = 0; j < e.shoots.size(); j++) {shoot s = e.shoots.get(j);if (s.lief) {g.setColor(Color.RED);g.drawRect(s.x + 20, s.y + 60, 3, 3);} else {e.shoots.remove(s);}}}}//画子弹if (tank.shoot!= null && tank.shoot.lief!=false){g.setColor(Color.PINK);g.drawRect(tank.shoot.x,tank.shoot.y,3,3);}//当爆炸效果集合不是null时,说明有坦克被击中了,得开始绘制爆炸效果了if (booms!=null){try {Thread.sleep(50);//因为第一次会额外调用一次paint,所以最好休眠一下,不然第一次爆炸不显示} catch (InterruptedException e) {e.printStackTrace();}for (int i = 0; i < booms.size(); i++) {boom boom = booms.get(i);if (boom.duration>=7){g.drawImage(image1,boom.x,boom.y,60,60,this);}else if(boom.duration>=5){g.drawImage(image2,boom.x,boom.y,60,60,this);}else if(boom.duration>=3){g.drawImage(image3,boom.x,boom.y,60,60,this);}boom.DownDur();if (boom.duration == 0){booms.remove(boom);}}}}/@param x 坦克的起始横坐标@param y 坦克的起始纵坐标@param g 画笔@param direct 根据方向绘制不同朝向的坦克@param type  根据0或者1 绘制不同颜色的坦克*/public void drawTank(int x,int y,Graphics g,int direct,int type){switch (type){case 0:g.setColor(Color.PINK);//自己的坦克就粉色break;case 1:g.setColor(Color.RED);//敌人的坦克就红色break;}switch (direct){//0上,1右,2下,3左case 0://0表示绘制方向朝上的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+22,y-5,x+22,y+25);break;case 1://1表示绘制方向朝右的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x+58,y+25);break;case 2://2表示绘制方向朝下的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+12,25,25);g.drawLine(x+22,y+55,x+22,y+25);break;case 3://3表示绘制方向朝左的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x-8,y+25);break;default://其他情况暂不考虑break;}}@Override //字符输出时,该方法触发public void keyTyped(KeyEvent e) {}@Override//有按键按下时,该方法触发public void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W){//前进moveUp();tank.direct = 0;}else if(e.getKeyCode() == KeyEvent.VK_S){moveDown();tank.direct = 2;}else if(e.getKeyCode() == KeyEvent.VK_A){moveLeft();tank.direct = 3;}else if(e.getKeyCode() == KeyEvent.VK_D){moveRight();tank.direct = 1;}else if(e.getKeyCode() == 32){System.out.println("氮气加速~");speek +=5;}else{System.out.println(e.getKeyCode());}if (e.getKeyCode() == KeyEvent.VK_J){tank.fire();}repaint();}@Override//有按键松开时,该方法触发public void keyReleased(KeyEvent e) {}public void moveUp(){tank.y-=speek;}

让敌方坦克动起来

首先分析这个需求:
坦克动起来得改变x和y的值,且需要不断的改变
根据上面的分析,可以使用以下方法:
将敌人坦克类EnemyTanks做成一个线程,然后再run方法中使用switch语句根据坦克的朝向改变x和y的值,再使用 Math. random方法随机反馈0~3之间的数字。不断的循环就可以实现让坦克动起来.
可以在初始化创建敌人坦克后,马上就启动线程

//EnemyTanks类
public class EnemyTanks extends Tank implements Runnable{boolean live = true;Vector<shoot> shoots = new Vector<>();public EnemyTanks(int x, int y, int direct) {super(x, y, direct);}@Overridepublic void run() {while (live){for (int i = 0; i < 30; i++) {try {Thread.sleep(50);//睡眠一会防止,还没动几步就换方向了} catch (InterruptedException e) {e.printStackTrace();}switch (direct){case 0://上y-=2;break;case 1://右x+=2;break;case 2://下y+=2;break;case 3://左x-=2;break;}}direct = (int)(Math.random()*4);//随机调整方向}}
}

控制坦克的移动范围

在上面的制作中,由于没有限制坦克的x和y最高和最低值是多少,所以坦克是可以出界的,而子弹又是不可以出界的,所以就会有漏洞,下面将限制坦克的x和y最高和最低值。

代码提现也很简单,只需要在改变x和y的时候加一个if判断,确定没有超出才改变,否则不改变。这个思路也可以拿来做障碍物

public class EnemyTanks extends Tank implements Runnable{boolean live = true;Vector<shoot> shoots = new Vector<>();public EnemyTanks(int x, int y, int direct) {super(x, y, direct);}@Overridepublic void run() {while (live){for (int i = 0; i < 30; i++) {try {Thread.sleep(50);//睡眠一会防止,还没动几步就换方向了} catch (InterruptedException e) {e.printStackTrace();}switch (direct){case 0://if (y >= 0) {y -= 2;}break;case 1://if ( x+60 < 500) {x += 2;}break;case 2://if (y+50 < 600) {y += 2;}break;case 3://if (x >= 0 ) {x -= 2;}break;}}direct = (int)(Math.random()*4);//随机调整方向}}
}

发射多颗子弹

在前面的制作中,只实现了,发射一颗子弹,下面要实现,可以发射多颗子弹。
要实现发射多颗子弹,先将前面的发射子弹的问题解决一下。
前面坦克发射子弹时,当发射子弹后,子弹还未达到边界时,再按J发射,那么之前的子弹对象就会被滞空,从而去绘制新的子弹。
那么我们先调整成,当,前一颗子弹还未到达边界时,或到达边界了且线程live是false时,才可以创建下一个子弹线程。
实现这个功能,只需要在创建子弹对象的时候加一个判断,tank.shoot == null || tank.shoot ==false

if (e.getKeyCode() == KeyEvent.VK_J){if(tank.shoot == null || !tank.shoot.lief) {tank.fire();}}

解决了上面的问题,再来思考怎么发射多颗子弹。
发射子弹其实就是创建一个子弹对象,不断的重绘它的位置,然后判断是否进入了敌人坦克的范围
要发射多颗子弹,可以创建一个子弹集合,每当按下J时,就创建一个子弹对象,且加入到集合中
再绘制的时候改成遍历子弹集合,挨个绘制。判断是否击中敌人时也遍历集合,挨个判断。

//Tank类 我方坦克类
public class Tank {int x;int y;int direct;//创建一个子弹集合,用于保存多个子弹Vector<shoot> shoots = new Vector<>();public Tank(int x, int y, int direct) {this.x = x;this.y = y;this.direct = direct;}shoot shoot;//定义一个子弹属性public void fire(){switch (direct){//根据此时坦克的朝向创建对应的子弹发送初始位置case 0://当坦克朝上时System.out.println("上");shoot = new shoot(x+20,y-6,direct);break;case 1://当坦克朝右时shoot = new shoot(x+60,y+24,direct);break;case 2://当坦克朝下时System.out.println("下");shoot = new shoot(x+21,y+60,direct);break;case 3://当坦克朝左边时System.out.println("左");shoot = new shoot(x-8,y+23,direct);break;}//将刚创建的子弹加入到子弹集合中shoots.add(shoot);//创建完子弹,启动子弹new Thread(shoot).start();}
}
//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{int speek = 1;Tank tank;Vector<EnemyTanks> enemyTanks = new Vector<>();//用于保存敌人坦克Vector<boom>booms = new Vector<>();//用于保存要爆炸的地方Image image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b1.png"));//用于保存爆炸效果的图片Image image2 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b2.png"));Image image3 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b3.png"));public MyPanel(){//初始化我方坦克tank = new Tank(20,460,0);//初始化敌方坦克for (int i = 0; i < 3; i++) {EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);//启动敌人坦克线程new Thread(enemyTank).start();//初始化敌方坦克子弹shoot shoot = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);//将子弹放入敌方坦克的shoots集合进行管理enemyTank.shoots.add(shoot);//启动敌方坦克的子弹new Thread(shoot).start();enemyTanks.add(enemyTank);}}@Overridepublic void paint(Graphics g) {super.paint(g);System.out.println("调用paint");g.fillRect(0,0,500,600);//画我方坦克drawTank(tank.x,tank.y,g,tank.direct,0);//画敌方坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = enemyTanks.get(i);if (e.live) {//如果敌方坦克的live不是false才进行绘制drawTank(e.x, e.y, g, e.direct, 1);//画出敌方坦克子弹for (int j = 0; j < e.shoots.size(); j++) {shoot s = e.shoots.get(j);if (s.lief) {g.setColor(Color.RED);g.drawRect(s.x + 20, s.y + 60, 3, 3);} else {e.shoots.remove(s);}}}}//画我方坦克的子弹for (int i = 0; i < tank.shoots.size(); i++) {shoot s = tank.shoots.get(i);if (s!= null && s.lief){g.setColor(Color.PINK);g.drawRect(s.x,s.y,3,3);}else if(!s.lief){tank.shoots.remove(s);//当该子弹线程消亡后就把此对象从集合中删除}}//当爆炸效果集合不是null时,说明有坦克被击中了,得开始绘制爆炸效果了if (booms!=null){try {Thread.sleep(50);//因为第一次会额外调用一次paint,所以最好休眠一下,不然第一次爆炸不显示} catch (InterruptedException e) {e.printStackTrace();}for (int i = 0; i < booms.size(); i++) {boom boom = booms.get(i);if (boom.duration>=7){g.drawImage(image1,boom.x,boom.y,60,60,this);}else if(boom.duration>=5){g.drawImage(image2,boom.x,boom.y,60,60,this);}else if(boom.duration>=3){g.drawImage(image3,boom.x,boom.y,60,60,this);}boom.DownDur();if (boom.duration == 0){booms.remove(boom);}}}}/@param x 坦克的起始横坐标@param y 坦克的起始纵坐标@param g 画笔@param direct 根据方向绘制不同朝向的坦克@param type  根据0或者1 绘制不同颜色的坦克*/public void drawTank(int x,int y,Graphics g,int direct,int type){switch (type){case 0:g.setColor(Color.PINK);//自己的坦克就粉色break;case 1:g.setColor(Color.RED);//敌人的坦克就红色break;}switch (direct){//0上,1右,2下,3左case 0://0表示绘制方向朝上的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+22,y-5,x+22,y+25);break;case 1://1表示绘制方向朝右的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x+58,y+25);break;case 2://2表示绘制方向朝下的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+12,25,25);g.drawLine(x+22,y+55,x+22,y+25);break;case 3://3表示绘制方向朝左的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x-8,y+25);break;default://其他情况暂不考虑break;}}@Override //字符输出时,该方法触发public void keyTyped(KeyEvent e) {}@Override//有按键按下时,该方法触发public void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W){//前进moveUp();tank.direct = 0;}else if(e.getKeyCode() == KeyEvent.VK_S){moveDown();tank.direct = 2;}else if(e.getKeyCode() == KeyEvent.VK_A){moveLeft();tank.direct = 3;}else if(e.getKeyCode() == KeyEvent.VK_D){moveRight();tank.direct = 1;}else if(e.getKeyCode() == 32){System.out.println("氮气加速~");speek +=5;}else{System.out.println(e.getKeyCode());}if (e.getKeyCode() == KeyEvent.VK_J){if (tank.shoots.size() <= 5) {tank.fire();}}repaint();}@Override//有按键松开时,该方法触发public void keyReleased(KeyEvent e) {}public void moveUp(){tank.y-=speek;}public void moveLeft(){tank.x-=speek;}public void moveRight(){tank.x+=speek;}public void moveDown(){tank.y+=speek;}//此方法判断我方坦克子弹是否击中敌人坦克public  void himEnemyTank(Vector<shoot> shoots , EnemyTanks e ){for(int i = 0 ; i<shoots.size();i++){shoot s = shoots.get(i);//上下和左右的x,y是相同的,所以写两个判断即可switch (e.direct){case 0:case 2:if (s.x>e.x&&s.x< e.x+40&& s.y>e.y&&s.y<e.y+60){//如果此条件成立就代表,地方在朝上或者朝下的方向被击中了e.live = false;s.lief = false;booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果}break;case 1:case 3:if (s.x>e.x&&s.x< e.x+60&& s.y>e.y&&s.y<e.y+40){//如果此条件成立就代表,地方在朝左或者朝右的方向被击中了e.live = false;s.lief = false;booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果}break;}}}@Overridepublic void run() {while (true){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (tank.shoot!=null && tank.shoot.lief ){//如果我方坦克的子弹创建了,且线程未结束就对每个敌人坦克进行判断for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = this.enemyTanks.get(i);himEnemyTank(tank.shoots,e);}}for (int i = 0; i < enemyTanks.size(); i++) {//如果敌人坦克被击中,那么就把此坦克从集合中删除if (!(enemyTanks.get(i).live)){enemyTanks.remove(i);}}this.repaint();}}
}

实现让敌人坦克的子弹消亡后再发射新子弹

在前面已经实现让敌人坦克发射一颗子弹,但是只能发射一颗。
要让坦克发射新的子弹,需要判断前一个子弹消亡再创建新的子弹。
因为在前面定义敌人坦克的子弹用的是集合存放,且绘制敌人坦克的子弹用的也是遍历。
所以只需在敌人坦克类EnemyTanks的run方法中做一个判断,当子弹集合 = =0时(如果要让敌人坦克发射多颗子弹,可以把0变大)
就创建新的子弹到集合中。

//敌人坦克类
public class EnemyTanks extends Tank implements Runnable{boolean live = true;Vector<shoot> shoots1 = new Vector<>();public EnemyTanks(int x, int y, int direct) {super(x, y, direct);}@Overridepublic void run() {while (live){System.out.println("敌人坦克");if (live && shoots1.size() == 0){shoot Ds = null;switch (direct){//根据此时坦克的朝向创建对应的子弹发送初始位置case 0://当坦克朝上时System.out.println("上");Ds = new shoot(x,y,0);break;case 1://当坦克朝右时Ds = new shoot(x+60,y-35,1);break;case 2://当坦克朝下时System.out.println("下");Ds = new shoot(x,y,2);break;case 3://当坦克朝左边时System.out.println("左");Ds = new shoot(x,y-35,3);break;}System.out.println("创建子弹");shoots1.add(Ds);new Thread(Ds).start();}for (int i = 0; i < 30; i++) {try {Thread.sleep(200);//睡眠一会防止,还没动几步就换方向了} catch (InterruptedException e) {e.printStackTrace();}switch (direct){case 0://if (y >= 0) {y -= 2;}break;case 1://if ( x+60 < 500) {x += 2;}break;case 2://if (y+50 < 600) {y += 2;}break;case 3://if (x >= 0 ) {x -= 2;}break;}}direct = (int)(Math.random()*4);//随机调整方向}}
}
//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{int speek = 1;Tank tank;Vector<EnemyTanks> enemyTanks = new Vector<>();//用于保存敌人坦克Vector<boom>booms = new Vector<>();//用于保存要爆炸的地方Image image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b1.png"));//用于保存爆炸效果的图片Image image2 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b2.png"));Image image3 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b3.png"));public MyPanel(){//初始化我方坦克tank = new Tank(20,460,0);//初始化敌方坦克for (int i = 0; i < 3; i++) {EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);//启动敌人坦克线程new Thread(enemyTank).start();//初始化敌方坦克子弹shoot S = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);//将子弹放入敌方坦克的shoots集合进行管理enemyTank.shoots1.add(S);//启动敌方坦克的子弹new Thread(S).start();enemyTanks.add(enemyTank);}}@Overridepublic void paint(Graphics g) {super.paint(g);System.out.println("调用paint");g.fillRect(0,0,500,600);//画我方坦克drawTank(tank.x,tank.y,g,tank.direct,0);//画敌方坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = enemyTanks.get(i);if (e.live) {//如果敌方坦克的live不是false才进行绘制drawTank(e.x, e.y, g, e.direct, 1);//画出敌方坦克子弹for (int j = 0; j < e.shoots1.size(); j++) {shoot s = e.shoots1.get(j);if (s.lief) {g.setColor(Color.RED);g.drawRect(s.x + 20, s.y + 60, 3, 3);} else {e.shoots1.remove(s);}}}}//画我方坦克的子弹for (int i = 0; i < tank.shoots.size(); i++) {shoot s = tank.shoots.get(i);if (s!= null && s.lief){g.setColor(Color.PINK);g.drawRect(s.x,s.y,3,3);}else if(!s.lief){tank.shoots.remove(s);//当该子弹线程消亡后就把此对象从集合中删除}}//当爆炸效果集合不是null时,说明有坦克被击中了,得开始绘制爆炸效果了if (booms!=null){try {Thread.sleep(50);//因为第一次会额外调用一次paint,所以最好休眠一下,不然第一次爆炸不显示} catch (InterruptedException e) {e.printStackTrace();}for (int i = 0; i < booms.size(); i++) {boom boom = booms.get(i);if (boom.duration>=7){g.drawImage(image1,boom.x,boom.y,60,60,this);}else if(boom.duration>=5){g.drawImage(image2,boom.x,boom.y,60,60,this);}else if(boom.duration>=3){g.drawImage(image3,boom.x,boom.y,60,60,this);}boom.DownDur();if (boom.duration == 0){booms.remove(boom);}}}}/@param x 坦克的起始横坐标@param y 坦克的起始纵坐标@param g 画笔@param direct 根据方向绘制不同朝向的坦克@param type  根据0或者1 绘制不同颜色的坦克*/public void drawTank(int x,int y,Graphics g,int direct,int type){switch (type){case 0:g.setColor(Color.PINK);//自己的坦克就粉色break;case 1:g.setColor(Color.RED);//敌人的坦克就红色break;}switch (direct){//0上,1右,2下,3左case 0://0表示绘制方向朝上的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+22,y-5,x+22,y+25);break;case 1://1表示绘制方向朝右的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x+58,y+25);break;case 2://2表示绘制方向朝下的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+12,25,25);g.drawLine(x+22,y+55,x+22,y+25);break;case 3://3表示绘制方向朝左的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x-8,y+25);break;default://其他情况暂不考虑break;}}@Override //字符输出时,该方法触发public void keyTyped(KeyEvent e) {}@Override//有按键按下时,该方法触发public void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W){//前进moveUp();tank.direct = 0;}else if(e.getKeyCode() == KeyEvent.VK_S){moveDown();tank.direct = 2;}else if(e.getKeyCode() == KeyEvent.VK_A){moveLeft();tank.direct = 3;}else if(e.getKeyCode() == KeyEvent.VK_D){moveRight();tank.direct = 1;}else if(e.getKeyCode() == 32){System.out.println("氮气加速~");speek +=5;}else{System.out.println(e.getKeyCode());}if (e.getKeyCode() == KeyEvent.VK_J){if (tank.shoots.size() <= 5) {tank.fire();}}repaint();}@Override//有按键松开时,该方法触发public void keyReleased(KeyEvent e) {}public void moveUp(){tank.y-=speek;}public void moveLeft(){tank.x-=speek;}public void moveRight(){tank.x+=speek;}public void moveDown(){tank.y+=speek;}//此方法判断我方坦克子弹是否击中敌人坦克public  void himEnemyTank(Vector<shoot> shoots , EnemyTanks e ){for(int i = 0 ; i<shoots.size();i++){shoot s = shoots.get(i);//上下和左右的x,y是相同的,所以写两个判断即可switch (e.direct){case 0:case 2:if (s.x>e.x&&s.x< e.x+40&& s.y>e.y&&s.y<e.y+60){//如果此条件成立就代表,地方在朝上或者朝下的方向被击中了e.live = false;s.lief = false;booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果}break;case 1:case 3:if (s.x>e.x&&s.x< e.x+60&& s.y>e.y&&s.y<e.y+40){//如果此条件成立就代表,地方在朝左或者朝右的方向被击中了e.live = false;s.lief = false;booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果}break;}}}@Overridepublic void run() {while (true){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (tank.shoot!=null && tank.shoot.lief ){//如果我方坦克的子弹创建了,且线程未结束就对每个敌人坦克进行判断for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = this.enemyTanks.get(i);himEnemyTank(tank.shoots,e);}}for (int i = 0; i < enemyTanks.size(); i++) {//如果敌人坦克被击中,那么就把此坦克从集合中删除if (!(enemyTanks.get(i).live)){enemyTanks.remove(i);}}this.repaint();}}
}

实现敌人坦克子弹击中我方坦克,我方坦克消失且爆炸

将我方坦克定义一个live属性,然后在MyPanel类,定义一个方法用于判断我方坦克是否被击中。(被击中的方法前面写过)
如果被击中就跟之前判断敌人坦克一样,live改成false,且添加一个爆炸对象。再在绘制我方坦克时,添加一个判断,如果我方坦克的live是false就不绘制。

//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{int speek = 1;Tank tank;Vector<EnemyTanks> enemyTanks = new Vector<>();//用于保存敌人坦克Vector<boom>booms = new Vector<>();//用于保存要爆炸的地方Image image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b1.png"));//用于保存爆炸效果的图片Image image2 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b2.png"));Image image3 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b3.png"));public MyPanel(){//初始化我方坦克tank = new Tank(20,460,0);//初始化敌方坦克for (int i = 0; i < 3; i++) {EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);//启动敌人坦克线程new Thread(enemyTank).start();//初始化敌方坦克子弹shoot S = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);//将子弹放入敌方坦克的shoots集合进行管理enemyTank.shoots1.add(S);//启动敌方坦克的子弹new Thread(S).start();enemyTanks.add(enemyTank);}}@Overridepublic void paint(Graphics g) {super.paint(g);System.out.println("调用paint");g.fillRect(0,0,500,600);//画我方坦克if (tank.live) {drawTank(tank.x, tank.y, g, tank.direct, 0);}//画敌方坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = enemyTanks.get(i);if (e.live) {//如果敌方坦克的live不是false才进行绘制drawTank(e.x, e.y, g, e.direct, 1);//画出敌方坦克子弹for (int j = 0; j < e.shoots1.size(); j++) {shoot s = e.shoots1.get(j);if (s.lief) {g.setColor(Color.RED);g.drawRect(s.x + 20, s.y + 60, 3, 3);} else {e.shoots1.remove(s);}}}}//画我方坦克的子弹for (int i = 0; i < tank.shoots.size(); i++) {shoot s = tank.shoots.get(i);if (s!= null && s.lief){g.setColor(Color.PINK);g.drawRect(s.x,s.y,3,3);}else if(!s.lief){tank.shoots.remove(s);//当该子弹线程消亡后就把此对象从集合中删除}}//当爆炸效果集合不是null时,说明有坦克被击中了,得开始绘制爆炸效果了if (booms!=null){try {Thread.sleep(50);//因为第一次会额外调用一次paint,所以最好休眠一下,不然第一次爆炸不显示} catch (InterruptedException e) {e.printStackTrace();}for (int i = 0; i < booms.size(); i++) {boom boom = booms.get(i);if (boom.duration>=7){g.drawImage(image1,boom.x,boom.y,60,60,this);}else if(boom.duration>=5){g.drawImage(image2,boom.x,boom.y,60,60,this);}else if(boom.duration>=3){g.drawImage(image3,boom.x,boom.y,60,60,this);}boom.DownDur();if (boom.duration == 0){booms.remove(boom);}}}}/@param x 坦克的起始横坐标@param y 坦克的起始纵坐标@param g 画笔@param direct 根据方向绘制不同朝向的坦克@param type  根据0或者1 绘制不同颜色的坦克*/public void drawTank(int x,int y,Graphics g,int direct,int type){switch (type){case 0:g.setColor(Color.PINK);//自己的坦克就粉色break;case 1:g.setColor(Color.RED);//敌人的坦克就红色break;}switch (direct){//0上,1右,2下,3左case 0://0表示绘制方向朝上的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+22,y-5,x+22,y+25);break;case 1://1表示绘制方向朝右的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x+58,y+25);break;case 2://2表示绘制方向朝下的坦克g.fill3DRect(x,y,10,50,false);g.fill3DRect(x+10,y+5,25,40,false);g.fill3DRect(x+35,y,10,50,false);g.fillOval(x+10,y+12,25,25);g.drawLine(x+22,y+55,x+22,y+25);break;case 3://3表示绘制方向朝左的坦克g.fill3DRect(x,y+7,50,10,false);g.fill3DRect(x+5,y+14,40,25,false);g.fill3DRect(x,y+37,50,10,false);g.fillOval(x+10,y+15,25,25);g.drawLine(x+25,y+25,x-8,y+25);break;default://其他情况暂不考虑break;}}@Override //字符输出时,该方法触发public void keyTyped(KeyEvent e) {}@Override//有按键按下时,该方法触发public void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W){//前进moveUp();tank.direct = 0;}else if(e.getKeyCode() == KeyEvent.VK_S){moveDown();tank.direct = 2;}else if(e.getKeyCode() == KeyEvent.VK_A){moveLeft();tank.direct = 3;}else if(e.getKeyCode() == KeyEvent.VK_D){moveRight();tank.direct = 1;}else if(e.getKeyCode() == 32){System.out.println("氮气加速~");speek +=5;}else{System.out.println(e.getKeyCode());}if (e.getKeyCode() == KeyEvent.VK_J){if (tank.shoots.size() <= 5) {tank.fire();}}repaint();}@Override//有按键松开时,该方法触发public void keyReleased(KeyEvent e) {}public void moveUp(){tank.y-=speek;}public void moveLeft(){tank.x-=speek;}public void moveRight(){tank.x+=speek;}public void moveDown(){tank.y+=speek;}//此方法判断敌人坦克是否击中我方坦克public void hitMyTank(){//便利敌人坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = this.enemyTanks.get(i);for (int j = 0; j < e.shoots1.size(); j++) {shoot Dshoot = e.shoots1.get(j);himMyTank(Dshoot,tank);}}}//此方法判断敌人坦克是否击中我方坦克public  void himMyTank(shoot s , Tank e ){//上下和左右的x,y是相同的,所以写两个判断即可switch (e.direct){case 0:case 2:if (s.x>e.x&&s.x< e.x+40&& s.y-40>e.y&&s.y<e.y+60){//如果此条件成立就代表,地方在朝上或者朝下的方向被击中了e.live = false;s.lief = false;booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果}break;case 1:case 3:if (s.x>e.x&&s.x< e.x+60&& s.y>e.y&&s.y<e.y-20){//如果此条件成立就代表,地方在朝左或者朝右的方向被击中了e.live = false;s.lief = false;booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果}break;}}//此方法判断我方子弹是否击中敌人坦克public  void himEnemyTank(Vector<shoot> shoots , Tank e ){for(int i = 0 ; i<shoots.size();i++){shoot s = shoots.get(i);//上下和左右的x,y是相同的,所以写两个判断即可switch (e.direct){case 0:case 2:if (s.x>e.x&&s.x< e.x+40&& s.y>e.y&&s.y<e.y+60){//如果此条件成立就代表,地方在朝上或者朝下的方向被击中了e.live = false;s.lief = false;booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果}break;case 1:case 3:if (s.x>e.x&&s.x< e.x+60&& s.y>e.y&&s.y<e.y+40){//如果此条件成立就代表,地方在朝左或者朝右的方向被击中了e.live = false;s.lief = false;booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果}break;}}}@Overridepublic void run() {while (true){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (tank.shoot!=null && tank.shoot.lief ){//如果我方坦克的子弹创建了,且线程未结束就对每个敌人坦克进行判断for (int i = 0; i < enemyTanks.size(); i++) {EnemyTanks e = this.enemyTanks.get(i);himEnemyTank(tank.shoots,e);}}for (int i = 0; i < enemyTanks.size(); i++) {//如果敌人坦克被击中,那么就把此坦克从集合中删除if (!(enemyTanks.get(i).live)){enemyTanks.remove(i);}}//判断我方坦克是否被击中hitMyTank();this.repaint();}}
}

无人机大全