柯桥建设局网站首页如何制作网站链接
目录
坦克大战【2】
线程-应用到坦克大战
坦克大战0.3
思路分析:
代码实现:
坦克大战0.4
增加功能
特别说明
思路分析:
代码实现:
坦克大战0.5
增加功能
思路分析:
代码实现:
坦克大战【2】
线程-应用到坦克大战
坦克大战0.3
在坦克大战游戏(0.2版)基础上添加如下功能:当玩家按一下j键,就发射一颗子弹
思路分析:
- 当发射一颗子弹后,就相当于启动的一个线程
- Hero有子弹的对象,当按下J时,就启动一个发射行为(线程),让子弹不停的移动,形成一个射击效果
- MyPanel需要不断地重绘,才能出现该效果
- 当子弹移动到面板的边界时,就应该销毁(把启动的子弹的线程销毁)
代码实现:
package com18.tank_game03;/*** @author 甲柒* @version 1.0* @title Shot* @package com18.tank_game03* @time 2023/3/29 21:11* 射击子弹*/
public class Shot implements Runnable {int x;//子弹x坐标int y;//子弹y坐标int direct = 0;//子弹方向int speed = 5;//子弹速度boolean isLive = true;//子弹是否存活//构造器public Shot(int x, int y, int direct) {this.x = x;this.y = y;this.direct = direct;}@Overridepublic void run() {//射击while (true) {//子弹线程休眠50mstry {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}//根据方向改变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 <= 1000 && y >= 0 && y <= 750)) {isLive = false;break;}}}
}
package com18.tank_game03;/*** @author 甲柒* @version 1.3* @title Tank* @package com18.tank_game03* @time 2023/3/29 22:27*/
public class Tank {private int x;//坦克的横坐标private int y;//坦克的纵坐标private int direct;//坦克方向 0 上 1 右 2 下 3 左private int speed = 1;public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}public Tank(int x, int y) {this.x = x;this.y = y;}//上下移动方法public void moveUp() {y -= speed;}public void moveRight() {x += speed;}public void moveDown() {y += speed;}public void moveLeft() {x -= speed;}public int getDirect() {return direct;}public void setDirect(int direct) {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;}
}
package com18.tank_game03;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;/*** @author 甲柒* @version 1.3* @title MyPanel* @package com18.tank_game03* @time 2023/3/29 22:30*/
//为了监听 键盘事件,实现KeyListener
//为了让Panel 不停的重绘子弹,需要将MyPanel实现Runnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable {//定义我的坦克Hero hero = null;//定义敌人的坦克,放到VectorVector<EnemyTank> enemyTanks = new Vector<>();int enemyTankSize = 3;public MyPanel() {hero = new Hero(100, 100);//初始化自己的坦克//初始化敌人的坦克for (int i = 0; i < enemyTankSize; i++) {//创建一个敌人的坦克EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);//设置方向enemyTank.setDirect(2);//加入enemyTanks.add(enemyTank);}hero.setSpeed(10);}@Overridepublic void paint(Graphics g) {super.paint(g);g.fillRect(0, 0, 1000, 750);//填充矩形,默认黑色//画出坦克-封装方法drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 0);//画出hero射击的子弹if (hero.shot != null && hero.shot.isLive == true) {System.out.println("子弹被绘制~~~~~");g.draw3DRect(hero.shot.x, hero.shot.y, 2, 2, false);}//画出敌人的坦克,遍历Vectorfor (int i = 0; i < enemyTanks.size(); i++) {//取出坦克EnemyTank enemyTank = enemyTanks.get(i);drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 1);}}//编写方法,画出坦克/*** @param x 坦克的左上角x坐标* @param y 坦克的左上角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://表示向右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("暂时没有处理");}}@Overridepublic void keyTyped(KeyEvent e) {}//处理ADWS键按下的情况@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键//改变坦克的方向hero.setDirect(0);//修改坦克坐标 y+=1hero.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,就发射if (e.getKeyCode() == KeyEvent.VK_J) {System.out.println("用户按下了J开始射击~~~~");hero.shotEnemyTank();}//重绘this.repaint();}@Overridepublic void keyReleased(KeyEvent e) {}@Overridepublic void run() {//每隔100ms 重绘区域 刷新绘图区域while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}this.repaint();}}
}
package com18.tank_game03;/*** @author 甲柒* @version 1.3* @title Hero* @package com18.tank_game03* @time 2023/3/29 22:29*/
public class Hero extends Tank {//定义一个Shot对象,表示一个射击(线程)Shot shot = null;public Hero(int x, int y) {super(x, y);}//射击public void shotEnemyTank() {//创建Shot对象,根据当前Hero对象的位置和方向来创建Shotswitch (getDirect()) {case 0://向上shot = new Shot(getX() + 20, getY(), 0);break;case 1://向右shot = new Shot(getX() + 60, getY() + 20, 1);break;case 2://向下shot = new Shot(getX() + 20, getY() + 60, 2);break;case 3://向左shot = new Shot(getX(), getY() + 20, 3);break;}//启动Shot线程new Thread(shot).start();}
}
package com18.tank_game03;/*** @author 甲柒* @version 1.3* @title EnemyTank* @package com18.tank_game03* @time 2023/3/29 12:06* 敌人的坦克*/
public class EnemyTank extends Tank {public EnemyTank(int x, int y) {super(x, y);}
}
package com18.tank_game03;import javax.swing.*;/*** @author 甲柒* @version 1.3* @title TankGame03* @package com18.tank_game03* @time 2023/3/29 22:33*/
public class TankGame03 extends JFrame {//定义MyPanelMyPanel mp = null;public TankGame03() {mp = new MyPanel();//将mp 放入到Thread,并启动Thread thread = new Thread(mp);thread.start();this.add(mp);//把面板(就是游戏的绘图区域)this.setSize(1000, 750);this.addKeyListener(mp);//让JFrame监听mp的键盘事件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);}public static void main(String[] args) {TankGame03 tankGame03 = new TankGame03();}
}
坦克大战0.4
增加功能
- 让敌人的坦克也能发射子弹(可以有多颗子弹)
- 当我方坦克击中敌人时,敌人的坦克就消失,如果能做出爆炸效果更好
- 让敌人的坦克也可以自由随机的上下左右移动
- 控制我方坦克和敌方坦克在规定的范围移动
特别说明
- 只要能实现就行
- 完成上面的任务
思路分析:
让敌人的坦克也能发射子弹(可以有多颗子弹)
- 在敌人的坦克类,使用Vector保存多个Shot
- 当每创建一个敌人坦克对象,给该敌人坦克对象初始化一个Shot对象,同时启动Shot
- 在绘制敌人坦克时,需要遍历敌人坦克对象Vector,绘制所有的子弹,当子弹isLive == false时,就从Vector移除
让敌人的坦克也可以自由随机的上下左右移动
- 需要将敌人坦克当成线程使用
- 需要EnemyTank implements Runnable
- 在run方法中写上相应的业务代码
- 在创建敌人坦克对象时,启动线程
代码实现:
package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Tank* @package com18.tank_game04* @time 2023/3/29 22:27*/
public class Tank {private int x;//坦克的横坐标private int y;//坦克的纵坐标private int direct;//坦克方向 0 上 1 右 2 下 3 左private int speed = 1;public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}public Tank(int x, int y) {this.x = x;this.y = y;}//上下移动方法public void moveUp() {y -= speed;}public void moveRight() {x += speed;}public void moveDown() {y += speed;}public void moveLeft() {x -= speed;}public int getDirect() {return direct;}public void setDirect(int direct) {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;}
}
package com18.tank_game04;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;/*** @author 甲柒* @version 1.4* @title MyPanel* @package com18.tank_game04* @time 2023/3/29 22:30*/
//为了监听 键盘事件,实现KeyListener
//为了让Panel 不停的重绘子弹,需要将MyPanel实现Runnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable {//定义我的坦克Hero hero = null;//定义敌人的坦克,放到VectorVector<EnemyTank> enemyTanks = new Vector<>();//定义一个Vector 用于存放炸弹//说明 当子弹击中坦克时,加入一个Bomb对象到bombsVector<Bomb> bombs = new Vector<>();int enemyTankSize = 3;//定义三张炸弹图片,用于显示爆炸效果Image image1 = null;Image image2 = null;Image image3 = null;public MyPanel() {hero = new Hero(100, 100);//初始化自己的坦克//初始化敌人的坦克for (int i = 0; i < enemyTankSize; i++) {//创建一个敌人的坦克EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);//设置方向enemyTank.setDirect(2);//启动敌人坦克进程new Thread(enemyTank).start();//给该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("/bomb_1.gif"));image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));//设置我方坦克速度hero.setSpeed(10);}//编写方法,判断我方的子弹是否击中敌方的坦克public void hiTank(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;//当我的子弹击中敌人坦克后,将enemyTank 从 Vector 拿掉enemyTanks.remove(enemyTank);//创建Bomb对象,加入到bombs集合Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());bombs.add(bomb);}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;//创建Bomb对象,加入到bombs集合Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());bombs.add(bomb);}break;}}//编写方法,画出坦克@Overridepublic void paint(Graphics g) {super.paint(g);g.fillRect(0, 0, 1000, 750);//填充矩形,默认黑色//画出坦克-封装方法drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 0);//画出hero射击的子弹if (hero.shot != null && hero.shot.isLive == true) {System.out.println("子弹被绘制~~~~~");g.draw3DRect(hero.shot.x, hero.shot.y, 2, 2, false);}//如果bombs 集合中有对象,就画出for (int i = 0; i < bombs.size(); i++) {//取出炸弹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);}}//画出敌人的坦克,遍历Vectorfor (int i = 0; i < enemyTanks.size(); i++) {//从 Vector 取出坦克EnemyTank enemyTank = enemyTanks.get(i);if (enemyTank.isLive) {//当敌人坦克是存活的,才画出该坦克drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 1);//画出 enemyTank 所有子弹for (int j = 0; j < enemyTank.shots.size(); j++) {//取出子弹Shot shot = enemyTank.shots.get(j);//绘制if (shot.isLive) {//isLive == trueg.draw3DRect(shot.x, shot.y, 1, 1, false);} else {//从Vector中移除enemyTank.shots.remove(shot);}}}}}/*** @param x 坦克的左上角x坐标* @param y 坦克的左上角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://表示向右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("暂时没有处理");}}@Overridepublic void keyTyped(KeyEvent e) {}//处理ADWS键按下的情况@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键//改变坦克的方向hero.setDirect(0);//修改坦克坐标 y+=1if (hero.getY() > 0) {hero.moveUp();}} else if (e.getKeyCode() == KeyEvent.VK_D) {//D键hero.setDirect(1);if (hero.getX() + 60 < 1000) {hero.moveRight();}} else if (e.getKeyCode() == KeyEvent.VK_S) {//S键hero.setDirect(2);if (hero.getY() + 60 < 750) {hero.moveDown();}} else if (e.getKeyCode() == KeyEvent.VK_A) {//A键hero.setDirect(3);if (hero.getX() > 0) {hero.moveLeft();}}//如果用户按下J,就发射if (e.getKeyCode() == KeyEvent.VK_J) {System.out.println("用户按下了J开始射击~~~~");hero.shotEnemyTank();}//重绘this.repaint();}@Overridepublic void keyReleased(KeyEvent e) {}@Overridepublic void run() {//每隔100ms 重绘区域 刷新绘图区域while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}//判断是否击中了敌人的坦克if (hero.shot != null && hero.shot.isLive) {//当我的子弹还存活//遍历敌人的所有坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTank enemyTank = enemyTanks.get(i);hiTank(hero.shot, enemyTank);}}this.repaint();}}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Hero* @package com18.tank_game04* @time 2023/3/29 22:29*/
public class Hero extends Tank {//定义一个Shot对象,表示一个射击(线程)Shot shot = null;public Hero(int x, int y) {super(x, y);}//射击public void shotEnemyTank() {//创建Shot对象,根据当前Hero对象的位置和方向来创建Shotswitch (getDirect()) {case 0://向上shot = new Shot(getX() + 20, getY(), 0);break;case 1://向右shot = new Shot(getX() + 60, getY() + 20, 1);break;case 2://向下shot = new Shot(getX() + 20, getY() + 60, 2);break;case 3://向左shot = new Shot(getX(), getY() + 20, 3);break;}//启动Shot线程new Thread(shot).start();}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Shot* @package com18.tank_game04* @time 2023/3/29 21:11* 射击子弹*/
public class Shot implements Runnable {int x;//子弹x坐标int y;//子弹y坐标int direct = 0;//子弹方向int speed = 5;//子弹速度boolean isLive = true;//子弹是否存活//构造器public Shot(int x, int y, int direct) {this.x = x;this.y = y;this.direct = direct;}@Overridepublic void run() {//射击while (true) {//子弹线程休眠50mstry {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}//根据方向改变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 <= 1000 && y >= 0 && y <= 750 && isLive)) {isLive = false;break;}}}
}
package com18.tank_game04;import java.util.Vector;/*** @author 甲柒* @version 1.4* @title EnemyTank* @package com18.tank_game04* @time 2023/3/29 12:06* 敌人的坦克*/
public class EnemyTank extends Tank implements Runnable {//在敌人的坦克类,使用Vector保存多个ShotVector<Shot> shots = new Vector<>();boolean isLive = true;public EnemyTank(int x, int y) {super(x, y);}@Overridepublic void run() {while (true) {//根据坦克的方向来继续移动switch (getDirect()) {case 0://向上for (int i = 0; i < 100; i++) {if (getY() > 0) {moveUp();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 1://向右for (int i = 0; i < 100; i++) {if (getX() + 60 < 1000) {moveRight();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 2://向下for (int i = 0; i < 100; i++) {if (getY() + 60 < 750) {moveDown();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 3://向左for (int i = 0; i < 100; i++) {if (getX() > 0) {moveLeft();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;}//休眠50mstry {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}//然后随机地改变坦克方向 0-3setDirect((int) (Math.random() * 4));//注意 写并发程序 一定要考虑清楚 该线程什么时候结束if (!isLive) {break;//退出程序}}}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.0* @title Bomb* @package com18.tank_game04* @time 2023/3/29 22:47* 炸弹*/
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;}}
}
package com18.tank_game04;import javax.swing.*;/*** @author 甲柒* @version 1.4* @title TankGame04* @package com18.tank_game04* @time 2023/3/29 22:33*/
public class TankGame04 extends JFrame {//定义MyPanelMyPanel mp = null;public TankGame04() {mp = new MyPanel();//将mp 放入到Thread,并启动Thread thread = new Thread(mp);thread.start();this.add(mp);//把面板(就是游戏的绘图区域)this.setSize(1000, 750);this.addKeyListener(mp);//让JFrame监听mp的键盘事件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);}public static void main(String[] args) {TankGame04 tankGame04 = new TankGame04();}
}
坦克大战0.5
增加功能
- 我方坦克在发射的子弹消亡后,才能发射新的子弹 =》拓展(发射多颗子弹,控制5颗子弹)
- 让敌方坦克发射的子弹消亡后,可以再次发射子弹
- 当敌方坦克击中我方坦克时,我方坦克消失,并出现爆炸效果
思路分析:
我方坦克在发射的子弹消亡后,才能发射新的子弹 =》拓展(发射多颗子弹,控制5颗子弹)
- 在按下J键,判断当前hero对象的子弹,是否已经销毁
- 如果没有销毁,就不去触发shotEnemyTank
- 如果已经销毁,才去触发shotEnemyTank
- 如果要发射多颗子弹,就使用Vector保存
- 在绘制我方子弹时,需要遍历该Vector集合
当敌方坦克击中我方坦克时,我方坦克消失,并出现爆炸效果
- 编写方法,判断敌方坦克是否击中我方坦克
代码实现:
package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Tank* @package com18.tank_game04* @time 2023/3/29 22:27*/
public class Tank {boolean isLive = true;private int x;//坦克的横坐标private int y;//坦克的纵坐标private int direct;//坦克方向 0 上 1 右 2 下 3 左private int speed = 1;public Tank(int x, int y) {this.x = x;this.y = y;}public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}//上下移动方法public void moveUp() {y -= speed;}public void moveRight() {x += speed;}public void moveDown() {y += speed;}public void moveLeft() {x -= speed;}public int getDirect() {return direct;}public void setDirect(int direct) {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;}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Hero* @package com18.tank_game04* @time 2023/3/29 22:29*/
public class Hero extends Tank {//定义一个Shot对象,表示一个射击(线程)Shot shot = null;//可以发射多颗子弹
// Vector<Shot> shots = new Vector<>();public Hero(int x, int y) {super(x, y);}//射击public void shotEnemyTank() {//控制只有5颗子弹
// if (shots.size() == 5) {
// return;
// }//创建Shot对象,根据当前Hero对象的位置和方向来创建Shotswitch (getDirect()) {case 0://向上shot = new Shot(getX() + 20, getY(), 0);break;case 1://向右shot = new Shot(getX() + 60, getY() + 20, 1);break;case 2://向下shot = new Shot(getX() + 20, getY() + 60, 2);break;case 3://向左shot = new Shot(getX(), getY() + 20, 3);break;}//把新创建的shot加入到集合shots中
// shots.add(shot);//启动Shot线程new Thread(shot).start();}
}
package com18.tank_game04;import java.util.Vector;/*** @author 甲柒* @version 1.4* @title EnemyTank* @package com18.tank_game04* @time 2023/3/29 12:06* 敌人的坦克*/
@SuppressWarnings({"all"})
public class EnemyTank extends Tank implements Runnable {//在敌人的坦克类,使用Vector保存多个ShotVector<Shot> shots = new Vector<>();boolean isLive = true;public EnemyTank(int x, int y) {super(x, y);}@Overridepublic void run() {while (true) {//这里判断如果shots size() == 0//创建一颗子弹 放入到shots 集合 并启动if (isLive && shots.size() < 1) {Shot s = null;//判断坦克的方向,创建对应的子弹switch (getDirect()) {case 0://上s = new Shot(getX() + 20, getY(), 0);break;case 1://右s = new Shot(getX() + 60, getY() + 20, 1);break;case 2://下s = new Shot(getX() + 20, getY() + 60, 2);break;case 3://左s = new Shot(getX(), getY() + 20, 3);break;}shots.add(s);//启动new Thread(s).start();}//根据坦克的方向来继续移动switch (getDirect()) {case 0://向上for (int i = 0; i < 50; i++) {if (getY() > 0) {moveUp();}//让坦克保持一个方向 走50步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 1://向右for (int i = 0; i < 50; i++) {if (getX() + 60 < 1000) {moveRight();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 2://向下for (int i = 0; i < 50; i++) {if (getY() + 60 < 750) {moveDown();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 3://向左for (int i = 0; i < 50; i++) {if (getX() > 0) {moveLeft();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;}//休眠50mstry {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}//然后随机地改变坦克方向 0-3setDirect((int) (Math.random() * 4));//注意 写并发程序 一定要考虑清楚 该线程什么时候结束if (!isLive) {break;//退出程序}}}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Shot* @package com18.tank_game04* @time 2023/3/29 21:11* 射击子弹*/
public class Shot implements Runnable {int x;//子弹x坐标int y;//子弹y坐标int direct = 0;//子弹方向int speed = 5;//子弹速度boolean isLive = true;//子弹是否存活//构造器public Shot(int x, int y, int direct) {this.x = x;this.y = y;this.direct = direct;}@Overridepublic void run() {//射击while (true) {//子弹线程休眠50mstry {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}//根据方向改变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 <= 1000 && y >= 0 && y <= 750 && isLive)) {isLive = false;break;}}}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.0* @title Bomb* @package com18.tank_game04* @time 2023/3/29 22:47* 炸弹*/
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;}}
}
package com18.tank_game04;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;/*** @author 甲柒* @version 1.4* @title MyPanel* @package com18.tank_game04* @time 2023/3/29 22:30*/
//为了监听 键盘事件,实现KeyListener
//为了让Panel 不停的重绘子弹,需要将MyPanel实现Runnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable {//定义我的坦克Hero hero = null;//定义敌人的坦克,放到VectorVector<EnemyTank> enemyTanks = new Vector<>();//定义一个Vector 用于存放炸弹//说明 当子弹击中坦克时,加入一个Bomb对象到bombsVector<Bomb> bombs = new Vector<>();int enemyTankSize = 3;//定义三张炸弹图片,用于显示爆炸效果Image image1 = null;Image image2 = null;Image image3 = null;public MyPanel() {hero = new Hero(600, 100);//初始化自己的坦克//初始化敌人的坦克for (int i = 0; i < enemyTankSize; i++) {//创建一个敌人的坦克EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);//设置方向enemyTank.setDirect(2);//启动敌人坦克进程new Thread(enemyTank).start();//给该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("/bomb_1.gif"));image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));//设置我方坦克速度hero.setSpeed(10);}//如果我方坦克可以发射多个子弹//在判断我方子弹是否击中敌方坦克时,就需要把我们的子弹集合中//所有的子弹,都取出和敌人的所有坦克,进行判断public void hitEnemyTank() {
// //遍历我们的子弹
// for (int j = 0; j < hero.shots.size(); j++) {
// Shot shot = hero.shots.get(j);
// //判断我方子弹是否击中了敌人的坦克
// if (shot != null && shot.isLive) {//当我的子弹还存活
//
// //遍历敌人的所有坦克
// for (int i = 0; i < enemyTanks.size(); i++) {
// EnemyTank enemyTank = enemyTanks.get(i);
// hiTank(shot, enemyTank);
// }
// }
// }//单颗子弹if (hero.shot != null && hero.shot.isLive) {//当我的子弹还存活//遍历敌人的所有坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTank enemyTank = enemyTanks.get(i);hiTank(hero.shot, enemyTank);}}}//编写方法,判断敌方坦克是否击中我方坦克public void hitHero() {//遍历所有敌方坦克for (int i = 0; i < enemyTanks.size(); i++) {//取出敌方坦克EnemyTank enemyTank = enemyTanks.get(i);//遍历enemyTank对象的所有子弹for (int j = 0; j < enemyTank.shots.size(); j++) {//取出子弹Shot shot = enemyTank.shots.get(j);//判断shot是否击中我方坦克if (hero.isLive && shot.isLive) {hiTank(shot, hero);}}}}//编写方法,判断我方的子弹是否击中敌方的坦克public void hiTank(Shot s, Tank 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;//当我的子弹击中敌人坦克后,将enemyTank 从 Vector 拿掉enemyTanks.remove(enemyTank);//创建Bomb对象,加入到bombs集合Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());bombs.add(bomb);}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;//创建Bomb对象,加入到bombs集合Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());bombs.add(bomb);}break;}}//编写方法,画出坦克@Overridepublic void paint(Graphics g) {super.paint(g);g.fillRect(0, 0, 1000, 750);//填充矩形,默认黑色if (hero != null && hero.isLive) {//画出坦克-封装方法drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 0);}//画出hero射击的子弹,单颗子弹if (hero.shot != null && hero.shot.isLive == true) {System.out.println("子弹被绘制~~~~~");g.draw3DRect(hero.shot.x, hero.shot.y, 2, 2, false);}//多颗子弹//将hero的子弹集合shots,遍历取出绘制
// for (int i = 0; i < hero.shots.size(); i++) {
// Shot shot = hero.shots.get(i);
// if (shot != null && shot.isLive) {
// System.out.println("子弹被绘制~~~~~");
// g.draw3DRect(shot.x, shot.y, 2, 2, false);
// } else {//如果该shot对象已经无效,就从shots集合中拿掉
// hero.shots.remove(shot);
// }
// }//如果bombs 集合中有对象,就画出for (int i = 0; i < bombs.size(); i++) {//取出炸弹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);}}//画出敌人的坦克,遍历Vectorfor (int i = 0; i < enemyTanks.size(); i++) {//从 Vector 取出坦克EnemyTank enemyTank = enemyTanks.get(i);if (enemyTank.isLive) {//当敌人坦克是存活的,才画出该坦克drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 1);//画出 enemyTank 所有子弹for (int j = 0; j < enemyTank.shots.size(); j++) {//取出子弹Shot shot = enemyTank.shots.get(j);//绘制if (shot.isLive) {//isLive == trueg.draw3DRect(shot.x, shot.y, 1, 1, false);} else {//从Vector中移除enemyTank.shots.remove(shot);}}}}}/*** @param x 坦克的左上角x坐标* @param y 坦克的左上角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://表示向右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("暂时没有处理");}}@Overridepublic void keyTyped(KeyEvent e) {}//处理ADWS键按下的情况@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键//改变坦克的方向hero.setDirect(0);//修改坦克坐标 y+=1if (hero.getY() > 0) {hero.moveUp();}} else if (e.getKeyCode() == KeyEvent.VK_D) {//D键hero.setDirect(1);if (hero.getX() + 60 < 1000) {hero.moveRight();}} else if (e.getKeyCode() == KeyEvent.VK_S) {//S键hero.setDirect(2);if (hero.getY() + 60 < 750) {hero.moveDown();}} else if (e.getKeyCode() == KeyEvent.VK_A) {//A键hero.setDirect(3);if (hero.getX() > 0) {hero.moveLeft();}}//如果用户按下J,就发射if (e.getKeyCode() == KeyEvent.VK_J) {System.out.println("用户按下了J开始射击~~~~");//判断hero的子弹是否销毁,发射一颗子弹if (hero.shot == null || !hero.shot.isLive) {hero.shotEnemyTank();}//发射多颗子弹
// hero.shotEnemyTank();}//重绘this.repaint();}@Overridepublic void keyReleased(KeyEvent e) {}@Overridepublic void run() {//每隔100ms 重绘区域 刷新绘图区域while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}//判断敌我方坦克是否击中敌方坦克hitEnemyTank();//判断敌方坦克是否击中了我方坦克hitHero();this.repaint();}}
}
package com18.tank_game04;import javax.swing.*;/*** @author 甲柒* @version 1.4* @title TankGame04* @package com18.tank_game04* @time 2023/3/29 22:33*/
public class TankGame04 extends JFrame {//定义MyPanelMyPanel mp = null;public TankGame04() {mp = new MyPanel();//将mp 放入到Thread,并启动Thread thread = new Thread(mp);thread.start();this.add(mp);//把面板(就是游戏的绘图区域)this.setSize(1200, 950);this.addKeyListener(mp);//让JFrame监听mp的键盘事件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);}public static void main(String[] args) {TankGame04 tankGame04 = new TankGame04();}
}