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

如何在Java Swing中实现MouseMotionListener让对象列表跟随鼠标?

解决蠕虫身体跟随鼠标时汇聚的问题

嘿,我一眼就看出你代码里的问题了——你的tracker方法更新身体段位置的逻辑完全搞反了,这才导致所有身体段突然挤到同一个点上。咱们一步步来梳理修复方案:

问题根源分析

先看你当前的追踪逻辑:

public void tracker(ArrayList <Worm> TracktheWorm) {
    for(int i = 0; i < TracktheWorm.size()-1 ; i++) {
        TracktheWorm.get(i+1).xWorm = TracktheWorm.get(i).xWorm;
        TracktheWorm.get(i+1).yWorm = TracktheWorm.get(i).yWorm;
    }
    repaint();
}

你是从前往后更新位置:把头部的位置直接赋值给第二个身体段,然后第二个的新位置又被传给第三个……循环下来,所有身体段都会在一次迭代里变成头部的位置,自然就全部汇聚了。

正确的身体跟随逻辑

要实现类似火车联动的效果,核心是让每个身体段跟随前一个身体段的上一帧位置,而不是当前帧的新位置。这里有两种靠谱的实现方式:

方式一:给Worm类添加历史位置记录

给蠕虫类新增保存上一帧位置的字段,让身体段始终跟着前一个的“足迹”移动:

public class Worm {
    Random rd = new Random();
    int xWorm;
    int yWorm;
    int prevX; // 新增:上一帧的X坐标
    int prevY; // 新增:上一帧的Y坐标
    int Speed = 100;
    int size = 10;

    // 构造函数初始化历史位置
    public Worm() {
        xWorm = rd.nextInt(250);
        yWorm = rd.nextInt(250);
        prevX = xWorm;
        prevY = yWorm;
    }

    public Worm(int xNew, int yNew){
        xWorm = xNew;
        yWorm = yNew;
        prevX = xNew;
        prevY = yNew;
    }

    // 修改move方法,先保存当前位置再移动
    public void move (int dx, int dy) {
        prevX = xWorm;
        prevY = yWorm;
        xWorm += dx;
        yWorm += dy;
    }

    // 修改setPosition方法,同步保存历史位置
    public void setPosition(int x,int y) {
        prevX = xWorm;
        prevY = yWorm;
        this.xWorm = x;
        this.yWorm = y;
    }

    // 新增历史位置的getter
    public int getPrevX() { return prevX; }
    public int getPrevY() { return prevY; }

    // 其余原有方法(getter、paint、iscollision等)保持不变
}

然后修改tracker方法,让每个身体段移动到前一个的历史位置:

public void tracker(ArrayList<Worm> TracktheWorm) {
    // 从第二个身体段开始,依次跟随前一个的上一帧位置
    for (int i = 1; i < TracktheWorm.size(); i++) {
        Worm prevWorm = TracktheWorm.get(i-1);
        TracktheWorm.get(i).setPosition(prevWorm.getPrevX(), prevWorm.getPrevY());
    }
    repaint();
}

方式二:临时保存所有身体段的当前位置

如果不想修改Worm类,可以在追踪时先保存所有身体段的当前位置,再批量更新:

public void tracker(ArrayList<Worm> TracktheWorm) {
    // 先保存所有身体段的当前位置
    List<Point> lastPositions = new ArrayList<>();
    for (Worm worm : TracktheWorm) {
        lastPositions.add(new Point(worm.getxWorm(), worm.getyWorm()));
    }

    // 从第二个身体段开始,设置为前一个的历史位置
    for (int i = 1; i < TracktheWorm.size(); i++) {
        Point prevPos = lastPositions.get(i-1);
        TracktheWorm.get(i).setPosition(prevPos.x, prevPos.y);
    }
    repaint();
}

实现MouseMotionListener让头部跟随鼠标

最后,在你的Panel类里实现鼠标监听,让蠕虫头部跟随鼠标移动:

public class WormPanel extends JPanel implements MouseMotionListener {
    private ArrayList<Worm> wormBody;

    public WormPanel() {
        wormBody = new ArrayList<>();
        wormBody.add(new Worm()); // 初始化头部
        createlongworm(); // 创建身体段
        addMouseMotionListener(this);
        setPreferredSize(new Dimension(500, 500));
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        Worm head = wormBody.get(0);
        // 平滑移动到鼠标位置(除以5控制移动速度,数值越小越平滑)
        int dx = (e.getX() - head.getxWorm()) / 5;
        int dy = (e.getY() - head.getyWorm()) / 5;
        head.move(dx, dy);
        // 更新身体跟随
        tracker(wormBody);
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        mouseMoved(e); // 拖拽时同样触发跟随逻辑
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (Worm worm : wormBody) {
            worm.paint(g);
        }
    }

    // 你的createlongworm方法保持不变
    public void createlongworm() {
        for(int i = 0; i < 100 ; i++) {
            wormBody.add(new Worm(wormBody.get(i).xWorm+10,wormBody.get(i).yWorm));
        }
    }
}

总结

核心就是一句话:不要让所有身体段同步复制头部的新位置,要让它们依次跟随前一个的旧位置,这样才能形成连贯的“火车联动”效果。

内容的提问来源于stack exchange,提问作者Türker Berke Yıldırım

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:49:45