JavaGUI-坦克大战03-2

7.线程的应用02

7.3.坦克大战4.0版

坦克大战4.0版

增加功能:

功能1.让敌人的坦克也能够发射子弹(可以有多个子弹)

功能2.当我方坦克集中敌人坦克时,敌人的坦克就消失,如果能做出爆炸的效果更好

功能3.让敌人的坦克也可以自由随机地上下左右移动

功能4.控制我方坦克和敌人坦克在规定的范围内移动

7.3.1功能1:敌方发射子弹

功能1思路

  1. 在敌人坦克类使用Vector保存多个Shot
  2. 当每创建一个敌人坦克时,就给该敌人坦克初始化一个Shot对象,同时启动Shot线程
  3. 在绘制敌人坦克时,需要遍历敌人坦克对象Vector,绘制所有的子弹,当子弹isLive==false时,就从Vector移除

EnemyTank:

package li.TankGame.version04;

import java.util.Vector;

public class EnemyTank extends Tank {

    //在敌人坦克类使用Vector保存多个Shot
Vector<Shot> shots = new Vector<>(); public EnemyTank(int x, int y) {
super(x, y);
} }

MyPanel:在创建敌人坦克的时候,每初始化一个敌人坦克,就创建并初始化一个shot对象,将该shot对象加入到shots集合中,然后启动这个shot对象线程

package li.TankGame.version04;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector; /**
* @author 李
* @version 4.0
* 坦克大战的绘图区域
*/
//为了监听键盘事件,要实现 KeyListener //为了让Panel不停地重绘子弹,需要将MyPanel实现Runnable,
// 当做一个线程使用(因为需要不停地重绘子弹的图案)
public class MyPanel extends JPanel implements KeyListener, Runnable {
//定义我的坦克
Hero hero = null; //定义敌方坦克,放入到Vector集合中
Vector<EnemyTank> enemyTanks = new Vector<>();
int enemyTankNum = 3;//初始化坦克数量 public MyPanel() {
hero = new Hero(100, 100);//初始化自己的坦克
//hero.setSpeed(5); //改变坦克的速度 //初始化敌人的坦克
for (int i = 0; i < enemyTankNum; 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);
}
} @Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 700, 550);//填充矩形,默认为黑色 //画出自己的坦克-封装方法
drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1); //画出hero发射的子弹
//如果子弹对象不为空,并且处于存活状态(即没有超出边界)
if (hero.shot != null && hero.shot.isLive) {
System.out.println("hero的子弹被绘制");
g.draw3DRect(hero.shot.x, hero.shot.y, 2, 2, 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++) {
//enemyTank.shots.size()是当前的enemy对象的shots集合的大小
//取出子弹
Shot shot = enemyTank.shots.get(j);
//绘制子弹
if (shot.isLive) {//shot.isLive==true
g.draw3DRect(shot.x, shot.y, 2, 2, false);
}else{
//从Vector移除子弹shot对象
enemyTank.shots.remove(shot);
}
}
}
} /**
* 编写方法,画出坦克
*
* @param x 坦克的左上角横坐标
* @param y 坦克的左上角纵坐标
* @param g 画笔
* @param direct 坦克方向(上下左右)
* @param type 坦克的类型(我方,敌方)
*/
public void drawTank(int x, int y, Graphics g, int direct, int type) { //根据不同类型的坦克设置不同的颜色
switch (type) {
case 0://敌方坦克
g.setColor(Color.cyan);//设置我方坦克颜色
break;
case 1://我方坦克
g.setColor(Color.yellow);//设敌方坦克颜色
break;
}
//根据坦克坐标方向,来绘制对应方向的坦克
//direct表示方向(0:向上 1:向右 2:向下 3:向左)
switch (direct) {
case 0://表示向上
g.fill3DRect(x, y, 10, 60, false);//画出坦克左边的轮子
g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边的轮子
g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克主体
g.fillOval(x + 10, y + 20, 20, 20);//画出坦克舱体
g.drawLine(x + 20, y + 30, x + 20, y);//画出炮管
break;
case 1://表示向右
//注意:以坦克左上角的(x,y)坐标为参考画图案
g.fill3DRect(x, y, 60, 10, false);//画出坦克上边的轮子
g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边的轮子
g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克方形主体
g.fillOval(x + 20, y + 10, 20, 20);//画出坦克舱体
g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮管
break;
case 2://表示向下
//向下只需要将向上的炮筒的位置改变即可
g.fill3DRect(x, y, 10, 60, false);//画出坦克左边的轮子
g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边的轮子
g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克主体
g.fillOval(x + 10, y + 20, 20, 20);//画出坦克舱体
g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮管
break;
case 3://表示向左
//向左只需要将向右的炮筒的位置改变即可
g.fill3DRect(x, y, 60, 10, false);//画出坦克上边的轮子
g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边的轮子
g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克方形主体
g.fillOval(x + 20, y + 10, 20, 20);//画出坦克舱体
g.drawLine(x + 30, y + 20, x, y + 20);//画出炮管
break;
default:
System.out.println("暂时没有处理");
} } @Override
public void keyTyped(KeyEvent e) { } //控制方向--处理 WSAD 键按下的情况
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键-向上
//改变坦克的方向
hero.setDirect(0);
//修改坦克的坐标
hero.moveUp();
} else if (e.getKeyCode() == KeyEvent.VK_D) {//按下D键-向右
hero.setDirect(1);
hero.moveRight();
} else if (e.getKeyCode() == KeyEvent.VK_S) {//按下S键-向下
hero.setDirect(2);
hero.moveDown();
} else if (e.getKeyCode() == KeyEvent.VK_A) {//按下A键-向左
hero.setDirect(3);
hero.moveLeft();
} //如果用户按下j键,hero就发射子弹
if (e.getKeyCode() == KeyEvent.VK_J) {
hero.shotEnemyTank();
} //让面板重绘
this.repaint();
} @Override
public void keyReleased(KeyEvent e) { } @Override
public void run() {//每个100毫秒,重绘区域,刷新绘图区域,子弹就移动
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.repaint();
}
}
}

