You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 08:24:03