如何为JComponent添加视觉检测能力,基于2D射线投射实现移动避障
实现思路
核心是在每次车辆移动前,从车辆当前朝向发射指定长度的射线,检测射线范围内是否存在其他车辆的碰撞边界,存在则触发停车逻辑。全程用Java原生awt几何类实现,无需引入额外库。
代码修改
1. Car.java 修改
首先新增辅助方法和射线检测逻辑,修改移动前的校验规则:
import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Random; public class Car extends JComponent implements ActionListener { private BufferedImage car; private final GeoPoint startPoint = GeoSides.pickGeoPoint(); public int x = 0; public int y = 0; private int xVelocity; private int yVelocity; private final int imageX = 160; private final int imageY = 120; private double turnAngle = 0; Timer timer; // Initializing Car() object Car() { this.setPreferredSize(new Dimension(170, 130)); this.timer = new Timer(1, this); // Setting up the timer for update method this.timer.start(); try { Random randomize = new Random(); car = ImageIO.read(new File("src/assets/" + (randomize.nextInt(10) + 1) + ".png")); // Getting a random image of car } catch (IOException e) { e.printStackTrace(); } // Setting start position } // 新增:获取当前车辆的碰撞边界(轴对齐包围盒,如需支持旋转可换成Area类实现旋转包围盒) public Rectangle getBounds() { return new Rectangle(x, y, imageX, imageY); } // 新增:射线检测前方是否有障碍物 private boolean hasObstacleInFront() { // 检测距离,可根据需求调整,这里设为2倍车身长度 int detectDistance = imageX * 2; if (xVelocity == 0 && yVelocity == 0) { return false; // 静止状态无需检测 } // 计算车辆中心点作为射线起点 int centerX = x + imageX / 2; int centerY = y + imageY / 2; // 计算速度方向的单位向量 double speed = Math.sqrt(xVelocity * xVelocity + yVelocity * yVelocity); double dirX = xVelocity / speed; double dirY = yVelocity / speed; // 计算射线终点 int endX = (int) (centerX + dirX * detectDistance); int endY = (int) (centerY + dirY * detectDistance); Line2D ray = new Line2D.Float(centerX, centerY, endX, endY); // 遍历父容器中所有其他车辆,检测射线是否相交 Container parent = getParent(); if (parent == null) return false; for (Component comp : parent.getComponents()) { if (comp == this || !(comp instanceof Car)) { continue; } Car otherCar = (Car) comp; if (ray.intersects(otherCar.getBounds())) { return true; } } return false; } // This method should be called when another car is in the way public void stopCar() { xVelocity = 0; yVelocity = 0; } // Method scales the image and rotate it if needed private void drawCar(Graphics2D render2D, double rotationAngle) { //Image scaling and rotating } // Inherited method from JComponent @Override public void paintComponent(Graphics g) { super.paintComponent(g); this.setLocation(new Point(x, y)); Graphics2D render2D = (Graphics2D) g; drawCar(render2D, turnAngle); } // Frame update of car's x, y speed and turn angle @Override public void actionPerformed(ActionEvent e) { switch (startPoint){ // Setting destination points } // 新增:移动前先检测前方障碍物 if (hasObstacleInFront()) { stopCar(); } else { x += xVelocity; y += yVelocity; } this.repaint(); } }
2. Map.java 优化
原有代码存在两个可优化点:
- 遍历移除组件时用正序会导致索引错乱漏删,改为倒序遍历
updateUI()是切换Swing观感的方法,无需每次调用,替换为repaint()即可
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Map extends JPanel implements ActionListener { final int windowWidth = 1300; final int windowHeight = 750; Map() { this.setPreferredSize(new Dimension(windowWidth, windowHeight)); Timer timer = new Timer(3000, this); timer.start(); } @Override public void paint(Graphics g) { super.paint(g); // painting map } // Update method of the frame @Override public void actionPerformed(ActionEvent e) { Car car = new Car(); this.add(car); // 倒序遍历删除超出边界的车辆 for(int i = this.getComponents().length - 1; i >= 0; i--) { Car iCar = (Car) this.getComponents()[i]; if(iCar.x >= 1400 || iCar.x <= -300 || iCar.y <= -300 || iCar.y >= 950) { this.remove(i); } } System.out.println(this.getComponentCount()); this.repaint(); } }
可选优化
如果需要支持车辆旋转后的精确碰撞检测,可以把getBounds()返回的矩形通过AffineTransform旋转生成Area对象,再调用ray.intersects(area.getBounds2D())即可实现旋转包围盒的射线检测。
内容的提问来源于stack exchange,提问作者Cojocaru Alin
相关产品推荐
相关产品推荐