7.3.2功能2:敌方坦克消失

功能2:当我方坦克集中敌人坦克时,敌人的坦克就消失,如果能做出爆炸的效果更好

功能2思路:

  1. 关键点在于判断子弹怎么样算击中敌人:即坦克的击中范围的坐标
  2. 在run()方法中不断地去判断是否击中敌人
  3. 然后在调用drawTank方法前也进行相应的判断,如果敌人未被击中,即enemy.isLive = true,就调用drawTank方法
  4. 关于子弹线程的退出问题:之前只是判断了子弹超出面板时会结束线程。现增加新的线程停止条件:当子弹击中敌方坦克时,该子弹的线程也应结束

更改的类:Shot、EnemyTank、MyPanel:

Shot:更改了子弹线程结束的条件

package li.TankGame.version04;

/**
* @author 李
* @version 4.0
* 射击子弹
*/
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) {
//休眠50 毫秒
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;
}
//测试。输出坐标
System.out.println("x=" + x + "y=" + y);
//如果子弹移动到面板边界时,就应该销毁(把启动的子弹的线程销毁)
//当子弹碰到敌人坦克时,也应该结束线程
if (!(x >= 0 && x <= 700 && y <= 550 && y >= 0 && isLive)) {//如果不符合条件
System.out.println("子弹线程退出...");
isLive = false;
break;//线程退出
}
}
}
}

EnemyTank:增加了isLive 属性

package li.TankGame.version04;

import java.util.Vector;

public class EnemyTank extends Tank {

    //在敌人坦克类使用Vector保存多个Shot
Vector<Shot> shots = new Vector<>();
boolean isLive = true; public EnemyTank(int x, int y) {
super(x, y);
} }

