坦克大战②
创始人
2024-03-15 09:52:25
0

1. 我方坦克发射单颗子弹

  1. 当发射一颗子弹后,就相当于启动一个线程来控制它的位置坐标;
  2. Hero[我方坦克]有子弹的对象,当按下J时,就创建一个发射子弹的线程,通过坐标变化让子弹不停的移动,形成一个射击的效果;
  3. 我们的MyPanel类需要通过不停地重绘画面来重绘子弹,这样才能让子弹实时出现在画面上;
  4. 当子弹移动到面板的边界时,就应该销毁(isLive设置为false,线程结束);
  1. 创建子弹类
public class Bullet implements Runnable {//子弹创建线程private int x;//子弹x坐标private int y;//y坐标private int speed = 2;//速度private int direct;//方向private boolean isLive = true;public Bullet(int x, int y, int direct) {//子弹初始位置this.x = x;this.y = y;this.direct = direct;}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}public boolean isLive() {return isLive;}public void setLive(boolean live) {isLive = live;}@Overridepublic void run() {while (true) {if (direct == 0) {//线程中根据方向调节坐标y -= speed;} else if (direct == 1) {x += speed;} else if (direct == 2) {y += speed;} else if (direct == 3) {x -= speed;}System.out.println("子弹 x=" + x + ",y=" + y);if (!(x >= 0 && x <= 500 && y >= 0 && y <= 400)) {System.out.println("子弹超出范围,线程结束");isLive = false;break;}try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
  1. 坦克射击子弹
public class Hero extends Tank {private Bullet bullet;//坦克射击子弹public Hero(int x, int y) {//坦克的初始位置super(x, y);}public Bullet getBullet() {return bullet;}public void shotBullet() {//根据坦克的方向确定子弹的位置switch (getDirect()) {case 0:bullet = new Bullet(getX() + 20, getY(), 0);break;case 1:bullet = new Bullet(getX() + 60, getY() + 20, 1);break;case 2:bullet = new Bullet(getX() + 20, getY() + 60, 2);break;case 3:bullet = new Bullet(getX(), getY() + 20, 3);break;}new Thread(bullet).start();//发射子弹之前要确定子弹的位置和射击方向}
}
  1. 面板通过线程实现重绘
new Thread(myPanel).start();
@Overridepublic void run() {while (true) {this.repaint();//面板时刻被重绘try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
  1. 画出子弹图形
if (hero.getBullet() != null && hero.getBullet().isLive() != false) {System.out.println("子弹被重绘");g.fillOval(hero.getBullet().getX()-2, hero.getBullet().getY()-2, 4, 4);}
  1. 按J发射子弹
if (e.getKeyCode() == KeyEvent.VK_J) {System.out.println("发射子弹");hero.shotBullet();}

2.敌方坦克发射子弹

让敌方坦克也能够发射子弹(可以有多颗子弹)

  1. 在敌人坦克类,使用Vector保存多个Bullet;
    在这里插入图片描述
  2. 当每创建一个敌人坦克对象时,给该敌人坦克对象初始化一个Bullet对象,同时启动Bullet线程;
    在这里插入图片描述
  3. 在绘制敌人坦克时,需要遍历敌人坦克对象Vector。绘制所有的子弹,当子弹isLive为false时,就从Vecotr上移除;
    在这里插入图片描述

3. 击中敌方坦克

  1. 给敌方坦克类创建一个isLive属性
    在这里插入图片描述
  1. 在MyPanel类中判断我方子弹是否击中敌方坦克
    调用时需要注意:(1)我方的子弹参数必须是存活的;(2)敌方坦克不止一辆;
//判断我方子弹是否击中敌方坦克public void hitTank(Bullet bullet, EnemyTank enemyTank) {switch (enemyTank.getDirect()) {case 0://向上case 2://向下if (bullet.getX() > enemyTank.getX() && bullet.getX() < enemyTank.getX() + 40&& bullet.getY() > enemyTank.getY() && bullet.getY() < enemyTank.getY() + 60) {bullet.setLive(false);enemyTank.setLive(false);}break;case 1://向右case 3://向左if (bullet.getX() > enemyTank.getX() && bullet.getX() < enemyTank.getX() + 60&& bullet.getY() > enemyTank.getY() && bullet.getY() < enemyTank.getY() + 40) {bullet.setLive(false);enemyTank.setLive(false);}}}
  1. 什么时候判断我方坦克是否击中敌方坦克?(run方法中判断)
    在这里插入图片描述
  2. 在绘制敌方坦克以及绘制敌方坦克的子弹时,要先判断敌方坦克是否还存活;
    在这里插入图片描述
  3. 子弹有两种途径被销毁:第一是碰到边界,第二是碰到敌方坦克;
    在Bullet类中加个条件
    在这里插入图片描述

4. 爆炸效果

  1. 创建一个炸弹类;
public class Bomb {//定义一个炸弹类int x,y;int life;//炸弹的生命周期boolean isLive = true;//炸弹是否存活public Bomb(int x, int y) {this.x = x;this.y = y;}//减少生命值public void HpDown() {if (life > 0) {life--;} else {isLive = false;}}
}
  1. 在MyPanel类创建炸弹集合
    在这里插入图片描述
    2.1 在out文件夹的chapter18文件夹的下一级里放入炸弹爆炸的图片;
    在这里插入图片描述
    2.2在MyPanel构造器中初始化图片对象
    在这里插入图片描述
  2. 在hitTank()方法中,当子弹击中坦克时,坦克被销毁,从坦克集合中去除这辆坦克;并且创建一个bomb对象加入到bombs集合中;
    在这里插入图片描述
  3. 如果bombs集合中有炸弹,就绘制;
//绘制爆炸图片for (int i = 0; i < bombs.size(); i++) {Bomb bomb = bombs.get(i);//取出炸弹if (bomb.getLife() > 6) {//根据当前这个bomb对象的life值判断绘制哪张图片g.drawImage(image1, bomb.getX(), bomb.getY(), 40, 60, this);} else if (bomb.getLife() > 3) {g.drawImage(image2, bomb.getX(), bomb.getY(), 40, 60, this);} else {g.drawImage(image3, bomb.getX(), bomb.getY(), 40, 60, this);}//每绘制一次,生命值减一bomb.HpDown();//最后不要忘了移除集合中的炸弹if (bomb.getLife() == 0) {bombs.remove(bomb);}}

5. 敌方坦克自由移动

  1. 将敌方坦克当做线程使用,需要让敌方坦克类继承Runnable接口实现线程,在run方法里实现坦克的自由移动;
@Overridepublic void run() {while (true) {double random = Math.random();long beginTime = System.currentTimeMillis();while (true) {if (random < 0.25) {moveUp();setDirect(0);} else if (random >= 0.25 && random < 0.5) {moveRight();setDirect(1);} else if (random >= 0.5 && random < 0.75) {moveDown();setDirect(2);} else {moveLeft();setDirect(3);}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}long nowTime = System.currentTimeMillis();if (nowTime - beginTime > 2000) {break;}if (!(isLive == true)) {break;}}if (!(isLive == true)) {break;}}}

改进后:

@Overridepublic void run() {while (true) {switch (getDirect()) {case 0:for (int i = 0; i < 30; i++) {moveUp();try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}break;case 1:for (int i = 0; i < 30; i++) {moveRight();try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}break;case 2:for (int i = 0; i < 30; i++) {moveDown();try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}break;case 3:for (int i = 0; i < 30; i++) {moveLeft();try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}break;}int random = (int) (Math.random() * 4);setDirect(random);if (!(isLive == true)) {break;}}}
  1. 在创建敌人坦克对象时启动线程;
    在这里插入图片描述

6.移动范围问题

我方坦克规定移动范围

if (e.getKeyCode() == KeyEvent.VK_W) {if (hero.getY() > 0) {hero.moveUp();hero.setDirect(0);}} else if (e.getKeyCode() == KeyEvent.VK_D) {if (hero.getX() + 60 < 1000) {hero.moveRight();hero.setDirect(1);}} else if (e.getKeyCode() == KeyEvent.VK_S) {if (hero.getY() + 60 < 600) {hero.moveDown();hero.setDirect(2);}} else if (e.getKeyCode() == KeyEvent.VK_A) {if (hero.getX() > 0) {hero.moveLeft();hero.setDirect(3);}}

敌方坦克规定移动范围

switch (getDirect()) {case 0://上for (int i = 0; i < 30; i++) {if (getY() > 0) {moveUp();}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}break;case 1://右for (int i = 0; i < 30; i++) {if (getX()+60 < 1000) {moveRight();}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}break;case 2://下for (int i = 0; i < 30; i++) {if (getY()+60 < 600) {moveDown();}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}break;case 3://左for (int i = 0; i < 30; i++) {if (getX() > 0) {moveLeft();}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}break;}

7. 我方坦克发射多颗子弹

  1. 需求一:我方坦克发射的子弹消亡后,才可以发射新的子弹;

思路:(1)在按下J键时,我们判断当前hero对象的子弹,是否已经销毁;
(2)如果没有销毁,就不去触发shotBullt()方法;
(3)如果已经销毁,才去触发shotBullt()方法;
在这里插入图片描述
发现的第一颗子弹射出边界销毁后,第二颗子弹发射不出来;
改进:
在这里插入图片描述

扩展:我方坦克发射多颗子弹
在这里插入图片描述
在这里插入图片描述
在绘制我方子弹时,需要遍历hero的bullets子弹集合
在这里插入图片描述
or(不清楚为什么要判断非空)
在这里插入图片描述
keyPress中条件去掉
在这里插入图片描述
如何控制面板上同时只能出现5颗子弹
在这里插入图片描述
or
在这里插入图片描述
当我方坦克可以发射多颗子弹时,在判断我方子弹是否命中敌方坦克时,需要拿出我方子弹集合中的每一颗子弹,和敌方坦克集合中的每一辆坦克进行判断;然后在MyPanel类的run方法中调用即可;
在这里插入图片描述

8. 敌方发射的子弹消亡后可以再发射子弹

在地方坦克类中创建这个方法

public void createShot() {switch (getDirect()) {case 0://上bullet = new Bullet(getX() + 20, getY(), 0);break;case 1://右bullet = new Bullet(getX() + 60, getY() + 20, 1);break;case 2://下bullet = new Bullet(getX() + 20, getY() + 60, 2);break;case 3://左:bullet = new Bullet(getX(), getY() + 20, 3);break;}bullets.add(bullet);new Thread(bullet).start();}

方案一:可以在paint()方法中,在移除被销毁的子弹时调用createShot()方法,给这辆坦克再赋予一颗子弹;(老师没有使用)
在这里插入图片描述
方案二:在敌人坦克类的run方法里,当子弹集合里没有子弹时,创建子弹;在这里,当坦克掉头后才能发射子弹;
在这里插入图片描述
在这里插入图片描述

9. 我方坦克爆炸

需求:当敌方坦克击中我方坦克时,我方坦克消失,并出现爆炸效果;

  1. 在父类坦克中添加isLive属性;
    在这里插入图片描述
  2. 编写一个方法,判断敌人坦克是否击中我方坦克;
    在这里插入图片描述
  3. 将hitTank()方法的参数修改如下,造成多态效果(可以实现重载,这里使用多态)在这里插入图片描述
  4. 在绘制我方坦克时,加上判断条件;
    在这里插入图片描述
    5.hero的方法中加上一个判断条件 ! isLive,以做到当我方坦克销毁后,不能再发射子弹在这里插入图片描述

相关内容

热门资讯

汽车油箱结构是什么(汽车油箱结... 本篇文章极速百科给大家谈谈汽车油箱结构是什么,以及汽车油箱结构原理图解对应的知识点,希望对各位有所帮...
美国2年期国债收益率上涨15个... 原标题:美国2年期国债收益率上涨15个基点 美国2年期国债收益率上涨15个基...
嵌入式 ADC使用手册完整版 ... 嵌入式 ADC使用手册完整版 (188977万字)💜&#...
重大消息战皇大厅开挂是真的吗... 您好:战皇大厅这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
盘点十款牵手跑胡子为什么一直... 您好:牵手跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游...
senator香烟多少一盒(s... 今天给各位分享senator香烟多少一盒的知识,其中也会对sevebstars香烟进行解释,如果能碰...
终于懂了新荣耀斗牛真的有挂吗... 您好:新荣耀斗牛这款游戏可以开挂,确实是有挂的,需要了解加客服微信8435338】很多玩家在这款游戏...
盘点十款明星麻将到底有没有挂... 您好:明星麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...
总结文章“新道游棋牌有透视挂吗... 您好:新道游棋牌这款游戏可以开挂,确实是有挂的,需要了解加客服微信【7682267】很多玩家在这款游...
终于懂了手机麻将到底有没有挂... 您好:手机麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...