陆游曾说:纸上得来终觉浅,绝知此事要躬行。前面已经了解Java线程基础知识,现在我们来实际运用一下。
在坦克大战游戏(0.2版)基础上添加如下功能:当玩家按一下j键,就发射一颗子弹。
package tankgame3; /** * Create By 刘鸿涛 * 2022/1/17 5:19 */ public class Shot implements Runnable{ int x; //子弹x坐标 int y; //子弹y坐标 int direct = 0; //子弹方向 int speed = 2; //子弹速度 boolean isLive = true; //子弹是否存活 public Shot(int x, int y, int direct) { this.x = x; this.y = y; this.direct = direct; } @Override public void run() { //射击 while (true){ //子弹休眠 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } //根据方向来改变x,y坐标 switch (direct){ case 0://上 y -= speed; break; case 1://右 x += speed; break; case 2://下 y += speed; break; case 3://左 x -= speed; break; } //测试,这里我们输出x,y坐标 System.out.println("子弹 x = " + x + " y= " + y); //当子弹移动到面板 if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750)){ isLive = false; break; } } } }
EnemyTank类
Vector<Shot> shots = new Vector<>();
MyPanel类无参构造器
public MyPanel(){ hero = new Hero(500,500); //初始化自己的坦克 hero.setSpeed(50); //初始化坦克速度 //初始化敌人坦克 for (int i = 0; i < enemyTanksSize; i++) { //创建一个敌人的坦克 EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0); //设置方向 enemyTank.setDirect(2); //给该enemyTank 加入一颗子弹 Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect()); //加入enemyTank的Vector 成员 enemyTank.shots.add(shot); //启动 shot 对象 new Thread(shot).start(); //加入坦克 enemyTanks.add(enemyTank); } }
MyPanel类paint()方法
@Override public void paint(Graphics g){ super.paint(g); g.fillRect(0,0,1000,750); //填充矩形,默认黑色 //画出坦克 - 封装方法 //自己的坦克 drawTank(hero.getX(),hero.getY(),g,hero.getDirect(),1); //type坦克类型 //画出hero射击的子弹 if (hero.shot != null && hero.shot.isLive == true){ g.draw3DRect(hero.shot.x,hero.shot.y,5 ,5,false); } //画出敌人的坦克,遍历Vector for (int i = 0; i < enemyTanks.size(); i++) { //取出坦克 EnemyTank enemyTank = enemyTanks.get(i); drawTank(enemyTank.getX(),enemyTank.getY(),g,enemyTank.getDirect(),0); //画出 enemyTank 所有子弹 for (int j = 0; j < enemyTank.shots.size(); j++){ //取出子弹 Shot shot = enemyTank.shots.get(j); //绘制 if (shot.isLive){ //isLive 为 true g.draw3DRect(shot.x,shot.y,2,2,false); }else { //从Vector 移除 enemyTank.shots.remove(shot); } } } }
实现子弹击中敌方坦克消失,我们需要写一个方法,判断我方子弹是否碰到敌方坦克,需要考虑敌方坦克面向问题。我们在paint方法中也加入判断语句,如果上面条件为真,则返回敌方坦克isLive属性为false,根据情况paint敌方坦克是否执行。而我们的线程集合每隔100毫秒遍历一次敌方坦克集合,刷新整个游戏区域,实现敌方坦克被击中后消失
hitTank()方法
public static void hitTank(Shot s, EnemyTank enemyTank){ //判断 s 击中坦克 switch (enemyTank.getDirect()){ case 0: //坦克向上 case 2: //坦克向下 if(s.x > enemyTank.getX() && s.x < enemyTank.getX() + 40 && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60){ s.isLive = false; enemyTank.isLive = false; } break; case 1: //坦克向右 case 3: //坦克向左 if(s.x > enemyTank.getX() && s.x < enemyTank.getX() + 60 && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40){ s.isLive = false; enemyTank.isLive = false; } break; } }
当敌方坦克被击中后出现爆炸效果,我们新建一个爆炸效果类,这个类需要有x,y大小,需要一个生命周期的方法,来让爆炸效果动态化。当然了,我们的爆炸效果也在一个集合中,也有自己的isLive属性,判断存活时间,我们用Image来初始化三张图片,并路径赋值,在坦克被击中方法里,我们添加执行效果-> 坦克被击中消失后,我们开始执行爆炸类的“生命周期方法”,按条件执行图片放映顺序
新建Bomb类
package tankgame4; /** * Create By 刘鸿涛 * 2022/1/19 4:56 */ public class Bomb { int x, y; //炸弹的坐标 int life = 9; //炸弹的生命周期 boolean isLive = true; //是否还存活 public Bomb(int x, int y){ this.x = x; this.y = y; } //减少生命值 public void lifeDown(){ if(life > 0){ life--; }else { isLive = false; } } }
当敌方坦克被击中后出现爆炸效果,我们新建一个爆炸效果类,这个类需要有x,y大小,需要一个生命周期的方法,来让爆炸效果动态化。当然了,我们的爆炸效果也在一个集合中,也有自己的isLive属性,判断存活时间,我们用Image来初始化三张图片,并路径赋值,在坦克被击中方法里,我们添加执行效果-> 坦克被击中消失后,我们开始执行爆炸类的“生命周期方法”,按条件执行图片放映顺序
为了使敌方坦克自由活动,我们需要为敌方每个坦克做成一个线程,因此,我们的敌人坦克类要实现Runnable接口,我们的run实现逻辑,通过setDirect封装方法,我们用switch循环加上Math.Random随机生成数,随机生成坦克方向,并按for循环来进行移动,每次朝一个方向移动后休眠50毫秒,体现缓冲效果,当然了,如果此线程坦克被击中,那么我们让此线程while循环break,不再执行
为了能使敌方坦克在游戏范围内移动,我们只需要在敌方坦克随机循环移动时,加入一个if条件即可,当超过范围,则不执行此次循环
package tankgame4; import java.util.Vector; /** * Create By 刘鸿涛 * 2022/1/13 2:16 */ public class EnemyTank extends Tank implements Runnable { //在敌人坦克类,使用Vector 保存多个Shot Vector<Shot> shots = new Vector<>(); boolean isLive = true; public EnemyTank(int x, int y) { super(x, y); } @Override public void run() { //然后随机的改变坦克方向 while (true) { //根据坦克的方向来继续移动 switch (getDirect()) { case 0://向上 //让坦克保存一个方向,走30步 for (int i = 0; i < 30; i++) { if (getY() > 0) { moveUp(); //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } break; case 1://右 for (int i = 0; i < 30; i++) { if (getX() < 902) { moveRight(); //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } break; case 2://下 for (int i = 0; i < 30; i++) { if (getY() < 620) { moveDown(); //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } break; case 3://左 for (int i = 0; i < 30; i++) { if (getX() > 0) { moveLeft(); //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } break; } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } //随机改变坦克方向0 - 3 setDirect((int) (Math.random() * 4)); //如果坦克被击中线程终止 if (!isLive) { break; } } } }