MyPanel:修改了:

  1. 在paint()方法中修改绘出敌人坦克的方法,当敌人坦克是活的(未被击中),才将其画出,同时循环取出该enemyTank对象shots集合中的所有子弹,将其子弹画出,如果该子弹超出画板边界:shot.isLive=false则从该从enemyTank对象的Vector集合shots中移除子弹shot对象

  2. 编写一个hitTank()方法,判定击中敌人的条件,判定成功则将该子弹对象的isLive 设为 false(结束子弹进程); 将击中的敌人坦克对象的isLive属性也设为false(不再绘出该坦克)


  1. 在run()方法中的while循环中,增加判断是否击中敌人坦克的语句,进行相应操作(因为子弹的坐标在一直变化,所以要一直判断),注意敌人坦克被击中之后要在相应集合中删除该敌人对象。
package li.TankGame.version04;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector; /**
* @author 李
* @version 4.0
* 坦克大战的绘图区域
*/
//为了监听键盘事件,要实现 KeyListener //为了让Panel不停地重绘子弹,需要将MyPanel实现Runnable,
// 当做一个线程使用(因为需要不停地重绘子弹的图案)
public class MyPanel extends JPanel implements KeyListener, Runnable {
//定义我的坦克
Hero hero = null; //定义敌方坦克,放入到Vector集合中
Vector<EnemyTank> enemyTanks = new Vector<>();
int enemyTankNum = 3;//初始化坦克数量 public MyPanel() {
hero = new Hero(100, 100);//初始化自己的坦克
//hero.setSpeed(5); //改变坦克的速度 //初始化敌人的坦克
for (int i = 0; i < enemyTankNum; 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);
}
} @Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 700, 550);//填充矩形,默认为黑色 //画出自己的坦克-封装方法
drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1); //画出hero发射的子弹
//如果子弹对象不为空,并且处于存活状态(即没有超出边界)
if (hero.shot != null && hero.shot.isLive) {
System.out.println("hero的子弹被绘制");
g.draw3DRect(hero.shot.x, hero.shot.y, 2, 2, false);
} //画出敌人的坦克,遍历Vector
for (int i = 0; i < enemyTanks.size(); i++) {
//取出坦克
EnemyTank enemyTank = enemyTanks.get(i);
if (enemyTank.isLive) {//当敌人坦克是活的,才将其画出
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
//画出enemyTank所有子弹
for (int j = 0; j < enemyTank.shots.size(); j++) {
//enemyTank.shots.size()是当前的enemy对象的shots集合的大小
//取出子弹
Shot shot = enemyTank.shots.get(j);
//绘制子弹
if (shot.isLive) {//shot.isLive==true
g.draw3DRect(shot.x, shot.y, 2, 2, false);
} else {
//从Vector移除子弹shot对象
enemyTank.shots.remove(shot);
}
}
}
}
} /**
* 编写方法,画出坦克
*
* @param x 坦克的左上角横坐标
* @param y 坦克的左上角纵坐标
* @param g 画笔
* @param direct 坦克方向(上下左右)
* @param type 坦克的类型(我方,敌方)
*/
public void drawTank(int x, int y, Graphics g, int direct, int type) { //根据不同类型的坦克设置不同的颜色
switch (type) {
case 0://敌方坦克
g.setColor(Color.cyan);//设置我方坦克颜色
break;
case 1://我方坦克
g.setColor(Color.yellow);//设敌方坦克颜色
break;
}
//根据坦克坐标方向,来绘制对应方向的坦克
//direct表示方向(0:向上 1:向右 2:向下 3:向左)
switch (direct) {
case 0://表示向上
g.fill3DRect(x, y, 10, 60, false);//画出坦克左边的轮子
g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边的轮子
g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克主体
g.fillOval(x + 10, y + 20, 20, 20);//画出坦克舱体
g.drawLine(x + 20, y + 30, x + 20, y);//画出炮管
break;
case 1://表示向右
//注意:以坦克左上角的(x,y)坐标为参考画图案
g.fill3DRect(x, y, 60, 10, false);//画出坦克上边的轮子
g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边的轮子
g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克方形主体
g.fillOval(x + 20, y + 10, 20, 20);//画出坦克舱体
g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮管
break;
case 2://表示向下
//向下只需要将向上的炮筒的位置改变即可
g.fill3DRect(x, y, 10, 60, false);//画出坦克左边的轮子
g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边的轮子
g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克主体
g.fillOval(x + 10, y + 20, 20, 20);//画出坦克舱体
g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮管
break;
case 3://表示向左
//向左只需要将向右的炮筒的位置改变即可
g.fill3DRect(x, y, 60, 10, false);//画出坦克上边的轮子
g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边的轮子
g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克方形主体
g.fillOval(x + 20, y + 10, 20, 20);//画出坦克舱体
g.drawLine(x + 30, y + 20, x, y + 20);//画出炮管
break;
default:
System.out.println("暂时没有处理");
} } /*编写方法,判断我方的子弹是否击中敌人*/ public static void hitTank(Shot s, EnemyTank enemyTank) {
//判断s击中坦克
switch (enemyTank.getDirect()) {
case 0://敌人坦克向上(向上和向下的击中范围一样)
case 2://敌人坦克向下
if (s.x > enemyTank.getX() && s.y < enemyTank.getX() + 40
&& s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60) {
s.isLive = false;
enemyTank.isLive = false;
//当子弹击中敌人后,将enemyTank从Vector中删除
enemyTanks.remove(enemyTank); }
case 1://右
case 3://左
if (s.x > enemyTank.getX() && s.y < enemyTank.getX() + 60
&& s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40) {
s.isLive = false;
enemyTank.isLive = false;
//当子弹击中敌人后,将enemyTank从Vector中删除
enemyTanks.remove(enemyTank); }
break;
} } @Override
public void keyTyped(KeyEvent e) { } //控制方向--处理 WSAD 键按下的情况
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键-向上
//改变坦克的方向
hero.setDirect(0);
//修改坦克的坐标
hero.moveUp();
} else if (e.getKeyCode() == KeyEvent.VK_D) {//按下D键-向右
hero.setDirect(1);
hero.moveRight();
} else if (e.getKeyCode() == KeyEvent.VK_S) {//按下S键-向下
hero.setDirect(2);
hero.moveDown();
} else if (e.getKeyCode() == KeyEvent.VK_A) {//按下A键-向左
hero.setDirect(3);
hero.moveLeft();
} //如果用户按下j键,hero就发射子弹
if (e.getKeyCode() == KeyEvent.VK_J) {
hero.shotEnemyTank();
} //让面板重绘
this.repaint();
} @Override
public void keyReleased(KeyEvent e) { } @Override
public void run() {//每个100毫秒,重绘区域,刷新绘图区域,子弹就移动
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} //判断是否击中敌人坦克
if (hero.shot != null && hero.shot.isLive) {//如果当前我方子弹不为空且子弹还存活
//就遍历敌人所有的坦克
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);
hitTank(hero.shot, enemyTank);
}
}
this.repaint();
}
}
}

