Java笔记034-坦克大战【2】
目录
坦克大战【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();}
}
相关文章:

Java笔记034-坦克大战【2】
目录 坦克大战【2】 线程-应用到坦克大战 坦克大战0.3 思路分析: 代码实现: 坦克大战0.4 增加功能 特别说明 思路分析: 代码实现: 坦克大战0.5 增加功能 思路分析: 代码实现: 坦克大战【2】 …...
【算法】【数组与矩阵模块】桶排序思想解决无序数组排序后相邻数间的最大差值
目录前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本思考感悟写在最后前言 当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识! 问题介绍 …...

C语言—函数
函数库函数自定义函数函数的参数函数的调用函数的嵌套调用和链式访问函数的声明和定义函数递归递归与迭代函数递归的经典题目维基百科(台湾方面维护的,翻译形式跟大陆有所差异)中对函数的定义:子程序在计算机科学中,子…...
Autosar模式管理实战系列03-基于Davinci工具的WDGM配置
本文框架 前言1.WdgMConfigSet 配置2. 新建监控实体(SE)2.1 新建检测点(Checkpoint)2.2 设置 WdgMInternalTransitions3. WdgMLocalStatusParams配置4. WdgMAliveSupervision配置5. 代码插入指导前言 前面我们介绍了WdgM(看门狗管理)是一个 AutoSAR 的基础模块,负责管理看门…...

AutoML-sklearn and torch
一、auto-sklearn 1.1 环境依赖 额外安装swig 第三方库 linux 支持, mac,windows不支持 1.2 示例代码 time_left_for_this_task 设定任务最大时间 per_run_time_limit 每个子任务最大训练时间 include 可以限制任务训练的模型 import autosklearn.classific…...

《扬帆优配》算力概念股大爆发,主力资金大扫货
3月22日,9股封单金额超亿元,工业富联、鸿博股份、鹏鼎控股分别为3.01亿元、2.78亿元、2.37亿元。 今日三大指数团体收涨,收盘共34股涨停,首要集中于数字经济方向,其间云核算、CPO大迸发。除去5只ST股,算计2…...

机械臂+底盘三维模型从solidworks到moveit配置功能包
文章目录 导出底盘STEP加载机械臂模型组合机械臂和底盘三维模型导出URDF在moveit中进行配置新建工作目录设置ROS工作空间的环境变量进入moveit setup加载URDF文件self-CollisionsPlanning groupsRobot posesControllersSimulationAuthor information生成配置包在rviz中进行可视…...

高并发系统设计:缓存、降级、限流、(熔断)
高并发系统设计:缓存、降级、限流、(熔断) 在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。 非核心服务可以采用降级、熔断,核心服务采用缓存和限流(隔离流量可以最大限度的保障业务无损)。 缓存 缓…...

《辉煌优配》放量大涨,A股成交额重回万亿!PCB板块继续领跑
多只绩优PCB概念股超跌。 今日A股放量反弹,成交额从头站上万亿关口。芯片板块掀涨停潮,景嘉微、芯原股份20cm涨停,紫光国微、兆易创新、跃岭股份等封板;AI算力、存储器、光模块、云核算等板块全线拉升,板块内个股再度批…...

Vue封装的过度与动画
动画效果 先把样式封装好,然后设置一个动画 不需要vue也能实现的动画的效果,我们只需要判断一下,然后动态的添加和删除类名即可 那能不能不自己写动态,就靠vue 首先我们要靠<transition>标签把需要动画的包裹起来 vue中…...

流量监控-ntopng
目录介绍安装使用介绍 ntopng是原始ntop的下一代版本,ntop是监视网络使用情况的网络流量探测器。ntopng基于libpcap,并且以可移植的方式编写,以便实际上可以在每个Unix平台,MacOSX和Windows上运行。 ntopng(是的&…...
C++ 21 set容器
目录 一、set容器 1.1 简介 1.2 构造和赋值 1.3 大小和交换 1.4 插入和删除 1.5 查找和统计 1.6 set和multiset区别 1.7 内置类型指定排序规则 1.8 自定义数据类型指定排序规则 一、set容器 1.1 简介 ① set容器中所有元素在插入时自动被排序。 ② set容器和multise…...

什么是JWT
JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。 传统的session认证 http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一…...

Gradle7.4安装
前置:本文基于IntelliJ IDEA 2022.2.1 、jdk1.8进行安装 目录 1.挑选Gradle版本 2.系统变量设置 1.挑选Gradle版本 gradle兼容性差, 1.跟idea会有版本问题。 2.跟springboot也有兼容问题Spring Boot Gradle Plugin Reference Guide 首先查询版本&…...

【华为OD机试 2023最新 】 箱子之字形摆放(C++ 100%)
文章目录 题目描述输入描述输出描述备注用例题目解析C++题目描述 有一批箱子(形式为字符串,设为str), 要求将这批箱子按从上到下以之字形的顺序摆放在宽度为 n 的空地,请输出箱子的摆放位置。 例如:箱子ABCDEFG,空地宽度为3,摆放结果如图: 则输出结果为: AFG BE CD …...

Matplotlib库入门
Matplotlib库的介绍 什么是Matplotlib库? Matplotlib是一个Python的数据可视化库,用于绘制各种类型的图表,包括线图、散点图、条形图、等高线图、3D图等等。它是一个非常强大和灵活的库,被广泛用于数据科学、机器学习、工程学、…...

学生党用什么蓝牙耳机比较好?300内高性价比蓝牙耳机排行
随着蓝牙技术的发展,蓝牙耳机越来越普及,不同价位、不同性能的蓝牙耳机数不胜数。那么,学生党用什么蓝牙耳机比较好?下面,我来给大家推荐几款三百内高性价比蓝牙耳机,一起来看看吧。 一、南卡小音舱蓝牙耳…...
Lambda 表达式与函数式接口
函数式接口 如果一个接口,只有一个抽象方法,该接口即为函数式接口。函数式接口,即可使用 Lambda 表达式。 如下面的接口 public interface Translate {void translate();}目前该接口的抽象方法为无参数无返回值 Lambda 表达式 无参无返回值…...

后端代码规范
1、报文入参尽量避免使用实体类(如果用实体类接受参数,一定要写好注解,具体用到了实体类的哪一个属性) /*** * Description: 新增玉米观测记录主表信息* param param params* param return 参数* return Result 返回类型* author…...

web自动化测试:Selenium+Python基础方法封装(建议收藏)
01、目的 web自动化测试作为软件自动化测试领域中绕不过去的一个“香饽饽”,通常都会作为广大测试从业者的首选学习对象,相较于C/S架构的自动化来说,B/S有着其无法忽视的诸多优势,从行业发展趋、研发模式特点、测试工具支持&…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...