在前面的坦克大战1.0已经完成了移动 绘制我方/敌方坦克。
接下来会运用到前面学习的多线程的基础,来实现发射子弹的效果
想要坦克发射子弹,可以用以下思路
按J发射子弹
将子弹变成一个线程,让子弹的x,y值不停变换,画板重绘,当子弹到达边界时就将线程销毁
步骤如下:
子弹类
子弹类(线程)定义好了自然要启动,而子弹是从坦克发送出来的,所以启动线程就在坦克类中定义
坦克类
监听器
画板类绘制子弹
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;
}
@Override
public 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);
}
}
@Override
public 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;
}
@Override
public 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);
}
}
@Override
public 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;
}
@Override
public 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);
}
}
@Override
public 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;
}
}
}
@Override
public 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);
}
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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);
}
}
@Override
public 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;
}
}
}
@Override
public 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);
}
@Override
public 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);
}
}
@Override
public 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;
}
}
}
@Override
public 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);
}
}
@Override
public 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;
}
}
}
@Override
public 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();
}
}
}