7.3.3功能2plus:坦克爆炸

功能2完善:当子弹击中坦克时,做出爆炸效果

思路:

  1. 新建一个炸弹Bomb类

  2. 在MyPanel里定义一个Vector集合,用于存放Bomb对象

  3. 在hitTank方法中判断,当子弹击中坦克时就加入一个Bomb对象到bombs集合里(这里将hitTank方法改为非静态方法)

  4. 想要显示出炸弹的效果图,就要在paint方法里面画出,如果bombs集合中有炸弹对象,就画出来

  5. 将爆炸的素材图片放到out目录的根目录下面

Bomb:

package li.TankGame.version04;

/**
* @author 李
* @version 4.0
* 炸弹
*/
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;
}
}
}

MyPanel:

package li.TankGame.version04;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector; /**
* @author 李
* @version 4.0
* 坦克大战的绘图区域
*/
//为了监听键盘事件,要实现 KeyListener //为了让Panel不停地重绘子弹,需要将MyPanel实现Runnable,
// 当做一个线程使用(因为需要不停地重绘子弹的图案)
public class MyPanel extends JPanel implements KeyListener, Runnable {
//定义我的坦克
Hero hero = null; //定义敌方坦克,放入到Vector集合中
Vector<EnemyTank> enemyTanks = new Vector<>();
//定义一个Vector,用于存放炸弹
//说明:当子弹击中坦克时就加入一个Bomb对象到bombs集合里
Vector<Bomb> bombs = new Vector<>();
int enemyTankNum = 3;//初始化坦克数量 //定义三张炸弹图片,用于显示爆炸效果
Image image1 = null;
Image image2 = null;
Image image3 = null; public MyPanel() {
hero = new Hero(100, 100);//初始化自己的坦克
//hero.setSpeed(5); //改变坦克的速度 //初始化敌人的坦克
for (int i = 0; i < enemyTankNum; 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);
} //初始化图片对象
image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb1.png"));
image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb2.png"));
image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb3.png"));
} @Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 700, 550);//填充矩形,默认为黑色 //画出自己的坦克-封装方法
drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1); //画出hero发射的子弹
//如果子弹对象不为空,并且处于存活状态(即没有超出边界)
if (hero.shot != null && hero.shot.isLive) {
System.out.println("hero的子弹被绘制");
g.draw3DRect(hero.shot.x, hero.shot.y, 2, 2, false);
} //如果bombs集合中有炸弹对象,就画出来
for (int i = 0; i < bombs.size(); i++) {
try {
Thread.sleep(50);//先休眠50毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
//取出炸弹
Bomb bomb = bombs.get(i);
//根据当前bomb对象的life的只去画出对应的图片
if (bomb.life > 6) {
g.drawImage(image1, bomb.x, bomb.y, 60, 60, this);
} else if (bomb.life > 3) {
g.drawImage(image2, bomb.x, bomb.y, 60, 60, this);
} else {
g.drawImage(image3, bomb.x, bomb.y, 60, 60, this);
}
//让炸弹的生命值减少
bomb.lifeDown();
//如果bomb.life==0,就从bombs集合中删除这个对象
if (bomb.life == 0) {
bombs.remove(bomb);
} } //画出敌人的坦克,遍历Vector
for (int i = 0; i < enemyTanks.size(); i++) {
//取出坦克
EnemyTank enemyTank = enemyTanks.get(i);
if (enemyTank.isLive) {//当敌人坦克是活的,才将其画出
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
//画出enemyTank所有子弹
for (int j = 0; j < enemyTank.shots.size(); j++) {
//enemyTank.shots.size()是当前的enemy对象的shots集合的大小
//取出子弹
Shot shot = enemyTank.shots.get(j);
//绘制子弹
if (shot.isLive) {//shot.isLive==true
g.draw3DRect(shot.x, shot.y, 2, 2, false);
} else {
//从Vector移除子弹shot对象
enemyTank.shots.remove(shot);
}
}
}
}
} /**
* 编写方法,画出坦克
*
* @param x 坦克的左上角横坐标
* @param y 坦克的左上角纵坐标
* @param g 画笔
* @param direct 坦克方向(上下左右)
* @param type 坦克的类型(我方,敌方)
*/
public void drawTank(int x, int y, Graphics g, int direct, int type) { //根据不同类型的坦克设置不同的颜色
switch (type) {
case 0://敌方坦克
g.setColor(Color.cyan);//设置我方坦克颜色
break;
case 1://我方坦克
g.setColor(Color.yellow);//设敌方坦克颜色
break;
}
//根据坦克坐标方向,来绘制对应方向的坦克
//direct表示方向(0:向上 1:向右 2:向下 3:向左)
switch (direct) {
case 0://表示向上
g.fill3DRect(x, y, 10, 60, false);//画出坦克左边的轮子
g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边的轮子
g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克主体
g.fillOval(x + 10, y + 20, 20, 20);//画出坦克舱体
g.drawLine(x + 20, y + 30, x + 20, y);//画出炮管
break;
case 1://表示向右
//注意:以坦克左上角的(x,y)坐标为参考画图案
g.fill3DRect(x, y, 60, 10, false);//画出坦克上边的轮子
g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边的轮子
g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克方形主体
g.fillOval(x + 20, y + 10, 20, 20);//画出坦克舱体
g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮管
break;
case 2://表示向下
//向下只需要将向上的炮筒的位置改变即可
g.fill3DRect(x, y, 10, 60, false);//画出坦克左边的轮子
g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边的轮子
g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克主体
g.fillOval(x + 10, y + 20, 20, 20);//画出坦克舱体
g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮管
break;
case 3://表示向左
//向左只需要将向右的炮筒的位置改变即可
g.fill3DRect(x, y, 60, 10, false);//画出坦克上边的轮子
g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边的轮子
g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克方形主体
g.fillOval(x + 20, y + 10, 20, 20);//画出坦克舱体
g.drawLine(x + 30, y + 20, x, y + 20);//画出炮管
break;
default:
System.out.println("暂时没有处理");
} } /*编写方法,判断我方的子弹是否击中敌人*/ public void hitTank(Shot s, EnemyTank enemyTank) {
//判断s击中坦克
switch (enemyTank.getDirect()) {
case 0://敌人坦克向上(向上和向下的击中范围一样)
case 2://敌人坦克向下
if (s.x > enemyTank.getX() && s.y < enemyTank.getX() + 40
&& s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60) {
s.isLive = false;
enemyTank.isLive = false;
//当子弹击中敌人后,将enemyTank从Vector中删除
enemyTanks.remove(enemyTank);
//创建一个Bomb对象,加入到bombs集合中
Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());
bombs.add(bomb);
}
case 1://右
case 3://左
if (s.x > enemyTank.getX() && s.y < enemyTank.getX() + 60
&& s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40) {
s.isLive = false;
enemyTank.isLive = false;
//当子弹击中敌人后,将enemyTank从Vector中删除
enemyTanks.remove(enemyTank);
//创建一个Bomb对象,加入到bombs集合中
Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());
bombs.add(bomb);
}
break;
}
} @Override
public void keyTyped(KeyEvent e) { } //控制方向--处理 WSAD 键按下的情况
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键-向上
//改变坦克的方向
hero.setDirect(0);
//修改坦克的坐标
hero.moveUp();
} else if (e.getKeyCode() == KeyEvent.VK_D) {//按下D键-向右
hero.setDirect(1);
hero.moveRight();
} else if (e.getKeyCode() == KeyEvent.VK_S) {//按下S键-向下
hero.setDirect(2);
hero.moveDown();
} else if (e.getKeyCode() == KeyEvent.VK_A) {//按下A键-向左
hero.setDirect(3);
hero.moveLeft();
} //如果用户按下j键,hero就发射子弹
if (e.getKeyCode() == KeyEvent.VK_J) {
hero.shotEnemyTank();
} //让面板重绘
this.repaint();
} @Override
public void keyReleased(KeyEvent e) { } @Override
public void run() {//每个100毫秒,重绘区域,刷新绘图区域,子弹就移动
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} //判断是否击中敌人坦克
if (hero.shot != null && hero.shot.isLive) {//如果当前我的子弹不为空并且子弹还存活
//就遍历敌人所有的坦克
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);
hitTank(hero.shot, enemyTank);
}
}
this.repaint();
}
}
}

day03-2-应用线程02的更多相关文章

  1. python并发编程-进程间通信-Queue队列使用-生产者消费者模型-线程理论-创建及对象属性方法-线程互斥锁-守护线程-02

    目录 进程补充 进程通信前言 Queue队列的基本使用 通过Queue队列实现进程间通信(IPC机制) 生产者消费者模型 以做包子买包子为例实现当包子卖完了停止消费行为 线程 什么是线程 为什么要有线 ...

  2. Java线程知识

    概念 线程生命周期 Java线程模型 线程方法 线程优先级 线程同步 线程在多任务处理应用程序中有着至关重要的作用 概念 基本概念 进程:在操作系统中每个独立运行的程序就是一个进程 线程:程序执行的一 ...

  3. 第一章 - Java与线程

    目录 01.Java和线程 02.Java与协程 03.CPU缓存结构和java内存模型 04.管程-悲观锁和锁优化 05.乐观锁 - 无锁方案 06.线程安全 07.线程池 08.JUC 09.高并 ...

  4. Java 仿迅雷多线程下载

    package net.webjoy.jackluo.android_json; /** * 1.http Range "bytes="+ start+end * 2.Random ...

  5. 即学即会 Java 程序设计基础视频教程(100课整)无水印版

    课程总共包含100个课时,总授课长达27多个小时,内容覆盖面广,从入门到精通,授课通俗易懂,分析问题独到精辟通过本套视频的学习,学员能够快速的掌握java编程语言,成为java高手. 课程目录:课时1 ...

  6. 20160212.CCPP体系详解(0022天)

    程序片段(01):01.二维数组.c 内容概要:二维数组 #include <stdio.h> #include <stdlib.h> //01.关于栈内存开辟数组: // 诀 ...

  7. Python并发编程之创建多线程的几种方法(二)

    大家好,并发编程 进入第二篇. 今天的内容会比较基础,主要是为了让新手也能无障碍地阅读,所以还是要再巩固下基础.学完了基础,你们也就能很顺畅地跟着我的思路理解以后的文章. 本文目录 学会使用函数创建多 ...

  8. 编程从入门到放弃(Java)

      1.Java入门篇 1.1 基础入门和面向对象 1.1.1 编程基础 [01] Java语言的基本认识 [02] 类和对象 [03] 类的结构和创建对象 [04] 包和访问权限修饰符 [05] 利 ...

  9. Android中多线程编程(三)Handler更新UI的方式

    Handler更新UI的方式和原因以及遇到的问题 1.方式: 仅仅能通过Handler来更新UI. 代码例如以下: package com.chengdong.su.handlerdemo; impo ...

  10. OpenMP笔记(五)

    任务调度主要用于并行的for循环中,当循环中每次迭代的计算量不相等时,如果简单地给各个线程分配相同次数的迭代的话,会造成各个线程计算负载不均衡,这会使得有些线程先执行完,有些后执行完,造成某些CPU核 ...

随机推荐

  1. Git企业开发控制理论和实操-从入门到深入(六)|多人协作开发

    前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量博客汇总 然后就是博主最近最花时间的一 ...

  2. C语言输出百分号%

    遭遇的问题 在学习时有一个课后题要求计算两个变量的加减乘除以及取余,其中去余需要输出如下的效果: 10 % 5 = 0; 我就写了这样的代码: printf("a % b = %d" ...

  3. 关于JAVA泛型数组类型擦除引发的问题及解决方案

    先看如下一个DEMO示例代码:(其中doBatchGet被子类重写了1次) public abstract class BaseDemoService<T> { public String ...

  4. 一个关于用netty的小错误反思

    一个关于用netty的小认知 在使用netty时,观看了黑马的netty网课,没想就直接用他的依赖了 依赖如下 <dependency> <groupId>io.netty&l ...

  5. 手把手带你上手swagger3

    配置POM 只需要加一个依赖,并且要注意,swagger3在springboot2.5版本以上会出现问题 <dependency> <groupId>io.springfox& ...

  6. delphi IDE 控件居中的方法

  7. NC24416 [USACO 2013 Nov G]No Change

    题目链接 题目 题目描述 Farmer John is at the market to purchase supplies for his farm. He has in his pocket K ...

  8. 收集 VSCode 常用快捷键

    快速复制行 Shift + Alt + ↑/↓ 都是往下复制行,区别是:按↓复制时光标会跟着向下移动,按↑复制时光标不移动. 向上/向下移动一行 Alt + ↑/↓ 删除整行 Ctrl + Shift ...

  9. java 手写并发框架(一)异步查询转同步的 7 种实现方式

    序言 本节将学习一下如何实现异步查询转同步的方式,共计介绍了 7 种常见的实现方式. 思维导图如下: 异步转同步 业务需求 有些接口查询反馈结果是异步返回的,无法立刻获取查询结果. 比如业务开发中我们 ...

  10. java 从零开始手写 redis(11)clock时钟淘汰算法详解及实现

    前言 java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不丢失? ...